I built a small multilingual research agent that plans its own searches and writes markdown reports, all backed by Qwen 3 32B on Oxlo.ai. Qwen 3 32B handles mixed-language reasoning and agentic workflows, and Oxlo.ai's flat per-request pricing keeps long agent loops predictable. In this tutorial we will wire up a self-contained version you can run in under ten minutes.
What you'll need
- Python 3.10 or newer
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai
Step 1: Configure the client and verify the model
First we point the OpenAI SDK at Oxlo.ai and confirm Qwen 3 32B responds. I like to start with a multilingual hello to sanity-check the endpoint.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Greet me in English, Chinese, and Spanish."},
],
)
print(response.choices[0].message.content)
Step 2: Define the agent's system prompt
This prompt forces the model to plan searches first, then synthesize a report after seeing evidence. Keeping the instructions strict reduces drift across turns.
SYSTEM_PROMPT = """You are a multilingual research agent running on Oxlo.ai.
When given a research question, follow this exact workflow:
1. Output a JSON object with key "search_queries" containing a list of 2 to 4 search strings needed to answer the question.
2. After receiving search results, output a JSON object with key "report" containing a markdown string that answers the question and cites the sources.
Rules:
- Think step by step.
- If the question contains multiple languages, preserve them in your reasoning.
- Do not answer until you have seen the search results."""
Step 3: Generate a search plan
We ask Qwen 3 32B to emit a structured plan before we touch any data. JSON mode keeps the output machine-readable.
import json
def plan_searches(question):
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Research question: {question}\n\nReturn only a JSON object with a \"search_queries\" array."},
],
response_format={"type": "json_object"},
)
return json.loads(response.choices[0].message.content)
question = "How does Qwen 3 32B handle multilingual tasks, and why is flat per-request pricing useful for agent workloads?"
plan = plan_searches(question)
print(json.dumps(plan, indent=2))
Step 4: Mock the search backend
I do not want to require a real search API key here, so I use a small hard-coded lookup. In production you would swap this for SerpAPI, DuckDuckGo, or an internal knowledge base.
MOCK_DB = {
"qwen 3 32b multilingual tasks": [
"Qwen 3 32B is built for multilingual reasoning and agentic workflows across dozens of languages.",
"It handles long-context comprehension and mixed-language inputs without extra fine-tuning."
],
"flat per-request pricing agent workloads": [
"Oxlo.ai charges one flat rate per API request regardless of prompt length.",
"That makes long-context agent loops predictable and often far cheaper than token-based billing. See https://oxlo.ai/pricing for details."
],
}
def run_search(queries):
out = []
for q in queries:
key = q.lower().strip()
hits = MOCK_DB.get(key, [f"No cached results for \"{q}\"."])
out.append({"query": q, "snippets": hits})
return out
search_results = run_search(plan["search_queries"])
print(json.dumps(search_results, indent=2))
Step 5: Synthesize the final report
Now we feed the results back into the conversation and ask for the final markdown report. The context grows here, which is where Oxlo.ai's flat pricing helps. You can add retrieved documents without watching the bill scale by token count.
def synthesize_report(question, queries, results):
context = json.dumps(results, ensure_ascii=False)
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Research question: {question}"},
{"role": "assistant", "content": json.dumps({"search_queries": queries})},
{"role": "user", "content": f"Search results: {context}\n\nReturn only a JSON object with a \"report\" key containing markdown."},
],
response_format={"type": "json_object"},
)
return json.loads(response.choices[0].message.content)["report"]
report = synthesize_report(question, plan["search_queries"], search_results)
print(report)
Run it
Here is the complete script. Save it as research_agent.py, replace YOUR_OXLO_API_KEY, and run python research_agent.py.
from openai import OpenAI
import json
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are a multilingual research agent running on Oxlo.ai.
When given a research question, follow this exact workflow:
1. Output a JSON object with key "search_queries" containing a list of 2 to 4 search strings needed to answer the question.
2. After receiving search results, output a JSON object with key "report" containing a markdown string that answers the question and cites the sources.
Rules:
- Think step by step.
- If the question contains multiple languages, preserve them in your reasoning.
- Do not answer until you have seen the search results."""
def plan_searches(question):
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Research question: {question}\n\nReturn only a JSON object with a \"search_queries\" array."},
],
response_format={"type": "json_object"},
)
return json.loads(response.choices[0].message.content)
MOCK_DB = {
"qwen 3 32b multilingual tasks": [
"Qwen 3 32B is built for multilingual reasoning and agentic workflows across dozens of languages.",
"It handles long-context comprehension and mixed-language inputs without extra fine-tuning."
],
"flat per-request pricing agent workloads": [
"Oxlo.ai charges one flat rate per API request regardless of prompt length.",
"That makes long-context agent loops predictable and often far cheaper than token-based billing. See https://oxlo.ai/pricing for details."
],
}
def run_search(queries):
out = []
for q in queries:
key = q.lower().strip()
hits = MOCK_DB.get(key, [f"No cached results for \"{q}\"."])
out.append({"query": q, "snippets": hits})
return out
def synthesize_report(question, queries, results):
context = json.dumps(results, ensure_ascii=False)
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Research question: {question}"},
{"role": "assistant", "content": json.dumps({"search_queries": queries})},
{"role": "user", "content": f"Search results: {context}\n\nReturn only a JSON object with a \"report\" key containing markdown."},
],
response_format={"type": "json_object"},
)
return json.loads(response.choices[0].message.content)["report"]
if __name__ == "__main__":
question = "How does Qwen 3 32B handle multilingual tasks, and why is flat per-request pricing useful for agent workloads?"
plan = plan_searches(question)
search_results = run_search(plan["search_queries"])
report = synthesize_report(question, plan["search_queries"], search_results)
print(report)
Example output:
{
"search_queries": [
"qwen 3 32b multilingual tasks",
"flat per-request pricing agent workloads"
]
}
[
{
"query": "qwen 3 32b multilingual tasks",
"snippets": [
"Qwen 3 32B is built for multilingual reasoning and agentic workflows across dozens of languages.",
"It handles long-context comprehension and mixed-language inputs without extra fine-tuning."
]
},
{
"query": "flat per-request pricing agent workloads",
"snippets": [
"Oxlo.ai charges one flat rate per API request regardless of prompt length.",
"That makes long-context agent loops predictable and often far cheaper than token-based billing. See https://oxlo.ai/pricing for details."
]
}
]
## Report
**Qwen 3 32B Multilingual Capabilities**
Qwen 3 32B is built for multilingual reasoning and agentic workflows across dozens of languages. It handles long-context comprehension and mixed-language inputs without extra fine-tuning.
**Cost Implications for Agents**
Oxlo.ai charges one flat rate per API request regardless of prompt length. That makes long-context agent loops predictable and often far cheaper than token-based billing. See https://oxlo.ai/pricing for details.
Wrap-up
This agent is intentionally minimal so you can extend it. Two directions I would try next: swap the MOCK_DB for a real retrieval pipeline using embeddings on Oxlo.ai, or add a critique loop where Qwen 3 32B reviews its own draft and issues follow-up searches before finalizing. Both patterns stay cheap on Oxlo.ai because the bill is per request, not per token.
Top comments (0)