Tool-calling agent
A support agent that looks up orders, issues refunds and asks clarifying questions. Each step, Simo picks the next tool and its arguments in one request, with a calibrated probability. When no action is clearly right, the agent stops and hands the ticket to a person instead of guessing.
from simoai import Agent, Client, Extract, Fill, History, Noul
c = Client()
POLICY = """Look up the order before any refund. Refund only orders delivered damaged, within 30 days.
Ask the customer for the order number if it is missing. Close the ticket once the customer is helped."""
ORDERS = {"A-1042": {"item": "Blender", "price": 89.0, "delivered": "2026-10-09", "status": "delivered"}}
TOOLS = {
"lookup_order": lambda order_id: ORDERS.get(order_id, "no such order"),
"issue_refund": lambda order_id, amount: f"refunded ${amount} on {order_id}",
"ask_customer": lambda question: f"(sent to customer) {question}",
"close_ticket": lambda: "ticket closed",
}
agent = Agent(c,
actions={"lookup_order": "look up an order by its number",
"issue_refund": "refund an order",
"ask_customer": "ask the customer a question",
"close_ticket": "close the ticket"},
args={"lookup_order": {"order_id": Extract("the order number to look up, exactly as written", max_tokens=8)},
"issue_refund": {"order_id": Extract("the order number to refund", max_tokens=8),
"amount": Fill("the amount to refund, in dollars", kind="number")},
"ask_customer": {"question": Fill("the question to ask, one short sentence", max_tokens=32)}},
question="Following the policy and what has happened so far, what is the agent's next action?",
act_at=0.8,
extra={"angry": Noul("The customer is angry")})
class Ticket:
def __init__(self, text: str):
self.text, self.history, self.done = text, History(), False
def state(self):
return {"policy": POLICY, "today": "2026-10-18", "ticket": self.text, "history": str(self.history)}
def act(self, action, args):
result = TOOLS[action](**args)
self.history.add(f"{action}({args})", result)
self.done = action == "close_ticket"
return result
run = agent.run(Ticket("My blender from order A-1042 arrived with a cracked jug. Please refund me."),
max_steps=8, on_step=lambda s: print(s.decision.call, f"p={s.decision.p:.2f}"))
# lookup_order(order_id='A-1042') p=0.98
# issue_refund(order_id='A-1042', amount=89.0) p=0.93
# close_ticket() p=0.91
if run.reason == "uncertain":
hand_to_person(run.steps[-1].decision) # the decision it wasn't sure about, with its probabilities{
"model": "simo-1-pro",
"state": {
"policy": "Look up the order before any refund. Refund only orders delivered damaged, within 30 days. ...",
"today": "2026-10-18",
"ticket": "My blender from order A-1042 arrived with a cracked jug. Please refund me.",
"history": "1. lookup_order({'order_id': 'A-1042'}) -> {'item': 'Blender', 'price': 89.0, 'delivered': '2026-10-09', 'status': 'delivered'}"
},
"questions": {
"action": {
"type": "choice",
"instructions": "Following the policy and what has happened so far, what is the agent's next action?",
"criteria": {"lookup_order": "look up an order by its number", "issue_refund": "refund an order",
"ask_customer": "ask the customer a question", "close_ticket": "close the ticket"},
"args": {
"lookup_order": {"order_id": {"type": "extract", "instructions": "the order number to look up, exactly as written", "kind": "text", "many": false, "max_tokens": 8}},
"issue_refund": {"order_id": {"type": "extract", "instructions": "the order number to refund", "kind": "text", "many": false, "max_tokens": 8},
"amount": {"type": "fill", "instructions": "the amount to refund, in dollars", "kind": "number", "max_tokens": 16}},
"ask_customer": {"question": {"type": "fill", "instructions": "the question to ask, one short sentence", "kind": "text", "max_tokens": 32}}
}
},
"angry": {"type": "noul", "instructions": "The customer is angry"}
}
}Why it works
- The tool and its arguments in one request. Only the chosen tool's arguments are generated.
- The policy and history are in the state, so each step decides from everything that happened so far.
- Uncertainty is a stop signal.
act_at=0.8means the agent acts only when the next step is clear; otherwiserun.reasonis"uncertain"and a person takes over with the full history. - Money moves only after a lookup. Keep hard rules like refund limits in your tool code as well; the model follows the policy, and the code guarantees it.