requires: conditions.
Read System Prompt first for the assembly order and
which agent.yml keys override which section.
{{ ... }} and {% ... %} are Jinja2, resolved from live conversation state or
your agent.yml.One engine-side gate is not shown as a condition because it is already resolved in
each template below: the acknowledgement section (resolved prompts.ack_enabled —
on by default on voice turns, off on text when unset) is rendered on the first LLM
iteration of a turn, and omitted on later iterations of the same turn. Out-of-scope
handling needs two conditions — the scope
handler is not the active skill, and the cannot_help tool is offered on that
call. With a knowledge base configured that tool is withheld until
search_knowledge has run this turn, and the section is withheld with it. Each
situation lists the other gates it has resolved, so what you see is what is
present at that point in the conversation.When a collect step is skipped because the value is already filled, the engine
surfaces ### Already collected (do not re-ask) in two places for the rest of
that user turn: inside the active-skill section of the main system prompt, and
again as a trailing system message after history / in-turn tool results (same
idea as the ack reminder, different insertion point).
See Already collected notice.After a non-filler canned response is delivered, the next main LLM request in
the same user turn receives a canned-response reminder
as its final system message.Values in the templates
The same handful of values fill every template. Everything else is literal prompt text.Main turn
Each of these five is a complete system prompt. A pending tool confirmation can appear on top of any of them. Bracketed sections are conditional.No skill active (routing)
No skill active (routing)
When. No skill is on the stack.Sections. Header, skill glossary, channel rules, [acknowledgement], lifecycle tools, [out-of-scope handling], current date and time, [project], routing catalogue, [pending confirmation].Already resolved.
has_active_skill is false, so there is no skill-completion, cancel, or referenced-skills guidance.Framework tools offered (what each one does)activate, when at least one skill is startable. A skill is startable when it is not disabled, not engine-managed, and nothiddensearch_knowledge, when a references index is packagedcannot_help, immediately when no knowledge base exists; with one, only after a search has runlisten, on main LLM calls except guided collect, post-search answer calls, and pending tool confirmationresolve_tool_confirmation, when a tool is paused onrequires_confirmation:
complete_skill, cancel_skill, set_fields, and correct all act on an active skill, and there is none.01_routing_no_skill_active.jinja2
Prose skill active
Prose skill active
When. A prose skill is on top of the stack.Sections. Header, skill glossary, channel rules, [acknowledgement], lifecycle tools, [out-of-scope handling], current date and time, [project], active skill, [pending confirmation].Already resolved.
has_active_skill is true.Framework tools offered (what each one does)activate, when at least one target is startable. That means another skill, or one of this skill’s own blocks reached with@block.<id>. The skill on top is never offeredsearch_knowledge, when a references index is packagedcannot_help, immediately when no knowledge base exists; with one, only after a search has runlisten, on main LLM calls except guided collect, post-search answer calls, and pending tool confirmationresolve_tool_confirmation, when a tool is paused onrequires_confirmation:complete_skillandcancel_skillset_fields, when the skill still has settable entries this turncorrect, when an earlier user-provided value is correctable
requires: gate currently holds.02_skill_active_prose.jinja2
Inside an ordered block
Inside an ordered block
When. An ordered block is on top of the stack. Its
activate target id is {skill_id}__{block_id}.Sections. Same as a prose skill: header, skill glossary, channel rules, [acknowledgement], lifecycle tools, [out-of-scope handling], current date and time, [project], active skill, [pending confirmation].Already resolved. has_active_skill is true, and the skill-level Instructions section is omitted because the block supplies the steps.Framework tools offered (what each one does)activate, when at least one target is startable. That means another skill, or another block of this skill. The block on top is never offeredsearch_knowledge, when a references index is packagedcannot_help, immediately when no knowledge base exists; with one, only after a search has runlisten, on main LLM calls except guided collect, post-search answer calls, and pending tool confirmationresolve_tool_confirmation, when a tool is paused onrequires_confirmation:cancel_skill(complete_skillis withheld; the block finishes atEND)set_fields, when the skill still has settable entries this turncorrect, when an earlier user-provided value is correctable
requires: gate currently holds.03_skill_active_ordered_block.jinja2
After a knowledge search
After a knowledge search
When. A skill is on the stack and
search_knowledge has already run this turn.Sections. Header, skill glossary, channel rules, [acknowledgement], lifecycle tools, [out-of-scope handling], current date and time, [project], knowledge answering, [pending confirmation].Already resolved. has_active_skill is false for the lifecycle section, matching the trimmed tool list, so skill-completion and cancel guidance is dropped. listen_available is false, so the listen() bullet in the lifecycle snippet below does not render.Framework tools offered (what each one does)activate, when at least one target is startablesearch_knowledge, to re-query with a refined searchcannot_helpresolve_tool_confirmation, when a tool is paused onrequires_confirmation:
activate, search_knowledge, and cannot_help (plus resolve_tool_confirmation when a confirmation is pending) and stops, so the skill’s lifecycle tools, its own tools, and listen are withheld for the rest of the turn and return on the next one. A post-search call must present the retrieved information rather than wait silently. search_knowledge and cannot_help are unconditional here: a knowledge base exists by definition, and a search has run.04_after_knowledge_search.jinja2
Scope handler active
Scope handler active
When. The bundled unsupported-request handler is the active skill.Sections. Header, skill glossary, channel rules, [acknowledgement], lifecycle tools, current date and time, [project], active skill, [pending confirmation].Already resolved. The out-of-scope handling section is omitted, since the decline is already underway.Framework tools offered (what each one does)
activate, when at least one target is startable. The handler itself is engine-managed, so it is never in its own listsearch_knowledge, when a references index is packagedcannot_help, immediately when no knowledge base exists; with one, only after a search has runlisten, on main LLM calls except guided collect, post-search answer calls, and pending tool confirmationresolve_tool_confirmation, when a tool is paused onrequires_confirmation:cancel_skill(complete_skillis withheld; the handler is an ordered block)set_fields, when the skill still has settable entries this turncorrect, when an earlier user-provided value is correctable
05_scope_handler_active.jinja2
Separate calls
These use their own, much smaller prompts rather than the main one.Rephrase call
Rephrase call
When. A response opted into Trailing system message, sent after the history and the user turn:
metadata.rephrase: true.Sections. A system message of header, rephrase task, channel rules, and the current date and time, then the history and user turn, then a trailing system message carrying the text to rephrase.Already resolved. No glossary, lifecycle, out-of-scope, routing, or active-skill sections.Framework tools offered (what each one does)None. The call is made with no tool schemas at all, so the model can only return text. That is what makes it safe for wording you declared verbatim.System message:06_rephrase_only.jinja2
06_rephrase_only.jinja2 (trailing)
Confirm-rephrase call
Confirm-rephrase call
When. A Trailing system message, sent after the history and the user turn:
collect: step confirms a value that is already filled.Sections. The same shell as the rephrase call, with a trailing message that names the field and its captured value.Already resolved. No glossary, lifecycle, out-of-scope, routing, or active-skill sections.Framework tools offered (what each one does)None, the same as the rephrase call.System message:07_confirm_rephrase.jinja2
07_confirm_rephrase.jinja2 (trailing)
Already collected notice
Already collected notice
When. One or more collect steps were skipped this user turn because the value
was already filled (
ask_before_filling off).Sections. The notice is rendered twice from the same template:- Inside the active-skill section of the main system prompt (next to
### Memory:) - As its own trailing system message after conversation history and in-turn tool results (recency after tool output), similar to how the ack reminder is an extra system message — but appended at the end, not after the last user message
skipped_collect_notice.jinja2
Canned-response reminder (injected)
Canned-response reminder (injected)
When. A non-filler response template was delivered since the latest user
message and the engine is making another main LLM request in that same turn.Sections. Inserted as its own trailing system message after conversation
history and in-turn tool results, so it remains salient after the canned text.Already resolved. Not shown for filler responses. Not injected after the
next user message.Framework tools offered (what each one does)Whatever the turn it is attached to offers. This is prompt text on an existing
call, not a call of its own.
canned_utter_notice.jinja2
Ack reminder (injected)
Ack reminder (injected)
When. The first LLM iteration of a turn, when resolved
ack_enabled is true and no collect step is active.Sections. Inserted as its own system message directly after the last user message.Already resolved. Not part of the main system prompt. Uses prompts.ack_reminder when set, otherwise the default below.Framework tools offered (what each one does)Whatever the turn it is injected into offers. This is a message added to an existing call, not a call of its own.08_ack_reminder_injected.jinja2
Memory discovery (after the turn)
Memory discovery (after the turn)
When. Once the agent’s reply has been sent, on turns that changed which skill is active. It runs off the customer’s latency path and is best-effort: a failure is logged and the turn still succeeds.Sections. This task text alone, appended after the conversation so far. None of the main-turn sections are included.Already resolved. Not part of the main system prompt, and not part of the turn’s response loop.Framework tools offered (what each one does)One purpose-built tool,
record_discovered_facts, and nothing else. It is not one of the nine framework tools offered during a turn.discover_facts_task.jinja2
See also
- System Prompt: assembly order and overrides
- Tools: what each framework tool does
agent.yml:persona,rules, and thepromptskeys