unmute init writes, one key at a time.
On this page:
- Quickstart - one task, start to finish
- Every key a task takes - all eleven
- Say what success looks like - let the tool end the step
- Choose conversation history - what the task can read
- What returns - and what does not
- Advanced - lists, sharing, opening and announcing
- Troubleshooting - the two that come up most
Quickstart
A task is one entry under an agent’stasks:. This one identifies the caller
and saves what it learns:
agent.yaml
name, instructions), when to run it
(when), what it can use (tools), and what it saves (assign).
Declare a task
agent.yaml
Every key a task takes
Two are required. The rest are how you shape the step.string
required
A lower snake case name, unique across all agents in the package. To reuse an existing
task, write its bare name instead of defining it again.
string
required
Path to the task’s Markdown prompt inside the package. No prompt is inferred.
string
The situation the model reads to decide whether to start the task. If omitted, the task
is a definition only and must be used in a task group; it cannot be attached elsewhere
by bare name.
list of strings
Names of tool files loaded by the package. Omit for no ordinary tools in this task; it
does not inherit its owner’s tools.
list of one-key pairs
Pairs of
variable: result.field, including dotted result paths. Use variable+ to
append one list item. Omit to save no values; the task can still finish. The destination variable already owns the type
and description, so the task does not repeat them.list of objects
Tools whose successful results finish the task automatically. Each entry requires
tool
and a non-empty success list of one-key output field/value pairs. Values must be
declared output enum choices; a list means alternatives. If omitted, the model ends the
task by calling its generated finish tool.list of strings
Names from the top-level
handoffs catalog. Omit for no handoffs from this task.string
A fixed spoken line with no
{{placeholders}}. Omit it for no fixed announcement.
Required when opening is listen.string
Accepts
generate or listen. Omitted means generate, so the model writes the
opening turn. listen speaks announce and waits for the caller without a model
request.object
The history fields. Omit for
history: messages. A
returning task restores its owner’s earlier context and adds only completion or unserved
status.string
A
models.think entry name. LiveKit only. If omitted, use the entry agent’s think
profile, even when another agent defines the task.tasks:, no task_groups: and no escalations: key on a task. A
task cannot run another task or reach a person directly, and that is structure
rather than a rule to remember: there is no key to write it in.
How a task finishes
Every task gets afinish call. The model uses it to say the task is done and
to hand over the values in assign:. The finish: key is different: it names
tools whose own result ends the task, so the model does not have to make that
call. The next section covers the key.
The finish call for this example takes customer_id and customer_name. It
checks every value before saving any of them. If one value is invalid, the task
stays open and no value changes.
A task that saves nothing omits assign:. The model can still call finish.
Say what success looks like
How does the task know it worked? Withoutfinish:, the model decides. It runs
a tool, reads the result, and calls finish. With finish:, the tool’s own
result decides. You name the tools that end the task, and what a success from
each one looks like.
This also removes a model request. A task that runs a tool and then asks the
model to call
finish spends a whole request on a decision the tool already
made. The saving grows with the number of tasks a call runs through.
agent.yaml
success: field holds
one of its allowed values, the task saves its assign: from that result and
ends. The result never reaches the model. Anything else is an ordinary result:
it goes back to the model and the task stays open. That is what keeps a failed
booking a conversation rather than a saved one.
A success: item is field: value, or a list of values that are alternatives.
Items on different fields are all required. Every value has to be one the tool
declares in its own output enum:, so a typo is a refusal at compile time
rather than a task that never ends by itself.
result.<field> in assign: names an output property of every listed tool, so
one tool returning appointment and another returning nothing is refused.
Four things follow from a task that ends on its tool:
- The listed tools close. Once one of them succeeds, none of the task’s
finish:tools runs again in that run of the task. A new booking is a new request from the caller, and a new run of the task. - The
finishcall is still there. The model uses it for a request the task cannot serve, for values the task already holds, and for recording a result whose save was refused. That repair keeps the values the tool returned, so a reference the tool handed back is never retyped by the model. - The appended text changes. The text Unmute adds to your instructions
still tells the model to call
finishwhen the step is complete. It then says that the named tools end the step by themselves, and thatfinishis not to be called after one of them succeeds. Your own instructions should not contradict it. - The owner speaks. The task says nothing after its tool. The next step of a group opens, or the owner speaks once when control returns, so the acknowledgement belongs in the owner’s prompt.
What the model gets for a task
The prompt a task runs on has four parts. You write the first one.- Your instructions file, as written.
- The appended finish rule. Unmute adds it after your text. It names the
finishcall, lists the values fromassign:, and gives the model a way out for a request the task cannot serve. You do not write “call finish when you are done” yourself. - The values your placeholders name. Each
{{variable}}in your instructions is replaced with the saved value. No other saved value is added. - The history you chose with
context.history. When omitted, it ismessages.
finish:, a sentence is added between those two paragraphs. It says the
named tools end the step by themselves and that finish is not to be called
after one of them succeeds. On the Pipecat target the call has a longer name
that includes the task’s name. The rule is the same.
A task’s contract
Four keys make a task that knows what it saves, when it is done, and when it can be skipped. They live in three places.Read a saved value
Saving and sharing are separate choices. Put a placeholder in the prompt that needs the value:tasks/booking.md
none recorded yet.
A dotted reference selects one field and does not send its siblings:
Choose conversation history
context.history is optional. When omitted, it is messages.
A task needs a code target, Pipecat or LiveKit. On SLNG a task cannot run at
all today, so every history value is refused there,
messages included. Tasks
on SLNG are coming.
agent.yaml
tasks/reschedule-booking.md
reset when the receiver should inherit no transcript.
The step-by-step context guide shows how to
save the request first and keep the owner informed after the task finishes.
What returns
The owner gets its pre-task context back plus one neutral status:finish call includes an optional unserved_request. A task
uses it when it cannot complete the request with its tools or handoffs. A
non-empty value skips all assignments. The owner sees the unserved status and
asks the caller what they need; it does not receive the private request text.
After a tool reports success, call finish right away with the result to save.
Let the owner confirm it once, using its own placeholders. If the task waits
for another turn, it may receive a new request before saving the first result.
An unserved status does not undo an action already completed by a tool.
Better still, let the tool end the task. See
Say what success looks like.
For requests that another agent can handle, a task may declare handoffs:.
Taking one ends the task and any remaining task-group steps. See
Handoffs.
Advanced
Append and project values
Append one result to a list with+:
agent.yaml
agent.yaml
Share a task across agents
Define a task once. Another agent can attach it by name:agent.yaml
Open by listening
A task’s first turn is a model request. When the task opens with one fixed question,opening: listen speaks that question and waits, and makes no
request:
agent.yaml
announce: line, spoken once and recorded as the task’s own
first turn, so a caller answering it is answering something the task can see.
The default is generate: the model opens the task from its instructions.
A listening task with no announce: is a warning, not a refusal: the caller
hears nothing until they speak, which is occasionally what you want and usually
a mistake.
Announce a task
announce: is an optional fixed line spoken as the task starts. Omit it for
a silent transition. If you add one, tell the model not to repeat it with a
second “let me check” line:
agent.yaml
Task or second agent
Try it
my-agent is the package unmute init wrote in
Your first agent. With a task in it:
HANDOFF, so you can
see it was entered. The dev loop explains the rest of that
page.
The salon is the shipped example with tasks, a task group and handoffs. These
commands need a clone of the unmute repo:
Troubleshooting
The model does the task’s work itself and never enters it
The agent holds every tool the task holds, so the model takes the short route and the task’sassign: never runs. unmute validate warns and names the tool
to move.
Fix: take the tool off the agent and leave it on the task. A task the model
can bypass is a task it will bypass.
A task saves nothing on a call where it clearly ran
Every assigned value is checked before any is saved. If one value is invalid, nothing changes and the task stays open so the model can correct it. Fix: check the destination’s type against what the tool actually returns. Anobject or array output landing on a plain destination is refused at
compile time; a wrong value at run time keeps the task open instead.
An older package uses a retired key
Ordinary tool
input: and output: schemas are unchanged. Run
unmute validate after updating an older package; retired keys produce a
located error with migration advice.
Where to go next
Task groups
Run several tasks in a fixed order. This is the next page.
Making a task actually run
What to give a task so the model enters it, and what to take away from the owner.
Designing declared state
Choosing what to save with
assign:, and how to type it.