Agents: the next tool call, in a loop
An agent step is a decision: which action next, with which arguments? Simo answers it as a Choice over your actions, generates the arguments for the chosen one in the same request, and gives you a calibrated probability. Below your threshold, the agent stops and hands over instead of guessing.
Decide one step
from simoai import Client, Agent, Extract, Fill, Noul
c = Client()
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 use, exactly as written", max_tokens=8)},
"issue_refund": {"order_id": Extract("the order number"),
"amount": Fill("the refund amount", 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, # below this, stop and hand over
extra={"angry": Noul("The customer is angry")})
d = agent.decide({"policy": POLICY, "ticket": ticket_text, "history": "(nothing yet)"})
d.action, d.args, d.p # "lookup_order", {"order_id": "A-1042"}, 0.97
d.confident # True: p >= act_at
d.probabilities # over every action
d.call # "lookup_order(order_id='A-1042')"
d.extra # {"angry": False}extra adds side questions answered in the same request, such as a mood flag for escalation. The agent's own question id is action, so don't use it in extra.
Run a loop
Give agent.run an environment with state(), act(action, args) and done:
from simoai import History
class Env:
def __init__(self):
self.history = History()
self.done = False
def state(self): # may also return (state, media) for screenshots
return {"policy": POLICY, "ticket": ticket_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(Env(), max_steps=20)
run.finished, run.reason, run.calls # True, "done", ["lookup_order(...)", "issue_refund(...)", ...]agent.run also takes on_step (called after each step; return False to stop) and on_uncertain (called with a decision below act_at; return True to act on it anyway). run.steps holds each decision and what act returned.
run.reason is one of:
| Reason | Meaning |
|---|---|
done |
The environment reported done |
uncertain |
A step fell below act_at: hand over to a person |
max_steps |
Hit max_steps |
stopped |
on_step returned False |
Without the Agent class
Under the hood a step is a single Choice with args. Over HTTP:
{
"model": "simo-1-pro",
"state": {"policy": "...", "ticket": "...", "history": "(nothing yet)"},
"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 use, exactly as written",
"kind": "text", "many": false, "max_tokens": 8}}}
}
}
}Read answers.action.choice, its probability in answers.action.probabilities, and the arguments in answers.action.args. See the tool-calling agent example and the browser agent example.