# Architecture Patterns > Architecture mechanics reference. Start with `references/patterns-by-requirement.md` > to choose patterns by scenario, then use this file for implementation details. > All architecture patterns below work for both `AgentforceServiceAgent` and `AgentforceEmployeeAgent`. The only difference is that employee agents cannot use `@utils.escalate` or `connection messaging:` — replace escalation with a `@utils.transition` to a help subagent or an action that creates a case/ticket. ## When to Use Each Pattern These are composable mechanics, not stages or a hierarchy. Start with the smallest architecture that satisfies the requirements, then add each independently required gate or post-action behavior. | Pattern | Use When | |---------|----------| | Single Scope | Default. One domain `start_agent` block, zero `subagent` blocks, and one compatible set of instructions, actions, authority, and escalation behavior | | Router-First Architecture | Multiple genuine domains require different objectives, instructions, actions, authority, or escalation behavior | | Verification Gate | Sensitive data, payments, or PII require identity verification first | | Post-Action Re-resolution | Trusted action output must drive a named follow-up instruction, gate, or action input | ## Smallest Architecture First Default to one domain `start_agent` block and zero `subagent` blocks. Do not create an `agent_router` that only transitions to that one domain. Add a subagent only when the boundary changes at least one of: ```text objective | instructions | available actions | authority | escalation behavior ``` Use `start_agent agent_router` only when multiple genuine domains require current-intent classification. Verification gates and post-action re-resolution are independent mechanics that can be combined with either a single-subagent or router-first design. Linear sequencing is workflow-local external ordering, not a default conversation architecture. ## Router-First Architecture Mechanics A central `agent_router` routes to specialized subagents. Transition paths should be use-case-driven: subagent -> subagent when workflow continues naturally, and subagent -> router when the conversation needs reclassification. ```agentscript start_agent agent_router: description: "Route user requests to the appropriate subagent" reasoning: instructions: | You are a router only. Do NOT answer questions directly. Always use a transition action to route immediately. actions: to_orders: @utils.transition to @subagent.order_support description: "Order questions" to_returns: @utils.transition to @subagent.return_support description: "Return or refund requests" to_general: @utils.transition to @subagent.general_support description: "General questions" subagent order_support: description: "Handle order inquiries" reasoning: instructions: -> | Help the customer with their order. actions: lookup: @actions.get_order description: "Look up order" to_returns: @utils.transition to @subagent.return_support description: "Continue to return workflow when needed" ``` > **Routing lives in `start_agent`** -- put classification transitions in `start_agent agent_router:`. Do NOT create a separate routing-only subagent (e.g. `main_menu`, `central_hub`) -- that duplicates the router, adds an extra LLM hop (~3-5s latency), and confuses the platform. A transition back to router is optional and should only be added when the use case requires reclassification. > **`instructions: |` in a router is probabilistic.** The LLM may respond > conversationally instead of emitting a transition. This is appropriate for > unstructured current-intent classification. Use `instructions: ->` only when > a named deterministic cause, such as verified authorization, selects the > transition; do not encode ordinary dialogue stages as state. ## Verification Gate Users must pass through identity verification before accessing protected subagents. Use when handling sensitive data, payments, or PII. Uses deterministic routing (`instructions: ->`) so the gate cannot be bypassed by LLM conversational drift. ```agentscript variables: is_verified: mutable boolean = False start_agent agent_router: description: "Route through identity verification" reasoning: instructions: -> if @variables.is_verified == False: transition to @subagent.identity_verification | Select the best tool to call based on conversation history and the user's current intent. actions: to_account: @utils.transition to @subagent.account_mgmt description: "Account management" available when @variables.is_verified == True to_refund: @utils.transition to @subagent.refund_processor description: "Process a refund" available when @variables.is_verified == True subagent identity_verification: description: "Verify customer identity" reasoning: instructions: -> if @variables.is_verified == True: | Identity verified. Ask which protected task to continue. else: | Ask for the minimum information needed to verify identity. actions: verify_email: @actions.verify_identity description: "Verify customer email" set @variables.is_verified = @outputs.verified to_account: @utils.transition to @subagent.account_mgmt description: "Account management" available when @variables.is_verified == True escalate_now: @utils.escalate description: "Transfer to human" ``` `is_verified` has one trusted writer (`verify_identity`) and named consumers (the authorization transitions). Define its expiry, reset, correction, and cancellation behavior. No mutable variable is needed for the user's ordinary follow-up context. ## Post-Action Re-resolution The subagent re-resolves after an action completes. Persist action output only when a named later runtime consumer needs the exact value. Place the post-action check at the top of `instructions: ->` so it applies on re-resolution: ```agentscript variables: risk_score: mutable number = -1 subagent retention_review: description: "Use a returned risk score to select retention guidance" reasoning: instructions: -> # POST-ACTION CHECK (at top on re-resolution) if @variables.risk_score >= 80: | The returned risk score is {!@variables.risk_score}. | Offer the approved retention options. else if @variables.risk_score >= 0: | The returned risk score is {!@variables.risk_score}. | Follow the standard retention policy. else: | Explain that a risk assessment is needed before making an offer. actions: assess_risk: @actions.load_risk_score with customer_id = ... available when @variables.risk_score < 0 set @variables.risk_score = @outputs.score ``` Here `risk_score` is trusted action output consumed by prompt branches and the repeat-prevention gate. Define when a new request resets or refreshes it. ## Migrating to Multi-Domain Router-First Architecture Refactor a flat agent only after identifying multiple genuine domains whose boundaries change objective, instructions, actions, authority, or escalation behavior: 1. **Prove the boundaries** — group related intents unless behavior changes 2. **Move instructions and actions** from the monolithic subagent into specialized subagents. Each subagent needs BOTH its Level 1 action definitions (under `subagent > actions`) AND Level 2 action invocations (under `subagent > reasoning > actions`). 3. **Create `start_agent agent_router:`** with transition actions pointing to each specialized subagent 4. **Add transitions based on workflow needs** — subagent -> subagent for continuous workflows, or subagent -> router for reclassification turns 5. **Re-preview immediately** — verify subagent routing works before making further changes **Common migration mistakes:** - Creating a separate `main_menu` subagent instead of using `start_agent agent_router:` as the hub — adds an unnecessary LLM hop - Leaving action definitions in `start_agent` instead of moving them to specialized subagents — all actions visible in all subagents, confusing the planner - Routing everything back to router by default, even when a direct subagent-to-subagent transition better matches the workflow - If trace shows `topic: "DefaultTopic"`, check that subagent descriptions contain keywords matching test utterances ## Multi-Intent Handling When a user sends requests for multiple domains in one message, route one domain and preserve the remaining request in conversation history: ```agentscript start_agent agent_router: description: "Route one domain request at a time" reasoning: instructions: | You are a router only. Do NOT answer questions directly. If the user asks about multiple domains in one message, route to the first domain. After that task is complete, remind the user about the other request from conversation history. ``` No queue variable is needed while the original turn survives in conversation history. Persist a queue only if exact external ordering must outlive the configured history window, and then define its writer, consumer, reset, expiry, correction, and cancellation behavior. ## Handling Incomplete Action Inputs - Use `with param = ...` (slot-fill) for inputs the LLM should extract from conversation - Add instructions that tell the LLM to invoke the action with whatever data is available - Anti-pattern: Making the LLM ask for ALL inputs before invoking ## Controlling Opportunistic Action Chains In long action chains (A->B->C->D), the LLM may invoke downstream actions as soon as prerequisites are met. To control this: - Add explicit gating in instructions: "Only invoke generate_resolution if the user explicitly asks" - Use `available when` guards when successful external output, authorization, confirmation, or required ordering supplies a machine-checkable precondition - Distinguish between "analyze only" and "full resolution" workflows in instructions Anti-pattern: Leaving action chains ungated so the LLM runs the entire pipeline for every query.