Agentic workloads are not single prompts. They are loops of reasoning, tool calls, and context accumulation that can quickly inflate costs on token-based providers. In this tutorial, I will build a research agent that answers complex questions by iterating through search and calculation steps, and I will run it on Oxlo.ai because flat per-request pricing keeps these multi-turn loops predictable.
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: Scaffold the client and tools
I start by initializing the OpenAI-compatible client pointing at Oxlo.ai. I run a quick connectivity test with the exact calling pattern, then define two mock tools so the tutorial is self-contained.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
# Verify the connection using the standard Oxlo.ai pattern
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Say hello"},
],
)
print("Oxlo.ai response:", response.choices[0].message.content)
def web_search(query: str):
"""Mock search. Replace with a real search API in production."""
data = {
"tokyo population": "Tokyo's population is approximately 14 million (2024).",
"gdp per capita japan": "Japan's GDP per capita is roughly $34,000 USD (2024 estimate)."
}
for key, val in data.items():
if key in query.lower():
return {"result": val}
return {"result": f"No direct result for: {query}"}
def calculate(expression: str):
allowed = set("0123456789+-*/.() ")
if all(c in allowed for c in expression):
try:
return {"result": eval(expression, {"__builtins__": {}}, {})}
except Exception as e:
return {"error": str(e)}
return {"error": "Invalid expression"}
TOOLS = [
{
"type": "function",
"function": {
"name": "web_search",
"description": "Search the web for factual information.",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "Evaluate a mathematical expression safely.",
"parameters": {
"type": "object",
"properties": {"expression": {"type": "string"}},
"required": ["expression"]
}
}
}
]
Step 2: Define the agent's system prompt
The system prompt is the agent's contract. It tells the model how to reason, when to call tools, and when to stop.
SYSTEM_PROMPT = """You are a research agent. Answer the user's question by reasoning step by step.
You have access to two tools:
- web_search: to find factual information.
- calculate: to perform math.
Follow this process:
1. Think about what you need to know.
2. Call one tool at a time.
3. After you have enough information, provide a concise final answer.
Do not guess. Use tools when facts or math are required."""
Step 3: Build the reasoning loop
This is the core of the agentic workload. I send the conversation to Oxlo.ai, let the model decide whether to call a tool, execute the tool locally, and feed the result back into the next request. The loop continues until the model returns a plain text answer.
def run_agent(question: str, max_steps: int = 5):
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": question}
]
for _ in range(max_steps):
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
tools=TOOLS,
tool_choice="auto"
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
return message.content
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
if name == "web_search":
observation = web_search(**args)
elif name == "calculate":
observation = calculate(**args)
else:
observation = {"error": "Unknown tool"}
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"name": name,
"content": json.dumps(observation)
})
return "Reached step limit without final answer."
Step 4: Manage growing context
Agentic runs accumulate messages quickly. On token-based providers, every extra line in the context window adds cost. Oxlo.ai charges per request, not per token, so the price stays flat even as the prompt grows. Still, I trim stale tool turns to keep latency low and the model focused.
def compact_history(messages, keep_last=6):
"""Preserve the system prompt and first user message, then keep only the most recent turns."""
if len(messages) <= keep_last + 2:
return messages
header = [messages[0], messages[1]]
tail = messages[-keep_last:]
return header + tail
def run_agent(question: str, max_steps: int = 5):
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": question}
]
for _ in range(max_steps):
messages = compact_history(messages)
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
tools=TOOLS,
tool_choice="auto"
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
return message.content
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
if name == "web_search":
observation = web_search(**args)
elif name == "calculate":
observation = calculate(**args)
else:
observation = {"error": "Unknown tool"}
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"name": name,
"content": json.dumps(observation)
})
return "Reached step limit without final answer."
Run it
Now I can ask a multi-step question that forces the agent to search twice and then calculate.
if __name__ == "__main__":
question = "What is the GDP per capita of Japan multiplied by the population of Tokyo?"
print(run_agent(question))
Example output:
Japan's GDP per capita is roughly $34,000 USD, and Tokyo's population is approximately 14 million.
34000 * 14000000 = 476000000000
The result is $476 billion.
Wrap-up
You now have a working agentic loop on Oxlo.ai. To productionize it, swap the mock web_search for a real search API, and add retry logic around the Oxlo.ai client calls. If you are running dozens of agent steps per task, Oxlo.ai's request-based pricing removes the penalty for long context windows that token-based providers charge.
Top comments (0)