8.9 KiB
The Zen of AgentScript
These are enforceable, unordered authoring rules shipped with the skill. Each rule includes a test that can fail. No rule takes precedence over another; a valid design satisfies all applicable rules at the same time.
Give each reachable branch one next outcome
For every branch, choose exactly one primary outcome:
answer | ask | invoke an action | transition | refuse | escalate
The model may explain an outcome, but it must not receive two incompatible duties.
Pass:
- A router transitions without answering the underlying request.
- A request handler answers or invokes its domain action.
- A verification gate asks for missing proof or transitions after proof.
Fail:
- Global instructions say “always answer,” while a router branch says “do not answer; transition.”
- A branch both escalates permanently and promises to continue the task.
Declare no mutable variable without a named consumer
Before adding a variable, identify at least one concrete consumer:
if | available when | transition | action input | later-turn exact output
The variable must also represent one of:
- trusted action output;
- authorization, eligibility, or confirmation proof;
- an exact value required by later deterministic logic; or
- a value explicitly required beyond the conversation-history window.
Otherwise, leave the information in conversation history.
Pass:
authenticated: mutable boolean = False
lookup_profile: @actions.get_profile
available when @variables.authenticated == True
Fail:
has_greeted: mutable boolean = False
question_asked: mutable boolean = False
conversation_stage: mutable string = "collecting"
Add deterministic control only for a named cause
Every if, available when, automatic run, or forced transition must cite
one cause in the design:
regulation | authorization | irreversible consequence |
external ordering | observed trace failure
If the decision depends on unstructured current intent and none of those causes applies, leave the decision to model reasoning.
Pass:
- Hide a refund action until the exact amount and explicit confirmation are recorded.
- Prevent step 2 until step 1’s external action returns
success=True.
Fail:
- Add a
current_stepcounter because a conversation happens to have several questions. - Force every follow-up back into an old subagent without a reproduced routing defect.
Create a subagent only when the boundary changes behavior
A subagent boundary must change at least one of:
objective | instructions | available actions | authority | escalation behavior
The difference must also be large enough that the two behaviors cannot remain coherent in one scope. A greeting, cancellation acknowledgment, completion message, ambiguity question, or ordinary dialogue step is a branch by default, not a separate subagent.
For a focused single-domain agent, the concrete default is:
start_agent event_search:
reasoning:
actions:
search: @actions.search_events
That means one execution block and zero subagent blocks—not an
agent_router that only transitions to event_search.
Pass:
- Separate public FAQ actions from authenticated account actions.
- Separate permanent human escalation from a returning specialist consultation.
Fail:
- Create
greeting,collect_name,collect_email, andpresent_resultssubagents solely to represent dialogue stages. - Wrap one read-only event search in a router and a cancellation subagent when the single search scope can cancel without invoking its action.
Make model-visible instructions concrete and self-contained
Write what the model must do now. Do not tell it to inspect AgentScript
constructs such as the active subagent, @variables, lifecycle hooks, or “the
reasoning instructions.”
For every branch, concatenate the effective system text and resolved reasoning text. The result must still prescribe one compatible outcome from the branch-outcome rule.
Pass:
Ask for the minimum information needed to verify identity. Do not use
account-changing actions.
Fail:
Inspect the current subagent and variables, then follow the response duty in
the reasoning instructions.
Use slot filling unless the value is controlled
Bind an action input with ... when the model can safely extract it from the
current turn and surviving history.
Use @variables.x only when the value is trusted, canonicalized, needed by
deterministic logic, or must be reused after its action-output scope ends. Use a
literal only for an actual constant.
Action descriptions need:
- the action’s outcome;
- when to choose it over its closest alternative; and
- any material consequence.
They do not need to script the surrounding conversation.
Pass:
find_events: @actions.search_events
with interest=...
Fail:
- End one turn with
setVariablesjust to copy “jazz” from history, then call the search action on the next turn. - Pin a user-correctable value from stale state when
...would use the latest turn.
Bind consequential actions to machine-checkable preconditions
For an action that changes money, access, records, commitments, or external state:
- bind the exact target and material parameters;
- make required authorization and confirmation machine-checkable;
- keep the action unavailable until those checks pass;
- record the action result or idempotency key when repeat execution would cause harm; and
- do not advance workflow state when the action fails.
Pass:
issue_refund: @actions.refund
with order_id=@variables.verified_order_id
with amount=@variables.confirmed_amount
available when @variables.customer_verified == True
available when @variables.refund_confirmed == True
available when @variables.refund_id == ""
set @variables.refund_id = @outputs.refund_id
Fail:
- A prose instruction says “only refund after confirmation,” but the action is always available.
current_stepadvances after@outputs.success == False.
Treat action execution—not model text—as evidence
The agent may claim an external fact or completed action only when the trace contains the corresponding successful action result.
Use direct @outputs chaining inside the same action scope. Persist only the
fields a later deterministic consumer needs. If later logic needs only
complete-versus-incomplete, persist a trusted boolean outcome rather than a
display-only external identifier. Persist the identifier itself only when an
exact-ID consumer exists, such as a later action input, idempotency guard, or
required later-turn evidence check.
Pass:
- The response names the returned status from the order lookup.
- A later action receives the canonical ID returned by verification.
- A final verification writes
verified=Truefor repeat gating while its display-only receipt remains in the action result and conversation history.
Fail:
- The model says “your refund was issued” because the action was visible or it intended to call it.
- An empty or failed action result is presented as success.
- A final receipt ID is copied into mutable state even though later logic tests only whether completion occurred.
Give every flag, cache, and latch a complete lifecycle
For each persistent control value, document:
owner | writer | reader | reset | expiry | correction behavior | cancel path
Reject the value if any field is missing.
Additional hard rules:
- Keep one source of truth; do not store both
current_stepand equivalent completion flags. - A cache must define when external data is refreshed.
- A focus latch must be justified by a reproduced trace and allow the next user turn to cancel or change intent.
Fail:
open_gatebypasses fresh routing and the locked subagent has no exit action.data_loaded=Truesuppresses refresh for the rest of the conversation.
Merge only on conversation behavior, with syntax as a hard precondition
Every candidate must first pass:
parse | lint | reference resolution | compile | emitted-artifact inspection
Then compare parent and candidate on multi-turn scenarios:
- natural follow-up;
- correction of an earlier value;
- intent change during a workflow;
- cancellation during verification or confirmation;
- action success, empty result, and failure;
- trusted authorization;
- consequential confirmation;
- exact later-turn action data flow; and
- completion without repeated actions.
Merge only when:
- the candidate has no new deterministic validation failure;
- safety, authorization, confirmation, and release boundaries do not regress;
- protected parent behaviors remain correct; and
- the candidate improves or ties conversation outcomes without adding unjustified state, turns, or tool calls.
Parser success alone is not evidence that the agent behaves well.