grade.py
Why type the path
- Fewer tokens. A tool result with a
ToolReplyasks the model for no reply. Most caller turns cost one model request: the one that reads the caller. - The same call every time. A receipt, a read-back or a question said by code has the same words on every call, and on both frameworks.
- The model cannot invent an outcome. A booking, a claim or a promise exists only when a tool returned it. The model can only ask for it.
- An open standard. The path is data: Pydantic types, task rows and reply rows. Another runner, a test or a tool can read it without a model.
Who decides what
The rules the examples follow
Require what the call cannot run without. Values the call starts with, like an account id or a customer’s name, arecall_start values marked required,
with a type. A dispatch that lacks one, or sends a wrong one, never reaches the
greeting. See Start values.
Type every argument. A tool’s parameters are Pydantic types with a
description the model reads. A wrong value is refused before the tool runs, and
the reason goes back to the model so it can ask again.
state.py
Spelled is said a character at a
time, BirthDate with its year, Phone in its groups, a date from today. See
Value types.
Kestrel types the policy number’s spelling. Faro types a grade, a level and a
learning goal as Literals. A rule about the request, like Wasla’s payment
window, is not a type: it is the tool’s, as below.
Let the result speak. Give each result a ToolReply. The line can read
{{result.<field>}} and State values. A value is said by its JSON Schema
format, so a date reads as a day. See Save a result and speak it.
Hide the words a reply says from the model with hidden=("said",) on the tool.
A model that reads a line may say it again in its own words.
Let the result save. An Assign row saves a result field into the State.
The next tool reads it as a locked argument, so the model never carries it.
Nosh saves the places it offered, and place_details refuses any id that was
not offered.
Lock what the caller does not choose. Account ids, the call’s id, the
report so far and the exercise waiting are locked. The model never sees them
and cannot change them.
Read back before an effect. A tool that changes something has a
FinishRule with a confirm line. Code says it, and the tool runs only on the
caller’s yes to the same input. See Ask before a closing tool runs.
Open each step in code. A step that opens with an OpeningTool, or with
opening: listen and an announce line, says its first line with no model
request. A group’s arguments carry what the caller already said into the
first step, so its read-back can play at once.
Decide in the tool, say it in code. An argument type says what the caller
meant: a date, a phone number, a spelled id. A rule about the request, like
“not in the future”, belongs to the tool. The tool returns a typed outcome, a
Literal field, and a reply on the tool turns each outcome into a line. Code
says it, and the turn costs one model request: the call. A validator that
raises would cost two more: one for the model to say the error, and a finish
check after it. See Replies on the tool itself.
A tool that reads its input back for a yes checks the rule in a precheck, so
it answers before the read-back. See Check a rule before the tool runs.
claims.py
ToolError with a
message that names what to do next. The model reads it and chooses what to ask.
finder.py
What it costs
Each example’s scenarios count the model requests of every caller turn. These are the whole scripted calls, typed by the caller, on the real model.
A request above one per turn is a turn where the model asked for a tool, read
the refusal, and asked again, or where a step opened with a model reply.
Where the model still talks
- A question off the path, like “Do you take cards?” Keep these short in the prompt.
- A goodbye. Or give the end tool an
announceline withannounce_wait=True, and the model says nothing. - A step whose first line depends on what was said. Prefer an
OpeningToolwhen a tool can compute it.
Troubleshooting
The model says the line code already said
The prompt tells the model to say something a reply also says. Fix: tell the model in the tool’s docstring that the application says the result. The model reads the docstring as the tool’s description.A turn makes two requests where you expected one
The tool was refused, or noToolReply matched its result. Fix: give every
result shape a reply, including the “not found” one. Read the refusal from the
tool.refused event. See Watch tool calls.
Where to go next
Nosh: a tools-only finder
The smallest typed path: three tools and their replies.
Reply and confirm in code
Every rule of
ToolReply, FinishRule and OpeningTool.