We are going to build a stateful customer support agent that handles order lookups, return requests, and general store questions. It keeps conversation history, uses tools to fetch data, and stays on topic. I will walk through the exact code I would ship to production, using Oxlo.ai's flat per-request pricing so context length never inflates the bill.
What you'll need
- Python 3.10 or higher
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai
Exact request-based pricing is available at https://oxlo.ai/pricing. Unlike token-based providers, long system prompts and conversation history do not increase the cost per turn on Oxlo.ai.
The system prompt
I treat the system prompt as a contract. It defines the agent's scope, tone, and the one tool it is allowed to call.
SYSTEM_PROMPT = """You are a support agent for TechMart, an online electronics store.
You help customers with order status, returns, and store policies.
You have access to a lookup_order tool that requires a customer email address.
Before retrieving any order details, confirm the user's email.
If the user asks about topics outside orders, returns, or store policies, politely refuse and offer to escalate to a human.
Keep responses concise and friendly."""
Step 1: Initialize the client
We import the OpenAI SDK and point it at Oxlo.ai. I use Llama 3.3 70B because it offers strong general performance and reliable function calling on Oxlo.ai.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ["OXLO_API_KEY"]
)
def ask(user_message: str) -> str:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
temperature=0.3,
)
return response.choices[0].message.content
if __name__ == "__main__":
print(ask("What is your return policy?"))
Step 2: Add message history
Conversational AI needs memory. We wrap the client in a class that accumulates messages so context persists across turns.
class SupportAgent:
def __init__(self):
self.messages = [
{"role": "system", "content": SYSTEM_PROMPT}
]
def chat(self, user_input: str) -> str:
self.messages.append({"role": "user", "content": user_input})
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=self.messages,
temperature=0.3,
)
reply = response.choices[0].message.content
self.messages.append({"role": "assistant", "content": reply})
return reply
if __name__ == "__main__":
agent = SupportAgent()
print(agent.chat("Hi, I need help with an order."))
print(agent.chat("It should be under alice@example.com."))
Step 3: Implement tool calling
We give the agent access to a mock order database via the function calling schema. When the model emits a tool call, we execute it locally and feed the result back into the conversation.
import json
ORDERS_DB = {
"alice@example.com": {
"order_id": "TM-8842",
"items": ["Wireless Headphones", "USB-C Cable"],
"status": "shipped",
"eta": "2025-01-15"
}
}
tools = [
{
"type": "function",
"function": {
"name": "lookup_order",
"description": "Retrieve order details by customer email",
"parameters": {
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "Customer email address"
}
},
"required": ["email"]
}
}
}
]
def lookup_order(email: str) -> str:
record = ORDERS_DB.get(email.lower(), {})
return json.dumps(record) if record else json.dumps({"error": "No order found."})
class SupportAgent:
def __init__(self):
self.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
def chat(self, user_input: str) -> str:
self.messages.append({"role": "user", "content": user_input})
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=self.messages,
tools=tools,
tool_choice="auto",
temperature=0.3,
)
msg = response.choices[0].message
if msg.tool_calls:
self.messages.append({
"role": "assistant",
"content": msg.content or "",
"tool_calls": [tc.model_dump() for tc in msg.tool_calls]
})
for tc in msg.tool_calls:
if tc.function.name == "lookup_order":
args = json.loads(tc.function.arguments)
result = lookup_order(args["email"])
self.messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": result
})
second = client.chat.completions.create(
model="llama-3.3-70b",
messages=self.messages,
temperature=0.3,
)
reply = second.choices[0].message.content
self.messages.append({"role": "assistant", "content": reply})
return reply
self.messages.append({"role": "assistant", "content": msg.content})
return msg.content
Step 4: Add guardrails and a REPL
We add a lightweight check for off-topic queries and wrap everything in an interactive loop so we can test the agent in real time.
BLOCKED_TOPICS = ["medical advice", "legal advice", "politics", "competitor"]
def is_off_topic(text: str) -> bool:
lowered = text.lower()
return any(topic in lowered for topic in BLOCKED_TOPICS)
class SupportAgent:
def __init__(self):
self.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
def chat(self, user_input: str) -> str:
if is_off_topic(user_input):
return "I am not able to help with that. Let me transfer you to a human agent."
self.messages.append({"role": "user", "content": user_input})
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=self.messages,
tools=tools,
tool_choice="auto",
temperature=0.3,
)
msg = response.choices[0].message
if msg.tool_calls:
self.messages.append({
"role": "assistant",
"content": msg.content or "",
"tool_calls": [tc.model_dump() for tc in msg.tool_calls]
})
for tc in msg.tool_calls:
if tc.function.name == "lookup_order":
args = json.loads(tc.function.arguments)
result = lookup_order(args["email"])
self.messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": result
})
second = client.chat.completions.create(
model="llama-3.3-70b",
messages=self.messages,
temperature=0.3,
)
reply = second.choices[0].message.content
self.messages.append({"role": "assistant", "content": reply})
return reply
self.messages.append({"role": "assistant", "content": msg.content})
return msg.content
if __name__ == "__main__":
agent = SupportAgent()
print("TechMart Support Agent. Type 'exit' to quit.")
while True:
user = input("> ")
if user.lower() == "exit":
break
print(agent.chat(user))
Run it
Save the full script as support_agent.py, export your key, and run it.
export OXLO_API_KEY="your-key-here"
python support_agent.py
Example session:
TechMart Support Agent. Type 'exit' to quit. > What is your return policy? You can return items within 30 days of delivery for a full refund. Just make sure the product is in original condition. Would you like help with a specific order? > Can you look up my order? It is alice@example.com. I found your order TM-8842. It contains Wireless Headphones and a USB-C Cable, and the status is shipped with an estimated arrival of 2025-01-15. > Can you give me medical advice? I am not able to help with that. Let me transfer you to a human agent.
Next steps
Swap the mock ORDERS_DB for a real database connection or internal API. You can also enable streaming by adding stream=True to the chat.completions.create call and iterating over chunks to reduce perceived latency. If you need deeper reasoning for complex escalation logic, try Kimi K2.6 on Oxlo.ai under the same request-based pricing.
Top comments (0)