Building Agentforce Agents with Agent Script

Building Agentforce Agents with Agent Script: A Comprehensive Guide

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

CapabilityDescription
Reasoning boundariesDefine exactly where the LLM reasons vs. where logic is deterministic
State managementVariables reliably store information across turns — no relying on LLM memory
Conditional logicif/else expressions control execution paths based on variable states
Deterministic actionsChain actions in precise sequences with guaranteed execution order
Topic transitionsRoute 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:

ReferencePurpose
@actions.action_nameReference an action
@topic.topic_nameReference a topic
@variables.variable_nameAccess a variable
@outputs.output_nameAccess action output
@utils.transitionUtility for topic transitions
@utils.escalateUtility 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."
Required
The 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

  1. Initial request → Always starts at start_agent
  2. Topic processing → Logic within a topic’s reasoning instructions
  3. 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:

  1. Initialize — Agentforce starts with an empty prompt
  2. Increment variablenum_turns increases by 1
  3. Execute action — Runs get_delivery_date with the stored order ID
  4. Store output — Saves the result into @variables.delivery_date
  5. Concatenate text — Adds the delivery date message to the prompt
  6. Evaluate condition — Checks if delivery_date is None
  7. Branch — Adds appropriate instruction text based on the condition
  8. Send to LLM — The complete, resolved prompt goes to the Atlas Reasoning Engine
  9. 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...
Key Insight
After a topic finishes and the LLM responds, Agentforce waits for the next customer message, then returns to 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

  1. Guard clauses — The order_management topic immediately redirects to identity if the customer isn’t verified — this is deterministic, not left to LLM interpretation
  2. State management — Variables like is_verified and verification_code persist across turns, so the agent never “forgets”
  3. Hybrid reasoning — The LLM handles natural conversation while the script enforces the verification flow
  4. After-reasoning hooks — The after_reasoning block 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
Pro Tip
The 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 .ascript files
  • 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

  1. 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.
  2. 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.
  3. Enforce Business Rules Deterministically
    Use if/else and transition for compliance-critical logic. Don’t ask the LLM to “remember to check verification” — make it a programmatic guard clause.
  4. 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.
  5. Use after_reasoning for Side Effects
    Post-reasoning hooks are perfect for state transitions, logging, or cleanup that should happen after the LLM responds.
  6. Keep Topics Focused
    Each topic should handle one domain. If a topic is getting complex, split it into subtopics with transitions.
  7. Use Unique Developer Names
    Every agent must have a unique developer_name. This is easy to overlook when copying examples.
  8. 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, and start_agent blocks
  • 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

Uday Bheemarpu
Uday Bheemarpu
Articles: 10

Leave a Reply

Your email address will not be published. Required fields are marked *

Discover more from Panther Schools

Subscribe now to keep reading and get access to the full archive.

Continue reading