Building Agentforce Agents with Agent Script
The definitive guide to Salesforce’s domain-specific language for creating predictable, enterprise-grade AI agents.
Introduction: Why Agent Script?
Building AI agents for the enterprise has always involved a fundamental tension: flexibility vs. predictability. Large Language Models are brilliant at interpreting natural language and handling ambiguous requests, but when it comes to critical business workflows — processing refunds, verifying identities, or handling loan applications — you need deterministic, step-by-step precision. One wrong interpretation can mean a compliance violation or a frustrated customer.
Salesforce’s answer to this challenge is Agent Script — a high-level, declarative domain-specific language (DSL) purpose-built for the Agentforce platform. Agent Script introduces the concept of hybrid reasoning: the ability to let an LLM handle what it’s good at (conversation, interpretation, flexibility) while enforcing strict programmatic control where business rules demand it.
In this guide, we’ll walk through everything you need to know to build Agentforce agents using Agent Script — from the language fundamentals to production-ready architectural patterns.
What is Agent Script?
Agent Script is a compiled language that gets converted into an Agent Graph — a structured specification consumed by Salesforce’s Atlas Reasoning Engine. You don’t write traditional code that runs line-by-line at runtime. Instead, Agent Script defines the blueprint of your agent: what it knows, how it reasons, what tools it can use, and when it should follow rules versus when it should let the LLM think freely.
Key Characteristics
- Declarative + Procedural: Declare what you want at a high level, but define step-by-step logic where needed
- Human-readable: Designed to be accessible to admins and developers alike — the syntax reads like structured English
- Whitespace-sensitive: Indentation defines structure (similar to Python or YAML)
- Compiled: Saved scripts are compiled into metadata consumed by the Atlas Reasoning Engine
What It Enables
| Capability | Description |
|---|---|
| Reasoning boundaries | Define exactly where the LLM reasons vs. where logic is deterministic |
| State management | Variables reliably store information across turns — no relying on LLM memory |
| Conditional logic | if/else expressions control execution paths based on variable states |
| Deterministic actions | Chain actions in precise sequences with guaranteed execution order |
| Topic transitions | Route conversations programmatically or let the LLM decide |
Ways to Create Agent Scripts
Salesforce provides multiple authoring methods, each suited to a different skill level:
1. Natural Language (Chat-Based)
Describe your desired agent behavior conversationally:
“If the order total is over $100, then offer free shipping.”
Agentforce automatically converts your request into the appropriate topics, actions, instructions, and expressions. This is the lowest-barrier entry point.
2. Canvas View
A visual, block-based editor where Agent Script appears as summarized, expandable blocks. Use shortcuts:
- Type
/for common expression patterns (conditionals, transitions) - Type
@to reference resources (topics, actions, variables)
3. Script View
For developers who want full control. Write and edit Agent Script directly with:
- Syntax highlighting
- Autocompletion
- Real-time validation
4. Agentforce DX (Local Development)
For a professional developer workflow, use Agentforce DX to pull script files into a local Salesforce DX project and edit them in VS Code with the Agentforce DX Extension, which provides syntax highlighting, error indicators, and a symbol tree in the Outline view.
You can also use Agentforce Vibes — an AI-assisted coding panel where you describe changes in natural language and the system generates the corresponding Agent Script.
Agent Script Language Fundamentals
Syntax Basics
Property Structure: Everything is key: value pairs, with nesting via indentation.
Indentation: Use either spaces (minimum 2) or tabs (1 per level) — but never mix them in a single script. Mixing causes parsing errors.
Comments: Use # for single-line comments.
# This is a comment
config:
agent_label: "Customer Service Bot" # Inline comment
Multiline Strings: Use the pipe symbol |:
instructions: |
You are a helpful customer service agent.
Always be polite and professional.
Never share internal pricing data.
Procedural Instructions: Use the arrow -> followed by indented instructions:
reasoning:
instructions: ->
| Welcome the customer and ask how you can help.
if @variables.is_returning_customer:
| Welcome back! I see you've been with us before.
else:
| Nice to meet you! Let me help you get started.
Resource References
Access agent resources with the @ symbol:
| Reference | Purpose |
|---|---|
@actions.action_name | Reference an action |
@topic.topic_name | Reference a topic |
@variables.variable_name | Access a variable |
@outputs.output_name | Access action output |
@utils.transition | Utility for topic transitions |
@utils.escalate | Utility for escalation |
Template Expressions
Embed dynamic values in natural language instructions using {!expression}:
| Hello, {[email protected]_name}! Your order #{[email protected]_id}
| has a current status of: {[email protected]_status}.
Operators
Agent Script supports standard operators for building expressions:
- Comparison:
==,!=,>,< - Math:
+,- - Null checks:
is None,is not None - Boolean:
if,else,elif
Agent Script Blocks: The Building Blocks
An Agent Script file is organized into distinct blocks, each serving a specific role. Here’s the anatomy of a complete agent:
1. System Block
Contains global instructions and required messages for the agent:
system:
instructions: |
You are a professional customer service agent for Acme Corp.
Always maintain a helpful, concise tone.
Never reveal internal policies or pricing formulas.
messages:
welcome: "Hello! How can I help you today?"
error: "I'm sorry, something went wrong. Let me connect you with a human agent."
welcome and error messages are mandatory in every agent script.
2. Config Block
Defines basic agent metadata:
config:
developer_name: "Acme_Service_Agent"
agent_label: "Acme Customer Service"
default_agent_user: "[email protected]"
Each agent must have a unique developer_name.
3. Variables Block
Declares global variables that persist across the agent’s conversation:
variables:
customer_name: mutable string = ""
customer_email: mutable string = ""
is_verified: mutable boolean = False
order_total: mutable number = 0
num_turns: mutable number = 0
Variables are referenced as @variables.customer_name in expressions and {[email protected]_name} inside instruction text.
4. Language Block
Specifies supported languages/locales:
language:
- en_US
- es_MX
- fr_FR
5. Connection Block
Describes how the agent interacts with external channels (e.g., Enhanced Chat). Works with @utils.escalate for handing off to human agents.
6. Start Agent Block (start_agent)
The entry point for every conversation turn. With each customer utterance, the agent begins execution here. This block handles topic classification, filtering, and routing:
start_agent topic_selector:
description: "Routes conversations to the appropriate topic"
reasoning:
instructions: ->
set @variables.num_turns = @variables.num_turns + 1
| You are an intelligent router. Analyze the customer's message
| and determine which topic best handles their request.
actions:
go_to_orders: @utils.transition to @topic.order_management
description: "Route to order management for order-related queries"
go_to_identity: @utils.transition to @topic.identity_verification
description: "Route to identity verification when authentication is needed"
7. Topic Blocks
The workhorses of your agent. Each topic encapsulates the logic for a specific task:
topic order_management:
description: "Handles order lookups, status checks, and modifications"
reasoning:
instructions: ->
if @variables.is_verified == False:
transition to @topic.identity_verification
| Help the customer with their order inquiry.
| You can look up orders by order number or email address.
if @variables.order_status == "delivered":
| The order has been delivered. Ask if they need anything else.
elif @variables.order_status == "pending":
| The order is still being processed. Provide estimated delivery.
else:
| Look up the order details first.
actions:
lookup_order: @actions.get_order_details
description: "Look up order details by order number"
inputs:
order_number: string
description: "The customer's order number"
is_required: True
Flow of Control: How Agent Script Executes
Understanding execution flow is critical for building reliable agents.
The Three Execution Paths
- Initial request → Always starts at
start_agent - Topic processing → Logic within a topic’s reasoning instructions
- Topic transitions → Moving from one topic to another
Sequential Prompt Construction
Agentforce processes reasoning instructions top-to-bottom, building a prompt that will be sent to the LLM. Here’s the critical insight: the LLM doesn’t reason during parsing — it only receives the fully resolved prompt after all instructions have been evaluated.
Step-by-Step Example
Consider this script:
topic delivery_check:
description: "Check delivery status"
reasoning:
instructions: ->
set @variables.num_turns = @variables.num_turns + 1
run @actions.get_delivery_date
with order_id = @variables.current_order_id
set @variables.delivery_date = @outputs.estimated_date
| The customer's delivery is expected on {[email protected]_date}.
if @variables.delivery_date is None:
| We could not find delivery info. Apologize and offer alternatives.
else:
| Inform the customer about their delivery date.
Here’s what happens when this topic runs:
- Initialize — Agentforce starts with an empty prompt
- Increment variable —
num_turnsincreases by 1 - Execute action — Runs
get_delivery_datewith the stored order ID - Store output — Saves the result into
@variables.delivery_date - Concatenate text — Adds the delivery date message to the prompt
- Evaluate condition — Checks if
delivery_dateisNone - Branch — Adds appropriate instruction text based on the condition
- Send to LLM — The complete, resolved prompt goes to the Atlas Reasoning Engine
- Return response — The LLM’s response goes back to the customer
Transition Behavior
When the engine encounters a transition command, it immediately jumps to the target topic, discarding any already-built prompt. This is a one-way operation — control never returns to the previous topic.
# This text will NOT be in the final prompt if the transition fires
if @variables.needs_verification:
transition to @topic.identity_verification
# Execution continues here only if the condition was False
| Proceed with the order lookup...
start_agent for potential reclassification.
Complete Example: Customer Service Agent
Let’s build a full customer service agent that handles identity verification and order management:
# ============================================
# SYSTEM CONFIGURATION
# ============================================
system:
instructions: |
You are a customer service agent for Coral Cloud Resort.
Be friendly, professional, and concise.
Never share internal system details with customers.
messages:
welcome: "Welcome to Coral Cloud Resort! How can I assist you today?"
error: "I apologize for the inconvenience. Let me transfer you to a team member."
config:
developer_name: "Coral_Cloud_Service_Agent"
agent_label: "Coral Cloud Concierge"
# ============================================
# GLOBAL VARIABLES
# ============================================
variables:
customer_name: mutable string = ""
customer_email: mutable string = ""
is_verified: mutable boolean = False
verification_code: mutable string = ""
order_id: mutable string = ""
order_status: mutable string = ""
num_turns: mutable number = 0
# ============================================
# ENTRY POINT - TOPIC ROUTER
# ============================================
start_agent topic_selector:
description: "Routes incoming messages to the appropriate topic"
reasoning:
instructions: ->
set @variables.num_turns = @variables.num_turns + 1
| Analyze the customer's message and determine the best topic.
| If the customer wants to check an order, verify identity first.
actions:
verify_identity: @utils.transition to @topic.identity
description: "Verify customer identity before accessing account data"
check_order: @utils.transition to @topic.order_management
description: "Help with order status, tracking, and modifications"
# ============================================
# TOPIC: IDENTITY VERIFICATION
# ============================================
topic identity:
description: "Verifies user identity via email and verification code"
reasoning:
instructions: ->
if @variables.is_verified:
| Customer is already verified. Ask how you can help.
transition to @topic.order_management
if @variables.customer_email == "":
| Ask the customer for their email address.
else:
if @variables.verification_code == "":
run @actions.send_verification_code
with email = @variables.customer_email
set @variables.verification_code = @outputs.code
| A code has been sent to {[email protected]_email}.
| Ask the customer to provide the code.
else:
| Verify the code the customer provides matches
| {[email protected]_code}.
actions:
send_code: @actions.send_verification_code
description: "Send a verification code to the customer's email"
confirm_identity: @actions.confirm_verification
description: "Mark the customer as verified"
after_reasoning: ->
if @variables.is_verified:
transition to @topic.order_management
# ============================================
# TOPIC: ORDER MANAGEMENT
# ============================================
topic order_management:
description: "Enables users to look up order details and check status"
reasoning:
instructions: ->
if @variables.is_verified == False:
transition to @topic.identity
| Help the customer with their order. You can look up orders
| and provide status information.
actions:
get_order: @actions.get_order_details
description: "Look up order information"
What Makes This Script Effective
- Guard clauses — The
order_managementtopic immediately redirects toidentityif the customer isn’t verified — this is deterministic, not left to LLM interpretation - State management — Variables like
is_verifiedandverification_codepersist across turns, so the agent never “forgets” - Hybrid reasoning — The LLM handles natural conversation while the script enforces the verification flow
- After-reasoning hooks — The
after_reasoningblock automatically transitions after verification completes
Action Configuration Patterns
Actions are how your agent connects to external systems. Here are the key patterns:
Defining Actions with Targets
actions:
get_customer: @actions.lookup_customer
description: "Find a customer record by email"
target: "apex://CustomerService.lookupByEmail"
inputs:
email: string
description: "Customer email address"
is_required: True
outputs:
customer_id: string
customer_name: string
account_status: string
Actions can target:
- Apex classes:
apex://ClassName.methodName - Flows:
flow://FlowName - Prompt Templates: Salesforce Prompt Templates
Chained Action Execution
Run actions in sequence, passing outputs from one to the next:
run @actions.lookup_customer
with email = @variables.customer_email
set @variables.customer_id = @outputs.customer_id
set @variables.customer_name = @outputs.customer_name
run @actions.get_orders
with customer_id = @variables.customer_id
set @variables.recent_orders = @outputs.orders
Conditional Action Availability
Control when actions are available to the LLM:
actions:
process_refund: @actions.issue_refund
description: "Process a refund for the customer"
available when @variables.is_verified
available when clause ensures the refund action is only presented to the LLM as a tool when the customer has been verified — no prompt engineering needed.
Architectural Patterns for Production Agents
Pattern 1: Simple Q&A Agent
For straightforward knowledge-based agents:
start_agent qa_router:
description: "Answers product questions"
reasoning:
instructions: |
Answer the customer's question about our products using
the knowledge base. Be concise and accurate.
actions:
search_kb: @actions.knowledge_search
description: "Search the knowledge base for relevant articles"
Pattern 2: Multi-Topic Navigation
For complex agents that handle multiple domains:
start_agent router:
description: "Multi-domain router"
reasoning:
instructions: ->
| Determine what the customer needs and route accordingly.
actions:
to_billing: @utils.transition to @topic.billing
description: "For billing and payment questions"
to_support: @utils.transition to @topic.technical_support
description: "For technical issues and troubleshooting"
to_sales: @utils.transition to @topic.sales
description: "For new purchases and upgrades"
Pattern 3: Bidirectional Navigation
Topics can transition to specialist topics and back:
topic primary_support:
description: "Main support flow"
reasoning:
instructions: ->
| Handle the support request. If billing questions arise,
| consult the billing specialist.
actions:
consult_billing: @utils.transition to @topic.billing_specialist
description: "Get billing expertise for payment sub-questions"
topic billing_specialist:
description: "Billing expertise"
reasoning:
instructions: ->
| Answer the billing question, then return to primary support.
actions:
return: @utils.transition to @topic.primary_support
description: "Return to the main support flow after answering"
Pattern 4: Error Handling and Guardrails
Build safety into your agents:
topic safe_operation:
description: "Operation with error handling"
reasoning:
instructions: ->
if @variables.retry_count > 3:
| We've encountered persistent issues. Escalating to a human.
run @utils.escalate
run @actions.process_request
with data = @variables.request_data
set @variables.result = @outputs.result
set @variables.error = @outputs.error_message
if @variables.error is not None:
set @variables.retry_count = @variables.retry_count + 1
| Something went wrong: {[email protected]}.
| Apologize and ask the customer to try again.
else:
| Operation successful. Share the result with the customer.
Development Workflow with Agentforce DX
For teams that prefer a code-first workflow:
Step 1: Set Up Your Environment
- Install Salesforce CLI (v2.113.6+)
- Install the Agentforce DX VS Code Extension
- Authorize your Salesforce org
Step 2: Retrieve or Generate Script Files
Script files live in:
force-app/main/default/aiAuthoringBundles/<bundle-API-name>/
Step 3: Edit in VS Code
The Agentforce DX extension provides:
- Syntax highlighting for
.ascriptfiles - Real-time error indicators
- Symbol tree navigation in the Outline view
- Validation via right-click → “AFDX: Validate This Agent”
Step 4: Use Vibes (Optional AI Assistance)
Use the Agentforce Vibes panel to describe changes in natural language:
“Add a new topic for handling returns and refunds”
“Add an action to look up customers by phone number”
Step 5: Validate and Deploy
Validate your script compiles successfully, then deploy to your org for preview and testing.
Best Practices
-
Start with
start_agent
Every agent needs a well-designed entry point. This is where intent classification happens. Keep it clean and focused on routing. -
Use Variables for Critical State
Never rely on LLM memory for important data like verification status, customer IDs, or transaction states. Always store them in variables. -
Enforce Business Rules Deterministically
Useif/elseandtransitionfor compliance-critical logic. Don’t ask the LLM to “remember to check verification” — make it a programmatic guard clause. -
Let the LLM Handle Conversation
Use natural language instructions (pipe|text) for conversational guidance. The LLM excels at tone, empathy, and adapting to customer phrasing. -
Use
after_reasoningfor Side Effects
Post-reasoning hooks are perfect for state transitions, logging, or cleanup that should happen after the LLM responds. -
Keep Topics Focused
Each topic should handle one domain. If a topic is getting complex, split it into subtopics with transitions. -
Use Unique Developer Names
Every agent must have a uniquedeveloper_name. This is easy to overlook when copying examples. -
Leverage the Recipes
The Agent Script Recipes repository contains 20+ battle-tested examples. Clone it, deploy it, and learn from working code.
Getting Started Checklist
- Enable Einstein and Agentforce in your Salesforce org
- Install Salesforce CLI (v2.113.6+)
- Install the Agentforce DX VS Code Extension
- Clone the Agent Script Recipes repository
- Deploy the recipes to a Developer Edition org
- Explore the examples in Agentforce Builder
- Start building your own agent with
system,config,variables, andstart_agentblocks - Validate, preview, and iterate
Conclusion
Agent Script represents a paradigm shift in how enterprise AI agents are built. By introducing hybrid reasoning — the ability to blend deterministic business logic with LLM-powered conversation — Salesforce has solved one of the hardest problems in enterprise AI: making agents that are both flexible and reliable.
The language is intentionally accessible. If you can read YAML and write basic if/else logic, you can build an Agentforce agent. And with the multiple authoring methods (chat, canvas, script, and DX), there’s an on-ramp for every skill level.
The key insight to take away: don’t fight the LLM, and don’t blindly trust it. Use Agent Script to draw clear boundaries — let the LLM shine at conversation and interpretation, but enforce your business rules with code. That’s the promise of hybrid reasoning, and Agent Script is the tool that delivers it.
Resources
- Agent Script Overview — Salesforce Developer Docs
- Agent Script Language Characteristics
- Agent Script Blocks
- Agent Script Flow of Control
- Agent Script Examples
- Agent Script Recipes (GitHub)
- Introducing Hybrid Reasoning with Agent Script (Blog)
- Agentforce DX — Code Your Agent
- Master Hybrid Reasoning with Agent Script Recipes (Blog)
