Agent Script Reference
Complete syntax reference for the Agent Script language used in Agentforce. Includes AiAuthoringBundle compatibility notes, reserved words, and error troubleshooting.
Updated: December 2025 - Verified through systematic testing.
File Structure
Agent Script files use the .agent extension and contain YAML-like syntax with specific Agent Script keywords.
⚠️ CRITICAL: Two Deployment Methods
There are two deployment methods with different capabilities:
| Aspect | GenAiPlannerBundle | AiAuthoringBundle |
|---|---|---|
| Deploy Command | sf project deploy start |
sf agent publish authoring-bundle |
| Visible in Agentforce Studio | ❌ NO | ✅ YES |
Flow Actions (flow://) |
✅ Supported | ✅ Supported (with exact name matching) |
Apex Actions (apex://) |
✅ Supported | ⚠️ Limited (class must exist) |
Escalation (@utils.escalate with reason) |
✅ Supported (tested Dec 2025) | ❌ NOT Supported (SyntaxError) |
run keyword (action callbacks) |
✅ Supported (tested Dec 2025) | ❌ NOT Supported (SyntaxError) |
| Variables without defaults | ✅ Supported | ✅ Supported (tested Dec 2025) |
Lifecycle blocks (before/after_reasoning) |
✅ Supported | ✅ Supported (tested Dec 2025) |
Topic transitions (@utils.transition) |
✅ Supported | ✅ Supported |
Basic escalation (@utils.escalate) |
✅ Supported | ✅ Supported |
| API Version | v65.0+ required | v65.0+ required |
Why the difference? These methods correspond to two authoring experiences:
- Script View (GenAiPlannerBundle): Full Agent Script syntax with utility actions (transition, set variables, escalate) inherent to the script
- Canvas/Builder View (AiAuthoringBundle): Low-code visual builder where some utility actions are not yet available
Salesforce is working on feature parity - future releases will add more actions and variable management to the Canvas view.
AiAuthoringBundle (Visible in Agentforce Studio)
Use this when: You need agents visible in Agentforce Studio UI.
Required Files:
force-app/main/default/aiAuthoringBundles/[AgentName]/
├── [AgentName].agent # Agent definition
└── [AgentName].bundle-meta.xml # Metadata XML
bundle-meta.xml content:
<?xml version="1.0" encoding="UTF-8"?>
<AiAuthoringBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<bundleType>AGENT</bundleType>
</AiAuthoringBundle>
GenAiPlannerBundle (Full Feature Support)
Use this when: You need flow actions, escalation with reasons, or full Agent Script syntax.
Required Files:
force-app/main/default/genAiPlannerBundles/[AgentName]/
├── [AgentName].genAiPlannerBundle # XML manifest
└── agentScript/
└── [AgentName]_definition.agent # Agent Script file
genAiPlannerBundle content:
<?xml version="1.0" encoding="UTF-8"?>
<GenAiPlannerBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<description>Agent description</description>
<masterLabel>Agent Label</masterLabel>
<plannerType>Atlas__ConcurrentMultiAgentOrchestration</plannerType>
</GenAiPlannerBundle>
⚠️ WARNING: Agents deployed via GenAiPlannerBundle exist in org metadata but do NOT appear in Agentforce Studio UI!
Block Order (CRITICAL)
Blocks MUST appear in this order:
system:- Instructions and messagesconfig:- Agent metadatavariables:- Linked and mutable variableslanguage:- Locale settingsstart_agent [name]:- Entry point topictopic [name]:- Additional topics
Complete Working Example
system:
instructions: "You are a helpful assistant. Be professional and friendly."
messages:
welcome: "Hello! How can I help you today?"
error: "I apologize, but I encountered an issue."
config:
agent_name: "My_Agent"
default_agent_user: "user@org.salesforce.com"
agent_label: "My Agent"
description: "A helpful assistant agent"
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: "User's current question"
language:
default_locale: "en_US"
additional_locales: ""
all_additional_locales: False
start_agent topic_selector:
label: "Topic Selector"
description: "Routes users to appropriate topics"
reasoning:
instructions: ->
| Determine user intent and route.
actions:
go_help: @utils.transition to @topic.help
go_farewell: @utils.transition to @topic.farewell
topic help:
label: "Help"
description: "Provides help to users"
reasoning:
instructions: ->
| Answer the user's question helpfully.
topic farewell:
label: "Farewell"
description: "Ends conversation gracefully"
reasoning:
instructions: ->
| Thank the user and say goodbye.
Indentation Rules
CRITICAL: Agent Script is whitespace-sensitive (like Python/YAML). Use CONSISTENT indentation throughout.
| Rule | Details |
|---|---|
| Tabs (Recommended) | ✅ Use tabs for easier manual editing and consistent alignment |
| Spaces | 2, 3, or 4 spaces also work if used consistently |
| Mixing | ❌ NEVER mix tabs and spaces (causes parse errors) |
| Consistency | All lines at same nesting level must use same indentation |
⚠️ RECOMMENDED: Use TAB indentation for all Agent Script files. Tabs are easier to edit manually and provide consistent visual alignment across editors.
# ✅ RECOMMENDED - consistent tabs (best for manual editing)
config:
agent_name: "My_Agent"
description: "Description"
# ✅ ALSO CORRECT - consistent spaces (if you prefer)
config:
agent_name: "My_Agent"
description: "Description"
# ❌ WRONG - mixing tabs and spaces
config:
agent_name: "My_Agent" # tab
description: "My agent" # spaces - PARSE ERROR!
Blocks
System Block
Global agent settings and instructions. Must be first block.
system:
instructions: "You are a helpful assistant. Be professional."
messages:
welcome: "Hello! How can I help you today?"
error: "I'm sorry, something went wrong. Please try again."
⚠️ NOTE: System instructions must be a single quoted string. The | pipe multiline syntax does NOT work in the system: block (only in reasoning: instructions: ->).
# ✅ CORRECT - Single quoted string
system:
instructions: "You are a helpful customer service agent. Be professional and courteous. Never share confidential information."
messages:
welcome: "Hello!"
error: "Sorry, an error occurred."
✅ Template Expressions Work in System Instructions:
While pipe syntax doesn't work, template expressions {!expression} ARE supported in system.instructions:
# ✅ Template expressions work in system.instructions
system:
instructions: "Welcome {!@variables.user_name}! You are speaking with {!@variables.agent_persona}. Today's date is {!@variables.current_date}."
messages:
welcome: "Hello {!@variables.user_name}!"
error: "Sorry, an error occurred."
This allows dynamic personalization in the system prompt while still using a quoted string.
Config Block
Defines agent metadata. Required fields: agent_name/developer_name, default_agent_user, agent_label, description.
📝 NOTE: No Separate Config File! All configuration goes directly in the .agent file's config: block. There is no separate .agentscript, .agentconfig, or similar config file format.
config:
agent_name: "Customer_Support_Agent"
default_agent_user: "agent.user@company.salesforce.com"
agent_label: "Customer Support"
description: "Helps customers with orders and inquiries"
agent_type: "AgentforceServiceAgent"
enable_enhanced_event_logs: False
Core Fields:
| Field | Required | Description |
|---|---|---|
agent_name |
Yes | API name (letters, numbers, underscores only) |
developer_name |
Yes | Same as agent_name - use one or the other (not both) |
default_agent_user |
Yes | Username for agent execution context |
agent_label |
Yes | Human-readable name |
description |
Yes | What the agent does |
Optional Fields:
| Field | Type | Default | Description |
|---|---|---|---|
agent_type |
String | "AgentforceServiceAgent" |
Agent type: "AgentforceServiceAgent" or "AgentforceEmployeeAgent" |
enable_enhanced_event_logs |
Boolean | False |
Enable detailed event logging for debugging |
agent_template |
String | None |
Base template for agent behavior |
outbound_flow |
String | None |
Flow to invoke for outbound messages |
additional_parameter__* |
Any | — | Dynamic parameters (prefix with additional_parameter__) |
Example with all fields:
config:
developer_name: "Enterprise_Service_Agent"
default_agent_user: "service.agent@company.salesforce.com"
agent_label: "Enterprise Service Agent"
description: "Handles complex enterprise service requests"
agent_type: "AgentforceServiceAgent"
enable_enhanced_event_logs: True
outbound_flow: "Send_Welcome_Message"
additional_parameter__tier: "enterprise"
additional_parameter__region: "AMER"
NOTE: Both agent_name and developer_name work (they're aliases for the same field). Use one or the other, not both.
Variables Block
Declares state variables. Linked variables first, then mutable.
Linked Variables (connect to Salesforce data - REQUIRED):
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"
Mutable Variables (agent state):
variables:
# Without defaults - works in both deployment methods
user_name: mutable string
description: "The customer's name"
order_count: mutable number
description: "Number of items in cart"
is_verified: mutable boolean
description: "Whether identity is verified"
# With explicit defaults - also valid (optional)
status: mutable string = ""
description: "Current status"
counter: mutable number = 0
description: "A counter"
Note: Both syntaxes (with or without defaults) work in both GenAiPlannerBundle and AiAuthoringBundle deployments. Tested December 2025.
Language Block
Locale settings. Required for deployment.
language:
default_locale: "en_US"
additional_locales: "en_GB,de,fr"
all_additional_locales: False
| Field | Type | Required | Description |
|---|---|---|---|
default_locale |
String | Yes | Primary locale (e.g., "en_US") |
additional_locales |
String | No | Comma-separated additional locales (e.g., "en_GB,de,fr") |
all_additional_locales |
Boolean | No | Set True to support all available locales |
Locale Format: Use standard locale codes like en_US, en_GB, de, fr, es, ja, etc.
Topic Blocks
Define conversation topics. Each topic requires label: and description:.
start_agent Block (Entry Point)
Every agent MUST have exactly one start_agent block - this is the entry point where conversations begin.
Two forms are supported:
# Named form (recommended for multi-topic agents)
start_agent topic_selector:
label: "Topic Selector"
description: "Routes users to appropriate topics"
# Unnamed form (simpler, for single-topic agents)
start_agent:
label: "Main Topic"
description: "Handles all user requests"
When to use each:
| Form | Syntax | Use When |
|---|---|---|
| Named | start_agent topic_name: |
Multi-topic agent with routing |
| Unnamed | start_agent: |
Single-topic agent, no routing needed |
Named form creates a topic that can be referenced with @topic.topic_name for transitions back to the entry point.
Entry point example (named, recommended):
start_agent topic_selector:
label: "Topic Selector"
description: "Routes users to appropriate topics"
reasoning:
instructions: ->
| Determine user intent and route.
actions:
go_orders: @utils.transition to @topic.orders
Regular topic:
topic orders:
label: "Order Management"
description: "Handles order inquiries"
reasoning:
instructions: ->
| Help with order questions.
actions:
back: @utils.transition to @topic.topic_selector
Topic-Level System Instruction Overrides
Topics can override the global system: block to change agent behavior per topic. This enables persona switching, tone changes, or specialized behavior without creating separate agents.
# Global system instructions (default behavior)
system:
instructions: "You are a versatile assistant that adapts based on context."
messages:
welcome: "Welcome! I adapt my behavior based on the conversation."
error: "I encountered an issue. Please try again."
# Topic with system override - professional mode
topic professional:
label: "Professional Mode"
description: "Professional business communication"
system:
instructions: "You are a formal business professional. Use professional language, avoid casual expressions, focus on efficiency and clarity."
reasoning:
instructions: ->
| [Professional Mode Engaged]
| Respond with formal business tone.
actions:
return_general: @utils.transition to @topic.general
# Topic with different override - creative mode
topic creative:
label: "Creative Mode"
description: "Creative brainstorming assistant"
system:
instructions: "You are a creative brainstorming partner. Think outside the box, suggest unconventional ideas, be imaginative and supportive."
reasoning:
instructions: ->
| [Creative Mode Activated]
| Generate creative solutions and explore possibilities.
actions:
return_general: @utils.transition to @topic.general
# Topic without override - uses global system instructions
topic general:
label: "General"
description: "General conversation"
reasoning:
instructions: ->
| Respond in the default conversational style.
actions:
go_professional: @utils.transition to @topic.professional
go_creative: @utils.transition to @topic.creative
How Topic-Level Overrides Work:
- If a topic has a
system:block → use that topic's instructions - If a topic has NO
system:block → inherit the globalsystem:instructions - Overrides apply only while in that topic; transitioning away restores the target topic's behavior
Use Cases:
- Persona Switching: Switch between professional, casual, technical modes
- Specialized Domains: Different expertise per topic (technical support vs billing)
- Localization: Different tone for different regions/contexts
- Compliance: Strict guidelines for sensitive topics (financial, medical)
Variable Types
Complete Type Reference
| Type | Description | Example | AiAuthoringBundle |
|---|---|---|---|
string |
Text values | name: mutable string = "John" |
✅ Supported |
number |
Numeric (integers & decimals) | price: mutable number = 99.99 |
✅ Supported |
boolean |
True/False (capitalized!) | active: mutable boolean = True |
✅ Supported |
date |
YYYY-MM-DD format | start: mutable date = 2025-01-15 |
✅ Supported |
datetime |
Full timestamp | created: mutable datetime |
✅ Supported |
time |
Time only | appointment: mutable time |
✅ Supported |
currency |
Money values | total: mutable currency |
✅ Supported |
id |
Salesforce Record ID | record_id: mutable id |
✅ Supported |
object |
Complex object with Lightning type | See advanced syntax below | ✅ Supported |
list[type] |
Array of values | list[string], list[number] |
✅ Supported |
integer |
Integer values only | count: mutable integer = 5 |
❌ NOT Supported |
long |
Long integers | big_num: mutable long = 9999999999 |
❌ NOT Supported |
⚠️ CRITICAL: integer and long types are NOT supported in AiAuthoringBundle!
- Validation fails with: "Variable with type integer is not supported for mutable variables"
- Use
numberinstead for all numeric values (works for both integers and decimals)
⚠️ CRITICAL: Collection syntax uses SQUARE BRACKETS, not angle brackets!
- ✅ CORRECT:
list[string],list[number],list[boolean] - ❌ WRONG:
list<string>(causes "Unexpected '<'" syntax error) - Only primitive types allowed in lists:
string,number,boolean list[object]is NOT supported
Notes:
- Boolean values must be capitalized:
True,False
Type Restrictions: Mutable vs Linked Variables
Mutable variables (state you modify during conversation):
| Type | Mutable | Notes |
|---|---|---|
string |
✅ | Primary text type |
number |
✅ | Use for ALL numeric (integer + decimal) |
boolean |
✅ | Must use True/False |
date |
✅ | YYYY-MM-DD format |
timestamp |
✅ | Use instead of datetime |
currency |
✅ | Money values |
id |
✅ | Salesforce Record IDs |
list[type] |
✅ | list[string], list[number], list[boolean] only |
object |
✅ | Complex types with complex_data_type_name |
datetime |
❌ | Use timestamp instead |
time |
❌ | Not supported for mutable variables |
integer |
❌ | Use number instead |
long |
❌ | Use number instead |
Linked variables (connect to Salesforce data):
| Type | Linked | Notes |
|---|---|---|
string |
✅ | Most common for IDs as strings |
number |
✅ | Numeric fields |
boolean |
✅ | Checkbox fields |
date |
✅ | Date fields |
timestamp |
✅ | DateTime fields |
currency |
✅ | Currency fields |
id |
✅ | Salesforce ID fields |
list[*] |
❌ | Collections NOT supported for linked |
object |
❌ | Complex types NOT supported for linked |
Advanced object Type with Lightning Data Types (Tested Dec 2025)
The object type enables fine-grained control over action inputs/outputs using Lightning data types:
inputs:
order_number: object
description: "The Order Number"
label: "order_number"
is_required: False
is_user_input: False
complex_data_type_name: "lightning__textType"
outputs:
order_id: object
description: "The Record ID"
label: "order_id"
complex_data_type_name: "lightning__textType"
filter_from_agent: False
is_used_by_planner: True
is_displayable: False
Lightning Data Types (complex_data_type_name)
Lightning Types are Salesforce's complex type system for action inputs/outputs. Use them when you need explicit type mapping between AgentScript and Flows/Apex.
When to use:
- Passing data to/from Flows with specific Salesforce types
- Ensuring proper serialization of complex values
- Mapping record types and relationships
Available Lightning Types:
| Lightning Type | Use For | Base Type |
|---|---|---|
lightning__textType |
Text/String values | string |
lightning__numberType |
Numeric values (integer/decimal) | number |
lightning__booleanType |
Boolean True/False | boolean |
lightning__dateTimeStringType |
DateTime as ISO string | timestamp |
lightning__recordInfoType |
Salesforce Record references | id |
lightning__currencyType |
Currency values | currency |
lightning__dateType |
Date values (no time) | date |
lightning__percentType |
Percentage values | number |
lightning__urlType |
URL/hyperlink values | string |
lightning__emailType |
Email addresses | string |
lightning__phoneType |
Phone numbers | string |
Example with complex types:
actions:
update_record: flow://Update_Customer_Record
inputs:
customer_id: object
description: "Customer record ID"
complex_data_type_name: "lightning__recordInfoType"
update_date: object
description: "Date of update"
complex_data_type_name: "lightning__dateTimeStringType"
outputs:
success: object
complex_data_type_name: "lightning__booleanType"
Input Attributes: is_required, is_user_input, label, complex_data_type_name
Output Attributes and Defaults:
| Attribute | Type | Default | Description |
|---|---|---|---|
filter_from_agent |
Boolean | False |
Hide sensitive output from LLM (still stored) |
is_used_by_planner |
Boolean | True* |
Allow LLM to use output for reasoning |
is_displayable |
Boolean | False |
Show output to user in chat |
complex_data_type_name |
String | — | Lightning type for Salesforce integration |
*Default behavior for is_used_by_planner:
- Defaults to
TrueUNLESSfilter_from_agentisTrue - When
filter_from_agent: True,is_used_by_plannerautomatically becomesFalse - This prevents sensitive data from reaching the LLM
Example - Sensitive data handling:
outputs:
customer_name: string
description: "Customer's name"
is_used_by_planner: True # LLM can use this
is_displayable: True # User sees this
ssn_last_four: string
description: "Last 4 of SSN (for verification)"
filter_from_agent: True # Hidden from LLM
is_used_by_planner: False # Auto-set by filter_from_agent
is_displayable: False # Don't show user
⚠️ CRITICAL: filter_from_agent is NOT supported in AiAuthoringBundle!
- Causes "Unexpected 'filter_from_agent'" syntax error
- Use conditional topic routing in instructions as alternative
- Works in GenAiPlannerBundle
Data Type Mappings with Flow (Tested Dec 2025)
| Agent Script Type | Flow Data Type | Status | Notes |
|---|---|---|---|
string |
String | ✅ Works | Standard text values |
number |
Number (scale=0) | ✅ Works | Integer values |
number |
Number (scale>0) | ✅ Works | Decimal values |
boolean |
Boolean | ✅ Works | Use True/False |
list[string] |
Text Collection | ✅ Works | Use isCollection=true in Flow |
string |
Date | ✅ Works* | *See String I/O pattern below |
string |
DateTime | ✅ Works* | *See String I/O pattern below |
⚠️ All Flow inputs must be provided! If Flow has more input variables than Agent Script defines, publish fails with "Internal Error".
Date/DateTime Handling (No Native Types)
Agent Script does NOT have native date or datetime types. Direct type coercion between string (Agent Script) and Date/DateTime (Flow) will fail.
Use the String I/O Pattern:
- Flow accepts String inputs (not Date/DateTime)
- Flow parses strings internally with
DATEVALUE()orDATETIMEVALUE() - Flow converts back to String for output with
TEXT()
<!-- Flow variables use String, not Date -->
<variables>
<name>inp_DateString</name>
<dataType>String</dataType> <!-- NOT Date -->
<isInput>true</isInput>
</variables>
<formulas>
<name>parsedDate</name>
<dataType>Date</dataType>
<expression>DATEVALUE({!inp_DateString})</expression>
</formulas>
# Agent Script uses string type for dates
inputs:
inp_DateString: string
description: "Date in YYYY-MM-DD format"
Resource References
Use the @ prefix to reference resources.
| Resource | Syntax | Usage | AiAuthoringBundle |
|---|---|---|---|
| Variables | @variables.name |
Access stored values | ✅ Supported |
| Actions | @actions.name |
Invoke defined actions | ✅ Supported |
| Topics | @topic.name |
Reference topics | ✅ Supported |
| Outputs | @outputs.field |
Action output values | ✅ Supported |
| Utilities | @utils.transition |
Built-in utilities | ✅ Supported |
| Utilities | @utils.escalate |
Escalate to human | ✅ Supported |
| Utilities | @utils.setVariables |
Set multiple variables | ⚠️ GenAiPlannerBundle only |
| Utilities | @utils.set |
Set single variable | ⚠️ GenAiPlannerBundle only |
@utils.setVariables Usage
In GenAiPlannerBundle (Supported):
# ✅ GenAiPlannerBundle - @utils.setVariables works
reasoning:
actions:
set_some_variables: @utils.setVariables
description: "Set user context variables"
available when @variables.needs_update == True
with user_name=... # LLM slot-fills, inherits type from variable
with visit_count=@variables.visit_count + 1 # Fixed expression
clear_session: @utils.setVariables
description: "Reset session state"
with is_logged_in=False
with cart_items=[]
Key Behaviors:
with var=...- LLM slot-fills the value (inherits type from variable definition)with var=@expression- Fixed value from expression (not slot-filled)- Types must match variable declarations
In AiAuthoringBundle (NOT Supported):
⚠️ @utils.setVariables and @utils.set cause "Unknown utils declaration type" errors in AiAuthoringBundle. Use the set keyword in instructions instead:
# ❌ WRONG - @utils.setVariables NOT supported in AiAuthoringBundle
reasoning:
actions:
update_state: @utils.setVariables
with user_name=...
with is_verified=True
# ✅ CORRECT - Use 'set' keyword in instructions (AiAuthoringBundle)
reasoning:
instructions: ->
| Ask the user for their name.
set @variables.user_name = ...
| Verify the user.
set @variables.is_verified = True
# Variable reference
if @variables.user_name is None:
# Action reference
invoke: @actions.get_order
with order_id=@variables.current_order_id
# Topic reference
go: @utils.transition to @topic.checkout
# Output capture
set @variables.status = @outputs.order_status
# Escalation
escalate: @utils.escalate
description: "Transfer to human agent"
Instructions
Syntax (CRITICAL)
Use instructions: -> (with space before arrow), NOT instructions:->.
# ✅ CORRECT
reasoning:
instructions: ->
| Determine user intent.
# ❌ WRONG - missing space before arrow
reasoning:
instructions:->
| Determine user intent.
Prompt Mode (|)
Use | for natural language instructions:
instructions: ->
| This is line one.
| This is line two.
| Each line starts with a pipe.
Procedural Mode (->)
Use -> for logic-based instructions:
instructions: ->
if @variables.amount > 1000:
| This is a large order.
else:
| Standard order processing.
Procedural Instructions with Inline Actions (Advanced Pattern)
You can execute actions directly inside the instructions:-> block for conditional data loading. This pattern fetches data only when needed, reducing unnecessary API calls.
topic order_status:
label: "Order Status"
description: "Looks up and explains order status"
actions:
get_order_status:
description: "Retrieves current status for an order"
inputs:
order_id: string
description: "The unique order identifier"
outputs:
status: string
description: "Current order status"
tracking_number: string
description: "Shipping tracking number"
target: "flow://GetOrderStatus"
reasoning:
instructions:->
# Step 1: Validate input - ask for order ID if missing
if not @variables.order_id:
| Ask the customer for their order number so you can look up the status.
# Step 2: Fetch data only when we have order_id but not status yet
if @variables.order_id and not @variables.order_status:
run @actions.get_order_status
with order_id=@variables.order_id
set @variables.order_status = @outputs.status
set @variables.tracking_number = @outputs.tracking_number
# Step 3: Provide context-specific guidance based on status
| The customer's order {!@variables.order_id} has status: {!@variables.order_status}
if @variables.order_status == "pending":
| The order is being processed. Let them know:
| - Order is confirmed and being prepared
| - They'll receive tracking info within 24 hours
if @variables.order_status == "shipped":
| The order has shipped! Provide:
| - Tracking number: {!@variables.tracking_number}
| - Link to track shipment
if @variables.order_status == "delivered":
| The order was delivered. Confirm they received it.
| Ask if everything was satisfactory.
| Be proactive and helpful. Anticipate what the customer might need next.
Key Pattern Features:
- Conditional Data Loading:
if @variables.x and not @variables.y:guards prevent redundant API calls - Action Inside Instructions:
run @actions.xexecutes directly in the instruction flow - State-Based Guidance: Different instructions appear based on variable values
- Progressive Flow: Ask for input → Fetch data → Provide tailored response
When to Use This Pattern:
- Data should only be fetched after certain conditions are met
- You need to adapt instructions based on fetched data
- The action result determines what guidance to provide
⚠️ Note on Deployment Methods:
- This pattern uses
runkeyword which has limited support in AiAuthoringBundle - Test thoroughly - if validation fails, consider GenAiPlannerBundle deployment
⚠️ CRITICAL: Slot Filling (...) Cannot Be Inside Conditionals (Tested Dec 2025)
Slot filling with the set @variables.x = ... syntax CANNOT be placed inside conditionals!
This causes SyntaxError: Unexpected 'if' during validation.
# ❌ WRONG - Slot filling inside conditional causes SyntaxError
instructions: ->
if @variables.name is None:
| Please provide your name.
set @variables.name = ... # FAILS! Cannot use ... inside if block
# ✅ CORRECT - Use unconditional slot filling, LLM handles context
instructions: ->
| If the user hasn't provided their name, ask for it.
set @variables.name = ...
| Once you have the name, proceed with the order.
# ✅ ALSO CORRECT - Use action with slot filling (not in conditional)
reasoning:
actions:
collect_info: @actions.collect_customer_info
with name=...
with email=...
set @variables.name = @outputs.name
Why? The ... ellipsis is evaluated at parse time, not runtime. The parser cannot determine conditional paths, so slot filling must be at the top level of instructions.
⚠️ CRITICAL: Pipes Cannot Be Nested Inside Pipes!
# ❌ WRONG - Nested pipes cause "Start token somehow not of the form | + 2 spaces" error
instructions: ->
| Some text.
| if @variables.name is None:
| | Please provide your name. # NESTED PIPE - FAILS!
# ✅ CORRECT - Conditionals at same level as pipes, not nested inside
instructions: ->
| Some introductory text.
if @variables.name is None:
| Please provide your name.
| More text continues here.
Template Expressions
Use {!...} for variable interpolation:
instructions: ->
| Hello {!@variables.user_name}!
| Your order total is ${!@variables.total}.
Conditionals
If/Else
instructions: ->
if @variables.amount > 1000:
| Large order - requires approval.
else:
| Standard order.
if @variables.status == "shipped":
| Your order is on its way!
if @variables.email is None:
| Please provide your email address.
if @variables.verified == True:
| Identity confirmed.
Comparison Operators
| Operator | Meaning | Example |
|---|---|---|
== |
Equals | @variables.status == "active" |
!= |
Not equals | @variables.count != 0 |
> |
Greater than | @variables.amount > 100 |
< |
Less than | @variables.count < 10 |
>= |
Greater or equal | @variables.age >= 18 |
<= |
Less or equal | @variables.priority <= 5 |
Null Checks
if @variables.name is None:
| Name not provided.
if @variables.email is not None:
| Email is available.
Logical Operators
| Operator | Meaning | Example |
|---|---|---|
and |
Both conditions true | @variables.a and @variables.b |
or |
At least one true | @variables.x or @variables.y |
not |
Negation | not @variables.flag |
# Combine conditions with AND
if @variables.verified == True and @variables.amount > 0:
| Processing verified request.
# Check multiple conditions with OR
if @variables.is_vip == True or @variables.loyalty_years > 5:
| Premium customer detected.
# Negate a condition
if not @variables.is_blocked:
| Access granted.
N-ary Boolean Operations (3+ Operands)
AgentScript fully supports N-ary boolean operations - you can chain 3 or more conditions with and or or operators. This is the recommended pattern instead of nested if statements.
# ✅ Three+ conditions with AND
if @variables.is_authenticated and @variables.has_permission and @variables.is_active:
transition to @topic.authorized
# ✅ Three+ conditions with OR
if @variables.is_admin or @variables.is_moderator or @variables.is_owner:
transition to @topic.elevated_access
# ✅ Complex multi-condition check (4+ operands)
if @variables.verified and @variables.amount > 0 and @variables.email is not None and @variables.consent == True:
| All validation criteria met. Proceeding with order.
# ✅ N-ary in available when (action conditional availability)
actions:
process_return: @actions.process_return
description: "Process a product return"
available when @variables.eligible == True and @variables.order_id is not None and @variables.tier != "basic"
apply_premium_discount: @actions.calculate_discount
description: "Apply premium customer discount"
available when @variables.is_premium or @variables.loyalty_years > 5 or @variables.total_spent > 10000
⚠️ Mixing and/or: While you can chain multiple and or multiple or operators, avoid mixing them without clear grouping (parentheses are NOT supported). Use separate conditions for complex mixed logic.
# ⚠️ AVOID - Mixed and/or can be ambiguous
if @variables.a and @variables.b or @variables.c: # Unclear precedence
# ✅ PREFER - Separate conditions for clarity
if @variables.a and @variables.b:
| Path A: Both A and B are true.
if @variables.c:
| Path B: C is true.
⚠️ CRITICAL: Nested If Statements NOT Supported (Tested Dec 2025)
Nested if statements (if inside if) cause parse errors in AiAuthoringBundle!
# ❌ WRONG - Nested if causes "Missing required element" and "Unexpected 'else'"
if @variables.is_premium == True:
| User is premium.
if @variables.order_total > 1000: # NESTED IF - FAILS!
| Large order.
else:
| Regular order.
else:
| Standard user.
# ✅ CORRECT - Use flat conditionals with 'and' operators
if @variables.is_premium == True and @variables.order_total > 1000:
| Premium user with large order.
if @variables.is_premium == True and @variables.order_total <= 1000:
| Premium user with regular order.
if @variables.is_premium == False:
| Standard user.
Math Operators (Tested Dec 2025)
Math operators work in both set statements and conditions:
# ✅ Addition in set statement
set @variables.counter = @variables.counter + 1
# ✅ Subtraction in set statement
set @variables.remaining = @variables.total - 100
# ✅ Math in conditions
if @variables.counter + 5 > 10:
| Counter plus 5 is greater than 10.
if @variables.total - 50 < 0:
| Total minus 50 would be negative.
| Operator | Works in set |
Works in if |
Example |
|---|---|---|---|
+ |
✅ Yes | ✅ Yes | set @variables.x = @variables.x + 1 |
- |
✅ Yes | ✅ Yes | if @variables.total - 50 < 0: |
Additional Expression Features
Length function (len):
# Check if list is empty
if len(@variables.cart_items) == 0:
| Your cart is empty.
# Check list size for pagination
if len(@variables.results) > 10:
| Showing first 10 of {!len(@variables.results)} results.
Ternary expression (x if condition else y):
# Conditional value assignment
set @variables.greeting = "VIP" if @variables.is_premium else "Customer"
# In output context
| Hello {!"VIP" if @variables.tier == "premium" else "valued customer"}!
Unary negation (-):
# Negate a value
set @variables.offset = -@variables.amount
⚠️ Grouping Parentheses:
Parentheses () for expression grouping are NOT well-supported. Avoid complex expressions that require precedence grouping:
# ⚠️ AVOID - complex grouped expressions may not parse
if (@variables.a and @variables.b) or @variables.c:
| This may not work.
# ✅ PREFER - split into separate conditions
if @variables.a and @variables.b:
| Path A.
if @variables.c:
| Path C.
Comments
Use the # symbol to add comments. Everything after # on a line is ignored.
# This is a comment - the parser ignores this line
config:
agent_name: "My_Agent" # Inline comment explaining the field
description: "A helpful agent"
# Comments help document your agent script
# Use them to explain complex logic
Actions
Defining Actions
topic my_topic:
label: "My Topic"
description: "Topic description"
actions:
get_order:
description: "Retrieves order details"
inputs:
order_id: string
description: "The order ID"
outputs:
status: string
description: "Order status"
total: number
description: "Order total"
target: "flow://Get_Order_Details"
reasoning:
instructions: ->
| Help the user with their order.
Action-Level Attributes (Advanced)
Beyond inputs, outputs, target, and description, actions support additional attributes for controlling behavior:
| Attribute | Type | Description | Default |
|---|---|---|---|
require_user_confirmation |
Boolean | Prompt user to confirm before executing | False |
include_in_progress_indicator |
Boolean | Show progress indicator during execution | False |
label |
String | Display label for the action | Action name |
Example with Advanced Attributes:
actions:
cancel_order:
description: "Cancels an order and issues refund"
require_user_confirmation: True # ★ Ask user before
…(truncated)