We are going to build a command-line virtual assistant that remembers conversation context and uses tools to answer questions about time, math, and weather. It runs on Oxlo.ai's request-based API, so long system prompts and multi-turn reasoning do not inflate your bill. By the end you will have a single Python file you can extend with real APIs and additional models.
What you'll need
- Python 3.10 or newer.
- The OpenAI SDK:
pip install openai. - An Oxlo.ai API key from https://portal.oxlo.ai.
Step 1: Connect to Oxlo.ai
First we instantiate the OpenAI-compatible client pointing at Oxlo.ai. I use llama-3.3-70b here because it supports tool calling and has no cold starts.
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="llama-3.3-70b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello"},
],
)
print(response.choices[0].message.content)
Step 2: Write the system prompt
A strong system prompt keeps the assistant focused and tells it how to behave. Store it in a constant so you can iterate quickly.
SYSTEM_PROMPT = """You are Oxi, a helpful virtual assistant.
You can answer general knowledge questions, but for time, math, or weather you must use the provided tools.
Keep responses concise and friendly.
If a user asks about the weather, ask which city if they did not specify one.
"""
Step 3: Define the tools
Oxlo.ai supports OpenAI-compatible function definitions. We define three tools the model can request: get_current_time, calculate, and get_weather.
import json
tools = [
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "Returns the current local date and time.",
"parameters": {"type": "object", "properties": {}, "required": []},
},
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "Evaluates a mathematical expression safely.",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "A math expression like '144 * 3'.",
}
},
"required": ["expression"],
},
},
},
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Returns simulated current weather for a given city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. Tokyo."}
},
"required": ["city"],
},
},
},
]
Step 4: Build the agent class with memory
An assistant is useless if it forgets the user's name in the next message. We keep a message list and append each turn so context persists across requests.
class VirtualAssistant:
def __init__(self, api_key):
self.client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=api_key)
self.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
def ask(self, user_text):
self.messages.append({"role": "user", "content": user_text})
response = self.client.chat.completions.create(
model="llama-3.3-70b",
messages=self.messages,
tools=tools,
tool_choice="auto",
)
return response.choices[0].message
Step 5: Handle tool calls and loop back
When the model requests a tool, we execute the matching Python function locally, append the result as a tool message, and call Oxlo.ai again so the model can generate the final user-facing answer.
import datetime
import random
def get_current_time():
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def calculate(expression):
try:
allowed = {"__builtins__": {}}
return str(eval(expression, allowed, {}))
except Exception as e:
return f"Error: {e}"
def get_weather(city):
conditions = ["sunny", "cloudy", "rainy", "windy"]
temp = random.randint(15, 30)
return f"{city} is currently {random.choice(conditions)} with a temperature of {temp}°C."
TOOL_MAP = {
"get_current_time": get_current_time,
"calculate": calculate,
"get_weather": get_weather,
}
class VirtualAssistant:
def __init__(self, api_key):
self.client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=api_key)
self.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
def ask(self, user_text):
self.messages.append({"role": "user", "content": user_text})
response = self.client.chat.completions.create(
model="llama-3.3-70b",
messages=self.messages,
tools=tools,
tool_choice="auto",
)
msg = response.choices[0].message
if msg.tool_calls:
tool_calls = []
for tc in msg.tool_calls:
tool_calls.append({
"id": tc.id,
"type": tc.type,
"function": {
"name": tc.function.name,
"arguments": tc.function.arguments,
},
})
self.messages.append({
"role": msg.role,
"content": msg.content or "",
"tool_calls": tool_calls,
})
for tc in msg.tool_calls:
fn = tc.function.name
args = json.loads(tc.function.arguments)
result = TOOL_MAP[fn](**args)
self.messages.append({
"role": "tool",
"tool_call_id": tc.id,
"name": fn,
"content": str(result),
})
response = self.client.chat.completions.create(
model="llama-3.3-70b",
messages=self.messages,
tools=tools,
tool_choice="auto",
)
msg = response.choices[0].message
self.messages.append({"role": "assistant", "content": msg.content})
return msg.content
Run it
Save the file as assistant.py, replace YOUR_OXLO_API_KEY, and run it. Here is a short interaction.
if __name__ == "__main__":
assistant = VirtualAssistant(api_key="YOUR_OXLO_API_KEY")
print("User: What is 144 times 3?")
print("Oxi:", assistant.ask("What is 144 times 3?"))
print("\nUser: What time is it?")
print("Oxi:", assistant.ask("What time is it?"))
print("\nUser: Will I need an umbrella in London?")
print("Oxi:", assistant.ask("Will I need an umbrella in London?"))
Example output:
Oxi: 144 times 3 is 432.
Oxi: The current local time is 2026-01-15 09:42:18.
Oxi: London is currently rainy with a temperature of 19°C, so yes, bring an umbrella.
Next steps
Swap llama-3.3-70b for qwen-3-32b or kimi-k2.6 if you want stronger multilingual or agentic reasoning. You can also replace the simulated get_weather function with a real HTTP call to an open weather API and add memory storage with SQLite so conversations survive restarts.
Top comments (0)