AgentSpec says what one agent of a call is: its prompt, its tools or tasks, its models, its language and its handoff tools. You write it once. UnmuteAgent(spec) runs it on LiveKit, and UnmuteFlows(spec, ...) runs it on Pipecat. A handoff takes the next agent’s spec on both.
The adapter adds only two kinds of thing:
- What every agent of the call shares:
state,callandtelemetry. - The framework’s own objects:
native_tools,then_targets,tts, LiveKit’sAgentoptions, and Pipecat’sllm,contextandaggregators.
RouterModelCall(state.call, ...) reads the call, so it cannot be built before it. The spec is frozen: it does not change once it is built.
AgentSpec
instructions is the only positional argument. Pass the others by keyword.
str | None
default:"None"
The agent’s name, which a
handoff event shows. One to 64 letters, digits, _ or -. None uses the call’s agent_id, from state.call. An agent the call is handed to sets its own name.TaskPlan | None
default:"None"
The checked tasks and groups. The plan holds every tool, task-only ones included. It needs a state on the adapter. See Tasks.
ToolRegistry | None
default:"None"
The agent’s tools, for an agent without tasks. Pass
tools or tasks, not both.ModelCall | None
default:"None"
The agent’s model. Its steps run on it too. On LiveKit, a plugin goes in as
LiveKitModel(plugin). On Pipecat, it needs the call’s llm to be a ModelCallLLMService. None uses the framework’s own LLM. See Model call.ModelCall | None
default:"None"
Writes the summary a
summary history asks for, for a task step or a handoff without its own. None asks the agent’s model. On Pipecat, it also answers each finish check when the call’s LLM cannot be asked.Mapping[str, ModelCall]
default:"{}"
The models a task’s
model names, by name. A step of that task runs on it. Pipecat runs every task on the agent’s LLM service, so it refuses a task with a model.str
default:"\"en\""
The call’s language, a BCP-47 tag like
es-MX. Confirm and reply lines say dates and times in it.str | None
default:"None"
Where the business is, an IANA name like
Europe/Madrid. Confirm and reply lines then name a day from today, like “today, October 8”. An unknown name raises ValueError when the adapter is built.bool
default:"False"
End each main agent request with the current text of the State values. The agent also gets the
save_values tool, for the values no task saves and the call does not start with. It needs a state on the adapter. It belongs to this agent only. See State.tuple[str, ...]
default:"()"
The names of the agent’s handoff tools, among its framework tools. The first reply after a handoff leaves them out, so the agent cannot hand the call straight back. A greeting the model words leaves them out too. See Handoffs.
AgentSpec raises ValueError in these cases:
toolscomes withtasks. The task plan holds the tools.- A task names a model that
task_modelsdoes not name. nameis not 1 to 64 letters, digits,_or-.
ValueError in these cases:
- The spec has
tasksorsave_values, and there is no state. Passstate=CallState(...)orcall=CallHooks(...). - A group transfers to an agent
then_targetslacks. - With
call, a task input reads a tool that no prefetch lookup of the call runs. - A placeholder makes a key longer than the model call takes. The router takes 64 characters. A native model has no limit.
Attributes on the adapter
AgentSpec
agent.spec on LiveKit: the spec the agent was built with. flows.spec on Pipecat: the spec of the agent that holds the call now. It changes on each handoff.CallTasks | None
The task runs the adapter built from the spec.
agent.tasks on LiveKit is None for an agent without tasks. flows.tasks on Pipecat always holds one, for the agent that holds the call. See Tasks reference.