Complete topic-to-subagent terminology update across skills

Comprehensive update replacing "topic" with "subagent" terminology throughout
the developing-agentforce and testing-agentforce skills to align with Agent
Script's `subagent` block naming.

Key changes:
- "Topic Selector" → "Subagent Router" in all agent templates and docs
- "Topic/action" → "Subagent/action" in documentation
- "Topic map" → "Subagent map" in diagram references
- Updated all architecture documentation to use "subagent" terminology
- Updated 19 .agent template files with new labels and comments
- Updated 8 reference documentation files with consistent terminology

API contract preservation:
- Test spec YAML files preserve "topic" terminology to match Testing Center API
- Added clarifying comments explaining topic/subagent equivalence in YAML files
- Field names like `expectedTopic` unchanged (Salesforce API requirement)

Preserved terms:
- "off-topic" (standard phrase for out-of-scope)
- "expectedTopic" field (Testing Center API)
- "platform topics" (Salesforce guardrail features)

32 files changed, 379 insertions(+), 366 deletions(-)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Hetzel 2026-04-24 10:00:40 -06:00
parent 0d97ace046
commit dca920f42e
No known key found for this signature in database
GPG Key ID: 4577056100BDA892
32 changed files with 379 additions and 366 deletions

View File

@ -11,14 +11,14 @@ The skill covers the full Agent Script lifecycle:
| Domain | What It Handles |
|--------|----------------|
| Create an Agent | Agent Spec design, environment validation, bundle generation, code authoring |
| Modify an Agent | Topic/action changes, instruction refinement, flow control updates |
| Modify an Agent | Subagent/action changes, instruction refinement, flow control updates |
| Create or Modify Backing Logic | Invocable Apex stubs, Flow wrappers, Prompt Templates |
| Deploy and Publish | Source deploy, agent activation, publishing to channels |
| Diagnose Compilation Errors | Compiler error interpretation, syntax fixes, metadata resolution |
| Diagnose Behavioral Issues | Trace-based debugging, topic routing, action I/O analysis |
| Diagnose Behavioral Issues | Trace-based debugging, subagent routing, action I/O analysis |
| Diagnose Production Issues | Runtime failures, reserved keyword conflicts, deployment gotchas |
| Test an Agent | Utterance-based validation, preview with live actions, trace analysis |
| Generate Diagrams | Topic map visualizations, agent architecture diagrams |
| Generate Diagrams | Subagent map visualizations, agent architecture diagrams |
## Skill Structure

View File

@ -39,7 +39,7 @@ Identify user intent from task descriptions. ALWAYS read indicated reference fil
3. **Diagnose before you fix.** When validating/debugging agent behavior,
ALWAYS `--use-live-actions` to preview authoring bundles. Send utterances
then read resulting session traces to ground your understanding of the
agent's behavior. Trace files reveal topic selection, action I/O, and
agent's behavior. Trace files reveal subagent selection, action I/O, and
LLM reasoning. DO NOT modify `.agent` files or backing logic without
this grounding. See [Validation & Debugging](references/agent-validation-and-debugging.md)
for trace file locations and diagnostic patterns.
@ -82,11 +82,11 @@ Read [CLI for Agents](references/salesforce-cli-for-agents.md) for exact command
`sf data query --json -q "SELECT <Relevant_Fields> FROM <SObject> LIMIT 100"`
Send test utterances with:
`sf agent preview send --json --authoring-bundle <Developer_Name> --session-id <ID> -u "<message>"`
Confirm topic routing, gating, and action invocations match Agent Spec. If behavior diverges, switch to **Diagnose Behavioral Issues** workflow. Return AFTER correcting issues.
Confirm subagent routing, gating, and action invocations match Agent Spec. If behavior diverges, switch to **Diagnose Behavioral Issues** workflow. Return AFTER correcting issues.
**CHECKPOINT — Do NOT proceed to Publish unless ALL are true:**
- `validate authoring-bundle` passes with zero errors
- Live preview (`--use-live-actions`) tested with representative utterances per topic
- Traces confirm correct topic routing and action invocation
- Live preview (`--use-live-actions`) tested with representative utterances per subagent
- Traces confirm correct subagent routing and action invocation
- User explicitly approves deployment
9. **Publish** — Publish validates metadata structure, not agent behavior. Every publish creates permanent version number.
`sf agent publish authoring-bundle --json --api-name <Developer_Name>`
@ -106,12 +106,12 @@ Read [CLI for Agents](references/salesforce-cli-for-agents.md) for exact command
2. [Core Language](references/agent-script-core-language.md) — execution
model, syntax, block structure, anti-patterns
3. [Design & Agent Spec](references/agent-design-and-spec-creation.md) —
topic graph design, flow control patterns, Agent Spec production,
subagent graph design, flow control patterns, Agent Spec production,
backing logic analysis; Section 3 for environment prerequisites
4. [Subagent Map Diagrams](references/agent-subagent-map-diagrams.md) —
Mermaid diagram conventions for visualizing the agent's topic graph
Mermaid diagram conventions for visualizing the agent's subagent graph
5. [Agent User Setup & Permissions](references/agent-user-setup.md) —
permission set assignment, object permissions, cross-topic validation
permission set assignment, object permissions, cross-subagent validation
6. [Metadata & Lifecycle](references/agent-metadata-and-lifecycle.md) —
directory structure, bundle metadata; publish troubleshooting
7. [Validation & Debugging](references/agent-validation-and-debugging.md) —
@ -137,8 +137,8 @@ User wants to understand Agent Script agent they didn't write or need to revisit
2. **Read code** — Read [Core Language](references/agent-script-core-language.md) for syntax and execution model BEFORE parsing `.agent` file.
3. **Map backing logic** — For each action with `target`, locate backing implementation (Apex class, Flow, Prompt Template) in project. Note input/output contracts.
4. **Reverse-engineer Agent Spec** — Read [Design & Agent Spec](references/agent-design-and-spec-creation.md) for Agent Spec structure. Produce Agent Spec from code and save as file.
5. **Produce Subagent Map diagram** — Read [Subagent Map Diagrams](references/agent-subagent-map-diagrams.md) for Mermaid conventions. Generate flowchart of topic graph showing transitions, gates, and action associations.
6. **Annotate source** — Ask if user wants Agent Script source annotated with explanations. If requested, add inline comments to `.agent` file explaining flow control decisions, gating rationale, and topic relationships.
5. **Produce Subagent Map diagram** — Read [Subagent Map Diagrams](references/agent-subagent-map-diagrams.md) for Mermaid conventions. Generate flowchart of subagent graph showing transitions, gates, and action associations.
6. **Annotate source** — Ask if user wants Agent Script source annotated with explanations. If requested, add inline comments to `.agent` file explaining flow control decisions, gating rationale, and subagent relationships.
7. **Present to user** — Share Agent Spec, Subagent Map, and annotated source if produced. Check Anti-Patterns section in Core Language reference and flag any matches found in code.
#### Reference Files
@ -148,7 +148,7 @@ User wants to understand Agent Script agent they didn't write or need to revisit
2. [Design & Agent Spec](references/agent-design-and-spec-creation.md) —
Agent Spec structure, flow control pattern recognition
3. [Subagent Map Diagrams](references/agent-subagent-map-diagrams.md) —
Mermaid conventions for topic graph visualization
Mermaid conventions for subagent graph visualization
4. [Metadata & Lifecycle](references/agent-metadata-and-lifecycle.md) —
directory conventions, bundle metadata
5. [Known Issues](references/known-issues.md) — only load when code
@ -156,7 +156,7 @@ User wants to understand Agent Script agent they didn't write or need to revisit
### Modify an Existing Agent
User wants to add, remove, or change topics, actions, instructions, or flow control on existing agent. May describe change in plain language ("add a billing topic") or reference specific Agent Script constructs.
User wants to add, remove, or change subagents, actions, instructions, or flow control on existing agent. May describe change in plain language ("add a billing topic") or reference specific Agent Script constructs.
#### Required Steps
@ -183,8 +183,8 @@ Read [CLI for Agents](references/salesforce-cli-for-agents.md) for exact command
Test changed paths first, then adjacent paths to catch regressions in existing behavior.
**CHECKPOINT — Do NOT proceed to Publish unless ALL are true:**
- `validate authoring-bundle` passes with zero errors
- Live preview (`--use-live-actions`) tested with representative utterances per topic
- Traces confirm correct topic routing and action invocation
- Live preview (`--use-live-actions`) tested with representative utterances per subagent
- Traces confirm correct subagent routing and action invocation
- User explicitly approves deployment
8. **Publish** — Publish validates metadata structure, not agent behavior. Every publish creates permanent version number.
`sf agent publish authoring-bundle --json --api-name <Developer_Name>`
@ -244,18 +244,18 @@ Read [CLI for Agents](references/salesforce-cli-for-agents.md) for exact command
### Diagnose Behavioral Issues
Agent compiles, preview can start and `--use-live-actions`, but agent does not behave as expected. User describes symptoms like "the agent keeps going to the wrong topic" or "the action isn't being called." Fundamentally different from `validate` or `preview start` errors — code is valid but behavior is wrong.
Agent compiles, preview can start and `--use-live-actions`, but agent does not behave as expected. User describes symptoms like "the agent keeps going to the wrong subagent" or "the action isn't being called." Fundamentally different from `validate` or `preview start` errors — code is valid but behavior is wrong.
#### Required Steps
Read [CLI for Agents](references/salesforce-cli-for-agents.md) for exact command syntax.
1. **Establish baseline** — Read Agent Spec. If no Agent Spec exists, follow *Comprehend an Existing Agent* workflow to reverse-engineer one, then continue.
2. **Form hypotheses** — Read [Core Language](references/agent-script-core-language.md) for execution model. Based on user's description, list candidate root causes. Think through: topic routing, gating conditions, action availability, instruction clarity, variable state, and transition timing.
2. **Form hypotheses** — Read [Core Language](references/agent-script-core-language.md) for execution model. Based on user's description, list candidate root causes. Think through: subagent routing, gating conditions, action availability, instruction clarity, variable state, and transition timing.
3. **Reproduce in preview** — Read [Validation & Debugging](references/agent-validation-and-debugging.md) for preview workflow and session trace analysis. Start preview session:
`sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name>`
then send test messages covering EACH topic with `sf agent preview send`. One message is not enough — confirm behavior per topic before proceeding.
4. **Analyze session traces** — Examine trace output to confirm topic selection, action availability/execution, LLM reasoning, and where behavior diverges from Agent Spec. Do NOT skip this step — preview output alone is insufficient for diagnosis.
then send test messages covering EACH subagent with `sf agent preview send`. One message is not enough — confirm behavior per subagent before proceeding.
4. **Analyze session traces** — Examine trace output to confirm subagent selection, action availability/execution, LLM reasoning, and where behavior diverges from Agent Spec. Do NOT skip this step — preview output alone is insufficient for diagnosis.
5. **Identify root cause** — Match trace evidence to hypotheses. Consult *Core Language reference and Gating Patterns* in [Design & Agent Spec](references/agent-design-and-spec-creation.md) reference to confirm absence of anti-patterns.
6. **Fix code** — Apply targeted fix. If fix involves flow control changes, update Agent Spec to match.
7. **Re-validate and re-preview** — Repeat steps 36 until behavior matches Agent Spec or you confirm a platform limitation. Run `validate authoring-bundle`, then `preview start --use-live-actions` to verify fix using same utterances. Then test adjacent paths that might be affected by your changes.
@ -291,8 +291,8 @@ Read [CLI for Agents](references/salesforce-cli-for-agents.md) for exact command
Test key conversation paths to validate agent behavior when backed by live actions.
**CHECKPOINT — Do NOT proceed to Publish unless ALL are true:**
- `validate authoring-bundle` passes with zero errors
- Live preview (`--use-live-actions`) tested with representative utterances per topic
- Traces confirm correct topic routing and action invocation
- Live preview (`--use-live-actions`) tested with representative utterances per subagent
- Traces confirm correct subagent routing and action invocation
- User explicitly approves deployment
4. **Publish** — Publish validates metadata structure, not agent behavior. DO NOT publish as part of a dev/test inner loop. ONLY publish as the FINAL step prior to activating the agent and surfacing it to end users.
`sf agent publish authoring-bundle --json --api-name <Developer_Name>`
@ -379,8 +379,8 @@ User wants to create automated tests for Agent Script agent. Involves writing `A
Read [CLI for Agents](references/salesforce-cli-for-agents.md) for exact command syntax.
1. **Establish coverage baseline** — Read Agent Spec. If no Agent Spec exists, reverse-engineer first by following Comprehend steps. Map every topic, action, and flow control path to identify what needs test coverage.
2. **Design test scenarios** — For test design methodology, expectations, metrics, test spec YAML format, and templates, use **testing-agentforce** skill. That skill owns all testing content. For each coverage target, write one or more test scenarios: user utterance, expected topic routing, expected action invocations, and expected agent response. Include both happy paths and edge cases.
1. **Establish coverage baseline** — Read Agent Spec. If no Agent Spec exists, reverse-engineer first by following Comprehend steps. Map every subagent, action, and flow control path to identify what needs test coverage.
2. **Design test scenarios** — For test design methodology, expectations, metrics, test spec YAML format, and templates, use **testing-agentforce** skill. That skill owns all testing content. For each coverage target, write one or more test scenarios: user utterance, expected subagent routing, expected action invocations, and expected agent response. Include both happy paths and edge cases.
3. **Write test spec YAML** — Use template and reference files from **testing-agentforce** skill. Save to `specs/<Agent_API_Name>-testSpec.yaml` in SFDX project.
4. **Create test metadata** — Generate `AiEvaluationDefinition` from test spec using CLI.
5. **Deploy test** — Deploy `AiEvaluationDefinition` to org.
@ -401,7 +401,7 @@ Read [CLI for Agents](references/salesforce-cli-for-agents.md) for exact command
## The Agent Spec
**Agent Spec** is the central artifact this skill produces and consumes. A structured design document representing agent's purpose, topic graph, actions with backing logic, variables, gating logic, and behavioral intent.
**Agent Spec** is the central artifact this skill produces and consumes. A structured design document representing agent's purpose, subagent graph, actions with backing logic, variables, gating logic, and behavioral intent.
Agent Specs evolve with the agent. Sparse during agent creation (purpose, topics, directional notes). Fleshed out during agent build (flowchart, backing logic mapped, gating documented). Reverse-engineered when comprehending existing agents. Critical for advanced troubleshooting, providing reference to compare expected vs. actual behavior. During testing, test coverage maps against it.
@ -417,9 +417,9 @@ The `assets/` directory contains templates and examples. Read when you need a st
- **`assets/local-info-agent-annotated.agent`** — Complete annotated example based on Local Info Agent, showing all major Agent Script constructs in context with inline comments explaining why each construct is used. Read when you need concrete reference for how concepts compose into working agent, or as fallback when focused examples in reference files aren't sufficient.
- **`assets/template-single-subagent.agent`** — Minimal agent with one topic. Copy and modify for simple agents.
- **`assets/template-single-subagent.agent`** — Minimal agent with one subagent. Copy and modify for simple agents.
- **`assets/template-multi-subagent.agent`** — Minimal agent with multiple topics and transitions. Copy and modify for complex agents.
- **`assets/template-multi-subagent.agent`** — Minimal agent with multiple subagents and transitions. Copy and modify for complex agents.
- **`assets/invocable-apex-template.cls`** — Reference for invocable Apex
classes. Copy and modify when complex Apex backing logic is desired.
@ -443,15 +443,15 @@ Invalid or missing `default_agent_user`. Re-run query from [Design & Agent Spec]
**Permission error referencing different username than configured:**
Same fix as above — error references org's default running user, but root cause is Einstein Agent User permissions.
**Agent fails with permission error even though current topic's actions work:**
Planner validates ALL actions across ALL topics at startup. One missing permission fails entire agent.
**Agent fails with permission error even though current subagent's actions work:**
Planner validates ALL actions across ALL subagents at startup. One missing permission fails entire agent.
**Apex action returns empty results in live preview but works in simulated:**
`WITH USER_MODE` + missing object permissions = silent failure (0 rows, no error). See [Agent User Setup & Permissions](references/agent-user-setup.md), Section 6.2.
## Syntax Quick Reference
- Block order: `system:``config:``variables:``connection:``knowledge:``language:``start_agent agent_router:``topic:` blocks
- Block order: `system:``config:``variables:``connection:``knowledge:``language:``start_agent agent_router:``subagent:` blocks
- Indentation: **4 spaces** per indent level. Never use tabs. Mixing spaces and tabs breaks the parser.
- Booleans: `True`/`False` (capitalized)
- Strings: always double-quoted
@ -467,8 +467,8 @@ See [Complex Data Types](references/complex-data-types.md) for the full Lightnin
Three primary FSM patterns. Full details with code in [Architecture Patterns](references/architecture-patterns.md).
- **Hub-and-Spoke** (most common): `start_agent` routes to specialized topics. Each topic has "back to hub" transition. Do NOT create a separate routing topic.
- **Verification Gate**: Identity verification before protected topics. `available when` guards on protected transitions.
- **Hub-and-Spoke** (most common): `start_agent` routes to specialized subagents. Each subagent has "back to hub" transition. Do NOT create a separate routing subagent.
- **Verification Gate**: Identity verification before protected subagents. `available when` guards on protected transitions.
- **Post-Action Loop**: Post-action checks at TOP of `instructions: ->` trigger on re-resolution after action completes.
## Scoring Rubric

View File

@ -37,13 +37,13 @@ language:
all_additional_locales: False
start_agent agent_router:
label: "Topic Selector"
description: "Routes users to the appropriate topic based on their needs"
label: "Subagent Router"
description: "Routes users to the appropriate subagent based on their needs"
reasoning:
instructions: ->
| Determine what the user needs help with.
| Route them to the most appropriate topic.
| Route them to the most appropriate subagent.
| If unclear, ask clarifying questions.
actions:
go_to_topic_one: @utils.transition to @subagent.{{topic_one_name}}

View File

@ -39,7 +39,7 @@ language:
all_additional_locales: False
start_agent agent_router:
label: "Topic Selector"
label: "Subagent Router"
description: "Routes incoming questions to the FAQ handler"
reasoning:

View File

@ -36,7 +36,7 @@ language:
all_additional_locales: False
start_agent agent_router:
label: "Topic Selector"
label: "Subagent Router"
description: "Routes incoming requests to the Q&A handler"
reasoning:

View File

@ -84,8 +84,8 @@ connection messaging:
# Entry point
start_agent agent_router:
label: "Topic Selector"
description: "Routes users to appropriate topics based on intent"
label: "Subagent Router"
description: "Routes users to appropriate subagents based on intent"
reasoning:
instructions: ->

View File

@ -114,7 +114,7 @@ start_agent agent_router:
with low_confidence_warning = False
# ============================================================
# ROUTED TOPICS
# ROUTED SUBAGENTS
# ============================================================
subagent billing:

View File

@ -76,7 +76,7 @@ start_agent entry:
description: "Help with support issue"
# ============================================================
# SUPPORT TOPIC (With Escalation Triggers)
# SUPPORT SUBAGENT (With Escalation Triggers)
# ============================================================
subagent support:
@ -117,7 +117,7 @@ subagent support:
with ready_to_escalate = True
# ============================================================
# PRE-ESCALATION TOPIC (Gather Context)
# PRE-ESCALATION SUBAGENT (Gather Context)
# ============================================================
subagent pre_escalation:
@ -160,7 +160,7 @@ subagent pre_escalation:
run @utils.transition to @subagent.support
# ============================================================
# ESCALATION TOPIC (Handoff)
# ESCALATION SUBAGENT (Handoff)
# ============================================================
subagent escalation:

View File

@ -54,7 +54,7 @@ start_agent entry:
description: "Start order lookup"
# ============================================================
# ORDER LOOKUP TOPIC (Flow Action Pattern)
# ORDER LOOKUP SUBAGENT (Flow Action Pattern)
# ============================================================
subagent order_lookup:

View File

@ -2,7 +2,7 @@
# ====================================
#
# This template demonstrates the Hub-and-Spoke pattern where a central
# agent_router (hub) routes conversations to specialized topics (spokes).
# agent_router (hub) routes conversations to specialized subagents (spokes).
#
# Pattern: Multi-purpose agents handling distinct request types
# Use when: Users may have different intents (orders, support, returns)
@ -33,13 +33,13 @@ variables:
# ============================================================
start_agent agent_router:
description: "Route to appropriate topic based on user intent"
description: "Route to appropriate subagent based on user intent"
reasoning:
instructions: |
Determine what the customer needs and route accordingly:
- Order questions → orders topic
- Return/refund requests → returns topic
- General questions → support topic
- Order questions → orders subagent
- Return/refund requests → returns subagent
- General questions → support subagent
actions:
check_order: @utils.transition to @subagent.orders
description: "Customer wants to check order status"
@ -49,7 +49,7 @@ start_agent agent_router:
description: "General support questions"
# ============================================================
# SPOKE: Orders Topic
# SPOKE: Orders Subagent
# ============================================================
subagent orders:
@ -69,7 +69,7 @@ subagent orders:
description: "Return to main menu"
# ============================================================
# SPOKE: Returns Topic
# SPOKE: Returns Subagent
# ============================================================
subagent returns:
@ -88,7 +88,7 @@ subagent returns:
description: "Return to main menu"
# ============================================================
# SPOKE: Support Topic
# SPOKE: Support Subagent
# ============================================================
subagent support:

View File

@ -55,10 +55,10 @@ language:
# TOPICS
# ============================================================================
# --- START_AGENT (Topic Selector) ---
# --- START_AGENT (Subagent Router) ---
# Every agent must have exactly one start_agent block. It is the entry point
# for every conversation. The Atlas Reasoning Engine evaluates the user's
# utterance against topic descriptions and transitions to the best match.
# utterance against subagent descriptions and transitions to the best match.
#
# The agent_router label makes it a routing-only subagent. Its actions are
# exclusively @utils.transition calls — one per subagent the agent can handle.
@ -66,8 +66,8 @@ start_agent agent_router:
description: "Welcome the user and determine the appropriate subagent based on user input"
reasoning:
actions:
# Each action is a transition to a topic.
# The runtime matches the user's utterance against topic descriptions
# Each action is a transition to a subagent.
# The runtime matches the user's utterance against subagent descriptions
# and selects the best transition.
go_to_local_weather: @utils.transition to @subagent.local_weather
go_to_local_events: @utils.transition to @subagent.local_events

View File

@ -4,7 +4,7 @@
# This template provides the minimum required structure for an Agent Script.
# Use this as a starting point for simple, single-purpose agents.
#
# Required blocks: system, config, topic, start_agent
# Required blocks: system, config, subagent, start_agent
# File extension: .agent
system:
@ -36,7 +36,7 @@ start_agent entry:
description: "Entry point for all conversations"
reasoning:
instructions: |
Greet the user and route to the main topic.
Greet the user and route to the main subagent.
actions:
begin: @utils.transition to @subagent.main
description: "Start the main conversation"

View File

@ -22,10 +22,10 @@
# This is a COMPLETE template - customize for your use case
#
# ★ Step Guard Pattern (Feb 2026 Community Best Practice):
# - The topic selector re-evaluates on EVERY user utterance
# - The subagent router re-evaluates on EVERY user utterance
# - Without a step guard, follow-up messages ("my email is X") get re-routed
# - The `if @variables.current_step > 1:` guard in start_agent forces
# re-entry to the current workflow, bypassing LLM topic selection
# re-entry to the current workflow, bypassing LLM subagent selection
# - Reset current_step to 1 when the workflow completes
system:
@ -168,7 +168,7 @@ start_agent onboarding:
reasoning:
instructions: ->
# ★ STEP GUARD — Force re-entry if workflow in progress
# This prevents the topic selector from re-routing the user
# This prevents the subagent router from re-routing the user
# mid-workflow when they provide follow-up information
if @variables.current_step > 1:
| Continuing your account setup...

View File

@ -1,11 +1,11 @@
# Open Gate Routing Pattern
# 3-variable state machine for auth-gated topic routing with LLM bypass
# 3-variable state machine for auth-gated subagent routing with LLM bypass
#
# ★ When To Use This Pattern:
# - Multiple protected topics require authentication before access
# - You want to bypass the LLM topic selector when a gate topic holds focus
# - Multiple protected subagents require authentication before access
# - You want to bypass the LLM subagent selector when a gate subagent holds focus
# - Users should be redirected to auth, then automatically returned to their
# original intended topic after authentication completes
# original intended subagent after authentication completes
# - You need an EXIT_PROTOCOL to reset state when users change intent
#
# ★ Key Insight (Zero Credit Bypass):
@ -14,13 +14,13 @@
# This saves credits on every turn the gate holds focus.
#
# ★ The 3 Variables:
# open_gate — Which topic currently holds focus ("null" = none)
# open_gate — Which subagent currently holds focus ("null" = none)
# next_topic — Deferred destination after auth completes
# authenticated — Whether the user has passed authentication
#
# ★ EXIT_PROTOCOL:
# Any topic can reset open_gate to "null" when the user changes intent.
# This releases the gate lock and returns control to the LLM topic selector.
# Any subagent can reset open_gate to "null" when the user changes intent.
# This releases the gate lock and returns control to the LLM subagent router.
#
# ★ Related Patterns:
# - Latch Variable (SKILL.md) — simpler 1-variable version, no auth gate
@ -34,21 +34,22 @@
variables:
# ... standard linked variables ...
open_gate: mutable string = "null"
description: "Which topic currently holds focus (null = LLM decides)"
description: "Which subagent currently holds focus (null = LLM decides)"
next_topic: mutable string = ""
description: "Deferred destination after authentication completes"
authenticated: mutable boolean = False
description: "Whether the user has passed authentication"
# ─────────────────────────────────────────────────────────────────────
# TOPIC SELECTOR (Entry Point)
# SUBAGENT ROUTER (Entry Point)
# When open_gate is set, bypasses LLM entirely (zero credit cost)
# ─────────────────────────────────────────────────────────────────────
start_agent agent_router:
description: "Routes to topics — deterministic bypass when open_gate is set"
label: "Subagent Router"
description: "Routes to subagents — deterministic bypass when open_gate is set"
before_reasoning:
# ★ GATE CHECK: If a topic holds focus, bypass LLM entirely
# ★ GATE CHECK: If a subagent holds focus, bypass LLM entirely
if @variables.open_gate == "protected_workflow":
transition to @subagent.protected_workflow
if @variables.open_gate == "account_management":
@ -59,7 +60,7 @@ start_agent agent_router:
reasoning:
instructions: ->
| You are a customer service agent.
| Route the customer to the appropriate topic:
| Route the customer to the appropriate subagent:
| - Order status, returns, or shipping → protected workflow
| - Account settings or profile changes → account management
| - General questions → general inquiry
@ -82,7 +83,7 @@ subagent protected_workflow:
set @variables.open_gate = "authentication_gate"
transition to @subagent.authentication_gate
# ★ FOCUS LOCK: Keep gate open so topic selector bypasses LLM
# ★ FOCUS LOCK: Keep gate open so subagent router bypasses LLM
set @variables.open_gate = "protected_workflow"
reasoning:
@ -102,11 +103,11 @@ subagent protected_workflow:
after_reasoning:
# Logging or cleanup after each turn (optional)
set @variables.last_topic = "protected_workflow"
set @variables.last_subagent = "protected_workflow"
# ─────────────────────────────────────────────────────────────────────
# ACCOUNT MANAGEMENT (Second Protected Topic)
# Demonstrates the pattern scales to N protected topics
# ACCOUNT MANAGEMENT (Second Protected Subagent)
# Demonstrates the pattern scales to N protected subagents
# ─────────────────────────────────────────────────────────────────────
subagent account_management:
description: "Handles account settings and profile changes (requires authentication)"
@ -142,7 +143,7 @@ subagent account_management:
# Handles auth flow, then routes back via next_topic
# ─────────────────────────────────────────────────────────────────────
subagent authentication_gate:
description: "Verifies customer identity before allowing access to protected topics"
description: "Verifies customer identity before allowing access to protected subagents"
before_reasoning:
# ★ FOCUS LOCK: Hold gate open during auth flow
@ -174,10 +175,10 @@ subagent authentication_gate:
# ─────────────────────────────────────────────────────────────────────
# EXIT PROTOCOL (Resets Gate State)
# Clears open_gate so LLM topic selector regains control
# Clears open_gate so LLM subagent router regains control
# ─────────────────────────────────────────────────────────────────────
subagent exit_protocol:
description: "Resets gate state and returns to topic selector"
description: "Resets gate state and returns to subagent router"
before_reasoning:
# ★ RELEASE GATE: Clear all gate state
@ -190,8 +191,8 @@ subagent exit_protocol:
| Redirecting you to the main menu.
# ─────────────────────────────────────────────────────────────────────
# GENERAL INQUIRY (Unprotected Topic)
# Demonstrates that not every topic needs gating
# GENERAL INQUIRY (Unprotected Subagent)
# Demonstrates that not every subagent needs gating
# ─────────────────────────────────────────────────────────────────────
subagent general_inquiry:
description: "Handles general questions that do not require authentication"
@ -199,7 +200,7 @@ subagent general_inquiry:
reasoning:
instructions: ->
| Help the customer with general questions.
| No authentication is needed for this topic.
| No authentication is needed for this subagent.
|
| If the customer needs help with orders or account settings,
| let them know they will need to verify their identity first.
@ -272,7 +273,7 @@ subagent general_inquiry:
# Step 7: User says "actually, never mind"
# → LLM selects exit_to_menu action
# → exit_protocol.before_reasoning: open_gate = "null"
# → transition to agent_router (LLM regains control)
# → transition to agent_router (subagent router regains control)
#
# ═════════════════════════════════════════════════════════════════════
# PATTERN COMPARISON

View File

@ -74,8 +74,8 @@ language:
default_locale: "en_US"
start_agent agent_router:
label: "Topic Selector"
description: "Routes to topics with procedural data loading"
label: "Subagent Router"
description: "Routes to subagents with procedural data loading"
actions:
# Data loading actions
@ -172,7 +172,7 @@ subagent order_status:
reasoning:
instructions: ->
# ★ LAZY LOADING: Only fetch orders when user enters this topic
# ★ LAZY LOADING: Only fetch orders when user enters this subagent
if @variables.orders_loaded == False:
run @actions.load_orders
with contact_id=@variables.ContactId
@ -267,7 +267,7 @@ subagent account_help:
# - Result: Efficient, context-aware agents
# ★ Performance Benefits:
# - Fetch only what's needed per topic
# - Fetch only what's needed per subagent
# - Avoid loading all data upfront
# - Reduce API calls for simple conversations
# - Better response times

View File

@ -15,7 +15,7 @@
#
# ★ Important Limitation:
# - The system: block itself cannot use conditionals or variables
# - Dynamic behavior must be implemented in topic reasoning
# - Dynamic behavior must be implemented in subagent reasoning
#
# This is a COMPLETE template - customize for your use case
@ -60,8 +60,8 @@ language:
all_additional_locales: False
start_agent agent_router:
label: "Topic Selector"
description: "Routes to appropriate topic based on user tier"
label: "Subagent Router"
description: "Routes to appropriate subagent based on user tier"
# ★ Use before_reasoning to set up context-based variables
before_reasoning:
@ -108,7 +108,7 @@ start_agent agent_router:
if @variables.agent_mode == "formal":
| Use formal language. Address customer as Sir/Madam.
| Route the customer to the appropriate topic.
| Route the customer to the appropriate subagent.
actions:
go_orders: @utils.transition to @subagent.orders
@ -140,7 +140,7 @@ subagent orders:
reasoning:
instructions: ->
# ★ Tier-specific instructions carry through to subtopics
# ★ Tier-specific instructions carry through to subagents
if @variables.customer_tier == "vip":
| This is a VIP customer. Expedite all order requests.
| Offer free shipping upgrades proactively.
@ -195,8 +195,8 @@ subagent support:
back: @utils.transition to @subagent.agent_router
# ═══════════════════════════════════════════════════════════════════════════
# ★ TOPIC-LEVEL SYSTEM OVERRIDES (NEW PATTERN)
# These topics demonstrate complete persona switching using topic-level
# ★ SUBAGENT-LEVEL SYSTEM OVERRIDES (NEW PATTERN)
# These subagents demonstrate complete persona switching using subagent-level
# system: blocks that OVERRIDE the global system instructions.
# ═══════════════════════════════════════════════════════════════════════════
@ -204,8 +204,8 @@ subagent formal_mode:
label: "Formal Communication"
description: "Professional business communication mode"
# ★ TOPIC-LEVEL SYSTEM OVERRIDE
# This completely replaces global system instructions for this topic
# ★ SUBAGENT-LEVEL SYSTEM OVERRIDE
# This completely replaces global system instructions for this subagent
system:
instructions: "You are a formal business professional. Use professional language at all times. Address users as Sir or Madam. Avoid contractions, slang, and casual expressions. Focus on efficiency and clarity. Maintain a respectful, corporate tone."
@ -223,7 +223,7 @@ subagent creative_mode:
label: "Creative Assistant"
description: "Creative and imaginative communication mode"
# ★ TOPIC-LEVEL SYSTEM OVERRIDE
# ★ SUBAGENT-LEVEL SYSTEM OVERRIDE
# Different persona entirely
system:
instructions: "You are a creative and imaginative assistant. Be playful, use metaphors and analogies. Think outside the box. Encourage brainstorming and wild ideas. Use emojis sparingly but effectively. Make conversations engaging and fun while still being helpful."
@ -244,7 +244,7 @@ subagent technical_expert:
label: "Technical Expert"
description: "Deep technical expertise mode"
# ★ TOPIC-LEVEL SYSTEM OVERRIDE
# ★ SUBAGENT-LEVEL SYSTEM OVERRIDE
# Specialist persona
system:
instructions: "You are a technical expert with deep knowledge. Use precise technical terminology. Provide detailed explanations with examples. Reference documentation when helpful. Assume the user has technical background. Be thorough but avoid unnecessary verbosity."
@ -265,29 +265,29 @@ subagent technical_expert:
#
# LEVEL 1: GLOBAL SYSTEM BLOCK
# - Static text only (no variables, no conditionals)
# - Applies to ALL topics as baseline
# - Applies to ALL subagents as baseline
# - Good for: Guardrails, base personality, universal rules
# - Example: "Never share confidential information"
#
# LEVEL 2: TOPIC-LEVEL SYSTEM BLOCK (NEW!)
# - Placed inside topic definition
# - COMPLETELY OVERRIDES global system for that topic
# LEVEL 2: SUBAGENT-LEVEL SYSTEM BLOCK (NEW!)
# - Placed inside subagent definition
# - COMPLETELY OVERRIDES global system for that subagent
# - Good for: Persona switching, mode changes, specialist behavior
# - Example: topic formal_mode: system: instructions: "Be professional..."
# - Example: subagent formal_mode: system: instructions: "Be professional..."
#
# LEVEL 3: TOPIC REASONING INSTRUCTIONS
# LEVEL 3: SUBAGENT REASONING INSTRUCTIONS
# - Dynamic (variables, conditionals, template expressions)
# - Extends/adjusts behavior within topic
# - Extends/adjusts behavior within subagent
# - Good for: Context-aware responses, personalization
# - Example: if @variables.is_vip: | Provide priority service
#
# OVERRIDE HIERARCHY:
# Topic system: > Global system: > Default behavior
# Subagent system: > Global system: > Default behavior
#
# COMBINING APPROACHES:
# - Use GLOBAL system for universal guardrails
# - Use TOPIC system: for complete persona changes
# - Use TOPIC reasoning for dynamic conditional behavior
# - Use SUBAGENT system: for complete persona changes
# - Use SUBAGENT reasoning for dynamic conditional behavior
#
# Best Practice: Put guardrails in global system, personas in topic system,
# and context-aware personalization in topic reasoning instructions.
# Best Practice: Put guardrails in global system, personas in subagent system,
# and context-aware personalization in subagent reasoning instructions.

View File

@ -47,7 +47,7 @@ start_agent entry:
description: "Start knowledge search"
# ============================================================
# KNOWLEDGE SEARCH TOPIC (RAG Pattern)
# KNOWLEDGE SEARCH SUBAGENT (RAG Pattern)
# ============================================================
subagent knowledge_search:
@ -110,7 +110,7 @@ subagent knowledge_search:
description: "Transfer to human agent"
# ============================================================
# FOLLOW-UP QUESTIONS TOPIC
# FOLLOW-UP QUESTIONS SUBAGENT
# ============================================================
subagent follow_up:

View File

@ -1,7 +1,7 @@
# Multi-Topic Agent Template
# Multi-Subagent Template
# Copy this file to your AiAuthoringBundle directory and rename it to match
# your agent's developer_name. Replace all placeholder values with real ones.
# Add, remove, or rename topics as needed.
# Add, remove, or rename subagents as needed.
system:
instructions: "You are a helpful assistant. Describe the agent's persona and purpose here."
@ -28,7 +28,7 @@ language:
all_additional_locales: False
start_agent agent_router:
description: "Welcome the user and determine the appropriate topic based on user input"
description: "Welcome the user and determine the appropriate subagent based on user input"
reasoning:
actions:
go_to_topic_a: @utils.transition to @subagent.topic_a
@ -38,12 +38,12 @@ start_agent agent_router:
go_to_off_topic: @utils.transition to @subagent.off_topic
subagent topic_a:
label: "Topic A"
description: "Describe what this topic handles"
label: "Subagent A"
description: "Describe what this subagent handles"
reasoning:
instructions: ->
| Replace this with instructions for topic A.
| Replace this with instructions for subagent A.
actions:
action_a: @actions.action_a
@ -67,12 +67,12 @@ subagent topic_a:
filter_from_agent: False
subagent topic_b:
label: "Topic B"
description: "Describe what this topic handles"
label: "Subagent B"
description: "Describe what this subagent handles"
reasoning:
instructions: ->
| Replace this with instructions for topic B. This topic demonstrates the gating pattern.
| Replace this with instructions for subagent B. This subagent demonstrates the gating pattern.
actions:
# Collect data into a variable first
@ -103,12 +103,12 @@ subagent topic_b:
is_displayable: False
subagent topic_c:
label: "Topic C"
description: "Describe what this topic handles"
label: "Subagent C"
description: "Describe what this subagent handles"
reasoning:
instructions: ->
| Replace this with base instructions for topic C.
| Replace this with base instructions for subagent C.
# Conditional instructions based on variable state
if @variables.collected_input != "":
@ -152,9 +152,9 @@ subagent escalation:
subagent off_topic:
label: "Off Topic"
description: "Redirect conversation to relevant topics when user request goes off-topic"
description: "Redirect conversation to relevant subagents when user request goes off-topic"
reasoning:
instructions: ->
| The user request is off-topic. Do not answer general knowledge questions.
Redirect the conversation by asking how you can help with topics this agent supports.
Redirect the conversation by asking how you can help with the topics this agent supports.

View File

@ -1,4 +1,4 @@
# Single-Topic Agent Template
# Single-Subagent Template
# Copy this file to your AiAuthoringBundle directory and rename it to match
# your agent's developer_name. Replace all placeholder values with real ones.
@ -23,7 +23,7 @@ language:
all_additional_locales: False
start_agent agent_router:
description: "Welcome the user and determine the appropriate topic based on user input"
description: "Welcome the user and determine the appropriate subagent based on user input"
reasoning:
actions:
go_to_main: @utils.transition to @subagent.main
@ -31,12 +31,12 @@ start_agent agent_router:
go_to_off_topic: @utils.transition to @subagent.off_topic
subagent main:
label: "Main Topic"
description: "Describe what this topic handles. The Atlas Reasoning Engine matches user utterances against this description."
label: "Main Subagent"
description: "Describe what this subagent handles. The Atlas Reasoning Engine matches user utterances against this description."
reasoning:
instructions: ->
| Replace this with instructions for how the agent should handle requests in this topic.
| Replace this with instructions for how the agent should handle requests in this subagent.
Reference actions with the syntax shown below.
actions:
@ -73,9 +73,9 @@ subagent escalation:
subagent off_topic:
label: "Off Topic"
description: "Redirect conversation to relevant topics when user request goes off-topic"
description: "Redirect conversation to relevant subagents when user request goes off-topic"
reasoning:
instructions: ->
| The user request is off-topic. Do not answer general knowledge questions.
Redirect the conversation by asking how you can help with topics this agent supports.
Redirect the conversation by asking how you can help with the topics this agent supports.

View File

@ -72,7 +72,7 @@ subagent identity_verification:
| Too many failed attempts. Transferring to a human agent.
transition to @subagent.escalation
# Already verified? Proceed to protected topics
# Already verified? Proceed to protected subagents
if @variables.customer_verified == True:
| Identity verified! How can I help you today?

View File

@ -5,7 +5,7 @@
1. Agent Spec: Structure and Lifecycle
2. Discovery Questions
3. Environment Prerequisites
4. Topic Architecture
4. Subagent Architecture
5. Mapping Logic to Actions
6. Transition Patterns
7. Deterministic vs. Subjective Flow Control
@ -16,15 +16,15 @@
## 1. Agent Spec: Structure and Lifecycle
An **Agent Spec** is a structured design document describing an agent's purpose, topics, actions, state, control flow, and behavioral intent. When creating a new agent, produce the Agent Spec before writing Agent Script code. When comprehending or diagnosing an existing agent, reverse-engineer an Agent Spec from the `.agent` file to make the agent's design explicit.
An **Agent Spec** is a structured design document describing an agent's purpose, subagents, actions, state, control flow, and behavioral intent. When creating a new agent, produce the Agent Spec before writing Agent Script code. When comprehending or diagnosing an existing agent, reverse-engineer an Agent Spec from the `.agent` file to make the agent's design explicit.
### What an Agent Spec Contains
- **Purpose & Scope** — what the agent does, in plain language
- **Behavioral Intent** — what the agent is supposed to achieve (requirements and constraints), not just what the code does
- **Subagent Map** — a Mermaid flowchart showing all topics, transitions (with type labels: handoff or delegation), and when transitions occur
- **Subagent Map** — a Mermaid flowchart showing all subagents, transitions (with type labels: handoff or delegation), and when transitions occur
- **Actions & Backing Logic** — each action's name, its backing implementation (Apex class, Flow, Prompt Template), inputs/outputs with visibility decisions, and whether the backing logic exists or needs creation
- **Variables** — declarations, types, default values, which topics set/read them, and what gates they control
- **Variables** — declarations, types, default values, which subagents set/read them, and what gates they control
- **Gating Logic** — conditions that govern action visibility or instruction evaluation, with rationale for each. Always include this section; if no gating applies, state "No gating required" so reviewers know it was considered, not overlooked.
### Directional vs. Observational Entries
@ -41,11 +41,11 @@ Both go in the same Agent Spec section.
The Agent Spec evolves across the agent's lifecycle:
**Creation (sparse).** Purpose, topic names, rough descriptions, directional notes about backing logic ("this action needs an Apex class that accepts X, returns Y"). No flowchart yet. Entries are mostly placeholders.
**Creation (sparse).** Purpose, subagent names, rough descriptions, directional notes about backing logic ("this action needs an Apex class that accepts X, returns Y"). No flowchart yet. Entries are mostly placeholders.
**Build (filled).** Flowchart added with transition types labeled. Backing logic mapped (existing implementations identified with filenames, missing implementations stubbed with protocols and I/O specs). Variables documented with their usage and gating impact. Gating rationale explained.
**Comprehension (reverse-engineered).** Starting from an existing `.agent` file, produce a complete Agent Spec by parsing topics, tracing transitions, analyzing actions, and documenting state. This is the "what does this agent do?" output.
**Comprehension (reverse-engineered).** Starting from an existing `.agent` file, produce a complete Agent Spec by parsing subagents, tracing transitions, analyzing actions, and documenting state. This is the "what does this agent do?" output.
**Diagnosis (reference).** Compare actual runtime behavior against the Agent Spec to find where intent and implementation diverge.
@ -69,19 +69,19 @@ These five question categories drive the content of your Agent Spec. When creati
- What personality should the agent have? (professional, friendly, formal, casual)
- What error message should the agent show if something breaks?
### Topics & Conversation Flow *(feeds Subagent Map)*
### Subagents & Conversation Flow *(feeds Subagent Map)*
- What distinct conversation areas (topics) does the agent need?
- Which topic is the entry point? (where conversations start)
- What are the possible transitions between topics?
- Are there topics that delegate to others and need to return?
- Are there guardrail topics (off-topic redirection, ambiguity handling, security gates)?
- What distinct conversation areas (subagents) does the agent need?
- Which subagent is the entry point? (where conversations start)
- What are the possible transitions between subagents?
- Are there subagents that delegate to others and need to return?
- Are there guardrail subagents (off-topic redirection, ambiguity handling, security gates)?
### Reasoning & Instructions *(feeds Behavioral Intent)*
- What should the agent do in each topic?
- What should the agent do in each subagent?
- What conditions change the instructions? (if guest is premium, if step 1 is complete)
- Should the agent do anything before or after reasoning in a given topic? (e.g., security checks, data fetches, automatic transitions)
- Should the agent do anything before or after reasoning in a given subagent? (e.g., security checks, data fetches, automatic transitions)
- What data transformations (if any) does the LLM need to do?
### Actions & External Systems *(feeds Actions & Backing Logic)*
@ -98,17 +98,17 @@ These five question categories drive the content of your Agent Spec. When creati
- What information must persist across the conversation? (customer name, preferences, process state)
- What external context is needed? (session ID, user record, linked fields)
- What conditions should trigger different behavior in the same topic? (is_premium, role, completed_steps)
- What conditions should trigger different behavior in the same subagent? (is_premium, role, completed_steps)
---
## 3. Environment Prerequisites
**⚠️ MANDATORY: Run these checks immediately after determining the agent type during discovery.** Do not proceed to topic architecture or code generation until the environment is validated.
**⚠️ MANDATORY: Run these checks immediately after determining the agent type during discovery.** Do not proceed to subagent architecture or code generation until the environment is validated.
### `AgentforceEmployeeAgent`
1. Confirm the config block does NOT include `default_agent_user`. If the generated boilerplate includes it, remove it along with any MessagingSession linked variables and escalation topic.
1. Confirm the config block does NOT include `default_agent_user`. If the generated boilerplate includes it, remove it along with any MessagingSession linked variables and escalation subagent.
**⚠️ Setting `default_agent_user` on an employee agent causes publish and preview to fail with an unhelpful "unknown error."**
@ -144,17 +144,17 @@ Add a "Configuration" section to the Agent Spec:
---
## 4. Topic Architecture
## 4. Subagent Architecture
Topics are states in a finite state machine. When designing a new agent, plan your topic structure before writing code. When comprehending an existing agent, identify which topic strategies and architecture pattern it uses.
Subagents are states in a finite state machine. When designing a new agent, plan your subagent structure before writing code. When comprehending an existing agent, identify which subagent strategies and architecture pattern it uses.
### Topic Strategies
### Subagent Strategies
Every topic in an agent serves one of three roles: domain, guardrail, or escalation.
Every subagent in an agent serves one of three roles: domain, guardrail, or escalation.
**Domain Topics.** The core conversation areas where the agent does its work. Each domain topic handles a specific area (orders, billing, weather, events) with its own instructions, actions, and state. Most agents have 1-5 domain topics.
**Domain Subagents.** The core conversation areas where the agent does its work. Each domain subagent handles a specific area (orders, billing, weather, events) with its own instructions, actions, and state. Most agents have 1-5 domain subagents.
**Guardrail Topics.** Specialized topics that enforce agent boundaries. The standard Agentforce template includes two guardrail topics by default: `off_topic` (redirects users back to the agent's scope) and `ambiguous_question` (asks for clarification instead of guessing). Preserve these when modifying existing agents.
**Guardrail Subagents.** Specialized subagents that enforce agent boundaries. The standard Agentforce template includes two guardrail subagents by default: `off_topic` (redirects users back to the agent's scope) and `ambiguous_question` (asks for clarification instead of guessing). Preserve these when modifying existing agents.
```agentscript
subagent off_topic:
@ -173,7 +173,7 @@ subagent ambiguous_question:
Can you provide more details about what you need?
```
**Escalation Topics.** Hand off to a human via `@utils.escalate`. This is a permanent exit — the user leaves the agent for a support channel (phone, email, chat with a human). Once triggered, the agent session ends. The escalation action does NOT return.
**Escalation Subagents.** Hand off to a human via `@utils.escalate`. This is a permanent exit — the user leaves the agent for a support channel (phone, email, chat with a human). Once triggered, the agent session ends. The escalation action does NOT return.
```agentscript
subagent escalation:
@ -183,7 +183,7 @@ subagent escalation:
description: "Connect with a human agent"
```
### Single-Topic vs. Multi-Topic
### Single-Subagent vs. Multi-Subagent
Decide this before choosing an architecture pattern.
@ -194,15 +194,15 @@ Use **single-subagent** if:
Use **multi-subagent** if:
- The agent handles multiple distinct domains (customer service: orders + billing + account)
- Different topics have different instructions or action sets
- Different subagents have different instructions or action sets
- Users may need to switch contexts mid-conversation
- You need different entry points or security gates
### Architecture Patterns
**Hub-and-Spoke.** One central topic (the router) transitions to specialized domain topics. The router is typically the `start_agent` topic. Each spoke handles a specific domain and may transition back to the router or to other spokes. Use when the agent handles multiple distinct domains that don't naturally flow together.
**Hub-and-Spoke.** One central subagent (the router) transitions to specialized domain subagents. The router is typically the `start_agent` subagent. Each spoke handles a specific domain and may transition back to the router or to other spokes. Use when the agent handles multiple distinct domains that don't naturally flow together.
Example: The Local Info Agent. The `agent_router` topic (hub) routes to domain and guardrail topics (spokes).
Example: The Local Info Agent. The `agent_router` subagent (hub) routes to domain and guardrail subagents (spokes).
```agentscript
start_agent agent_router:
@ -214,7 +214,7 @@ start_agent agent_router:
go_to_off_topic: @utils.transition to @subagent.off_topic
go_to_ambiguous_question: @utils.transition to @subagent.ambiguous_question
# Domain topics — each has its own instructions and actions
# Domain subagents — each has its own instructions and actions
subagent local_weather:
reasoning:
instructions: | Handle weather questions.
@ -226,7 +226,7 @@ subagent local_events:
# resort_hours, off_topic, ambiguous_question defined further down the file
```
**Linear Flow.** Topics form a pipeline: start → step 1 → step 2 → step 3 → end. Users progress through stages without backtracking. Use for multi-step workflows with mandatory ordering (application forms, onboarding, troubleshooting trees).
**Linear Flow.** Subagents form a pipeline: start → step 1 → step 2 → step 3 → end. Users progress through stages without backtracking. Use for multi-step workflows with mandatory ordering (application forms, onboarding, troubleshooting trees).
```agentscript
start_agent intake:
@ -264,7 +264,7 @@ subagent level_2_support:
escalate_to_human: @utils.escalate
```
**Verification Gate.** A security or permission check before allowing access to protected topics. The gate validates the user, then transitions to the protected topic or denies access.
**Verification Gate.** A security or permission check before allowing access to protected subagents. The gate validates the user, then transitions to the protected subagent or denies access.
```agentscript
start_agent security_gate:
@ -280,7 +280,7 @@ subagent access_denied:
instructions: | You don't have permission to access this.
```
**Single-Topic.** The entire agent is one topic — no transitions. Use for focused QA agents where all interactions stay in the same domain.
**Single-Subagent.** The entire agent is one subagent — no transitions. Use for focused QA agents where all interactions stay in the same domain.
```agentscript
start_agent faq:
@ -293,7 +293,7 @@ start_agent faq:
### Composing Patterns
Real agents often combine patterns. A hub-and-spoke agent may use a verification gate before protected spokes. A linear flow may include escalation exits at each stage. When composing, each topic still serves exactly one role (domain, guardrail, or escalation) — the architecture pattern determines how they connect.
Real agents often combine patterns. A hub-and-spoke agent may use a verification gate before protected spokes. A linear flow may include escalation exits at each stage. When composing, each subagent still serves exactly one role (domain, guardrail, or escalation) — the architecture pattern determines how they connect.
---
@ -630,11 +630,11 @@ When creating a new agent, label every transition in your Agent Spec's Subagent
### Handoff: Permanent Transition
A handoff is a one-way transition. The user moves to a new topic and control never returns to the original topic. Handoffs use `@utils.transition to` in `reasoning.actions`.
A handoff is a one-way transition. The user moves to a new subagent and control never returns to the original subagent. Handoffs use `@utils.transition to` in `reasoning.actions`.
Use handoff when:
- Switching modes (preview → confirm → complete)
- Entry point routing (agent_router → domain topics)
- Entry point routing (agent_router → domain subagents)
- One-way workflows (checkout → order_confirmation → end)
```agentscript
@ -655,14 +655,14 @@ After `go_to_confirm` executes, the user is in `order_confirmation`. If they lat
### Delegation: Handoff with Explicit Return
Delegation hands control to another topic using `@subagent.X` in `reasoning.actions`. It signals *intent* to return, but the return does not happen automatically — the delegated topic must explicitly transition back to the caller.
Delegation hands control to another subagent using `@subagent.X` in `reasoning.actions`. It signals *intent* to return, but the return does not happen automatically — the delegated subagent must explicitly transition back to the caller.
Use delegation when:
- One topic needs advice from a specialist and should continue after
- Reusable sub-workflows (e.g., identity verification called from multiple topics)
- A topic needs to temporarily visit another topic, then resume
- One subagent needs advice from a specialist and should continue after
- Reusable sub-workflows (e.g., identity verification called from multiple subagents)
- A subagent needs to temporarily visit another subagent, then resume
**Critical Rule:** `@subagent.X` delegates control. It does NOT implement call-return semantics. If you want the user to return to the calling topic, code an explicit `transition to @subagent.<caller>` in the delegated topic. Without it, the next user utterance falls through to `agent_router`.
**Critical Rule:** `@subagent.X` delegates control. It does NOT implement call-return semantics. If you want the user to return to the calling subagent, code an explicit `transition to @subagent.<caller>` in the delegated subagent. Without it, the next user utterance falls through to `agent_router`.
WRONG: Assuming `@subagent.specialist` returns automatically
```agentscript
@ -675,7 +675,7 @@ subagent main:
# The next user utterance routes through agent_router.
```
RIGHT: Delegated topic defines explicit return transition
RIGHT: Delegated subagent defines explicit return transition
```agentscript
subagent main:
reasoning:
@ -775,7 +775,7 @@ Grounding validation requires **live mode preview** (`sf agent preview --use-liv
### Post-Action Behavior
When an action completes without triggering a transition, the topic stays active. The runtime re-evaluates the entire topic — resolving instructions top-to-bottom again with updated variables, then passing the new prompt to the LLM. The LLM may call the same action again. To prevent unwanted loops, see Section 9 (Action Loop Prevention).
When an action completes without triggering a transition, the subagent stays active. The runtime re-evaluates the entire subagent — resolving instructions top-to-bottom again with updated variables, then passing the new prompt to the LLM. The LLM may call the same action again. To prevent unwanted loops, see Section 9 (Action Loop Prevention).
---
@ -833,7 +833,7 @@ Use conditional instructions when you want to steer the LLM's reasoning without
### `before_reasoning` Guards — Early Exit
The `before_reasoning` block runs before the LLM is invoked. Code here executes every time the topic is entered. The LLM never sees it, cannot override it, and cannot skip it.
The `before_reasoning` block runs before the LLM is invoked. Code here executes every time the subagent is entered. The LLM never sees it, cannot override it, and cannot skip it.
```agentscript
subagent admin_panel:
@ -845,7 +845,7 @@ subagent admin_panel:
instructions: | You are in the admin panel.
```
If the user is not an admin, they transition out before the LLM is invoked. The admin topic's reasoning instructions never execute.
If the user is not an admin, they transition out before the LLM is invoked. The admin subagent's reasoning instructions never execute.
### Multi-Condition Gating
@ -898,9 +898,9 @@ Each step becomes visible only after the previous step completes (updates its va
### Same-Turn Behavior After Gate Transitions
When a gate topic (e.g., username collection) uses `after_reasoning` to transition into a routing topic, both topics process in the **same user turn**. The router receives the user's original message — the one that satisfied the gate — not a fresh utterance.
When a gate subagent (e.g., username collection) uses `after_reasoning` to transition into a routing subagent, both subagents process in the **same user turn**. The router receives the user's original message — the one that satisfied the gate — not a fresh utterance.
This means if the user said "My username is alex" and the gate transitions to a topic selector, the selector's reasoning fires against "My username is alex." Since that message doesn't match any domain topic, the router may misclassify it (e.g., routing to `off_topic`).
This means if the user said "My username is alex" and the gate transitions to a subagent router, the router's reasoning fires against "My username is alex." Since that message doesn't match any domain subagent, the router may misclassify it (e.g., routing to `off_topic`).
**Mitigation:** Write the router's reasoning instructions defensively. Tell the LLM that if the user just arrived from the gate, it should greet them and ask how it can help instead of routing the triggering message. See the Anti-Patterns section in the Core Language reference for a full WRONG/RIGHT example.
@ -952,7 +952,7 @@ subagent events:
**2. Post-Action Transitions (state-based).**
Move the agent out of the topic after the action completes, breaking the cycle.
Move the agent out of the subagent after the action completes, breaking the cycle.
```agentscript
subagent events:
@ -969,7 +969,7 @@ subagent events:
transition to @subagent.results_displayed
```
After `check_events` runs, the `after_reasoning` block transitions to a new topic. The agent never cycles back to `events`, so the action can't be called again.
After `check_events` runs, the `after_reasoning` block transitions to a new subagent. The agent never cycles back to `events`, so the action can't be called again.
**3. LLM Slot-Filling Over Variable Binding (friction-based).**
@ -1003,7 +1003,7 @@ subagent lookup:
after_reasoning:
if @outputs.data_found:
transition to @subagent.done # Exit the topic
transition to @subagent.done # Exit the subagent
```
Combine mitigations for reinforcement.

View File

@ -21,11 +21,11 @@
Agent Script operates in two phases: deterministic resolution, then LLM reasoning.
**Phase 1: Deterministic Resolution.** The runtime executes a topic's reasoning instructions top to bottom — evaluating `if`/`else` conditions, running actions via `run`, and setting variables via `set`. The LLM is NOT involved yet. The runtime builds a prompt string by accumulating `|` pipe text and resolving conditional logic. If a `transition` command occurs, the runtime discards the current prompt and starts fresh with the target topic.
**Phase 1: Deterministic Resolution.** The runtime executes a subagent's reasoning instructions top to bottom — evaluating `if`/`else` conditions, running actions via `run`, and setting variables via `set`. The LLM is NOT involved yet. The runtime builds a prompt string by accumulating `|` pipe text and resolving conditional logic. If a `transition` command occurs, the runtime discards the current prompt and starts fresh with the target subagent.
**Phase 2: LLM Reasoning.** The runtime passes the resolved prompt to the LLM along with any reasoning actions (tools) the topic exposes. The LLM decides what to do — it can call available actions but cannot modify the prompt text. It only reasons against what Phase 1 resolved.
**Phase 2: LLM Reasoning.** The runtime passes the resolved prompt to the LLM along with any reasoning actions (tools) the subagent exposes. The LLM decides what to do — it can call available actions but cannot modify the prompt text. It only reasons against what Phase 1 resolved.
**Worked Example.** Consider this topic:
**Worked Example.** Consider this subagent:
```agentscript
subagent check_order:
@ -94,7 +94,7 @@ subagent my_topic:
**Within `start_agent` and `subagent` blocks**, the internal ordering is:
1. `description` (required)
2. `system` (optional — topic-level override of global system instructions)
2. `system` (optional — subagent-level override of global system instructions)
3. `before_reasoning` (optional — runs before reasoning phase)
4. `reasoning` (required)
5. `after_reasoning` (optional — runs after reasoning phase)
@ -104,7 +104,7 @@ subagent my_topic:
## 3. Naming and Formatting Rules
**Naming constraints for all identifiers** (developer_name, topic names, variable names, action names, connection names):
**Naming constraints for all identifiers** (developer_name, subagent names, variable names, action names, connection names):
- Contain only letters, numbers, and underscores
- Begin with a letter (never underscore)
@ -117,7 +117,7 @@ Example: `check_order_status` is valid. `check_order__status` is invalid (consec
**Indentation:** Use 4 spaces per indent level. NEVER use tabs. Mixing spaces and tabs breaks the parser. All lines at the same nesting level must use the same indentation.
Each nesting level adds 4 spaces. The hierarchy follows the block structure — topic → reasoning → instructions → logic/prompt:
Each nesting level adds 4 spaces. The hierarchy follows the block structure — subagent → reasoning → instructions → logic/prompt:
```agentscript
subagent process_order:
@ -188,8 +188,8 @@ The expression inside `{! ... }` is evaluated by the runtime during deterministi
**Resource references**:
- `@actions.<name>` — reference an action defined in the topic's `actions` block
- `@subagent.<name>` — reference a topic by name
- `@actions.<name>` — reference an action defined in the subagent's `actions` block
- `@subagent.<name>` — reference a subagent by name
- `@variables.<name>` — reference a variable (use in logic)
- `{!@variables.<name>}` — reference a variable in prompt text (template injection)
- `@outputs.<name>` — action output (only in `set`/`if` immediately after the action — unavailable elsewhere)
@ -220,7 +220,7 @@ system:
error: "Sorry, something went wrong. Please try again."
```
The `instructions` field is required and contains text directives sent to the LLM in every reasoning phase. Topic-level system blocks can override this.
The `instructions` field is required and contains text directives sent to the LLM in every reasoning phase. Subagent-level system blocks can override this.
Both `welcome` and `error` messages are required.
@ -242,7 +242,7 @@ config:
- `"AgentforceEmployeeAgent"` — internal employee-facing. Agent Script files with this agent type MUST NOT include:
- `default_agent_user`
- MessagingSession linked variables (`EndUserId`, `RoutableId`, `ContactId`, `EndUserLanguage`)
- Escalation topic with `@utils.escalate`
- Escalation subagent with `@utils.escalate`
- `connection messaging:` block
**Common mistake — service-agent constructs on employee agent:**
@ -364,9 +364,9 @@ In prompt text (inside `|` pipe sections), always use `{!@variables.X}` with bra
---
## 7. Topics
## 7. Subagents
**Topic structure** — a named scope for reasoning, actions, and flow control:
**Subagent structure** — a named scope for reasoning, actions, and flow control:
```agentscript
subagent order_lookup:
@ -389,9 +389,9 @@ subagent order_lookup:
status: string
```
**Description is required** — the LLM uses this to understand when the topic is relevant.
**Description is required** — the LLM uses this to understand when the subagent is relevant.
**Topic-level system override** (optional) — override global system instructions for this topic only:
**Subagent-level system override** (optional) — override global system instructions for this subagent only:
```agentscript
subagent product_specialist:
@ -403,7 +403,7 @@ subagent product_specialist:
| Help with product specs.
```
**Internal block ordering within a topic**:
**Internal block ordering within a subagent**:
1. `description`
2. `system` (optional override)
@ -423,7 +423,7 @@ before_reasoning:
reasoning:
instructions: ->
| Main topic logic
| Main subagent logic
after_reasoning:
if @variables.transaction_complete:
@ -586,15 +586,15 @@ instructions: ->
## 9. Flow Control
Flow control determines how execution moves between topics and responds to conditions.
Flow control determines how execution moves between subagents and responds to conditions.
**Start agent topic** — the mandatory entry point:
**Start agent subagent** — the mandatory entry point:
Every conversation begins at `start_agent`. The LLM classifies the user's intent and routes to the appropriate topic:
Every conversation begins at `start_agent`. The LLM classifies the user's intent and routes to the appropriate subagent:
```agentscript
start_agent agent_router:
description: "Route to appropriate topic"
description: "Route to appropriate subagent"
reasoning:
instructions: ->
| Welcome. I can help with orders, accounts, or billing.
@ -613,7 +613,7 @@ Expose the transition as a reasoning action when the LLM should judge the right
reasoning:
actions:
go_next: @utils.transition to @subagent.next_topic
description: "Move to the next topic"
description: "Move to the next subagent"
available when @variables.ready == True
```
@ -635,18 +635,18 @@ The runtime evaluates the condition and transitions immediately. Do NOT use `@ut
**Delegation with return**:
When a topic needs another topic's expertise but still has work to do afterward, use `@subagent.X` to delegate. The target topic runs its reasoning, then returns control to the caller:
When a subagent needs another subagent's expertise but still has work to do afterward, use `@subagent.X` to delegate. The target subagent runs its reasoning, then returns control to the caller:
```agentscript
reasoning:
actions:
ask_expert: @subagent.expert_consultation
description: "Consult the expert topic"
description: "Consult the expert subagent"
```
This is different from `@utils.transition to`, which is one-way — the calling topic does not resume.
This is different from `@utils.transition to`, which is one-way — the calling subagent does not resume.
**Conditional branching within topics**:
**Conditional branching within subagents**:
Conditions in reasoning instructions control which prompt text the LLM ultimately receives. The runtime evaluates `if`/`else` branches and includes only the matching `|` pipe sections in the resolved prompt:
@ -665,7 +665,7 @@ reasoning:
Actions invoke Flows, Apex classes, Prompt Templates, or other target types. They can run deterministically (the runtime always executes them) or be exposed as tools for the LLM to choose at reasoning time.
**Action definition** — each action is defined in the topic's `actions` block with required and optional properties:
**Action definition** — each action is defined in the subagent's `actions` block with required and optional properties:
```agentscript
actions:
@ -877,7 +877,7 @@ run @actions.fetch_order
Utility functions control flow and state. They do not call external systems.
**`@utils.transition to`** — permanent one-way handoff to another topic:
**`@utils.transition to`** — permanent one-way handoff to another subagent:
```agentscript
reasoning:
@ -887,7 +887,7 @@ reasoning:
available when @variables.cart_has_items == True
```
Transition discards the current topic's prompt and starts fresh with the target topic.
Transition discards the current subagent's prompt and starts fresh with the target subagent.
**`@utils.escalate`** — route to a human agent (**service agents only** — requires a `connection messaging:` block, which is only valid for `AgentforceServiceAgent`; do not use in employee agents):
@ -914,7 +914,7 @@ reasoning:
The LLM extracts values from the conversation and populates the specified variables.
**`@subagent.X`** — delegation to another topic with return:
**`@subagent.X`** — delegation to another subagent with return:
```agentscript
reasoning:
@ -924,7 +924,7 @@ reasoning:
available when @variables.needs_expert_help == True
```
Calling a topic as a tool runs that topic's reasoning, then returns control to the calling topic.
Calling a subagent as a tool runs that subagent's reasoning, then returns control to the calling subagent.
**Post-action directives apply only to `@actions`, not `@utils`**:
@ -1204,7 +1204,7 @@ Always pair actions with guiding instructions in the reasoning block.
---
**WRONG: Gate topic transitions to router via `after_reasoning` without defensive instructions**
**WRONG: Gate subagent transitions to router via `after_reasoning` without defensive instructions**
```agentscript
# WRONG — the router processes the gate's triggering message in the same turn
@ -1226,7 +1226,7 @@ subagent agent_router:
- Anything else → @subagent.off_topic
```
**Why it fails:** When `collect_username` captures the username and `after_reasoning` transitions to `agent_router`, both topics process in the same user turn. The router's reasoning fires against the user's original message (e.g., "My username is vivek.chawla"), not a fresh utterance. Since that message doesn't match any domain topic, the router sends it to `off_topic`.
**Why it fails:** When `collect_username` captures the username and `after_reasoning` transitions to `agent_router`, both subagents process in the same user turn. The router's reasoning fires against the user's original message (e.g., "My username is vivek.chawla"), not a fresh utterance. Since that message doesn't match any domain subagent, the router sends it to `off_topic`.
**CORRECT:**
@ -1242,7 +1242,7 @@ subagent collect_username:
subagent agent_router:
reasoning:
instructions: ->
| Route the customer's message to the right topic.
| Route the customer's message to the right subagent.
If the customer just arrived from the username collection
step, greet them and ask how you can help — do NOT route
their previous message.
@ -1252,4 +1252,4 @@ subagent agent_router:
- Anything else → @subagent.off_topic
```
This pattern applies whenever a gate topic transitions into a routing topic via `after_reasoning`.
This pattern applies whenever a gate subagent transitions into a routing subagent via `after_reasoning`.

View File

@ -5,7 +5,7 @@
- [Purpose and Context](#purpose-and-context)
- [Fundamental Structure Rules](#fundamental-structure-rules)
- [Node Types and Agent Script Elements](#node-types-and-agent-script-elements)
- [Subagent Map Patterns](#topic-map-patterns)
- [Subagent Map Patterns](#subagent-map-patterns)
- [Complete Example: Local_Info_Agent](#complete-example-local_info_agent)
- [Validation Checklist](#validation-checklist)
- [Anti-patterns](#anti-patterns)
@ -14,12 +14,12 @@
## Purpose and Context
A Subagent Map diagram is a Mermaid flowchart that visualizes an agent's topic graph structure. It shows the architecture of an agent before implementation, displaying:
A Subagent Map diagram is a Mermaid flowchart that visualizes an agent's subagent graph structure. It shows the architecture of an agent before implementation, displaying:
- The start_agent agent_router entry point
- All topics in the agent
- Topic transitions and routing logic
- Action calls within topics (with backing type: Apex, Prompt Template, Flow)
- All subagents in the agent
- Subagent transitions and routing logic
- Action calls within subagents (with backing type: Apex, Prompt Template, Flow)
- Gating conditions (available_when expressions)
- Variable state changes
- Escalation and off-topic handling
@ -35,14 +35,14 @@ Subagent Map diagrams are the primary visual deliverable in an Agent Spec (desig
- ALWAYS use `graph TD` (Top-Down orientation)
- Start with start_agent agent_router at the top
- Topics flow downward from the selector
- Subagents flow downward from the router
- Never use other orientations
### Node Identification
- Use sequential capital letters (A, B, C, ...) for node IDs
- Start with `A` for start_agent
- Increment sequentially through topics and decisions
- Increment sequentially through subagents and decisions
- Use descriptive labels within brackets
### Flow Direction
@ -50,17 +50,17 @@ Subagent Map diagrams are the primary visual deliverable in an Agent Spec (desig
- Primary flow moves top-to-bottom
- Use `-->` for standard transitions
- Label decision branches with `|Label|` syntax
- Separate paths for different topics
- Separate paths for different subagents
---
## Node Types and Agent Script Elements
### Start Agent Topic Selector Node
### Start Agent Subagent Router Node
Format: `[start_agent<br/>agent_router]`
Represents the entry point where user input is evaluated and routed to appropriate topics.
Represents the entry point where user input is evaluated and routed to appropriate subagents.
```mermaid
%%{init: {'theme':'neutral'}}%%
@ -68,18 +68,18 @@ graph TD
A[start_agent<br/>agent_router]
```
### Topic Nodes
### Subagent Nodes
Format: `[topic_name<br/>Topic]`
Format: `[subagent_name<br/>Subagent]`
Represents a topic within the agent.
Represents a subagent within the agent.
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD
A[start_agent<br/>agent_router]
B[order_status<br/>Topic]
C[billing<br/>Topic]
B[order_status<br/>Subagent]
C[billing<br/>Subagent]
```
### Action Call Nodes
@ -93,7 +93,7 @@ Example: `[Call check_weather<br/>backing: Apex]`
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD
A[local_weather<br/>Topic] --> B[Call check_weather<br/>backing: Apex]
A[local_weather<br/>Subagent] --> B[Call check_weather<br/>backing: Apex]
```
### Decision/Gating Nodes
@ -102,12 +102,12 @@ Use curly braces `{}` for conditions. Common formats:
- Variable availability gates: `{Check: variable_name != empty?}`
- Conditional instructions: `{variable_name == value?}`
- Topic transition logic: `{user_intent matches?}`
- Subagent transition logic: `{user_intent matches?}`
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD
A[topic<br/>Topic] --> B{Check: guest_interests<br/>!= empty?}
A[subagent<br/>Subagent] --> B{Check: guest_interests<br/>!= empty?}
B -->|Yes| C[Call collect_events<br/>backing: Prompt Template]
B -->|No| D[Ask for clarification]
```
@ -133,32 +133,32 @@ For escalation and system utilities.
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD
A[escalation<br/>Topic] --> B[Call @utils.escalate]
A[escalation<br/>Subagent] --> B[Call @utils.escalate]
```
---
## Subagent Map Patterns
### Basic Topic with Single Action
### Basic Subagent with Single Action
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD
A[start_agent<br/>agent_router]
A -->|route to topic| B[simple_topic<br/>Topic]
A -->|route to subagent| B[simple_subagent<br/>Subagent]
B --> C[Call do_action<br/>backing: Apex]
C --> D[Continue]
```
### Topic with Gating Condition
### Subagent with Gating Condition
Available_when expressions prevent action execution until conditions are met.
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD
A[topic_with_gate<br/>Topic]
A[subagent_with_gate<br/>Subagent]
A --> B{Check: required_var<br/>!= empty?}
B -->|No| C[Instruction: collect info first]
B -->|Yes| D[Call action<br/>backing: Prompt Template]
@ -166,9 +166,9 @@ graph TD
E --> A
```
### Topic with Conditional Instructions
### Subagent with Conditional Instructions
Variable values control which instructions apply to a topic.
Variable values control which instructions apply to a subagent.
```mermaid
%%{init: {'theme':'neutral'}}%%
@ -180,18 +180,18 @@ graph TD
D --> E[Continue]
```
### Topic Transitions
### Subagent Transitions
When logic determines a new topic should be active.
When logic determines a new subagent should be active.
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD
A[current_topic<br/>Topic]
A[current_subagent<br/>Subagent]
A --> B{Transition<br/>condition?}
B -->|Yes| C[Transition to<br/>next_topic]
C --> D[next_topic<br/>Topic]
B -->|No| E[Continue in<br/>current_topic]
B -->|Yes| C[Transition to<br/>next_subagent]
C --> D[next_subagent<br/>Subagent]
B -->|No| E[Continue in<br/>current_subagent]
```
### Off-Topic and Escalation Routing
@ -202,8 +202,8 @@ How the agent handles out-of-scope requests.
%%{init: {'theme':'neutral'}}%%
graph TD
A[start_agent<br/>agent_router]
A -->|out of scope| B[off_topic<br/>Topic]
A -->|needs help| C[escalation<br/>Topic]
A -->|out of scope| B[off_topic<br/>Subagent]
A -->|needs help| C[escalation<br/>Subagent]
B --> D[Instruction: redirect user]
C --> E[Call @utils.escalate]
```
@ -212,19 +212,19 @@ graph TD
## Complete Example: Local_Info_Agent
This example demonstrates a complete Subagent Map for a guest information agent with multiple topics, gating conditions, variable state, and escalation handling.
This example demonstrates a complete Subagent Map for a guest information agent with multiple subagents, gating conditions, variable state, and escalation handling.
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD
A[start_agent<br/>agent_router]
A -->|weather query| B[local_weather<br/>Topic]
A -->|events query| C[local_events<br/>Topic]
A -->|hours query| D[resort_hours<br/>Topic]
A -->|unclear intent| E[ambiguous_question<br/>Topic]
A -->|out of scope| F[off_topic<br/>Topic]
A -->|needs escalation| G[escalation<br/>Topic]
A -->|weather query| B[local_weather<br/>Subagent]
A -->|events query| C[local_events<br/>Subagent]
A -->|hours query| D[resort_hours<br/>Subagent]
A -->|unclear intent| E[ambiguous_question<br/>Subagent]
A -->|out of scope| F[off_topic<br/>Subagent]
A -->|needs escalation| G[escalation<br/>Subagent]
B --> B1[Call check_weather<br/>backing: Apex]
B1 --> B2[Continue]
@ -248,14 +248,14 @@ graph TD
E1 --> E2[Await user input]
E2 --> A
F --> F1[Instruction: explain available topics]
F --> F1[Instruction: explain available subagents]
F1 --> F2[Continue]
G --> G1[Call @utils.escalate]
G1 --> G2[Continue]
```
### Topic Descriptions
### Subagent Descriptions
**local_weather**: Provides weather information via Apex-backed action. No preconditions.
@ -265,11 +265,11 @@ graph TD
**ambiguous_question**: No actions. Requests clarification and routes back to start_agent.
**off_topic**: No actions. Explains available topics and continues conversation.
**off_topic**: No actions. Explains available subagents and continues conversation.
**escalation**: Calls @utils.escalate utility to route to human agent.
**start_agent agent_router**: Routes incoming user input to appropriate topics based on intent.
**start_agent agent_router**: Routes incoming user input to appropriate subagents based on intent.
---
@ -281,14 +281,14 @@ Before finalizing a Subagent Map diagram:
- [ ] Starts with `%%{init: {'theme':'neutral'}}%%`
- [ ] start_agent agent_router is node A at top
- [ ] Nodes use sequential capital letter IDs
- [ ] All topics labeled with `[topic_name<br/>Topic]` format
- [ ] All subagents labeled with `[subagent_name<br/>Subagent]` format
- [ ] Action calls include backing type (Apex, Prompt Template, Flow)
- [ ] Gating conditions shown as decision nodes with `{Check: ...?}` format
- [ ] Variable state changes explicitly labeled with `[Set variable = value]`
- [ ] Escalation uses `[Call @utils.escalate]` format
- [ ] All transition branches are labeled
- [ ] Diagram fits in 20-30 nodes
- [ ] Topic routing from start_agent is clear
- [ ] Subagent routing from start_agent is clear
- [ ] Off-topic and escalation paths are visible
- [ ] Conditional instruction logic is shown
@ -304,20 +304,20 @@ Before finalizing a Subagent Map diagram:
- Use ambiguous decision node labels (avoid `{Process?}`)
- Hide gating conditions in node descriptions instead of showing as decisions
- Omit variable state changes that affect downstream behavior
- Create topic routing without labels on the decision logic
- Mix topic nodes with action nodes at same level without clear containment
- Create subagent routing without labels on the decision logic
- Mix subagent nodes with action nodes at same level without clear containment
- Use custom color styling (breaks in dark mode)
- Leave off-topic and escalation paths out of diagram
### Do
- Keep start_agent agent_router at the top
- Show all topics reachable from start_agent
- Show all subagents reachable from start_agent
- Include backing type for every action call
- Make gating conditions explicit as decision nodes
- Show variable updates as separate nodes when they affect logic flow
- Label all transition branches
- Include off-topic and escalation topics
- Include off-topic and escalation subagents
- Show conditional instructions with decision nodes
- Use `%%{init: {'theme':'neutral'}}%%` for light/dark mode compatibility
- Focus diagram on topic structure, not detailed action logic
- Focus diagram on subagent structure, not detailed action logic

View File

@ -168,7 +168,7 @@ process: @actions.process_order
set @variables.result = @outputs.result
```
Post-action directives (`set`, `run`, `if`, `transition`) only work after `@actions.*` invocations. Utility actions (`@utils.*`) and topic delegates (`@subagent.*`) do not produce outputs, so post-action directives are not applicable.
Post-action directives (`set`, `run`, `if`, `transition`) only work after `@actions.*` invocations. Utility actions (`@utils.*`) and subagent delegates (`@subagent.*`) do not produce outputs, so post-action directives are not applicable.
---
@ -336,7 +336,7 @@ If multiple agents have concurrent sessions against the same agent, omitting the
### Context Variable Limitations in Preview
Agent behavior requiring `@context` or `@session` variables for routing or guards CAN NOT be tested via `sf agent preview`. Commands in the `preview` topic DO NOT support context or session variable injection. Flags like `--context`, `--session-var`, or `--variables` DO NOT EXIST.
Agent behavior requiring `@context` or `@session` variables for routing or guards CAN NOT be tested via `sf agent preview`. Commands in the `preview` command DO NOT support context or session variable injection. Flags like `--context`, `--session-var`, or `--variables` DO NOT EXIST.
- `@session.sessionID`, `@context.customerId`, `@context.RoutableId` — do NOT work in preview.
- Mutable variables with default values — work normally in preview.
@ -346,16 +346,16 @@ Agent behavior requiring `@context` or `@session` variables for routing or guard
Utterances provided to `sf agent preview send` must be derived from the `.agent` file using these guidelines:
1. **One per non-start topic** — based on `description:` keywords. Pick the most natural user phrasing.
1. **One per non-start subagent** — based on `description:` keywords. Pick the most natural user phrasing.
2. **One that should trigger each key action** — match the action's `description:` to a realistic user request.
3. **One off-topic utterance** — tests guardrails (e.g., "Tell me a joke", "What's the weather?").
4. **One multi-turn pair** — if agent has topic transitions, send two related utterances to test handoff (e.g., "Check my order" → "Actually I want to return it").
4. **One multi-turn pair** — if agent has subagent transitions, send two related utterances to test handoff (e.g., "Check my order" → "Actually I want to return it").
---
## 4. Session Traces
After each utterance in a preview session, the runtime writes trace files. Traces show the complete execution path: what topic was selected, what variables were set, what the LLM saw in its prompt, what it decided to do, and whether the response passed grounding.
After each utterance in a preview session, the runtime writes trace files. Traces show the complete execution path: what subagent was selected, what variables were set, what the LLM saw in its prompt, what it decided to do, and whether the response passed grounding.
### Trace File Location
@ -394,14 +394,14 @@ Each trace step type reveals specific execution information:
- **`UserInputStep`** — The user's utterance that triggered this turn.
- **`SessionInitialStateStep`** — Variable values and directive context at turn start.
- **`NodeEntryStateStep`** — Which agent/topic is executing and its full state snapshot.
- **`NodeEntryStateStep`** — Which agent/subagent is executing and its full state snapshot.
- **`VariableUpdateStep`** — A variable was changed — shows old/new value and reason.
- **`BeforeReasoningIterationStep`** — `before_reasoning` block ran — lists actions executed.
- **`EnabledToolsStep`** — Which tools/actions are available to the LLM for this reasoning cycle.
- **`LLMStep`** — The LLM call — full prompt, response, available tools, latency.
- **`FunctionStep`** — An action executed — shows input, output, and latency.
- **`ReasoningStep`** — Grounding check result — `GROUNDED` or `UNGROUNDED` with reason.
- **`TransitionStep`** — Topic transition — shows from/to topics and transition type.
- **`TransitionStep`** — Subagent transition — shows from/to subagents and transition type.
- **`PlannerResponseStep`** — Final response delivered to user — includes safety scores.
@ -410,19 +410,19 @@ Each trace step type reveals specific execution information:
Read steps in chronological order:
1. Locate `UserInputStep` — the trigger for this turn
2. Check `NodeEntryStateStep` — which topic is running and what is the current variable state?
2. Check `NodeEntryStateStep` — which subagent is running and what is the current variable state?
3. Look for `EnabledToolsStep` — what actions are available to the LLM?
4. Find `LLMStep` — examine `messages_sent` (the full prompt), `tools_sent` (available actions), and `response_messages` (what the LLM chose to do)
5. If an action was called, find the corresponding `FunctionStep` — compare inputs sent and outputs received
6. Check `ReasoningStep` — did the response pass grounding?
7. Look for `TransitionStep` — did the agent move to another topic?
7. Look for `TransitionStep` — did the agent move to another subagent?
8. Check `PlannerResponseStep` — what did the user receive?
### The LLMStep in Detail
The `LLMStep` is the most diagnostic step type. It contains:
- `agent_name` — which topic or selector is running
- `agent_name` — which subagent or router is running
- `messages_sent` — the FULL prompt sent to the LLM (system message, conversation history, and injected instructions)
- `tools_sent` — action names available to the LLM
- `response_messages` — the LLM's response (text or tool invocation)
@ -437,10 +437,10 @@ The `messages_sent` array shows you exactly what the LLM saw. This is invaluable
### When to Use Traces vs. Transcript
Use the **transcript** to quickly identify WHICH turn failed (unexpected response, wrong topic, agent crash).
Use the **transcript** to quickly identify WHICH turn failed (unexpected response, wrong subagent, agent crash).
Use the **trace files** when:
- The agent routes to the wrong topic
- The agent routes to the wrong subagent
- An action isn't firing
- The response is unexpectedly worded
- Grounding is failing
@ -453,13 +453,13 @@ The transcript is sufficient for conversation-level understanding. Traces provid
Use these `jq` commands against trace files (`traces/<PLAN_ID>.json`) to quickly extract diagnostic information.
#### Check 1: Topic Routing
#### Check 1: Subagent Routing
```bash
jq '[.steps[] | select(.stepType == "TransitionStep") | .data.to]' "$TRACE"
```
**Expected**: Array contains the target topic name (e.g., `["order_mgmt"]`). Empty array means the agent stayed in Topic Selector — topic descriptions are too vague. Wrong topic name means keyword overlap between topics.
**Expected**: Array contains the target subagent name (e.g., `["order_mgmt"]`). Empty array means the agent stayed in Subagent Router — subagent descriptions are too vague. Wrong subagent name means keyword overlap between subagents.
#### Check 2: Action Invocation
@ -467,7 +467,7 @@ jq '[.steps[] | select(.stepType == "TransitionStep") | .data.to]' "$TRACE"
jq '[.steps[] | select(.stepType == "FunctionStep") | .data.function]' "$TRACE"
```
**Expected**: Array contains the target action name. If missing: `available when:` guards too restrictive, action `description:` doesn't match user request, or action not listed in `reasoning.actions:` for this topic.
**Expected**: Array contains the target action name. If missing: `available when:` guards too restrictive, action `description:` doesn't match user request, or action not listed in `reasoning.actions:` for this subagent.
#### Check 3: Wrong Action Selected
@ -495,7 +495,7 @@ jq '.steps[] | select(.stepType == "PlannerResponseStep") | .data.safetyScore' "
jq '[.steps[] | select(.stepType == "EnabledToolsStep") | .data.enabled_tools]' "$TRACE"
```
**Expected**: Array includes the action names defined in the topic's `reasoning.actions:`. If missing: `available when:` conditions not met, action defined in wrong topic, or action `target:` protocol invalid (flow not deployed, apex class not found).
**Expected**: Array includes the action names defined in the subagent's `reasoning.actions:`. If missing: `available when:` conditions not met, action defined in wrong subagent, or action `target:` protocol invalid (flow not deployed, apex class not found).
---
@ -503,25 +503,25 @@ jq '[.steps[] | select(.stepType == "EnabledToolsStep") | .data.enabled_tools]'
These patterns map symptoms to trace analysis techniques. Each pattern follows the same structure: symptom → which trace steps to examine → root cause → fix (with code example).
### Pattern: Wrong Topic Routing
### Pattern: Wrong Subagent Routing
**Symptom:** The agent enters the wrong topic. For example, asking about weather sends the agent to the events topic instead.
**Symptom:** The agent enters the wrong subagent. For example, asking about weather sends the agent to the events subagent instead.
**Trace Analysis:**
1. Find the `LLMStep` where `agent_name` is `agent_router` (the entry point that routes to topics)
2. Examine `tools_sent` — are the transition actions for all expected topics listed? (e.g., `go_to_local_weather`, `go_to_local_events`, `go_to_resort_hours`)
1. Find the `LLMStep` where `agent_name` is `agent_router` (the entry point that routes to subagents)
2. Examine `tools_sent` — are the transition actions for all expected subagents listed? (e.g., `go_to_local_weather`, `go_to_local_events`, `go_to_resort_hours`)
3. Examine `response_messages` — which action tool did the LLM select?
4. Examine `messages_sent` — does the system prompt (what topic selector instructions were compiled to) give the LLM enough context to route correctly?
4. Examine `messages_sent` — does the system prompt (what subagent router instructions were compiled to) give the LLM enough context to route correctly?
**Root Cause:** Topic selector instructions are ambiguous, missing context, or don't map user requests to the correct topics.
**Root Cause:** Subagent router instructions are ambiguous, missing context, or don't map user requests to the correct subagents.
**Fix:** A minimal topic selector with well-named actions often routes correctly. When it doesn't, add routing instructions and action descriptions to give the LLM more context:
**Fix:** A minimal subagent router with well-named actions often routes correctly. When it doesn't, add routing instructions and action descriptions to give the LLM more context:
```agentscript
# BEFORE — relies on action names alone for routing
start_agent agent_router:
description: "Route to appropriate topics"
description: "Route to appropriate subagents"
reasoning:
actions:
go_to_weather: @utils.transition to @subagent.local_weather
@ -529,20 +529,20 @@ start_agent agent_router:
# AFTER — explicit instructions and descriptions improve routing accuracy
start_agent agent_router:
description: "Route to appropriate topics"
description: "Route to appropriate subagents"
reasoning:
instructions: ->
| If the user asks about weather conditions, temperature, or forecasts, go to the weather topic.
If the user asks about local events, activities, or entertainment, go to the events topic.
If the user asks about facility hours, reservations, or amenities, go to the hours topic.
| If the user asks about weather conditions, temperature, or forecasts, go to the weather subagent.
If the user asks about local events, activities, or entertainment, go to the events subagent.
If the user asks about facility hours, reservations, or amenities, go to the hours subagent.
actions:
go_to_weather: @utils.transition to @subagent.local_weather
description: "Route to weather topic for weather questions"
description: "Route to weather subagent for weather questions"
go_to_events: @utils.transition to @subagent.local_events
description: "Route to events topic for local event questions"
description: "Route to events subagent for local event questions"
go_to_hours: @utils.transition to @subagent.resort_hours
description: "Route to hours topic for facility hours questions"
description: "Route to hours subagent for facility hours questions"
```
@ -552,7 +552,7 @@ start_agent agent_router:
**Trace Analysis:**
1. Find the `EnabledToolsStep` for the topic — is the expected action listed?
1. Find the `EnabledToolsStep` for the subagent — is the expected action listed?
2. If missing:
- Check the action definition's `available when` condition (e.g., `available when @variables.guest_interests != ""`)
- Look at the `NodeEntryStateStep` to see if the gating variable has the expected value
@ -595,12 +595,12 @@ reasoning:
**Symptom:** The agent keeps asking the same question or repeating the same response across multiple turns, even though the user already provided the requested information.
**Diagnosis:** Observe the conversation output first — the behavioral symptom is often obvious (e.g., the agent asking the same question repeatedly). A common cause is instructions that collect information and act on it within the same topic — when the topic is re-entered, the collection logic runs again even though the data was already gathered.
**Diagnosis:** Observe the conversation output first — the behavioral symptom is often obvious (e.g., the agent asking the same question repeatedly). A common cause is instructions that collect information and act on it within the same subagent — when the subagent is re-entered, the collection logic runs again even though the data was already gathered.
**Fix Example:** In this real scenario, the `local_events` topic asks about interests and then looks up events. But each time the topic is re-entered, the agent asks about interests again instead of checking whether it already knows them:
**Fix Example:** In this real scenario, the `local_events` subagent asks about interests and then looks up events. But each time the subagent is re-entered, the agent asks about interests again instead of checking whether it already knows them:
```agentscript
# BEFORE — agent asks about interests every time the topic is entered
# BEFORE — agent asks about interests every time the subagent is entered
reasoning:
instructions: ->
| If you do not already know the guest's interests, ask them about their
@ -648,11 +648,11 @@ Note: repeated `LLMStep` → `ReasoningStep` pairs in a trace may indicate groun
1. Find the `PlannerResponseStep` — is the message the system error message?
2. Look backward through the trace for consecutive `ReasoningStep` entries with `category: "UNGROUNDED"` — two consecutive UNGROUNDED results cause this error
3. If no grounding failures, look for `FunctionStep` entries with error outputs (action execution failed)
4. Check if a topic transition failed (the target topic doesn't exist or has a circular reference)
4. Check if a subagent transition failed (the target subagent doesn't exist or has a circular reference)
**Root Cause:** Grounding failed twice in a row, OR an action returned an error, OR a topic transition is misconfigured.
**Root Cause:** Grounding failed twice in a row, OR an action returned an error, OR a subagent transition is misconfigured.
**Fix:** See Diagnostic Workflow: Grounding subsection for grounding failures. For action errors, verify the backing Apex/Flow/Prompt Template is deployed and handles edge cases correctly. For transition errors, verify all referenced topics exist and are spelled correctly.
**Fix:** See Diagnostic Workflow: Grounding subsection for grounding failures. For action errors, verify the backing Apex/Flow/Prompt Template is deployed and handles edge cases correctly. For transition errors, verify all referenced subagents exist and are spelled correctly.
### Pattern: Agent Responds with Generic Message but No Data After Successful Action
@ -669,7 +669,7 @@ Note: repeated `LLMStep` → `ReasoningStep` pairs in a trace may indicate groun
| Failure | Target Block | Edit Strategy | Example |
|---------|-------------|---------------|---------|
| Topic not matched | `topic X: description:` | Add keywords from test utterance | `"Handle orders"``"Handle order queries, order status, package tracking, shipping updates"` |
| Subagent not matched | `subagent X: description:` | Add keywords from test utterance | `"Handle orders"``"Handle order queries, order status, package tracking, shipping updates"` |
| Action not invoked | `reasoning.actions: X description:` | Make description more trigger-specific | `"Get order"``"Look up order status when user asks about their order, package, or delivery"` |
| Action not invoked | `available when:` | Relax guard condition | Remove overly restrictive `@variables.X == True` if variable isn't set yet |
| Wrong action selected | Both competing `description:` fields | Differentiate with exclusion language | Add `"NOT for returns"` to order action, `"ONLY for returns"` to refund action |
@ -690,7 +690,7 @@ Use this systematic 8-step approach when diagnosing any agent behavior issue.
3. **Read the Trace** — Open `traces/<PLAN_ID>.json` for the failing turn. Read the plan array in order.
4. **Follow Execution** — As you read each step, note:
- Which topic was selected? (Look at `NodeEntryStateStep`)
- Which subagent was selected? (Look at `NodeEntryStateStep`)
- What state were variables in? (Look at `SessionInitialStateStep` and `VariableUpdateStep`)
- What actions were available vs. invoked? (Look at `EnabledToolsStep` and `LLMStep` response)
- What did the LLM see in its prompt? (Look at `LLMStep.messages_sent`)
@ -723,7 +723,7 @@ When the platform's grounding checker flags a response as UNGROUNDED:
```
2. The LLM is given another chance to respond
3. If the second attempt is also UNGROUNDED, the agent returns the system error message ("I apologize, but I encountered an unexpected error") and gives up
4. This retry is visible in traces as repeated `LLMStep``ReasoningStep` pairs for the same topic
4. This retry is visible in traces as repeated `LLMStep``ReasoningStep` pairs for the same subagent
5. When this happens, the actual action output is still in the trace's `FunctionStep.function.output`. The LLM's failed response attempts are in the `LLMStep.response_messages`. Use these to understand what the agent tried to say versus what the action actually returned.

View File

@ -187,9 +187,9 @@ KNOWN BUG: Chained actions with Prompt Templates don't properly map inputs using
For prompt template action definitions, input binding syntax, and grounded data patterns, see [Action Prompt Templates](action-prompt-templates.md).
## Latch Variable Pattern for Topic Re-entry
## Latch Variable Pattern for Subagent Re-entry
Topic selector doesn't properly re-evaluate after user provides missing input. Use a "latch" variable to force re-entry:
Subagent router doesn't properly re-evaluate after user provides missing input. Use a "latch" variable to force re-entry:
```yaml
variables:
@ -219,9 +219,9 @@ subagent verification:
## Loop Protection Guardrail
Agent Scripts have a built-in guardrail that limits iterations to approximately **3-4 loops** before breaking out and returning to the Topic Selector.
Agent Scripts have a built-in guardrail that limits iterations to approximately **3-4 loops** before breaking out and returning to the Subagent Router.
**Best Practice**: Map out your execution paths and test for unintended circular references between topics.
**Best Practice**: Map out your execution paths and test for unintended circular references between subagents.
## Token & Size Limits

View File

@ -16,7 +16,7 @@ Automated testing for Agentforce agents with smoke tests, batch execution, and i
## Overview
This skill provides comprehensive testing capabilities for Agentforce agents, including automated utterance derivation from agent topics, preview-based smoke testing, trace analysis, and an iterative fix loop for identified issues. It bridges the gap between initial development and production deployment.
This skill provides comprehensive testing capabilities for Agentforce agents, including automated utterance derivation from agent subagents, preview-based smoke testing, trace analysis, and an iterative fix loop for identified issues. It bridges the gap between initial development and production deployment.
## Platform Notes
@ -83,10 +83,10 @@ This skill supports two testing modes plus direct action execution:
### Test Case Planning
If no utterances file is provided, auto-derive test cases from the `.agent` file:
1. **Topic-based utterances** -- one per non-start topic from description keywords
1. **Subagent-based utterances** -- one per non-start subagent from description keywords
2. **Action-based utterances** -- target each key action
3. **Guardrail test** -- off-topic utterance
4. **Multi-turn scenarios** -- topic transitions
4. **Multi-turn scenarios** -- subagent transitions
5. **Safety probes** -- adversarial utterances (always included)
**Always present the plan first** -- never silently auto-run tests without showing what will be tested. Ask the user to review/modify before executing.
@ -171,13 +171,13 @@ Max 3 iterations. For each failure, diagnose from trace and apply targeted fix:
| Failure Type | Fix Location | Fix Strategy |
|--------------|--------------|--------------|
| TOPIC_NOT_MATCHED | `topic: description:` | Add keywords from utterance |
| TOPIC_NOT_MATCHED | `subagent: description:` | Add keywords from utterance |
| ACTION_NOT_INVOKED | `available when:` | Relax guard conditions |
| WRONG_ACTION | Action descriptions | Add exclusion language |
| UNGROUNDED | `instructions: ->` | Add `{!@variables.x}` references |
| LOW_SAFETY | `system: instructions:` | Add safety guidelines |
| DEFAULT_TOPIC | `topic: description:` or `start_agent: actions:` | Add keywords or transition actions |
| NO_ACTIONS_IN_TOPIC | `topic: reasoning: actions:` | Add `reasoning: actions:` block |
| DEFAULT_TOPIC | `subagent: description:` or `start_agent: actions:` | Add keywords or transition actions |
| NO_ACTIONS_IN_TOPIC | `subagent: reasoning: actions:` | Add `reasoning: actions:` block |
See `references/preview-testing.md` for full diagnosis table mapping trace steps to failures.
@ -209,7 +209,7 @@ testCases:
```
**Key rules:**
- `expectedActions` is a **flat string array** with **Level 2 invocation names** (from `reasoning: actions:`), NOT Level 1 definition names (from `topic: actions:`)
- `expectedActions` is a **flat string array** with **Level 2 invocation names** (from `reasoning: actions:`), NOT Level 1 definition names (from `subagent: actions:`)
- Action assertion uses **superset matching** -- test PASSES if actual actions include all expected
- **Always add `expectedOutcome`** -- most reliable assertion type (LLM-as-judge)
- For guardrail tests, omit `expectedTopic` and use `expectedOutcome` only. Filter out `topic_assertion` FAILURE for these (false negatives from empty assertion XML).
@ -246,7 +246,7 @@ for tc in data['result']['testCases']:
### Topic Name Resolution
Topic names in Testing Center may differ from `.agent` file names. If assertions fail on topic:
Topic names in Testing Center may differ from `.agent` file names. If assertions fail on subagent routing:
1. Run test with best-guess names
2. Check actual: `jq '.result.testCases[].generatedData.topic' /tmp/results.json`
3. Update YAML with actual runtime names and redeploy with `--force-overwrite`
@ -295,7 +295,7 @@ See `references/action-execution.md` for integration testing patterns, debugging
> Full reference: `references/test-report-format.md`
Reports include: topic routing %, action invocation %, grounding %, safety %, response quality %, overall score, and status (PASSED / PASSED WITH WARNINGS / FAILED). Safety verdict (SAFE/UNSAFE/NEEDS_REVIEW) is always included.
Reports include: subagent routing %, action invocation %, grounding %, safety %, response quality %, overall score, and status (PASSED / PASSED WITH WARNINGS / FAILED). Safety verdict (SAFE/UNSAFE/NEEDS_REVIEW) is always included.
### Test File Location Convention

View File

@ -8,6 +8,10 @@
#
# IMPORTANT: This YAML is parsed by @salesforce/agents — NOT a generic AiEvaluationDefinition format.
# Only the fields below are recognized. Do NOT add apiVersion, kind, metadata, or settings.
#
# NOTE: The Testing Center API uses "topic" terminology. In Agent Script, topics are called
# "subagents" (e.g., the `subagent` block). When writing tests, use "topic" to match the API,
# but understand that each expectedTopic value maps to a subagent in your .agent file.
# Required: Display name for the test (MasterLabel) — deploy FAILS without this
name: "<Agent_Name> Basic Tests"

View File

@ -11,6 +11,10 @@
# 1. Replace <placeholders> with actual values
# 2. Deploy: sf agent test create --spec guardrail-test-spec.yaml --api-name Guardrail_Tests --target-org <alias>
# 3. Run: sf agent test run --api-name Guardrail_Tests --wait 10 --result-format json --target-org <alias>
#
# NOTE: The Testing Center API uses "topic" terminology. In Agent Script, topics are called
# "subagents" (e.g., the `subagent` block). When writing tests, use "topic" to match the API,
# but understand that each expectedTopic value maps to a subagent in your .agent file.
name: "<Agent_Name> Guardrail Tests"
subjectType: AGENT

View File

@ -8,6 +8,10 @@
#
# IMPORTANT: This YAML is parsed by @salesforce/agents — NOT a generic AiEvaluationDefinition format.
# Only use the fields documented below.
#
# NOTE: The Testing Center API uses "topic" terminology. In Agent Script, topics are called
# "subagents" (e.g., the `subagent` block). When writing tests, use "topic" to match the API,
# but understand that each expectedTopic value maps to a subagent in your .agent file.
# Required: Display name for the test (MasterLabel)
name: "<Agent_Name> Standard Tests"

View File

@ -13,13 +13,13 @@ subjectType: AGENT
subjectName: OrderService # BotDefinition DeveloperName (API name)
testCases:
# Topic routing test
# Subagent routing test
- utterance: "Where is my order #12345?"
expectedTopic: order_status
# Action invocation test (FLAT string list -- NOT objects)
# CRITICAL: Use Level 2 INVOCATION names from reasoning: actions: (e.g. "lookup_order")
# NOT Level 1 DEFINITION names from topic: actions: (e.g. "get_order_status")
# NOT Level 1 DEFINITION names from subagent: actions: (e.g. "get_order_status")
- utterance: "I want to return my order from last week"
expectedTopic: returns
expectedActions:
@ -62,7 +62,7 @@ testCases:
| `subjectName` | Yes | Agent BotDefinition DeveloperName (API name, e.g. `OrderService`) |
| `testCases` | Yes | Array of test case objects |
| `testCases[].utterance` | Yes | User input message to test |
| `testCases[].expectedTopic` | No | Expected topic name |
| `testCases[].expectedTopic` | No | Expected subagent name |
| `testCases[].expectedActions` | No | Flat list of action name strings |
| `testCases[].expectedOutcome` | No | Natural language description (LLM-as-judge) |
| `testCases[].conversationHistory` | No | Prior conversation turns for multi-turn tests |
@ -81,7 +81,7 @@ testCases:
- Single-turn tests only capture the first response. If an action requires info collection first (e.g. identity verification asks for email before calling `verify_customer`), the action won't fire in one turn.
- For multi-turn workflows, either: (1) omit `expectedActions` and rely on `expectedOutcome`, or (2) use `conversationHistory` to simulate prior turns.
- For guardrail tests (off-topic), omit `expectedTopic` and use `expectedOutcome` only -- the agent correctly stays in `entry` which has no matching topic assertion. NOTE: The generated XML still includes an empty `topic_assertion` expectation, which will return `FAILURE` with score=0. This is expected and harmless -- only check the `output_validation` result for guardrail tests.
- For guardrail tests (off-topic), omit `expectedTopic` and use `expectedOutcome` only -- the agent correctly stays in `entry` which has no matching subagent assertion. NOTE: The generated XML still includes an empty `topic_assertion` expectation, which will return `FAILURE` with score=0. This is expected and harmless -- only check the `output_validation` result for guardrail tests.
### Parsing Results for Guardrail/Safety Tests
@ -187,15 +187,15 @@ For each failed test case:
1. **Topic assertion failed** -- compare `expectedValue` vs `actualValue`
- If actual is a hash-suffixed name (e.g. `p_16j...`), see Topic Name Resolution below
- If actual is wrong topic, fix the `.agent` file topic description
- If actual is wrong subagent, fix the `.agent` file subagent description
2. **Action assertion failed** -- check `generatedData.actionsSequence`
- If action not invoked: fix topic instructions or action `available when` guard
- If action not invoked: fix subagent instructions or action `available when` guard
- If wrong action: fix action descriptions to disambiguate
3. **Outcome validation failed** -- check `generatedData.outcome`
- Review the agent's actual response against `expectedOutcome`
- Tighten topic instructions to guide the response
- Tighten subagent instructions to guide the response
After fixing the `.agent` file, redeploy and re-run:
@ -211,16 +211,16 @@ sf agent test run --json --api-name <TestSuiteName> --wait 10 --result-format js
Topic names in Testing Center may differ from what you see in the `.agent` file:
| Topic type | Name to use in YAML | Example |
| Subagent type | Name to use in YAML | Example |
|---|---|---|
| Standard topics | `localDeveloperName` (short name) | `Escalation`, `Off_Topic` |
| Custom topics | Short name from `.agent` file | `home_search`, `warranty_service` |
| Custom subagents | Short name from `.agent` file | `home_search`, `warranty_service` |
| Promoted topics | Full runtime `developerName` with hash suffix | `p_16jPl000000GwEX_Topic_16j8eeef13560aa` |
**Discovery workflow** (when topic names don't match):
**Discovery workflow** (when subagent names don't match):
1. Run the test with best-guess topic names
2. Check actual topics in results: `jq '.result.testCases[].generatedData.topic' /tmp/test_results.json`
1. Run the test with best-guess subagent names
2. Check actual subagents in results: `jq '.result.testCases[].generatedData.topic' /tmp/test_results.json`
3. Update YAML with actual runtime names
4. Redeploy with `--force-overwrite` and re-run
@ -230,17 +230,17 @@ Topic names in Testing Center may differ from what you see in the `.agent` file:
Derive a Testing Center spec from the `.agent` file:
1. **One test case per non-entry topic** -- utterance from topic description keywords
1. **One test case per non-entry subagent** -- utterance from subagent description keywords
2. **One test case per key action** -- utterance that triggers the action's primary use case
3. **One guardrail test** -- off-topic utterance
4. **`expectedTopic`** from topic name in `.agent` file
4. **`expectedTopic`** from subagent name in `.agent` file
5. **`expectedActions`** from action names under `reasoning: actions:` (only `@actions.*`, not `@utils.transition`)
### Level 1 vs Level 2 Action Names (CRITICAL)
The `.agent` file has two levels of action definitions:
- **Level 1** (definition): under `topic > actions:` — defines target, inputs, outputs (e.g. `get_order_status:`)
- **Level 2** (invocation): under `topic > reasoning > actions:` — wires actions to the LLM (e.g. `check_order: @actions.get_order_status`)
- **Level 1** (definition): under `subagent > actions:` — defines target, inputs, outputs (e.g. `get_order_status:`)
- **Level 2** (invocation): under `subagent > reasoning > actions:` — wires actions to the LLM (e.g. `check_order: @actions.get_order_status`)
Testing Center reports **Level 2 invocation names** (e.g. `check_order`), NOT Level 1 definition names (e.g. `get_order_status`). Using Level 1 names in `expectedActions` causes action assertions to FAIL even when the agent correctly invokes the action. Always use the Level 2 name from `reasoning: actions:`.

View File

@ -6,10 +6,10 @@
If no utterances file is provided, derive test cases from the `.agent` file:
1. **Topic-based utterances** - One per non-start topic based on description keywords
1. **Subagent-based utterances** - One per non-start subagent based on description keywords
2. **Action-based utterances** - Target each key action's functionality
3. **Guardrail test** - Off-topic utterance to test boundaries
4. **Multi-turn scenarios** - Test topic transitions if defined
4. **Multi-turn scenarios** - Test subagent transitions if defined
5. **Safety probes** - Adversarial utterances to test safety boundaries (see below)
**Step 2: Present the derived tests and ask the user to review.**
@ -17,7 +17,7 @@ If no utterances file is provided, derive test cases from the `.agent` file:
```
Auto-generated test plan (8 utterances):
Topic tests:
Subagent tests:
1. "I need to check my order status" -> order_support
2. "I want to return an item" -> return_support
3. "What are your store hours?" -> general_support
@ -111,7 +111,7 @@ Compromised probes:
### Example Derivation from Agent Structure
```yaml
# Agent topics:
# Agent subagents:
subagent order_management:
description: "Handle order status, tracking, shipping"
actions:
@ -125,8 +125,8 @@ subagent returns:
- check_refund_status
# Derived utterances:
1. "Where is my order?" -> should route to order_management
2. "I want to return this item" -> should route to returns
1. "Where is my order?" -> should route to order_management subagent
2. "I want to return this item" -> should route to returns subagent
3. "Track my shipment" -> should invoke track_shipment action
4. "What's my refund status?" -> should invoke check_refund_status
5. "Tell me a joke" -> should trigger guardrail
@ -196,21 +196,21 @@ Each trace is a `PlanSuccessResponse` JSON with this root structure:
- `type` — always `"PlanSuccessResponse"`
- `planId` — unique plan ID for this turn
- `sessionId` — the preview session ID
- `subagent` — which topic handled this turn
- `subagent` — which subagent handled this turn
- `plan[]` — array of step objects (the execution trace)
## Phase 3: Trace Analysis
Analyze execution traces for 8 key aspects:
### 1. Topic Routing Verification
### 1. Subagent Routing Verification
```bash
# Which topic handled this turn (root-level field)
# Which subagent handled this turn (root-level field)
jq -r '.topic' "$TRACE"
# Detailed: which agent/topic was entered
# Detailed: which agent/subagent was entered
jq -r '.plan[] | select(.type == "NodeEntryStateStep") | .data.agent_name' "$TRACE"
```
Expected: Correct topic name matches the expected topic for the utterance.
Expected: Correct subagent name matches the expected subagent for the utterance.
### 2. Action Invocation Check
```bash
@ -301,38 +301,38 @@ If issues are detected, the system enters an automated fix loop (max 3 iteration
### Iteration Process
1. **Identify failure category**:
- `TOPIC_NOT_MATCHED` - Topic description too vague
- `TOPIC_NOT_MATCHED` - Subagent description too vague
- `ACTION_NOT_INVOKED` - Action guard too restrictive
- `WRONG_ACTION_SELECTED` - Action descriptions overlap
- `UNGROUNDED_RESPONSE` - Missing data references
- `LOW_SAFETY_SCORE` - Inadequate safety instructions
- `TOOL_NOT_VISIBLE` - Available when conditions not met
- `DEFAULT_TOPIC` - Trace shows `topic: "DefaultTopic"` — no real topic matched the utterance
- `NO_ACTIONS_IN_TOPIC` - `EnabledToolsStep` shows only guardrail tools; `BeforeReasoningIterationStep.data.action_names[]` shows only `__state_update_action__` entries — topic has no `reasoning: actions:` block
- `DEFAULT_TOPIC` - Trace shows `topic: "DefaultTopic"` — no real subagent matched the utterance
- `NO_ACTIONS_IN_TOPIC` - `EnabledToolsStep` shows only guardrail tools; `BeforeReasoningIterationStep.data.action_names[]` shows only `__state_update_action__` entries — subagent has no `reasoning: actions:` block
2. **Diagnose from trace** (when using `--authoring-bundle` with local traces):
| Failure | Trace step to inspect | What to look for |
|---------|----------------------|------------------|
| TOPIC_NOT_MATCHED | `NodeEntryStateStep` | `.data.agent_name` shows wrong topic |
| TOPIC_NOT_MATCHED | `NodeEntryStateStep` | `.data.agent_name` shows wrong subagent |
| ACTION_NOT_INVOKED | `EnabledToolsStep` | Action missing from `.data.enabled_tools[]` |
| UNGROUNDED_RESPONSE | `ReasoningStep` | `.category == "UNGROUNDED"`, read `.reason` |
| Variable not set | `VariableUpdateStep` | No update for expected variable |
| Wrong LLM behavior | `LLMStep` | Read `.data.messages_sent[0].content` to see what prompt was sent |
| DEFAULT_TOPIC | Root `.topic` field | Value is `"DefaultTopic"` instead of a real topic name — no topic matched |
| NO_ACTIONS_IN_TOPIC | `BeforeReasoningIterationStep` | `.data.action_names[]` shows only `__state_update_action__` — topic has no `reasoning: actions:` block |
| DEFAULT_TOPIC | Root `.topic` field | Value is `"DefaultTopic"` instead of a real subagent name — no subagent matched |
| NO_ACTIONS_IN_TOPIC | `BeforeReasoningIterationStep` | `.data.action_names[]` shows only `__state_update_action__`subagent has no `reasoning: actions:` block |
3. **Apply targeted fix**:
| Failure Type | Fix Location | Fix Strategy |
|--------------|--------------|--------------|
| TOPIC_NOT_MATCHED | `topic: description:` | Add keywords from utterance |
| TOPIC_NOT_MATCHED | `subagent: description:` | Add keywords from utterance |
| ACTION_NOT_INVOKED | `available when:` | Relax guard conditions |
| WRONG_ACTION | Action descriptions | Add exclusion language |
| UNGROUNDED | `instructions: ->` | Add `{!@variables.x}` references |
| LOW_SAFETY | `system: instructions:` | Add safety guidelines |
| DEFAULT_TOPIC | `topic: description:` or `start_agent: actions:` | No topic matched — add keywords to topic descriptions or add transition actions to `start_agent` |
| NO_ACTIONS_IN_TOPIC | `topic: reasoning: actions:` | Topic has zero actions — add `reasoning: actions:` block with transition and/or invocation actions |
| DEFAULT_TOPIC | `subagent: description:` or `start_agent: actions:` | No subagent matched — add keywords to subagent descriptions or add transition actions to `start_agent` |
| NO_ACTIONS_IN_TOPIC | `subagent: reasoning: actions:` | Subagent has zero actions — add `reasoning: actions:` block with transition and/or invocation actions |
4. **Validate fix** - LSP auto-validates on save
@ -343,7 +343,7 @@ If issues are detected, the system enters an automated fix loop (max 3 iteration
### Example Fix
```yaml
# Before (topic not matched)
# Before (subagent not matched)
subagent order_mgmt:
description: "Orders"

View File

@ -12,7 +12,7 @@ Test Cases: 6
Duration: 45.2s
Results:
Topic Routing: 5/6 passed (83.3%)
Subagent Routing: 5/6 passed (83.3%)
Action Invocation: 4/6 passed (66.7%)
Grounding: 6/6 passed (100%)
Safety: 6/6 passed (100%)
@ -37,21 +37,21 @@ Test Case 1: "Where is my order?"
Test Case 2: "I want to return this"
Expected Topic: returns
Actual Topic: order_mgmt (fail - misrouted)
Fix Applied: Expanded 'returns' topic description
Fix Applied: Expanded 'returns' subagent description
Retry Result: Correctly routed (pass)
```
## Coverage Analysis
Track which topics and actions are tested across both modes:
Track which subagents and actions are tested across both modes:
| Dimension | Target | How to measure |
|-----------|--------|----------------|
| Topic coverage | 100% of non-entry topics | Count topics with at least 1 test case |
| Subagent coverage | 100% of non-entry subagents | Count subagents with at least 1 test case |
| Action coverage | 100% of actions | Count actions with at least 1 test case targeting them |
| Phrasing diversity | 3+ utterances per topic (production) | Multiple wordings per intent |
| Phrasing diversity | 3+ utterances per subagent (production) | Multiple wordings per intent |
| Guardrail coverage | At least 1 off-topic test | Verify agent deflects non-relevant queries |
| Multi-turn coverage | Test topic transitions | Conversation history tests |
| Multi-turn coverage | Test subagent transitions | Conversation history tests |
| Escalation coverage | Test escalation triggers | Verify human handoff works |
## CI/CD with Testing Center