
Developing Agentforce
- 1.5k installs
- 763 repo stars
- Updated July 24, 2026
- forcedotcom/afv-library
This is a copy of developing-agentforce by forcedotcom - installs and ranking accrue to the original listing.
Developing-agentforce is a Claude Code skill that helps developers create complete, production-ready Salesforce Agentforce agent specifications including purpose, behavioral rules, subagent routing, variables, and backin
About
Developing-agentforce is a forcedotcom/afv-library skill for authoring Agentforce agent specs ready for implementation on Salesforce. Specs define purpose and scope, behavioral intent with guardrails and escalation rules, and a subagent map diagram routing from a start_agent router to specialized subagents including ambiguous-question handling. Backing logic types cover Apex, Flow, and Prompt Template with rules for what the agent must know before acting and what persists across subagent switches. Developers reach for this skill when scaffolding enterprise Agentforce agents instead of ad-hoc prompt drafts without routing or logic mappings.
- Generates full Agent Spec markdown with Purpose & Scope, Behavioral Intent, Subagent Map, Variables, and Actions & Backi
- Produces ready-to-use Mermaid subagent routing diagrams with explicit gating logic and state transitions
- Maps every action to concrete backing logic targets (Apex classes, Flows, or Prompt Templates)
- Enforces consistent variable tracking with mutability, defaults, setters and readers
- Includes guardrails, off-topic handling, escalation paths and persistence rules across subagents
Developing Agentforce by the numbers
- 1,548 all-time installs (skills.sh)
- +1 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/forcedotcom/afv-library --skill developing-agentforceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.5k |
|---|---|
| repo stars | ★ 763 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 24, 2026 |
| Repository | forcedotcom/afv-library ↗ |
How do you write an Agentforce agent specification?
Create complete, production-ready Agentforce agent specifications that include purpose, behavioral rules, subagent routing, variables, and backing logic mappings.
Who is it for?
Salesforce developers and architects implementing Agentforce agents who need structured specs before Apex, Flow, or prompt configuration.
Skip if: Non-Salesforce agent frameworks or teams only experimenting with generic LLM chatbots outside Agentforce.
When should I use this skill?
The user builds Agentforce agents, writes agent specs, designs subagent routing, or maps Apex, Flow, or Prompt Template backing logic.
What you get
Agent spec document, Mermaid subagent routing map, behavioral rules, variable definitions, and backing logic type mappings.
- Agent specification document
- Subagent routing diagram
- Backing logic mapping
Files
Agent Script Skill
What This Skill Is For
Agent Script is Salesforce's scripting language for authoring next-generation AI agents on the Atlas Reasoning Engine. Introduced in 2025 with zero training data in any AI model. Everything needed to write, modify, diagnose, or deploy Agent Script agents is in this skill's reference files.
⚠️CRITICAL: Agent Script is NOT AppleScript, JavaScript, Python, or any other language. Do NOT confuse Agent Script syntax or semantics with any other language you have been trained on.
Agent Script agents are defined by AiAuthoringBundle metadata — a directory with a .agent file containing Agent Script source that describes actions, instructions, subagents, flow control, and configuration; and a bundle-meta.xml file containing bundle metadata. Agents process utterances by routing through subagents, each with instructions and actions backed by Apex, Flows, Prompt Templates, and other types of backing logic.
This skill covers the full Agent Script lifecycle: designing agents, writing Agent Script code, validating and debugging, deploying and publishing, and testing.
How to Use This Skill
This file maps user intent to task domains and relevant reference files in references/. Detailed knowledge includes syntax rules, design patterns, CLI commands, debugging workflows, and more.
Identify user intent from task descriptions. ALWAYS read indicated reference files BEFORE starting work.
Rules That Always Apply
1. Always `--json`. ALWAYS include --json on EVERY sf CLI command. Do NOT pipe CLI output through jq or 2>/dev/null. Read the full JSON response directly — LLMs parse JSON natively.
2. Verify target org. Before any org interaction, run sf config get target-org --json to confirm a target org is set. If none configured, ask the user to set one with sf config set target-org <alias>.
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 subagent selection, action I/O, and LLM reasoning. DO NOT modify .agent files or backing logic without this grounding. See Validation & Debugging for trace file locations and diagnostic patterns.
4. Spec approval is a hard gate. Never proceed past Agent Spec creation without explicit user approval.
Task Domains
Every task domain below has Required Steps. Follow verbatim, in order. Do not substitute your own plan or skip steps.
Create an Agent
User wants to build new agent from scratch. ALWAYS use Agent Script. Work with User to understand the agent's purpose, subagents, and actions using plain language without Salesforce-specific terminology.
Required Steps
Read CLI for Agents for exact command syntax.
1. Design — Read Design & Agent Spec to draft an Agent Spec. Always ask if you should scan for existing backing logic. Unless instructed otherwise, scan by reading sfdx-project.json to identify package directories, then search each for @InvocableMethod in classes/, AutoLaunchedFlow in flows/, and template metadata in promptTemplates/. Mark matches EXISTS; unmatched actions NEEDS STUB. Also scan objects/ for .object-meta.xml to discover custom objects — related objects often contain data the agent should expose even when not mentioned in the prompt. Always save Agent Spec as file. 2. STOP for user approval of Agent Spec. Present to user. Ask for approval or feedback. Do not proceed without approval. Once approved, proceed without stopping unless a step fails. 3. Validate environment prerequisites — Read Design & Agent Spec, Section 3 (Environment Prerequisites). Based on agent type from design, validate org environment:
- Employee agent: Confirm config block does NOT include
default_agent_user,connection messaging:, or MessagingSession linked variables. Remove if present. See Examples for a complete employee agent example. - Service agent: Query org for Einstein Agent User. If one exists, confirm username with user. If none, guide user through creation. See CLI for Agents, Section 12 for creation steps and Agent User Setup for required permissions.
Do not proceed to code generation until environment is validated. 4. Generate authoring bundle — sf agent generate authoring-bundle --json --no-spec --name "<Label>" --api-name <Developer_Name> 5. Write code — Read Core Language for syntax, block structure, and anti-patterns. Edit generated .agent file using reference files and templates. Do not create .agent or bundle-meta.xml files manually. 6. Validate compilation — sf agent validate authoring-bundle --json --api-name <Developer_Name> If validation fails, read Validation & Debugging to diagnose and fix, then re-validate. ALWAYS fix syntax and structural errors before generating backing logic. 7. Generate backing logic — For each action marked NEEDS STUB: sf template generate apex class --name <ClassName> --output-dir <PACKAGE_DIR>/main/default/classes Replace class body with invocable pattern from Design & Agent Spec. ALWAYS deploy: sf project deploy start --json --metadata ApexClass:<ClassName> ALWAYS fix deploy errors BEFORE generating and deploying next stub. 8. Validate behavior — Read Validation & Debugging for preview workflow and session trace analysis. sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> If actions query data, ground test utterances with: 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 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-bundlepasses with zero errors- 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> If publish fails, follow troubleshooting checklist in Metadata & Lifecycle, Section 5 before retrying. 10. Activate — Makes new version available to users. sf agent activate --json --api-name <Developer_Name> 11. Verify published agent — Preview user-facing behavior AFTER activation with sf agent preview start --json --api-name <Developer_Name> Use --api-name, not --authoring-bundle. 12. Configure end-user access — ONLY for employee agents. Read Agent Access Guide to configure perms and assign access.
Reference Files
1. CLI for Agents — exact command syntax for generate, validate, deploy, publish, activate; Section 12 for Einstein Agent User creation 2. Core Language — execution model, syntax, block structure, anti-patterns 3. Design & Agent Spec — subagent graph design, flow control patterns, Agent Spec production, backing logic analysis; Section 3 for environment prerequisites 4. Subagent Map Diagrams — Mermaid diagram conventions for visualizing the agent's subagent graph 5. Agent User Setup & Permissions — permission set assignment, object permissions, cross-subagent validation 6. Metadata & Lifecycle — directory structure, bundle metadata; publish troubleshooting 7. Validation & Debugging — validate the agent compiles, preview to confirm behavior 8. Agent Access Guide — end-user access permissions, visibility troubleshooting 9. Known Issues — only load when errors persist after code fixes 10. Architecture Patterns — hub-and-spoke, verification gate, post-action loop 11. Complex Data Types — type mapping decision tree 12. Safety Review — 7-category safety review 13. Discover Reference — target discovery CLI 14. Scaffold Reference — stub generation CLI 15. Deploy Reference — deployment lifecycle, error recovery
Comprehend an Existing Agent
User wants to understand Agent Script agent they didn't write or need to revisit. May point to AiAuthoringBundle directory or ask "what does this agent do?" or "I need to fix this agent but I don't understand how it works.".
Required Steps
1. Locate agent — Read sfdx-project.json to identify package directories. Find AiAuthoringBundle directory within them. Read .agent file and bundle-meta.xml. 2. Read code — Read Core Language 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 for Agent Spec structure. Produce Agent Spec from code and save as file. 5. Produce Subagent Map diagram — Read Subagent Map Diagrams 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
1. Core Language — syntax, execution model, anti-patterns 2. Design & Agent Spec — Agent Spec structure, flow control pattern recognition 3. Subagent Map Diagrams — Mermaid conventions for subagent graph visualization 4. Metadata & Lifecycle — directory conventions, bundle metadata 5. Known Issues — only load when code contains unexplained workaround patterns
Modify an Existing Agent
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 subagent") or reference specific Agent Script constructs.
Required Steps
Read CLI for Agents for exact command syntax.
1. Comprehend — If no Agent Spec exists, reverse-engineer first by following "Comprehend an Existing Agent" workflow above. 2. Update Agent Spec — Read Design & Agent Spec for flow control patterns and backing logic analysis. Modify Agent Spec to reflect intended changes. For new actions, always ask if you should scan for existing backing logic. Unless instructed otherwise, scan by reading sfdx-project.json to identify package directories, then search each for @InvocableMethod in classes/, AutoLaunchedFlow in flows/, and template metadata in promptTemplates/. Mark matches EXISTS; unmatched actions NEEDS STUB. Always save updated Agent Spec as file. 3. STOP for user approval of updated Agent Spec. Present to user. Ask for approval or feedback. Do not proceed without approval. Once approved, proceed without stopping unless a step fails. 4. Edit code — Read Core Language for syntax and anti-patterns. Edit .agent file to implement approved changes. 5. Validate compilation — sf agent validate authoring-bundle --json --api-name <Developer_Name> If validation fails, read Validation & Debugging to diagnose and fix, then re-validate. 6. Generate new backing logic — For each new action marked NEEDS STUB: sf template generate apex class --name <ClassName> --output-dir <PACKAGE_DIR>/main/default/classes Replace class body with invocable pattern from Design & Agent Spec. ALWAYS deploy: sf project deploy start --json --metadata ApexClass:<ClassName> ALWAYS fix deploy errors BEFORE generating and deploying next stub. Skip if no new actions added. 7. Validate behavior — Read Validation & Debugging for preview workflow and session trace analysis. sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> If actions query data, ground test utterances with: 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>" 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-bundlepasses with zero errors- 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> If publish fails, follow troubleshooting checklist in Metadata & Lifecycle, Section 5 before retrying. 9. Activate — Makes new version available to users. sf agent activate --json --api-name <Developer_Name> 10. Verify published agent — Preview user-facing behavior AFTER activation with sf agent preview start --json --api-name <Developer_Name> Use --api-name, not --authoring-bundle.
Reference Files
1. CLI for Agents — exact command syntax for validate, deploy, preview, publish, activate 2. Core Language — syntax, anti-patterns 3. Design & Agent Spec — Agent Spec updates, backing logic analysis 4. Validation & Debugging — compilation diagnosis, preview workflow, session trace analysis 5. Known Issues — only load when errors persist after code fixes
Diagnose Compilation Errors
User has Agent Script that won't compile. Errors surface from sf agent validate or sf agent preview start, or User describes symptoms like "I'm getting a validation error."
Required Steps
Read CLI for Agents for exact command syntax.
1. Reproduce error — Run sf agent validate authoring-bundle --json --api-name <Developer_Name> to capture basic compile errors. If no errors, run sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> to capture complex compile errors. If user provides specific error output, ALWAYS reproduce to confirm. 2. Classify error — Read Validation & Debugging for error taxonomy. Map each error message to root cause category. 3. Locate fault — Read Core Language to understand correct syntax. Find specific line(s) in .agent file that cause each error. 4. Fix code — Apply targeted fixes. Check Anti-Patterns section in Core Language reference to ensure you're not introducing known bad pattern. 5. Re-validate — Run sf agent validate authoring-bundle --json --api-name <Developer_Name> then run sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> Repeat steps 2–5 if errors persist. 6. Explain fix — Tell user what was wrong and what you changed. Explain root cause in terms of Core Language agent execution model.
Reference Files
1. Core Language — syntax, block structure, anti-patterns 2. Validation & Debugging — error taxonomy, error-to-root-cause mapping 3. Known Issues — only load when error doesn't match user code; may be a platform bug 4. Production Gotchas — only load when error involves reserved keywords or lifecycle hook syntax
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 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 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 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 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 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 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 3–6 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. 8. Explain fix — Tell user what was wrong and what you changed. Explain root cause in terms of Core Language agent execution model.
Reference Files
1. Core Language — execution model, anti-patterns 2. Design & Agent Spec — Agent Spec as behavioral baseline, gating patterns 3. Validation & Debugging — preview workflow, session trace analysis 4. Known Issues — only load when behavior is wrong but code logic is correct
Deploy, Publish, and Activate
User wants to take working agent from local development to running state in Salesforce org. Involves deploying AiAuthoringBundle and its dependencies, publishing to commit version, then activating to make it live.
Required Steps
Read CLI for Agents for exact command syntax.
1. Validate compilation — sf agent validate authoring-bundle --json --api-name <Developer_Name> Do not proceed if validation fails. 2. Deploy bundle and dependencies — Read Metadata & Lifecycle for dependency management and deploy commands. Deploy AiAuthoringBundle and all backing logic (Apex classes, Flows, Prompt Templates) and dependencies to org. 3. Live preview — Read Validation & Debugging for preview workflow and session trace analysis. sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> then send test utterances with: sf agent preview send --json --authoring-bundle <Developer_Name> --session-id <ID> -u "<message>" 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-bundlepasses with zero errors- 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> If publish fails, follow Troubleshooting Publish Failures in Metadata & Lifecycle before retrying. 5. Activate — Makes new version available to users. sf agent activate --json --api-name <Developer_Name> 6. Verify published agent — Preview user-facing behavior AFTER activation with sf agent preview start --json --api-name <Developer_Name> Use --api-name, not --authoring-bundle. 7. Configure end-user access — ONLY for employee agents. Read Agent Access Guide to configure perms and assign access.
Reference Files
1. CLI for Agents — exact command syntax for deploy, publish, activate, deactivate 2. Validation & Debugging — compilation validation, preview workflow 3. Metadata & Lifecycle — dependency management, deploy commands; publish troubleshooting 4. Agent Access Guide — end-user access permissions, visibility troubleshooting 5. Known Issues — only load when deploy hangs, publish fails, or activate fails unexpectedly
Diagnose Production Issues
User's agent is published and active but experiencing issues not caught during preview. Includes credit overconsumption, token or size limit failures, loop guardrail interruptions, reserved keyword runtime errors, VS Code sync failures, or unexpected behavioral differences between preview and production.
Required Steps
Read CLI for Agents for exact command syntax.
1. Classify issue — Determine whether this is billing/cost concern, runtime limit, naming conflict, tooling issue, or behavioral difference between preview and production. 2. Check known production gotchas — Read Production Gotchas for credit consumption, token limits, loop guardrails, reserved keywords, lifecycle hooks, and VS Code workarounds. 3. Compare preview vs production behavior — If issue is behavioral, preview published agent with sf agent preview start --json --api-name <Developer_Name> (not --authoring-bundle). Compare against live-actions authoring bundle preview --authoring-bundle <Developer_Name> --use-live-actions to isolate preview-vs-production differences. 4. Check known issues — Read Known Issues for platform bugs that may explain production-only failures. 5. Fix and republish — Apply fixes, validate, re-preview, publish, activate, verify. Follow Deploy, Publish, and Activate steps. 6. Explain diagnosis — Tell user what was happening and what you changed. Explain root cause.
Reference Files
1. Production Gotchas — credit consumption, token limits, loop guardrails, reserved keywords, lifecycle hooks, VS Code workarounds 2. CLI for Agents — command syntax for preview, publish, activate 3. Validation & Debugging — preview workflow, session trace analysis 4. Known Issues — only load when issue may be a platform bug
Delete or Rename an Agent
User wants to remove agent or change its name. Maintenance tasks complicated by AiAuthoringBundle versioning and published version dependencies.
Required Steps
Read CLI for Agents for exact command syntax.
1. Understand current state — Read Metadata & Lifecycle for versioning, delete mechanics, and rename mechanics. Identify whether agent has been published, how many versions exist, and whether it's currently active. 2. Deactivate if active — sf agent deactivate --json --api-name <Developer_Name> Active agent cannot be deleted or renamed. 3. Execute operation — For delete: follow delete mechanics in Metadata & Lifecycle reference. For rename: follow rename mechanics in same reference. 4. Clean up orphans — Check for and remove orphaned metadata: Bot, BotVersion, GenAiPlannerBundle, GenAiPlugin, GenAiFunction. Metadata & Lifecycle reference details what to look for. 5. Validate — Confirm operation completed cleanly. For rename, validate new bundle compiles and preview to confirm behavior.
Reference Files
1. CLI for Agents — exact command syntax for delete, deactivate, retrieve 2. Validation & Debugging — compilation validation, preview workflow 3. Metadata & Lifecycle — delete mechanics, rename mechanics, orphan cleanup
Test an Agent
User wants to create automated tests for Agent Script agent. Involves writing AiEvaluationDefinition test specs in YAML format that define test scenarios, expected behaviors, and quality metrics.
Required Steps
Read CLI for Agents 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 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. 6. Run tests — Execute test run using CLI. Capture results. 7. Analyze results — Compare actual outcomes against expectations. For failures, identify whether issue is in agent code, backing logic, or test spec itself. 8. Iterate — Fix agent code or test spec as needed, redeploy, and re-run until coverage targets are met.
Reference Files
1. CLI for Agents — exact command syntax for test create, test run, test results 2. Core Language — agent structure for designing meaningful tests 3. Design & Agent Spec — Agent Spec as test coverage baseline 4. testing-agentforce skill — test spec YAML format, expectations, metrics, test design methodology, and test spec template
The Agent Spec
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.
Always produce or update Agent Spec as first step of any operation that changes or analyzes agent. It is consistent grounding to work from, and a durable artifact a developer can review.
Read Design & Agent Spec for Agent Spec structure and production methodology.
Assets
The assets/ directory contains templates and examples. Read when you need a starting point or a concrete reference for artifacts and source files.
- `assets/agent-spec-template.md` — Agent Spec template with all sections and placeholder content. Copy to
<AgentName>-AgentSpec.mdin project directory, then fill in during design. Save Agent Spec as file — significant design artifact that benefits from proper rendering, especially Mermaid Subagent Map diagram.
- `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 subagent. Copy and modify for simple 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.
Important Constraints
- Use only Salesforce CLI and Salesforce org. Do not reference or depend on other skills, MCP servers, or external tooling. All commands use
sf(Salesforce CLI).
- Only certain backing logic types are valid for actions. For example, only invocable Apex (not arbitrary Apex classes) can back action. Similar constraints may apply to Flows and Prompt Templates. When wiring actions to backing logic, consult Design & Agent Spec reference file for valid types and stubbing methodology.
- `sf agent generate test-spec` is not for agentic use. It is interactive, REPL-style command designed for humans. When creating test specs, start from boilerplate template in assets instead.
Common Issues Quick Reference
`Internal Error, try again later` during publish: Invalid or missing default_agent_user. Re-run query from Design & Agent Spec, Section 3. Do not invent username.
`Unable to access Salesforce Agent APIs...` during preview: default_agent_user lacks permissions. See Agent User Setup & Permissions. Do NOT publish as fix — --use-live-actions does not require published agent.
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 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, Section 6.2.
Syntax Quick Reference
- 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
- Numeric action I/O: bare
numberworks for variables but fails at publish in action I/O. Useobject+complex_data_type_namefor numeric action parameters. See Complex Data Types for the full decision tree. after_reasoning:has NOinstructions:wrapper- No
else if— use compoundif x and y:or sequential flat ifs - Reserved
@InvocableVariablenames:model,description,label— cannot be used as Apex parameter names @inputsand@outputsare ephemeral:@inputsonly inwith;@outputsonly inset/ifimmediately after the action.@inputsinset= silent failure.
See Complex Data Types for the full Lightning type mapping decision tree. See Instruction Resolution for the 3-phase runtime model.
Architecture Patterns
Three primary FSM patterns. Full details with code in Architecture Patterns.
- Hub-and-Spoke (most common):
start_agentroutes 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 whenguards on protected transitions. - Post-Action Loop: Post-action checks at TOP of
instructions: ->trigger on re-resolution after action completes.
Scoring Rubric
Score every generated agent on 100 points across 7 categories: Structure (15), Safety (15), Deterministic Logic (20), Instruction Resolution (20), FSM Architecture (10), Action Configuration (10), Deployment Readiness (10).
See Scoring Rubric for the complete rubric.
Review Mode
When user provides an existing .agent file (e.g., review path/to/file.agent):
1. Read the file 2. Score against the 100-point rubric 3. List every issue grouped by category 4. Provide corrected code snippets 5. Offer to apply fixes
Safety Review
7-category LLM-driven safety review for .agent files. Integrated into Phase 0 of authoring and deployment. Categories: Identity & Transparency, User Safety, Data Handling, Content Safety, Fairness, Deception, Scope & Boundaries.
See Safety Review for the complete framework, severity levels, false positive guidance, and adversarial test prompts.
Discover & Scaffold
Validate action targets exist in org and generate stubs for missing ones.
See Discover Reference and Scaffold Reference.
CRITICAL: Stubs must return realistic data, not 'TODO'. Placeholder responses cause SMALL_TALK grounding because the LLM falls back to training data.
Deploy Lifecycle
Validate → deploy metadata → publish bundle → activate. See Deploy Reference for phases, error recovery, CI/CD, and rollback.
Template Assets
Ready-to-use .agent templates in assets/agents/ (hello-world, simple-qa, multi-subagent, production-faq, order-service, verification-gate). See also assets/patterns/ for 11+ reusable design patterns and Examples for inline walkthroughs.
Additional References
| Topic | File |
|---|---|
| Architecture patterns | architecture-patterns.md |
| Type mapping decision tree | complex-data-types.md |
| Feature validity by context | feature-validity.md |
| Instruction resolution model | instruction-resolution.md |
| Complete agent examples | examples.md |
Agent Spec: Agent_API_Name
Purpose & Scope
Describe the agent's purpose in 1-2 sentences. What does it help users do? What domain does it operate in?
Behavioral Intent
Describe the key behavioral rules that govern the agent:
- What must the agent know before taking action?
- What backing logic types are used (Apex, Flow, Prompt Template)?
- What guardrails apply (off-topic handling, escalation)?
- What information persists across subagent switches?
Subagent Map
%%{init: {'theme':'neutral'}}%%
graph TD
A[start_agent<br/>agent_router]
A -->|description of routing condition| B[subagent_name<br/>Subagent]
A -->|unclear intent| C[ambiguous_question<br/>Subagent]
A -->|out of scope| D[off_topic<br/>Subagent]
A -->|needs escalation| E[escalation<br/>Subagent]Expand the diagram to show actions, gating logic, and variable state changes within each subagent. See the Subagent Map Diagrams reference for conventions.
Variables
variable_name(mutable type = default) — What this variable tracks.
Set by: which action or utility. Read by: which topics for gating or conditional instructions.
Actions & Backing Logic
action_name (subagent_name subagent)
- Target:
apex://ClassNameorflow://FlowNameorprompt://PromptTemplateName - Backing Status: EXISTS / NEEDS STUB / NEEDS IMPLEMENTATION
Inputs
| Name | Type | Required | Source |
|---|---|---|---|
| property_id | string | Yes | User input |
| max_results | integer | No | Defaults to 10 |
Outputs
| Name | Type | Visible to User? | Source | Notes |
|---|---|---|---|---|
| property | object | Yes | Property__c | Complete property details |
| related_applications | list[object] | Yes | Application__c | Records for this property |
| active_listing | boolean | Yes | Listing__c | Listing status |
| hasData | boolean | No | Computed | Internal empty-result flag |
"Visible to User?" maps tofilter_from_agentin the.agentfile: Yes →filter_from_agent: False, No →filter_from_agent: True.
Stubbing Requirement
If NEEDS STUB:
- Apex class name and inner class wrappers needed
complex_data_type_namefor eachobject/list[object]output- Key queries or computation logic the stub must implement
Repeat for each action.
Gating Logic
action_namevisibility:available when @variables.variable_name != ""
— Rationale for why this gate exists.
List all gating conditions with their rationale.
Architecture Pattern
State the architecture pattern: hub-and-spoke, chain, hybrid, etc. Describe the routing strategy and how topics relate to each other.
Agent Configuration
- developer_name:
Agent_API_Name - agent_label:
Agent Display Name - agent_type:
AgentforceEmployeeAgentorAgentforceServiceAgent— state the reasoning based on prompt signals (e.g., "accessible by employees" → Employee, "customer-facing channel" → Service) - default_agent_user: Required for
AgentforceServiceAgent. Forbidden forAgentforceEmployeeAgent. If specified, MUST be user name. MUST NEVER be user ID. User MUST haveEinstein Agentlicense.
# Hello World Agent
# The minimal viable Agentforce agent - start here!
#
# This template shows the absolute minimum structure required for a working agent.
# Use this as your starting point when learning Agent Script.
#
# ★ Why This Structure?
# - system: Sets agent personality and default messages
# - config: Required metadata for deployment (agent_name must be unique)
# - variables: Linked variables connect to Messaging context (required for deployment)
# - language: Locale settings (required for deployment)
# - start_agent: Entry point subagent (exactly one required)
#
# ★ Key Validation Points (from 100-point scoring):
# - [10 pts] config block with all 4 required fields
# - [10 pts] 3 linked variables (EndUserId, RoutableId, ContactId)
# - [5 pts] language block present
# - [10 pts] At least one start_agent subagent
#
# Deploy with: sf agent publish authoring-bundle --api-name Hello_World_Agent --target-org [alias]
system:
instructions: "You are a friendly assistant. Greet users warmly and help them."
messages:
welcome: "Hello! I'm here to help. What can I do for you today?"
error: "I'm sorry, something went wrong. Please try again."
config:
agent_name: "Hello_World_Agent"
default_agent_user: "your.user@company.com"
agent_label: "Hello World Agent"
description: "A minimal example agent to learn Agent Script basics"
variables:
# Linked variables (required) - Connect to Salesforce Messaging context
EndUserId: linked string
source: @MessagingSession.MessagingEndUserId
description: "Messaging End User ID"
RoutableId: linked string
source: @MessagingSession.Id
description: "Messaging Session ID"
ContactId: linked string
source: @MessagingEndUser.ContactId
description: "Contact ID"
language:
default_locale: "en_US"
additional_locales: ""
all_additional_locales: False
# Entry point subagent - this is where every conversation starts
start_agent main:
label: "Main"
description: "Greets users and provides help"
reasoning:
instructions: ->
| Welcome the user warmly.
| Ask how you can help them today.
| Be friendly and conversational.
# Multi-Subagent Agent Template
# An agent with multiple conversation subagents (hub-and-spoke pattern)
# Users are routed to specialized subagents based on their needs
#
# Usage: Replace {{placeholders}} with your values
# Required: agent_name, default_agent_user, agent_label, description
# Required: At least 2 subagents with label and description
system:
instructions: "{{SystemInstructions}}"
messages:
welcome: "{{WelcomeMessage}}"
error: "I'm sorry, I encountered an issue. Please try again."
config:
agent_name: "{{AgentApiName}}"
default_agent_user: "{{AgentUser}}"
agent_label: "{{AgentLabel}}"
description: "{{AgentDescription}}"
variables:
EndUserId: linked string
source: @MessagingSession.MessagingEndUserId
description: "Messaging End User ID"
RoutableId: linked string
source: @MessagingSession.Id
description: "Messaging Session ID"
ContactId: linked string
source: @MessagingEndUser.ContactId
description: "Contact ID"
user_intent: mutable string
description: "What the user is trying to accomplish"
language:
default_locale: "en_US"
additional_locales: ""
all_additional_locales: False
start_agent agent_router:
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 subagent.
| If unclear, ask clarifying questions.
actions:
go_to_subagent_one: @utils.transition to @subagent.{{subagent_one_name}}
go_to_subagent_two: @utils.transition to @subagent.{{subagent_two_name}}
go_to_subagent_three: @utils.transition to @subagent.{{subagent_three_name}}
go_to_farewell: @utils.transition to @subagent.farewell
go_to_escalation: @utils.transition to @subagent.escalation
subagent {{subagent_one_name}}:
label: "{{SubagentOneLabel}}"
description: "{{SubagentOneDescription}}"
reasoning:
instructions: ->
| {{SubagentOneInstructions}}
actions:
back_to_menu: @utils.transition to @subagent.agent_router
subagent {{subagent_two_name}}:
label: "{{SubagentTwoLabel}}"
description: "{{SubagentTwoDescription}}"
reasoning:
instructions: ->
| {{SubagentTwoInstructions}}
actions:
back_to_menu: @utils.transition to @subagent.agent_router
subagent {{subagent_three_name}}:
label: "{{SubagentThreeLabel}}"
description: "{{SubagentThreeDescription}}"
reasoning:
instructions: ->
| {{SubagentThreeInstructions}}
actions:
back_to_menu: @utils.transition to @subagent.agent_router
subagent farewell:
label: "Farewell"
description: "Ends the conversation gracefully"
reasoning:
instructions: ->
| Thank the user for reaching out.
| Wish them a great day.
| Let them know they can return anytime.
subagent escalation:
label: "Escalation"
description: "Handles requests to transfer to a live human agent"
reasoning:
instructions: ->
| If the user explicitly asks to speak with a human, escalate.
| Acknowledge the request and transfer gracefully.
actions:
escalate_to_human: @utils.escalate
description: "Escalate to a human agent"
# Order Service Agent
# ========================
#
# A complex real-world agent for e-commerce customer service featuring:
# - Verification gate before order access
# - Multiple specialized subagents (order status, tracking, returns)
# - Two-level action system with Flow targets
# - after_reasoning for post-action routing
# - available when guards for conditional action visibility
system:
instructions: |
You are an AI Customer Service Assistant helping customers with their orders.
Be helpful, empathetic, and efficient in resolving order-related issues.
Always verify customer identity before sharing order details.
Do not fabricate data — always use action results.
messages:
welcome: "Hello! I'm here to help you with your orders. How can I assist you today?"
error: "I apologize for the inconvenience. Let me try a different approach."
config:
developer_name: "OrderServiceAgent"
agent_label: "Order Service Assistant"
description: "Helps customers check order status, process returns, and track shipments"
default_agent_user: "einsteinagent@00dxx000001234.ext"
variables:
EndUserId: linked string
source: @MessagingSession.MessagingEndUserId
description: "Messaging End User ID"
visibility: "External"
RoutableId: linked string
source: @MessagingSession.Id
description: "Messaging Session ID"
visibility: "External"
ContactId: linked string
source: @MessagingEndUser.ContactId
description: "Contact ID"
visibility: "External"
customer_email: mutable string = ""
description: "Customer email for verification"
customer_verified: mutable boolean = False
description: "Whether customer identity has been verified"
order_number: mutable string = ""
description: "Current order number"
order_status: mutable string = ""
description: "Current order status"
tracking_number: mutable string = ""
description: "Shipment tracking number"
return_reason: mutable string = ""
description: "Reason for return"
return_initiated: mutable boolean = False
description: "Whether a return has been initiated"
return_label_url: mutable string = ""
description: "URL for return shipping label"
language:
default_locale: "en_US"
additional_locales: ""
all_additional_locales: False
start_agent agent_router:
description: "Route customers through verification then to the right subagent"
reasoning:
instructions: |
You are a router only. Do NOT answer questions or provide help directly.
Route all users to identity verification first.
If already verified, route to the appropriate subagent:
- Order questions -> use to_orders
- Returns -> use to_returns
- Tracking -> use to_tracking
actions:
to_verify: @utils.transition to @subagent.verification
description: "Begin identity verification"
to_orders: @utils.transition to @subagent.order_status
description: "Check order status"
available when @variables.customer_verified == True
to_returns: @utils.transition to @subagent.returns
description: "Process a return"
available when @variables.customer_verified == True
to_tracking: @utils.transition to @subagent.shipment_tracking
description: "Track a shipment"
available when @variables.customer_verified == True
subagent verification:
label: "Identity Verification"
description: "Verify customer identity before accessing order information"
actions:
verify_customer:
description: "Verify customer identity by email"
target: "flow://Verify_Customer_Identity"
inputs:
email: string
description: "Customer email address"
outputs:
verified: boolean
description: "Whether verification succeeded"
customer_name: string
description: "Verified customer name"
reasoning:
instructions: ->
if @variables.customer_verified == True:
| Welcome back! How can I help you today?
if @variables.customer_verified == False:
| To help you with your order, I need to verify your identity.
| What is the email address associated with your account?
actions:
verify: @actions.verify_customer
description: "Verify customer identity by email"
with email = ...
set @variables.customer_verified = @outputs.verified
set @variables.customer_email = @outputs.customer_name
proceed_to_orders: @utils.transition to @subagent.order_status
description: "Move to order lookup"
available when @variables.customer_verified == True
escalate_now: @utils.escalate
description: "Transfer to human agent"
subagent order_status:
label: "Order Status"
description: "Look up order details and provide status updates"
actions:
lookup_order:
description: "Look up order by order number"
target: "flow://Lookup_Order"
inputs:
order_number: string
description: "Order number to look up"
customer_email: string
description: "Customer email for validation"
outputs:
status: string
description: "Order status"
is_displayable: True
tracking_number: string
description: "Shipment tracking number"
is_displayable: True
estimated_delivery: string
description: "Estimated delivery date"
is_displayable: True
reasoning:
instructions: ->
if @variables.order_status != "":
| Order {!@variables.order_number} status: {!@variables.order_status}
| Would you like to track your shipment or initiate a return?
if @variables.order_status == "":
| What is your order number? You can find it in your confirmation email.
| Use the find_order action to look up order details.
actions:
find_order: @actions.lookup_order
description: "Look up order details"
with order_number = ...
with customer_email = @variables.customer_email
set @variables.order_status = @outputs.status
set @variables.tracking_number = @outputs.tracking_number
to_returns: @utils.transition to @subagent.returns
description: "Start a return"
available when @variables.order_status != ""
to_tracking: @utils.transition to @subagent.shipment_tracking
description: "Track shipment"
available when @variables.tracking_number != ""
back: @utils.transition to @subagent.agent_router
description: "Route to a different subagent"
subagent shipment_tracking:
label: "Shipment Tracking"
description: "Track shipment status and estimated delivery"
actions:
get_tracking_details:
description: "Get detailed tracking information"
target: "flow://Get_Tracking_Details"
inputs:
tracking_number: string
description: "Shipment tracking number"
outputs:
current_location: string
description: "Current package location"
is_displayable: True
estimated_delivery: string
description: "Estimated delivery date"
is_displayable: True
delivery_status: string
description: "Delivery status"
is_displayable: True
reasoning:
instructions: |
Look up the tracking information for the customer's shipment.
Use the track action with the tracking number.
Do not fabricate tracking details — always use the action result.
actions:
track: @actions.get_tracking_details
description: "Get tracking details"
with tracking_number = @variables.tracking_number
back: @utils.transition to @subagent.agent_router
description: "Route to a different subagent"
subagent returns:
label: "Returns"
description: "Process return requests and generate return labels"
actions:
initiate_return:
description: "Initiate a return for an order"
target: "flow://Initiate_Return"
inputs:
order_number: string
description: "Order number to return"
reason: string
description: "Reason for return"
outputs:
return_label_url: string
description: "URL for return shipping label"
is_displayable: True
return_deadline: string
description: "Deadline to ship return"
is_displayable: True
reasoning:
instructions: ->
if @variables.return_initiated == True:
| Your return has been initiated!
| Return label: {!@variables.return_label_url}
if @variables.return_initiated == False:
| I can help you with a return for order {!@variables.order_number}.
| What is the reason for your return?
| Use the process_return action to start the return.
actions:
process_return: @actions.initiate_return
description: "Process the return request"
with order_number = @variables.order_number
with reason = ...
set @variables.return_initiated = True
set @variables.return_label_url = @outputs.return_label_url
back: @utils.transition to @subagent.agent_router
description: "Route to a different subagent"
after_reasoning: ->
if @variables.return_initiated == True:
transition to @subagent.follow_up
subagent follow_up:
label: "Follow Up"
description: "Handle follow-up actions and next steps"
reasoning:
instructions: |
The customer's request has been processed.
Ask if there is anything else you can help with.
actions:
new_order: @utils.transition to @subagent.order_status
description: "Look up another order"
back: @utils.transition to @subagent.agent_router
description: "Start over"
# Simple FAQ Agent
# A minimal working example of an Agentforce agent
# Uses pure LLM reasoning without external actions
#
# Deploy with: sf agent publish authoring-bundle --api-name Simple_FAQ_Agent --target-org [alias]
system:
instructions: "You are a helpful FAQ assistant for our company. Answer questions accurately and concisely. If you don't know the answer, say so honestly. Never share sensitive or confidential information. Keep responses friendly and professional."
messages:
welcome: "Hello! I'm your FAQ assistant. How can I help you today?"
error: "I'm sorry, I encountered an issue. Please try again."
config:
agent_name: "Simple_FAQ_Agent"
default_agent_user: "agent.user@company.com"
agent_label: "Simple FAQ Agent"
description: "A minimal FAQ agent that answers common questions using AI"
variables:
EndUserId: linked string
source: @MessagingSession.MessagingEndUserId
description: "Messaging End User ID"
RoutableId: linked string
source: @MessagingSession.Id
description: "Messaging Session ID"
ContactId: linked string
source: @MessagingEndUser.ContactId
description: "Contact ID"
user_question: mutable string
description: "The user's current question"
conversation_topic: mutable string
description: "The current subject being discussed"
question_count: mutable number
description: "Number of questions answered in this session"
language:
default_locale: "en_US"
additional_locales: ""
all_additional_locales: False
start_agent agent_router:
label: "Subagent Router"
description: "Routes incoming questions to the FAQ handler"
reasoning:
instructions: ->
| Listen to the user's question and determine how to help.
| If the question is about a specific subject, note it.
| Route to the FAQ handler for processing.
actions:
handle_faq: @utils.transition to @subagent.faq_handler
end_conversation: @utils.transition to @subagent.farewell
subagent faq_handler:
label: "FAQ Handler"
description: "Handles frequently asked questions and provides helpful answers"
reasoning:
instructions: ->
| Answer the user's question based on your knowledge.
| Be helpful, accurate, and concise.
| Keep responses under 3-4 sentences when possible.
| If you need more information, ask clarifying questions.
|
| Common topics you can help with:
| - Business hours and location
| - Return and refund policies
| - Shipping information
| - Product questions
| - Account and billing
|
| If the question is outside your knowledge:
| - Acknowledge you don't have that information
| - Suggest contacting customer support
| - Offer to help with something else
actions:
new_question: @utils.transition to @subagent.agent_router
end_conversation: @utils.transition to @subagent.farewell
escalate: @utils.transition to @subagent.escalation
subagent farewell:
label: "Farewell"
description: "Ends the conversation politely"
reasoning:
instructions: ->
| Thank the user for their questions.
| Wish them a great day.
| Let them know they can return anytime for more help.
subagent escalation:
label: "Escalation"
description: "Handles requests to speak with a human agent"
reasoning:
instructions: ->
| If the user wants to speak with a human, escalate gracefully.
| Acknowledge their request and transfer the conversation.
actions:
escalate_to_human: @utils.escalate
description: "Transfer to a human agent"
<?xml version="1.0" encoding="UTF-8"?>
<AiAuthoringBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<bundleType>AGENT</bundleType>
</AiAuthoringBundle>
Complete Agent Templates
Templates for building complete, deployable agents.
Learning Path
| Template | Complexity | Description |
|---|---|---|
hello-world.agent | Beginner | Minimal viable agent - start here |
simple-qa.agent | Beginner | Single-subagent Q&A agent |
multi-subagent.agent | Intermediate | Multi-subagent routing agent |
production-faq.agent | Advanced | Production-ready FAQ with escalation |
Quick Start
1. Copy a template to your SFDX project:
mkdir -p force-app/main/default/aiAuthoringBundles/My_Agent
cp hello-world.agent force-app/main/default/aiAuthoringBundles/My_Agent/My_Agent.agent
cp ../metadata/bundle-meta.xml force-app/main/default/aiAuthoringBundles/My_Agent/My_Agent.bundle-meta.xml2. Validate and deploy:
sf agent validate authoring-bundle --json --api-name My_Agent --target-org your-org
sf agent publish authoring-bundle --json --api-name My_Agent --target-org your-orgRequired Blocks
Every agent must have these blocks in this order:
| Block | Purpose |
|---|---|
system: | Agent personality and default messages |
config: | Deployment metadata (agent_name, label, etc.) |
variables: | Data connections and state storage |
language: | Locale configuration |
start_agent | Entry point subagent (exactly one required) |
Next Steps
- components/ - Reusable action and subagent templates
- patterns/ - Advanced patterns for complex behaviors
- metadata/ - XML metadata templates
# Simple Q&A Agent Template
# A minimal agent that handles basic questions using LLM reasoning only
# No external actions - just conversational AI
#
# Usage: Replace {{placeholders}} with your values
# Required: agent_name, default_agent_user, agent_label, description
system:
instructions: "{{SystemInstructions}}"
messages:
welcome: "{{WelcomeMessage}}"
error: "I'm sorry, I encountered an issue. Please try again."
config:
agent_name: "{{AgentApiName}}"
default_agent_user: "{{AgentUser}}"
agent_label: "{{AgentLabel}}"
description: "{{AgentDescription}}"
variables:
EndUserId: linked string
source: @MessagingSession.MessagingEndUserId
description: "Messaging End User ID"
RoutableId: linked string
source: @MessagingSession.Id
description: "Messaging Session ID"
ContactId: linked string
source: @MessagingEndUser.ContactId
description: "Contact ID"
user_query: mutable string
description: "The user's current question or request"
language:
default_locale: "en_US"
additional_locales: ""
all_additional_locales: False
start_agent agent_router:
label: "Subagent Router"
description: "Routes incoming requests to the Q&A handler"
reasoning:
instructions: ->
| Listen to the user's question.
| Route to the Q&A handler for processing.
actions:
handle_question: @utils.transition to @subagent.qa_handler
end_conversation: @utils.transition to @subagent.farewell
subagent qa_handler:
label: "Q&A Handler"
description: "Handles questions and provides answers"
reasoning:
instructions: ->
| Answer the user's question based on your knowledge.
| Be helpful, accurate, and concise.
| Keep responses clear and easy to understand.
| If you need more information, ask clarifying questions.
actions:
new_question: @utils.transition to @subagent.agent_router
end_conversation: @utils.transition to @subagent.farewell
subagent farewell:
label: "Farewell"
description: "Ends the conversation gracefully"
reasoning:
instructions: ->
| Thank the user for their questions.
| Wish them a great day.
| Let them know they can return anytime for more help.
# Verification Gate Architecture Template
# ========================================
#
# Users must pass identity verification before accessing protected subagents.
#
# Pattern: Security gate before protected functionality.
# Use when: Handling sensitive data, payments, PII access.
#
# Key features:
# - Deterministic verification (code-enforced, not LLM suggestions)
# - available when guards make actions invisible until verified
# - Post-action checks at TOP of instructions: ->
# - Automatic escalation after 3 failed attempts
system:
instructions: |
You are an AI secure customer service agent.
Always verify identity before sensitive operations.
Do not fabricate data — always use action results.
messages:
welcome: "Welcome! I'll need to verify your identity before we proceed."
error: "I apologize, something went wrong. Let me try again."
config:
developer_name: "SecureAgent"
agent_label: "Secure Customer Agent"
description: "Agent with verification gate for sensitive operations"
default_agent_user: "einsteinagent@00dxx000001234.ext"
variables:
EndUserId: linked string
source: @MessagingSession.MessagingEndUserId
description: "Messaging End User ID"
visibility: "External"
RoutableId: linked string
source: @MessagingSession.Id
description: "Messaging Session ID"
visibility: "External"
ContactId: linked string
source: @MessagingEndUser.ContactId
description: "Contact ID"
visibility: "External"
customer_verified: mutable boolean = False
description: "Has customer passed identity verification"
failed_attempts: mutable number = 0
description: "Number of failed verification attempts"
customer_email: mutable string = ""
description: "Customer email for verification"
refund_status: mutable string = ""
description: "Status of refund operation"
refund_amount: mutable number = 0
description: "Refund amount to process"
churn_risk_score: mutable number = 0
description: "Customer churn risk from Data Cloud"
language:
default_locale: "en_US"
additional_locales: ""
all_additional_locales: False
start_agent agent_router:
description: "Route through identity verification"
reasoning:
instructions: |
You are a router only. Do NOT answer questions or provide help directly.
Route all users to identity verification first.
If already verified, route to the appropriate subagent:
- Account questions -> use to_account
- Refund requests -> use to_refund
actions:
to_verify: @utils.transition to @subagent.identity_verification
description: "Begin identity verification"
to_account: @utils.transition to @subagent.account_management
description: "Access account settings"
available when @variables.customer_verified == True
to_refund: @utils.transition to @subagent.refund_processor
description: "Process a refund request"
available when @variables.customer_verified == True
subagent identity_verification:
label: "Identity Verification"
description: "Verify customer identity before proceeding"
actions:
verify_email:
description: "Verify customer email against records"
target: "flow://Verify_Customer_Email"
inputs:
email: string
description: "Customer email to verify"
outputs:
verified: boolean
description: "Whether verification succeeded"
reasoning:
instructions: ->
if @variables.failed_attempts >= 3:
| Too many failed attempts. Transferring to a human agent.
transition to @subagent.escalation
if @variables.customer_verified == True:
| Identity verified! How can I help you today?
if @variables.customer_verified == False:
| Please verify your identity by confirming your email address.
actions:
check_email: @actions.verify_email
description: "Verify customer email"
with email = ...
set @variables.customer_verified = @outputs.verified
go_to_account: @utils.transition to @subagent.account_management
description: "Access account settings"
available when @variables.customer_verified == True
go_to_refund: @utils.transition to @subagent.refund_processor
description: "Process a refund request"
available when @variables.customer_verified == True
escalate_now: @utils.escalate
description: "Transfer to human agent"
subagent account_management:
label: "Account Management"
description: "Manage customer account settings (requires verification)"
actions:
update_email:
description: "Update customer email address"
target: "apex://UpdateCustomerEmail"
inputs:
new_email: string
description: "New email address"
outputs:
success: boolean
description: "Whether update succeeded"
update_preferences:
description: "Update communication preferences"
target: "apex://UpdatePreferences"
inputs:
pref_type: string
description: "Preference type to update"
pref_value: string
description: "New preference value"
outputs:
success: boolean
description: "Whether update succeeded"
reasoning:
instructions: ->
if @variables.customer_verified == False:
transition to @subagent.identity_verification
| Welcome to account management.
| What would you like to do with your account?
| Use the update actions to make changes.
actions:
change_email: @actions.update_email
description: "Update email address"
with new_email = ...
available when @variables.customer_verified == True
change_prefs: @actions.update_preferences
description: "Update communication preferences"
with pref_type = ...
with pref_value = ...
available when @variables.customer_verified == True
back: @utils.transition to @subagent.agent_router
description: "Return to main menu"
subagent refund_processor:
label: "Refund Processor"
description: "Process refund requests (requires verification)"
actions:
check_churn_risk:
description: "Check customer churn risk score"
target: "apex://CheckChurnRisk"
inputs:
customer_id: string
description: "Customer ID"
outputs:
score: object
description: "Churn risk score 0-100"
complex_data_type_name: "lightning__numberType"
process_refund:
description: "Process a full cash refund"
target: "flow://Process_Refund"
inputs:
refund_type: string
description: "Type of refund (full or partial)"
outputs:
status: string
description: "Refund status"
issue_credit:
description: "Issue store credit"
target: "flow://Issue_Store_Credit"
inputs:
amount: object
description: "Credit amount"
complex_data_type_name: "lightning__numberType"
outputs:
status: string
description: "Credit status"
create_crm_case:
description: "Create a CRM case for the refund"
target: "flow://Create_CRM_Case"
inputs:
customer_id: string
description: "Customer ID"
refund_amount: object
description: "Refund amount"
complex_data_type_name: "lightning__numberType"
outputs:
case_id: string
description: "Created case ID"
reasoning:
instructions: ->
if @variables.customer_verified == False:
transition to @subagent.identity_verification
if @variables.refund_status == "Approved":
run @actions.create_crm_case
with customer_id = @variables.ContactId
with refund_amount = @variables.refund_amount
transition to @subagent.success_confirmation
run @actions.check_churn_risk
with customer_id = @variables.ContactId
set @variables.churn_risk_score = @outputs.score
| Customer risk score: {!@variables.churn_risk_score}
if @variables.churn_risk_score >= 80:
| HIGH RISK - Offer a full cash refund to retain this customer.
else:
| STANDARD - Offer a $10 store credit as goodwill.
actions:
approve_full_refund: @actions.process_refund
description: "Approve full cash refund"
available when @variables.churn_risk_score >= 80
with refund_type = "full"
set @variables.refund_status = @outputs.status
offer_credit: @actions.issue_credit
description: "Offer store credit"
available when @variables.churn_risk_score < 80
with amount = 10
set @variables.refund_status = @outputs.status
subagent success_confirmation:
label: "Success Confirmation"
description: "Confirm successful operation"
reasoning:
instructions: |
Great news! Your request has been processed successfully.
Is there anything else I can help you with?
actions:
new_request: @utils.transition to @subagent.agent_router
description: "Start a new request"
subagent escalation:
label: "Escalation"
description: "Escalate to human agent"
reasoning:
instructions: |
I'm transferring you to a human agent who can better assist you.
Please hold while I connect you.
actions:
handoff: @utils.escalate
description: "Transfer to human agent"
/**
* @description Queueable job for AI generation using Agentforce Models API
* Generates {{Description}} for {{ObjectName}} records
* @author {{Author}}
* @date {{Date}}
*
* @requires API v61.0+ (Spring '24)
* @requires Einstein Generative AI enabled
* @requires Einstein Generative AI User permission set
*
* @example
* // Invoke from trigger or other context:
* List<Id> recordIds = new List<Id>{ '001xx000003DGXXX' };
* System.enqueueJob(new {{ClassName}}_AI_Queueable(recordIds));
*/
public with sharing class {{ClassName}}_AI_Queueable implements Queueable, Database.AllowsCallouts {
// ═══════════════════════════════════════════════════════════════════════
// CONFIGURATION
// ═══════════════════════════════════════════════════════════════════════
/**
* Available Models:
* - sfdc_ai__DefaultOpenAIGPT4OmniMini (Cost-effective, faster)
* - sfdc_ai__DefaultOpenAIGPT4Omni (More capable, slower)
* - sfdc_ai__DefaultAnthropic (Claude - nuanced)
* - sfdc_ai__DefaultGoogleGemini (Multimodal capable)
*/
private static final String AI_MODEL = 'sfdc_ai__DefaultOpenAIGPT4OmniMini';
/**
* Maximum records to process in a single job.
* Recommended: 10-20 for AI processing to avoid timeouts.
*/
private static final Integer MAX_RECORDS_PER_JOB = 20;
// ═══════════════════════════════════════════════════════════════════════
// INSTANCE VARIABLES
// ═══════════════════════════════════════════════════════════════════════
private List<Id> recordIds;
// ═══════════════════════════════════════════════════════════════════════
// CONSTRUCTOR
// ═══════════════════════════════════════════════════════════════════════
/**
* @description Constructor
* @param recordIds List of {{ObjectName}} record IDs to process
*/
public {{ClassName}}_AI_Queueable(List<Id> recordIds) {
this.recordIds = recordIds;
}
// ═══════════════════════════════════════════════════════════════════════
// QUEUEABLE EXECUTION
// ═══════════════════════════════════════════════════════════════════════
/**
* @description Execute the queueable job
* @param context QueueableContext
*/
public void execute(QueueableContext context) {
if (recordIds == null || recordIds.isEmpty()) {
return;
}
// Split into current batch and remaining
List<Id> currentBatch = new List<Id>();
List<Id> remainingIds = new List<Id>();
for (Integer i = 0; i < recordIds.size(); i++) {
if (i < MAX_RECORDS_PER_JOB) {
currentBatch.add(recordIds[i]);
} else {
remainingIds.add(recordIds[i]);
}
}
// Process current batch
processRecords(currentBatch);
// Chain next job if more records remain
if (!remainingIds.isEmpty() && !Test.isRunningTest()) {
System.enqueueJob(new {{ClassName}}_AI_Queueable(remainingIds));
}
}
// ═══════════════════════════════════════════════════════════════════════
// PROCESSING LOGIC
// ═══════════════════════════════════════════════════════════════════════
/**
* @description Process a batch of records
* @param batchIds Record IDs to process in this batch
*/
private void processRecords(List<Id> batchIds) {
// Query records with fields needed for AI prompt
List<{{ObjectName}}> records = [
SELECT Id, Name
// TODO: Add fields needed for AI context
// , Description, Subject, Type
FROM {{ObjectName}}
WHERE Id IN :batchIds
WITH USER_MODE
];
List<{{ObjectName}}> toUpdate = new List<{{ObjectName}}>();
for ({{ObjectName}} record : records) {
try {
// Generate AI content
String aiContent = generateAIContent(record);
if (String.isNotBlank(aiContent)) {
// TODO: Update the target field with AI-generated content
// record.AI_Summary__c = aiContent;
toUpdate.add(record);
}
} catch (Exception e) {
// Log error but continue processing other records
logError(record.Id, e);
}
}
// Batch update
if (!toUpdate.isEmpty()) {
try {
update toUpdate;
} catch (DmlException e) {
System.debug(LoggingLevel.ERROR, 'DML Error: ' + e.getMessage());
}
}
}
// ═══════════════════════════════════════════════════════════════════════
// AI GENERATION
// ═══════════════════════════════════════════════════════════════════════
/**
* @description Generate AI content for a single record
* @param record The record to generate content for
* @return Generated text content
*/
private String generateAIContent({{ObjectName}} record) {
// Build the prompt with record context
String prompt = buildPrompt(record);
// Create Models API request
aiplatform.ModelsAPI.createGenerations_Request request =
new aiplatform.ModelsAPI.createGenerations_Request();
request.modelName = AI_MODEL;
aiplatform.ModelsAPI_GenerationRequest genRequest =
new aiplatform.ModelsAPI_GenerationRequest();
genRequest.prompt = prompt;
request.body = genRequest;
// Call the API
aiplatform.ModelsAPI.createGenerations_Response response =
aiplatform.ModelsAPI.createGenerations(request);
// Extract and return generated text
if (response.Code200 != null &&
response.Code200.generations != null &&
!response.Code200.generations.isEmpty()) {
return response.Code200.generations[0].text;
}
return null;
}
/**
* @description Build the AI prompt for a record
* @param record The record to build prompt for
* @return Formatted prompt string
*/
private String buildPrompt({{ObjectName}} record) {
// TODO: Customize this prompt for your use case
String prompt =
'{{PromptInstructions}}\n\n' +
'Record Information:\n' +
'- Name: ' + record.Name + '\n';
// TODO: Add more fields as needed
// '- Description: ' + record.Description + '\n' +
// '- Type: ' + record.Type + '\n';
return prompt;
}
// ═══════════════════════════════════════════════════════════════════════
// ERROR HANDLING
// ═══════════════════════════════════════════════════════════════════════
/**
* @description Log processing errors
* @param recordId The record that failed
* @param e The exception that occurred
*/
private void logError(Id recordId, Exception e) {
System.debug(LoggingLevel.ERROR,
'{{ClassName}}_AI_Queueable Error for ' + recordId + ': ' + e.getMessage());
System.debug(LoggingLevel.ERROR, 'Stack Trace: ' + e.getStackTraceString());
// TODO: Implement custom error logging
// Options:
// 1. Create Error_Log__c record
// 2. Publish Platform Event for monitoring
// 3. Send email notification
}
// ═══════════════════════════════════════════════════════════════════════
// TEST SUPPORT
// ═══════════════════════════════════════════════════════════════════════
/**
* @description Test-visible method to verify prompt generation
* @param record Test record
* @return Generated prompt
*/
@TestVisible
private String testBuildPrompt({{ObjectName}} record) {
return buildPrompt(record);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
AUTHORING BUNDLE METADATA FILE
==============================
CRITICAL NAMING CONVENTION:
- File MUST be named: AgentName.bundle-meta.xml
- NOT: AgentName.aiAuthoringBundle-meta.xml
DIRECTORY STRUCTURE:
force-app/main/default/aiAuthoringBundles/
└── MyAgent/
├── MyAgent.agent <- Agent Script file
└── MyAgent.bundle-meta.xml <- This file (rename to match agent)
DEPLOYMENT COMMAND:
sf agent publish authoring-bundle --json --api-name MyAgent --target-org TARGET_ORG
DO NOT USE: sf project deploy start (will fail with "Required fields are missing: [BundleType]")
-->
<AiAuthoringBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<bundleType>AGENT</bundleType>
</AiAuthoringBundle>
# Apex-Based Action Template
# Define an action that calls a Salesforce Apex @InvocableMethod
# This is a PARTIAL template - define actions inside a subagent block
#
# Usage: Replace {{placeholders}} with your values
# Place this inside a subagent's actions: block
#
# ⚠️ The target format is apex://ClassName (NOT ClassName.MethodName)
# The runtime auto-discovers the @InvocableMethod on the class.
# ⚠️ NO GenAiFunction metadata needed for AiAuthoringBundle (Agent Script).
# Action Definition (place inside subagent's actions: block)
{{action_name}}:
description: "{{ActionDescription}}"
inputs:
{{input_1_name}}: {{input_1_type}}
description: "{{Input1Description}}"
{{input_2_name}}: {{input_2_type}}
description: "{{Input2Description}}"
outputs:
{{output_1_name}}: {{output_1_type}}
description: "{{Output1Description}}"
success: boolean
description: "Whether the operation succeeded"
error_message: string
description: "Error message if operation failed"
target: "apex://{{ApexClassName}}"
# Usage in reasoning block:
#
# reasoning:
# instructions: ->
# | Help the user with their request.
# actions:
# invoke_{{action_name}}: @actions.{{action_name}}
# with {{input_1_name}}=... # LLM fills from conversation
# with {{input_2_name}}=@variables.some_var # From variable
# set @variables.result = @outputs.{{output_1_name}}
# set @variables.success = @outputs.success
# Common Apex Target Patterns:
# - apex://AccountService
# - apex://CaseService
# - apex://OrderService
# - apex://IntegrationService
# - apex://CalculationService
# Apex Class Requirements:
# - Must be global or public with sharing
# - Must have exactly ONE @InvocableMethod annotation
# - Use @InvocableVariable annotations on input/output wrapper class fields
# - No GenAiFunction metadata needed for AiAuthoringBundle (Agent Script)
# Error Handling Subagent Template
# A subagent with validation and guard clauses for critical operations
# This is a PARTIAL template - use within a complete agent file
#
# Usage: Replace {{placeholders}} with your values
# Note: Includes validation patterns and error handling
subagent {{subagent_name}}:
label: "{{SubagentLabel}}"
description: "{{SubagentDescription}} - includes validation and error handling"
actions:
{{action_name}}:
description: "{{ActionDescription}}"
inputs:
{{input_name}}: {{input_type}}
description: "{{InputDescription}}"
outputs:
success: boolean
description: "Whether the operation succeeded"
error_message: string
description: "Error message if operation failed"
{{output_name}}: {{output_type}}
description: "{{OutputDescription}}"
target: "{{ActionTarget}}"
reasoning:
instructions: ->
# Validation guard clauses
if @variables.{{required_field}} is None:
set @variables.validation_passed = False
| I need {{RequiredFieldDescription}} before I can proceed.
| Please provide this information.
if @variables.{{amount_field}} > {{MaxAmount}}:
set @variables.validation_passed = False
| The {{AmountFieldDescription}} exceeds the maximum of {{MaxAmount}}.
| Would you like to:
| - Use the maximum allowed amount
| - Split into multiple operations
| - Contact support for a higher limit
if @variables.validation_passed == True:
| All validations passed. Proceeding with the operation.
actions:
# Only available when validation passes
execute_action: @actions.{{action_name}}
with {{input_name}}=@variables.{{input_variable}}
set @variables.operation_success = @outputs.success
set @variables.result = @outputs.{{output_name}}
available when @variables.validation_passed == True
# Handle errors
retry_operation: @utils.transition to @subagent.{{subagent_name}}
available when @variables.operation_success == False
back_to_menu: @utils.transition to @subagent.agent_router
# Escalation Setup Pattern
# Complete agent template with connection block for human escalation
#
# ★ When To Use This Pattern:
# - Agent needs to transfer conversations to human agents
# - Using Omni-Channel for routing
# - Enhanced Chat or other messaging channels
#
# ★ Key Components:
# 1. connection messaging: block - defines routing destination
# 2. @utils.escalate action - triggers the transfer
# 3. escalation subagent - handles the handoff flow
#
# ★ Prerequisites:
# - Omni-Channel configured in Salesforce
# - Queue/Skill created for routing
# - Messaging channel active (Enhanced Chat, etc.)
#
# This is a COMPLETE template - customize for your use case
system:
instructions: "You are a helpful customer service agent. Be professional, friendly, and helpful. If you cannot resolve an issue or the customer requests a human, transfer them to a live agent."
messages:
welcome: "Hello! I'm here to help you today. What can I assist you with?"
error: "I apologize, but I encountered an issue. Let me connect you with a human agent."
config:
agent_name: "{{AGENT_NAME}}"
default_agent_user: "{{AGENT_USER_EMAIL}}"
agent_label: "{{AGENT_LABEL}}"
description: "Customer service agent with human escalation capability"
variables:
# Required linked variables for messaging context
EndUserId: linked string
source: @MessagingSession.MessagingEndUserId
description: "Messaging End User ID"
RoutableId: linked string
source: @MessagingSession.Id
description: "Messaging Session ID"
ContactId: linked string
source: @MessagingEndUser.ContactId
description: "Contact ID"
# Escalation tracking variables
escalation_requested: mutable boolean = False
description: "Whether customer requested human agent"
escalation_reason: mutable string
description: "Reason for escalation subagent"
# ⚠️ Use 'number' not 'integer' - integer type is NOT supported in AiAuthoringBundle
attempts_before_escalation: mutable number = 0
description: "Number of attempts before escalating"
language:
default_locale: "en_US"
additional_locales: ""
all_additional_locales: False
# ★ CONNECTION BLOCK - Required for @utils.escalate to work
# This defines where escalated conversations are routed
# Use singular 'connection' for one channel, plural 'connections' for multiple
connection messaging:
# ⚠️ IMPORTANT: Only "OmniChannelFlow" is supported (not "queue", "skill", or "agent")
outbound_route_type: "OmniChannelFlow"
# API name of your Omni-Channel Flow
outbound_route_name: "{{OMNI_CHANNEL_FLOW_NAME}}"
# ⚠️ REQUIRED: escalation_message must be included when connection block is present
escalation_message: "Transferring you to a human agent now..."
# Optional: Allow agent to adapt responses during escalation
adaptive_response_allowed: True
# ★ MULTI-CHANNEL EXAMPLE (use 'connections' plural for multiple channels)
# connections:
# messaging:
# outbound_route_type: "OmniChannelFlow"
# outbound_route_name: "Chat_Support_Flow"
# escalation_message: "Connecting you to chat support..."
# adaptive_response_allowed: True
# telephony:
# outbound_route_type: "OmniChannelFlow"
# outbound_route_name: "Phone_Support_Flow"
# escalation_message: "Transferring to phone support..."
# adaptive_response_allowed: False
# Entry point
start_agent agent_router:
label: "Subagent Router"
description: "Routes users to appropriate subagents based on intent"
reasoning:
instructions: ->
| Greet the customer and determine their needs.
| If they ask for a human or live agent, route to escalation.
| Otherwise, try to help them directly.
actions:
go_help: @utils.transition to @subagent.help
go_escalation: @utils.transition to @subagent.escalation
available when @variables.escalation_requested == True
subagent help:
label: "Help"
description: "Provides assistance to customers"
reasoning:
instructions: ->
| Help the customer with their question.
| If you cannot resolve their issue after 2-3 attempts, offer to connect them with a human.
| If they explicitly ask for a human agent at any time, transfer immediately.
|
| Phrases that indicate escalation request:
| - "talk to a human"
| - "speak to someone"
| - "real person"
| - "live agent"
| - "customer service representative"
|
| Track escalation reason if provided:
set @variables.escalation_reason = ...
actions:
offer_escalation: @utils.transition to @subagent.escalation
immediate_escalation: @utils.transition to @subagent.escalation
subagent escalation:
label: "Escalation"
description: "Transfers conversation to human agent"
reasoning:
instructions: ->
| The customer is being transferred to a human agent.
| Acknowledge their request and apologize for any inconvenience.
| Let them know a human will be with them shortly.
|
| Say something like:
| "I understand you'd like to speak with a human agent. I'm connecting you now.
| A customer service representative will be with you shortly. Thank you for your patience."
actions:
# ★ ESCALATION ACTION
# This transfers the conversation to the queue defined in connection block
transfer_to_human: @utils.escalate
description: "Transfer to human agent when customer requests or issue cannot be resolved"
# ★ NOTE: Skill-Based and Queue-Based Routing
# ⚠️ As of Dec 2025, only "OmniChannelFlow" is supported for outbound_route_type
# "queue", "skill", and "agent" cause validation errors
# You must create an Omni-Channel Flow that routes to your desired queue/skill
# ★ Alternative: GenAiPlannerBundle Escalation with Reason
# If using GenAiPlannerBundle (not visible in Studio), you can use:
#
# actions:
# escalate_with_reason: @utils.escalate with reason="Customer requested human assistance"
#
# NOTE: The "with reason" syntax only works in GenAiPlannerBundle!
# AiAuthoringBundle will fail with SyntaxError if you use it.
# ★ Troubleshooting Escalation:
#
# Issue: "escalate" action not recognized
# Fix: Add the connection messaging: block
#
# Issue: Transfer fails silently
# Fix: Verify Omni-Channel queue exists and has available agents
#
# Issue: SyntaxError: Unexpected 'with'
# Fix: You're using AiAuthoringBundle - remove "with reason" syntax
#
# Issue: Agent user lacks permissions
# Fix: Grant Omni-Channel permissions to the default_agent_user
# Flow-Based Action Template
# Define an action that calls a Salesforce Flow
# This is a PARTIAL template - define actions inside a subagent block
#
# ⚠️ NOTE ON DEPLOYMENT METHODS:
# - AiAuthoringBundle: `with`/`set` clauses ARE supported (TDD validated v1.7.0+)
# - GenAiPlannerBundle: Full syntax including `with`/`set` also supported
#
# Both deployment methods support the Two-Level Action System:
# Level 1: Action definition in subagent `actions:` block (with target/inputs/outputs)
# Level 2: Action invocation in `reasoning.actions:` block (with `with`/`set` clauses)
#
# Usage: Replace {{placeholders}} with your values
# Place this inside a subagent's actions: block
# Action Definition (place inside subagent's actions: block)
{{action_name}}:
description: "{{ActionDescription}}"
inputs:
{{input_1_name}}: {{input_1_type}}
description: "{{Input1Description}}"
{{input_2_name}}: {{input_2_type}}
description: "{{Input2Description}}"
outputs:
{{output_1_name}}: {{output_1_type}}
description: "{{Output1Description}}"
{{output_2_name}}: {{output_2_type}}
description: "{{Output2Description}}"
target: "flow://{{FlowApiName}}"
# Usage in reasoning block:
#
# ═══════════════════════════════════════════════════════════════════════════════
# ✅ Recommended Pattern — Level 2 invocation with `with`/`set` (works in both
# AiAuthoringBundle and GenAiPlannerBundle, TDD validated v1.7.0+)
# ═══════════════════════════════════════════════════════════════════════════════
#
# reasoning:
# instructions: ->
# | Help the user with their request.
# actions:
# invoke_{{action_name}}: @actions.{{action_name}}
# with {{input_1_name}}=... # LLM fills from conversation
# with {{input_2_name}}=@variables.some_var # From variable
# set @variables.result1 = @outputs.{{output_1_name}}
# set @variables.result2 = @outputs.{{output_2_name}}
#
# ═══════════════════════════════════════════════════════════════════════════════
# Alternative: LLM auto-invoke based on action description (no explicit binding)
# ═══════════════════════════════════════════════════════════════════════════════
#
# reasoning:
# instructions: ->
# | Help the user with their request.
# | Use the available actions when needed.
# actions:
# back_to_menu: @utils.transition to @subagent.agent_router
# Common Flow Target Patterns:
# - flow://Get_Account_Details
# - flow://Create_Case
# - flow://Update_Opportunity
# - flow://Send_Email_Notification
# - flow://Calculate_Discount
# Input/Output Types: string, number, boolean, list[string], object
# N-ary Boolean Conditions Template
# Demonstrates using 3+ conditions with and/or operators
#
# This is a PARTIAL template - integrate into a complete agent file
#
# ★ KEY CONCEPTS:
#
# N-ary AND: All conditions must be true
# N-ary OR: At least one condition must be true
# Grouping: Use () for complex expressions
#
# ★ Supported Contexts:
# - if statements in before_reasoning/after_reasoning
# - available when clauses on actions
#
# ★ Common Mistake:
# DO NOT nest if statements. Use N-ary and/or instead.
# ❌ if a: if b: if c: (nested - INVALID)
# ✅ if a and b and c: (flat - CORRECT)
# ═══════════════════════════════════════════════════════════════
# PATTERN 1: Three+ AND conditions in lifecycle
# ═══════════════════════════════════════════════════════════════
# Example: Require multiple authentication checks
subagent secure_action:
label: "Secure Action"
description: "Performs security-sensitive operations"
before_reasoning:
# All three conditions must be true
if @variables.is_authenticated and @variables.has_permission and @variables.session_valid:
transition to @subagent.authorized_action
# Otherwise stay in this subagent
reasoning:
instructions: ->
| User needs to authenticate before proceeding.
# ═══════════════════════════════════════════════════════════════
# PATTERN 2: Three+ OR conditions in lifecycle
# ═══════════════════════════════════════════════════════════════
# Example: Any elevated role gets access
subagent admin_panel:
label: "Admin Panel"
description: "Administrative features"
before_reasoning:
# Any one of these roles grants access
if @variables.is_admin or @variables.is_moderator or @variables.is_superuser:
transition to @subagent.admin_features
# Non-admins redirected
transition to @subagent.access_denied
# ═══════════════════════════════════════════════════════════════
# PATTERN 3: N-ary conditions in available when
# ═══════════════════════════════════════════════════════════════
subagent order_management:
label: "Order Management"
description: "Handles order operations"
reasoning:
instructions: ->
| Help the customer with their order.
actions:
# Action available only when ALL conditions met
process_return: @actions.handle_return
description: "Process a return request"
available when @variables.order_exists == True and @variables.within_return_window == True and @variables.item_eligible == True
# Action available when ANY premium tier matches
use_priority: @actions.priority_service
description: "Use priority service queue"
available when @variables.tier == "gold" or @variables.tier == "platinum" or @variables.tier == "enterprise"
# Mixed: specific product AND any valid status
expedite: @actions.expedite_order
description: "Expedite the current order"
available when @variables.product_type == "perishable" and (@variables.status == "pending" or @variables.status == "processing")
# ═══════════════════════════════════════════════════════════════
# PATTERN 4: Complex grouped conditions
# ═══════════════════════════════════════════════════════════════
subagent smart_routing:
label: "Smart Routing"
description: "Routes based on complex criteria"
before_reasoning:
# Premium with any product type OR standard with warranty
if (@variables.tier == "premium" and @variables.product_type != None) or (@variables.tier == "standard" and @variables.has_warranty == True):
transition to @subagent.priority_support
# ═══════════════════════════════════════════════════════════════
# ANTI-PATTERNS - DO NOT USE
# ═══════════════════════════════════════════════════════════════
# ❌ WRONG - Nested if statements (causes "Missing required element" error)
# before_reasoning:
# if @variables.a == True:
# if @variables.b == True:
# if @variables.c == True:
# transition to @subagent.x
# ✅ CORRECT - Flat N-ary condition
# before_reasoning:
# if @variables.a == True and @variables.b == True and @variables.c == True:
# transition to @subagent.x
# Subagent with Actions Template
# A subagent that integrates with external systems via Flow or Apex actions
# This is a PARTIAL template - use within a complete agent file
#
# Two-Level Action System:
# Level 1: subagent.actions block DEFINES actions (with target:, inputs:, outputs:)
# Level 2: reasoning.actions block INVOKES them (with @actions.name, with/set)
#
# Usage: Replace {{placeholders}} with your values
# Note: Actions defined inside subagent are local to that subagent
subagent {{subagent_name}}:
description: "{{SubagentDescription}}"
# Level 1: DEFINE actions available to this subagent
actions:
{{action_name}}:
description: "{{ActionDescription}}"
inputs:
{{input_name}}: {{input_type}}
description: "{{InputDescription}}"
outputs:
{{output_name}}: {{output_type}}
description: "{{OutputDescription}}"
target: "{{ActionTarget}}" # flow://FlowName or apex://ClassName
reasoning:
instructions: ->
| {{SubagentInstructions}}
| Use the available actions to help the user.
| Capture and communicate results clearly.
# Level 2: INVOKE the actions defined above
actions:
invoke_action: @actions.{{action_name}}
with {{input_name}}=...
set @variables.{{result_variable}} = @outputs.{{output_name}}
back_to_menu: @utils.transition to @subagent.agent_router
description: "Return to main menu"
# Deterministic Routing Template (Zero-Hallucination Pattern)
# ============================================================
#
# This template demonstrates the zero-hallucination intent classification
# pattern using action output flags to control LLM behavior.
#
# Pattern: Classify intent deterministically, route without hallucination
# Use when: Critical routing decisions that must not be influenced by LLM creativity
#
# KEY PATTERN:
# In Agentforce Assets, set action outputs with:
# - is_displayable: False (LLM cannot show this to user)
# - is_used_by_planner: True (LLM can use for routing)
#
# This ensures the LLM routes based on classification but cannot
# generate hallucinated responses based on the classification data.
system:
messages:
welcome: "Hello! How can I help you today?"
error: "I apologize, something went wrong. Let me try again."
instructions: "You are a customer service agent. Route users to the correct department based on their intent."
config:
agent_name: "DeterministicRoutingAgent"
agent_label: "Smart Router Agent"
description: "Agent demonstrating zero-hallucination intent routing pattern"
default_agent_user: "agent@yourorg.com" # REQUIRED: Change to valid Einstein Agent User
variables:
# Intent classification (populated by action with is_displayable: False)
classified_intent: mutable string = ""
description: "Classification result - hidden from user responses"
confidence_score: mutable number = 0
description: "Classification confidence (0-100)"
# Routing state
needs_classification: mutable boolean = True
description: "Whether user intent needs classification"
low_confidence_warning: mutable boolean = False
description: "Flag for low confidence routing"
start_agent agent_router:
description: "Classify intent and route deterministically"
reasoning:
instructions: ->
# ====================================================
# DETERMINISTIC ROUTING (based on classified_intent)
# ====================================================
# Route ONLY when classification is complete
if @variables.needs_classification == False:
# HIGH-CONFIDENCE ROUTING
if @variables.confidence_score >= 80:
if @variables.classified_intent == "billing":
transition to @subagent.billing
if @variables.classified_intent == "technical_support":
transition to @subagent.technical_support
if @variables.classified_intent == "sales":
transition to @subagent.sales
if @variables.classified_intent == "returns":
transition to @subagent.returns
# LOW-CONFIDENCE: Confirm with user
if @variables.confidence_score < 80:
set @variables.low_confidence_warning = True
| I want to make sure I route you correctly.
| It sounds like you need help with **{!@variables.classified_intent}**.
| Is that correct?
# INITIAL STATE: Ask for help subject
if @variables.needs_classification == True:
| I can help with billing, technical support, sales, or returns.
| What do you need help with today?
actions:
# CRITICAL: This action's outputs must be configured in Agentforce Assets:
# - classified_intent: is_displayable=False, is_used_by_planner=True
# - confidence_score: is_displayable=False, is_used_by_planner=True
#
# This ensures LLM cannot hallucinate based on classification data
classify_intent: @actions.Classify_User_Intent
description: "Determine what the user needs help with"
with user_message = ... # LLM extracts from conversation
set @variables.classified_intent = @outputs.intent
set @variables.confidence_score = @outputs.confidence
set @variables.needs_classification = False
# Manual routing for low-confidence cases
go_billing: @utils.transition to @subagent.billing
description: "Yes, I need billing help"
available when @variables.low_confidence_warning == True and @variables.classified_intent == "billing"
go_support: @utils.transition to @subagent.technical_support
description: "Yes, I need technical support"
available when @variables.low_confidence_warning == True and @variables.classified_intent == "technical_support"
go_sales: @utils.transition to @subagent.sales
description: "Yes, I need sales help"
available when @variables.low_confidence_warning == True and @variables.classified_intent == "sales"
go_returns: @utils.transition to @subagent.returns
description: "Yes, I need returns help"
available when @variables.low_confidence_warning == True and @variables.classified_intent == "returns"
# Reclassify if user said "no"
reclassify: @utils.setVariables
description: "That's not what I need - let me clarify"
available when @variables.low_confidence_warning == True
with needs_classification = True
with classified_intent = ""
with confidence_score = 0
with low_confidence_warning = False
# ============================================================
# ROUTED SUBAGENTS
# ============================================================
subagent billing:
description: "Handle billing inquiries"
reasoning:
instructions: |
Help the customer with their billing question.
You can view invoices, explain charges, or process payments.
actions:
back: @utils.transition to @subagent.agent_router
description: "Return to main menu"
escalate_now: @utils.escalate
description: "Transfer to billing specialist"
subagent technical_support:
description: "Handle technical support issues"
reasoning:
instructions: |
Help the customer with their technical issue.
Troubleshoot problems and provide solutions.
actions:
back: @utils.transition to @subagent.agent_router
description: "Return to main menu"
escalate_now: @utils.escalate
description: "Transfer to technical specialist"
subagent sales:
description: "Handle sales inquiries"
reasoning:
instructions: |
Help the customer with sales questions.
Provide product information and pricing.
actions:
back: @utils.transition to @subagent.agent_router
description: "Return to main menu"
escalate_now: @utils.escalate
description: "Transfer to sales representative"
subagent returns:
description: "Handle return requests"
reasoning:
instructions: |
Help the customer with their return request.
Check eligibility and process returns.
actions:
back: @utils.transition to @subagent.agent_router
description: "Return to main menu"
escalate_now: @utils.escalate
description: "Transfer to returns specialist"
# Escalation Pattern Template
# ===========================
#
# This template demonstrates the complete escalation pattern with:
# - Multi-channel connection blocks (messaging, voice, web)
# - Graceful handoff with escalation messages
# - Pre-escalation data gathering
# - OmniChannel routing configuration
#
# Pattern: Multi-channel escalation with context preservation
# Use when: Any agent that needs human handoff capabilities
system:
messages:
welcome: "Hello! I'm here to help. If I can't assist you, I can connect you with a specialist."
error: "I apologize, something went wrong. Let me connect you with someone who can help."
instructions: "You are a helpful agent. When you cannot resolve an issue, escalate to a human agent with full context."
config:
agent_name: "EscalationPatternAgent"
agent_label: "Escalation Demo Agent"
description: "Agent demonstrating complete escalation patterns"
default_agent_user: "agent@yourorg.com" # REQUIRED: Change to valid Einstein Agent User
# ============================================================
# CONNECTION BLOCKS (Multi-Channel Escalation)
# ============================================================
connections:
# Messaging channel (SMS, WhatsApp, etc.)
connection messaging:
escalation_message: "One moment, I'm transferring our conversation to a specialist who can better assist you."
outbound_route_type: "OmniChannelFlow"
outbound_route_name: "<flow://Escalate_Messaging_To_Agent>"
adaptive_response_allowed: False
# Voice channel
connection voice:
escalation_message: "Please hold while I transfer you to a specialist. They will have full context of our conversation."
outbound_route_type: "Queue"
outbound_route_name: "Customer_Support_Queue"
adaptive_response_allowed: True
# Web chat channel
connection web:
escalation_message: "Connecting you with a live agent now. They'll be with you shortly."
outbound_route_type: "OmniChannelFlow"
outbound_route_name: "<flow://Web_Chat_Escalation_Flow>"
adaptive_response_allowed: False
variables:
# Customer context
customer_name: mutable string = ""
description: "Customer's name for personalization"
customer_issue: mutable string = ""
description: "Summary of customer's issue"
issue_category: mutable string = ""
description: "Category of issue for routing"
# Escalation state
escalation_reason: mutable string = ""
description: "Reason for escalation"
attempts_before_escalation: mutable number = 0
description: "Number of resolution attempts"
ready_to_escalate: mutable boolean = False
description: "Whether pre-escalation data is gathered"
start_agent entry:
description: "Entry point - greet and assess needs"
reasoning:
instructions: |
Greet the customer and understand their needs.
Try to help directly, escalate if needed.
actions:
go_support: @utils.transition to @subagent.support
description: "Help with support issue"
# ============================================================
# SUPPORT SUBAGENT (With Escalation Triggers)
# ============================================================
subagent support:
description: "Attempt to resolve issue, escalate if unable"
reasoning:
instructions: ->
# POST-ACTION: Check if escalation was triggered
if @variables.ready_to_escalate == True:
transition to @subagent.pre_escalation
# Track attempts
| I'm here to help you with your issue.
if @variables.attempts_before_escalation >= 2:
| It seems like I'm having trouble resolving this.
| Would you like me to connect you with a specialist?
actions:
# Attempt resolution
try_resolve: @actions.Attempt_Resolution
description: "Try to resolve the issue"
with issue = ... # LLM extracts from conversation
set @variables.customer_issue = @outputs.issue_summary
set @variables.issue_category = @outputs.category
set @variables.attempts_before_escalation = @variables.attempts_before_escalation + 1
# User-initiated escalation
request_human: @utils.setVariables
description: "I'd like to speak with a human"
with escalation_reason = "Customer requested human agent"
with ready_to_escalate = True
# Agent-initiated escalation (after failed attempts)
escalate_complex: @utils.setVariables
description: "Connect me with a specialist"
available when @variables.attempts_before_escalation >= 2
with escalation_reason = "Unable to resolve after multiple attempts"
with ready_to_escalate = True
# ============================================================
# PRE-ESCALATION SUBAGENT (Gather Context)
# ============================================================
subagent pre_escalation:
description: "Gather information before escalating"
reasoning:
instructions: ->
# Ensure we have customer name for personalization
if @variables.customer_name == "":
| Before I transfer you, may I have your name so the specialist can address you properly?
else:
| Thank you, {!@variables.customer_name}. Let me prepare the transfer.
# Display summary of what we'll share
if @variables.customer_name != "" and @variables.customer_issue != "":
| I'll share the following with the specialist:
| - Your issue: {!@variables.customer_issue}
| - Category: {!@variables.issue_category}
| - Reason for transfer: {!@variables.escalation_reason}
|
| Ready to connect you now.
transition to @subagent.escalation
actions:
# Capture customer name
save_name: @utils.setVariables
description: "Save customer name"
available when @variables.customer_name == ""
with customer_name = ... # LLM extracts name from response
# Proceed to escalation
proceed: @utils.transition to @subagent.escalation
description: "Proceed with transfer"
available when @variables.customer_name != ""
# Cancel escalation
cancel_escalation: @utils.setVariables
description: "Actually, let me try again with the bot"
with ready_to_escalate = False
with escalation_reason = ""
run @utils.transition to @subagent.support
# ============================================================
# ESCALATION SUBAGENT (Handoff)
# ============================================================
subagent escalation:
description: "Execute escalation to human agent"
reasoning:
instructions: ->
# Log escalation context (this gets passed to human agent)
run @actions.Log_Escalation_Context
with customer_name = @variables.customer_name
with issue_summary = @variables.customer_issue
with category = @variables.issue_category
with reason = @variables.escalation_reason
with attempt_count = @variables.attempts_before_escalation
| Transferring you now. Thank you for your patience!
actions:
# The actual escalation - uses connection block configuration
handoff: @utils.escalate
description: "Transfer to human agent"
# ============================================================
# OPTIONAL: SPECIALIZED ESCALATION QUEUES
# ============================================================
subagent escalate_billing:
description: "Escalate specifically to billing team"
reasoning:
instructions: |
I'm connecting you with our billing specialists.
They'll be able to help with your account questions.
actions:
# Note: In production, you'd configure this action
# to route to a specific billing queue
billing_handoff: @utils.escalate
description: "Transfer to billing team"
subagent escalate_technical:
description: "Escalate specifically to technical support"
reasoning:
instructions: |
I'm connecting you with our technical support team.
They specialize in resolving complex technical issues.
actions:
tech_handoff: @utils.escalate
description: "Transfer to technical support"
# Flow Action Lookup Template
# ============================
#
# This template demonstrates the pattern for calling Flow actions
# that return complex data types (SObjects, lists, custom types).
#
# Pattern: Data lookup and display using Flow actions
# Use when: Fetching records from Salesforce (Cases, Orders, Accounts)
#
# CRITICAL: When defining Flow action outputs in Agentforce Assets:
# - For SObject returns: complex_data_type_name = "lightning__recordInfoType"
# - For list[string]: complex_data_type_name = "lightning__textType"
# - For currency: complex_data_type_name = "lightning__currencyType"
system:
messages:
welcome: "Hello! I can help you look up order information."
error: "I apologize, something went wrong retrieving your data."
instructions: "You are a customer service agent helping users look up their orders."
config:
agent_name: "FlowActionLookupAgent"
agent_label: "Order Lookup Agent"
description: "Agent demonstrating Flow action patterns with complex data types"
default_agent_user: "agent@yourorg.com" # REQUIRED: Change to valid Einstein Agent User
variables:
# Customer context (from session)
customer_id: linked string
source: @session.customerId
description: "Customer ID from session context"
# Order data (populated by Flow action)
order_id: mutable string = ""
description: "Current order being viewed"
order_status: mutable string = ""
description: "Order status from lookup"
order_total: mutable string = ""
description: "Order total amount"
order_date: mutable string = ""
description: "Order date"
# Error handling
lookup_error: mutable boolean = False
description: "Whether lookup encountered an error"
start_agent entry:
description: "Entry point - welcome and route to order lookup"
reasoning:
instructions: |
Welcome the customer and offer to help with order lookups.
actions:
go_lookup: @utils.transition to @subagent.order_lookup
description: "Start order lookup"
# ============================================================
# ORDER LOOKUP SUBAGENT (Flow Action Pattern)
# ============================================================
subagent order_lookup:
description: "Look up order details using Flow action"
reasoning:
instructions: ->
# POST-ACTION CHECK: Display results if order was found
if @variables.order_status != "":
| **Order Details**
| - Order ID: {!@variables.order_id}
| - Status: {!@variables.order_status}
| - Total: {!@variables.order_total}
| - Date: {!@variables.order_date}
|
| Is there anything else you'd like to know about this order?
# ERROR CHECK: Handle lookup failures
if @variables.lookup_error == True:
| I couldn't find that order. Please check the order ID and try again.
set @variables.lookup_error = False
# INITIAL STATE: Ask for order ID
if @variables.order_id == "":
| What order would you like me to look up?
| Please provide your order ID.
actions:
# Flow action with SObject return type
# In Agentforce Assets, set outputs.order_record complex_data_type_name = "lightning__recordInfoType"
lookup_order: @actions.Get_Order_Details
description: "Look up order by ID"
with order_id = ... # LLM extracts from user message
include_in_progress_indicator: True
progress_indicator_message: "Looking up your order..."
set @variables.order_id = @outputs.order_id
set @variables.order_status = @outputs.status
set @variables.order_total = @outputs.total_amount
set @variables.order_date = @outputs.order_date
# Error handling: check if lookup failed
if @outputs.found == False:
set @variables.lookup_error = True
# Flow action returning a list of strings
# In Agentforce Assets, set outputs.product_names complex_data_type_name = "lightning__textType"
get_items: @actions.Get_Order_Line_Items
description: "Get list of items in the order"
available when @variables.order_id != ""
with order_id = @variables.order_id
clear_search: @utils.setVariables
description: "Search for a different order"
with order_id = ""
with order_status = ""
with order_total = ""
with order_date = ""
escalate_now: @utils.escalate
description: "Transfer to human agent"
# Hub-and-Spoke Architecture Template
# ====================================
#
# This template demonstrates the Hub-and-Spoke pattern where a central
# 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)
system:
messages:
welcome: "Welcome! I can help with orders, returns, or general support."
error: "I apologize, something went wrong. Let me try again."
instructions: "You are a customer service agent for an e-commerce company."
config:
agent_name: "HubAndSpokeAgent"
agent_label: "Customer Service Agent"
description: "Multi-purpose agent with hub-and-spoke architecture"
default_agent_user: "agent@yourorg.com" # REQUIRED: Change to valid Einstein Agent User
variables:
customer_id: linked string
source: @session.customerId
description: "Customer ID from session"
order_id: mutable string = ""
description: "Current order being discussed"
issue_resolved: mutable boolean = False
description: "Whether the issue has been resolved"
# ============================================================
# HUB: Central Router
# ============================================================
start_agent agent_router:
description: "Route to appropriate subagent based on user intent"
reasoning:
instructions: |
Determine what the customer needs and route accordingly:
- 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"
process_return: @utils.transition to @subagent.returns
description: "Customer wants to return or refund"
general_help: @utils.transition to @subagent.support
description: "General support questions"
# ============================================================
# SPOKE: Orders Subagent
# ============================================================
subagent orders:
description: "Handle order status and tracking inquiries"
reasoning:
instructions: ->
| Help the customer with their order inquiry.
if @variables.order_id != "":
| Current order: {!@variables.order_id}
actions:
lookup_order: @actions.get_order_status
description: "Look up order details"
with order_id = @variables.order_id
back_to_hub: @utils.transition to @subagent.agent_router
description: "Return to main menu"
# ============================================================
# SPOKE: Returns Subagent
# ============================================================
subagent returns:
description: "Handle return and refund requests"
reasoning:
instructions: ->
| Help the customer with their return or refund request.
| Verify the order details before processing.
actions:
start_return: @actions.initiate_return
description: "Start a return process"
process_refund: @actions.process_refund
description: "Process a refund"
back_to_hub: @utils.transition to @subagent.agent_router
description: "Return to main menu"
# ============================================================
# SPOKE: Support Subagent
# ============================================================
subagent support:
description: "Handle general support questions"
reasoning:
instructions: |
Help the customer with general questions.
If the question requires specialized help, route appropriately.
actions:
escalate: @utils.escalate
description: "Transfer to human agent"
back_to_hub: @utils.transition to @subagent.agent_router
description: "Return to main menu"
# Minimal Agent Script Starter Template
# =====================================
#
# 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, subagent, start_agent
# File extension: .agent
system:
messages:
welcome: "Hello! How can I help you today?"
error: "I apologize, but something went wrong. Let me try again."
instructions: "You are a helpful assistant."
config:
agent_name: "MinimalAgent"
agent_label: "Minimal Agent"
description: "A minimal agent template to get started"
default_agent_user: "agent@yourorg.com" # REQUIRED: Change to valid Einstein Agent User
# Optional: Add variables for state tracking
# variables:
# counter: mutable number = 0
# session_id: linked string
# source: @session.sessionID
subagent main:
description: "Main conversation handler"
reasoning:
instructions: |
Help the user with their request.
Be friendly and helpful.
start_agent entry:
description: "Entry point for all conversations"
reasoning:
instructions: |
Greet the user and route to the main subagent.
actions:
begin: @utils.transition to @subagent.main
description: "Start the main conversation"
Related skills
How it compares
Pick developing-agentforce for Salesforce Agentforce enterprise specs; pick generic agent-building skills for non-Salesforce LLM agent frameworks.
FAQ
What sections does an Agentforce agent spec include?
Developing-agentforce specs include purpose and scope, behavioral intent with guardrails, a subagent map with routing conditions, variables, and backing logic mappings to Apex, Flow, or Prompt Template. A Mermaid diagram documents router-to-subagent flows.
How does developing-agentforce handle unclear user intent?
Agent specs include an ambiguous_question subagent routed when intent is unclear, plus escalation and off-topic guardrails. Developing-agentforce documents what information must persist when users switch between subagents.
Is Developing Agentforce safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.