Skip to main content
A package is one agent.yaml plus the prompts, tool files and knowledge folders it points at. It answers three questions: who answers the call, what each agent may do, and what each of those things actually is. One rule explains the whole file:
An agent owns its own handoffs, transfers, flows and tasks. Each one is written in full inside the agent that uses it first. Any other agent names it with a bare name.
On this page:

Quickstart

Two agents. front_desk answers, and can hand the caller to complaint_specialist:
agent.yaml
front_desk writes the handoff block, so the definition sits beside the agent that uses it. Another agent that wants the same handoff writes its bare name, - to_complaints. A bare name nobody defines is refused, and so are two blocks with one name. The same goes for tasks, flows and transfers.

The five things an agent can do

An agent is a person on the phone. It can do five kinds of things, and each kind has its own list: If you remember one thing, remember the third column. Tools, tasks and flows come back. Handoffs and transfers do not.

Every key an agent takes

An agent has four required keys and five optional lists. There are no others.
string
required
What the agent is called. Other agents and entry_agent: use this name.
file path
required
The Markdown file holding this agent’s prompt, relative to the package root.
model name
required
Which reasoning model this agent uses. Names one entry under models.think.
model name
required
Which voice this agent speaks in. Names one entry under models.speak.
model name
Which transcriber this agent hears with, for an agent that speaks another language. Names one entry under models.listen. Omit it to use the package’s.
list of names
Tools this agent may call. Each name is a file under tools/<name>.yaml.
list of tasks or names
Tasks this agent may run. Write the task in full to define it here, or write a bare name to run a task another agent already defined.
list of flows or names
Flows this agent may run. Write the flow in full to define it here, or write a bare name to run a flow another agent defined.
list of handoffs or names
Agents this one can hand the caller to. Write the handoff in full to define it here, or write a bare name to use one another agent defined.
list of transfers or names
People this agent can put the caller through to. Write the transfer in full to define it here, or write a bare name to use one another agent defined.

Build one in three steps

Say you want two agents, and each one can run a task. Step 1. Say who answers.
agent.yaml
Step 2. Write the agents, with their tasks, handoffs and transfers nested right inside. A task carries both what it is and when to run it, so there is one name and one place to read it. Each handoff may choose how much conversation travels with the caller. Omitted history means messages. Saved values reach the receiving model only through placeholders in its prompt. A transfer’s to: names the environment variable that holds the phone number, never the number.
agent.yaml
Read front_desk top to bottom. You already know everything it can do, and which kind of thing each one is. That glance is the point of the five lists. Step 3. Add the tools. Every file under tools/ is a tool, so there is no list to keep. Declare MANAGER_PHONE_NUMBER in secrets:. The top-level tools: list is only for a tool that needs behaviour around it on every agent, such as a line said as it starts:
agent.yaml
Done. Notice you never wrote verify_customer twice. It is defined once, inside front_desk, and complaint_specialist just names it. Sharing is naming the same thing.

How a value gets from one step to the next

Three keys do this, and they each do one thing. Each has its own page.
top-level list
Says where a value comes from and which step confirms it. The value itself, its type and its default are a field on State in state.py, the box. Nothing fills it by itself.Variables
on a task
Fills a variable when the task finishes. - customer_phone: result.phone means “put the task’s phone answer into customer_phone”.Tasks
on a task or a handoff
Chooses how much of the conversation so far the next step can read. It moves the transcript, not your saved values.Context scope
Saved values are never added to a prompt on their own. A prompt reads a value only when it names it, as {{state.customer_phone}}.

Ordering work without a gate

In the example above, verify_customer fills customer_phone with save:, and manage_booking’s own when: says it runs only once the caller is identified. That is how “verify before booking” works: the order lives in the prompt, not in a gate, so the caller hears nothing about it. Put that clause on the work that needs the value, not on the route to it. And never hold up the way to a person: somebody asking for a manager should not be interviewed first.

Tasks attach handoffs, and nothing else

A task has tools: and handoffs: and no other list. There is no tasks:, no flows: and no transfers: key on a task, so a task cannot run another task or flow, or reach a person directly. That is not a rule you have to remember. The key does not exist, so the file cannot be written.

What the compiler holds for you

  • A bare name in an agent’s list must be defined by some agent. A typo is a build error, not a silent gap.
  • A name listed under the wrong kind is refused, and the message names the list it belongs on.
  • All five kinds share one namespace, because every name becomes a callable function at runtime. Two things cannot share a name, and two agents defining the same name is refused the same way.
  • A handoff, transfer, flow or tool that no agent reaches is also a build error. There is no dead config to forget about.
  • A task can list tools: and handoffs: only, enforced by the shape of the file.
Two of those errors, as you will actually see them:

Order, and what it does not mean

Nothing an agent lists runs in list order. The model picks what to use from the when: text. Two things in a package do run in a fixed order: the steps: of a flow, and the entries of prefetch:. Pre-fetch entries resolve top to bottom, and an entry that reads a value only a later entry saves stops the build. Order is not free everywhere, though: the order of names in a list is the order the tools are declared to the model in the generated code, so reordering a list produces a different file. Treat that as presentation, not as control flow.

The order to write the file in

That order is a convention, not a schema rule, and it exists so a reader meets the agents first and the plumbing last. unmute init writes the shape without prefetch:, since the scaffold declares none. examples/salon-concierge shows where it lands once a package uses it: right after variables:. channels: and capacity: are not in this file. They sit at the top of targets.yaml. Every one of those keys, with its type, whether it is required, and what values it accepts, is in the agent.yaml key table.

Where to go next

Your first task

Add one task to the agent you just made, one key at a time.

Tasks

Nested tasks and typed results.

Handoffs

When to split into two agents.

Flows

Tasks in a fixed order, sharing what they learn.

Human transfers

Cold and warm transfer to a person.