Skip to main content
Declare a variable when another part of the call needs a reliable fact. Then reference it only in the prompts or tools that need it. Keeping a value in call state does not put it in every model request. On this page:

One saved fact, end to end

Every saved fact has the same three parts. Declare it, save it in the step that learns it, and read it in the prompt that needs it:
agent.yaml
instructions.md
The rest of this page is how to choose those three parts: what to declare, who saves it, and who reads it next.

Start with the next reader

For each value, identify who saves it and who needs it later: If nothing reads a value, you probably do not need it. If a later prompt needs it, add the placeholder there. A description alone does not share the value.

Separate a request from an outcome

A caller asking for Friday does not mean Friday is booked. Keep the requested date separate from the successfully saved appointment. Update the saved appointment only after the booking tool reports success. This prevents a failed change from looking like a confirmed booking. It also gives later tasks a clear answer when the conversation mentions both the old and new times.

Group fields that describe one result

Start with separate variables for independent facts, such as a caller’s name and preferred language. Use a shape when the fields describe the same record and should be saved together. For a booking flow:
  1. Look at the successful booking tool result. Identify the fields later tasks need, such as its ID, service, date, and time.
  2. Declare an Appointment shape with those fields. Use Id only if the actual booking ID fits its limits, and Date and Time for the saved time.
  3. Declare appointment with type: Appointment. Give it a description such as “The latest appointment successfully saved by the booking tool.”
  4. Assign appointment: result.appointment in the booking task. Tell the task to copy the successful result and finish immediately.
  5. Reference {{appointment}} in the owner, or just {{appointment.date}} in a prompt that needs only the date.
This keeps the booking ID and its current time together. Do not add unrelated caller details to the shape just because the booking task can see them. The Variables guide shows the YAML declaration and field descriptions.

Match the type to the actual value

Use a shape for fields that belong together. Include fields your tools return or the caller can supply. Do not require an ID that no tool produces. Use T | None when a successful result may legitimately have no value. A required Id cannot be replaced by an empty string just to finish the task. An external identifier may need str. For example, a booking slot containing | does not fit Id. Preserve the exact value the tool returned. The type reference lists the format checks and their limits.

Choose a current value or a list

Use one shaped variable for the latest successful appointment. Replacing it makes the new date the current one. Use a list when later tasks need several records. Append with +:
This needs a variable declared as list[str], or list[Note] for a shape. An identical structured item is not added twice. A changed item is added separately; append does not find and replace an earlier record with the same ID. Plain values may repeat.

Finish as soon as the work succeeds

A task that performs an action and then waits for another caller turn can accidentally absorb a new request. For example, a booking task might complete a move, hear a complaint, and return unserved instead of saving the move. Write the task prompt to save the successful result and make the finish call immediately. Let the owner read the saved value and confirm it once. For a task designed to save one record, finish after that record; run it again for another request. Better still, when the task’s last action is a tool, name that tool under finish:. The task then ends on the tool’s success result by itself, saves its assign: from that result, and the model never has to decide that the work is done. See Say what success looks like. A non-empty unserved_request saves no assignments and returns only a status. It does not undo an action already completed by a tool.

Reuse verification deliberately

When verification is the first step of a task group, declare the skip rather than asking a prompt to remember it. Put skip_when_confirmed: on that step, naming the variable the step confirms. The group then skips the step when that variable is confirmed at the moment the group starts, and runs it otherwise. See Skip a step whose work is already done. Confirmed here is a real mark on the variable, set by confirm: alone: when the task it names saves the value, and cleared by any other write. A prompt sentence saying the caller was verified is not that mark, and neither is a separate status variable a task assigns on success. Do not also keep a separate status variable for this: once the group reads the confirm mark itself, nothing needs a second copy of the same fact. Use confirm: for a candidate value that must be checked with the caller. When that candidate changes, prefetched values derived from the old candidate are cleared, and the group runs the confirming step again on its own.

Write for a missing value

Prefer a label and a clear fallback:
An unset value renders as none recorded yet. An empty list renders as []. Neither proves that your booking system has no records; it means the call has not saved that information.

Check the whole path

Read the task prompt and the owner’s prompt together. Check who asks the question, who saves the answer, and who speaks after completion. Then inspect a trace to confirm that each receiving prompt contains the facts it needs.

Where to go next

Reduce context sharing step by step

A practical walkthrough, one boundary at a time.

Making a task actually run

Task structure and tool placement, so the step gets entered.

Variables

The YAML for declaring a value, a shape, and a list.

Checking what the agent did

Reading a trace to see which saved value each prompt received.