2.1 Deterministic Behavior: Filters, Variables & Template Expressions
Key Takeaways
- Agentforce variables include context (linked) variables mapped to object fields, custom (mutable) variables, and read-only system variables.
- Custom variables can store string, number, list, object, or boolean values and are internal by default, but can be made external so the Agent API can set them.
- Filters, written as available when in Agent Script, make a subagent or action usable only when variable-based conditions are true.
- Template expressions such as {!@variables.order_id} resolve variable values into prompt text before the prompt reaches the LLM.
- Context variables such as MessagingSession.ContactId identify a customer but don't control data access; use verification plus filters.
2.1 Deterministic Behavior: Filters, Variables & Template Expressions
Quick Answer: Use variables to remember facts reliably, filters to control when a subagent or action can be used, and template expressions to put exact values into prompt text. Together with conditional logic, these give you deterministic behavior: the same inputs produce the same path through the agent. Scenario questions often describe an agent that "sometimes" skips a step. The fix is almost always a variable-based filter or logic, not a louder instruction.
Variables
Variables let an agent deterministically remember information across turns, track progress, and share values between subagents and actions. In Agent Script, variables are declared in the variables block and referenced as @variables.name.
| Variable type | Also called | Where the value comes from | Can it change? |
|---|---|---|---|
| Context variable | Linked variable | Mapped to an object field, such as a Messaging Session field (custom fields supported) | Populated from its source |
| Custom variable | Mutable variable | Set by logic, action outputs, or the LLM through the set-variables utility | Yes, during the session |
| System variable | – | Predefined by Agentforce for the session | No (read-only) |
Custom variables can store string, number, list, object, or boolean values and hold them only for the conversation session. By default they're internal (set only inside the agent). Marking a variable external lets the Agent API set it.
Examples in Agent Script:
variables:
RoutableId: linked string
description: "The messaging session ID"
source: @MessagingSession.Id
verified: mutable boolean = False
description: "Whether the customer passed verification"
order_summary: mutable string = ""
description: "Summary of the customer's current order"
System variables are read with @system_variables.name. Two useful ones:
@system_variables.user_input– the customer's most recent utterance, not the whole history@system_variables.current_modality–"voice"on a telephony connection and"text"on Messaging or Enhanced Chat v2, useful for formatting dates for speech instead of text
Ways to set and use variables
- Store an action output:
set @variables.order_id = @outputs.order_id - Bind an action input:
with order_number = @variables.orderNumber - Let the LLM set it with the
@utils.setVariablesutility, for example to capture a name the customer typed - Set it directly in logic:
set @variables.userName = "New User" - Use it in conditions and filters:
if @variables.order_summary == "":
When you run an action inside reasoning instructions, you must set the input and output variables yourself, because the action runs before any reasoning. For actions the LLM chooses during reasoning, variable binding is optional, but it's useful when you need one action's output to feed another.
Filters
A filter ensures an agent can use a subagent or action only when one or more conditions are met. In Canvas view it appears as "Make this action available when:". In Agent Script it's the available when clause on a reasoning action.
start_agent agent_router:
reasoning:
actions:
go_to_identity: @utils.transition to @subagent.Identity_Verification
description: "Verifies user identity"
available when @variables.verified == False
go_to_order: @utils.transition to @subagent.Order_Management
description: "Handles order lookup, refunds, and order updates"
available when @variables.verified == True
go_to_escalation: @utils.transition to @subagent.Escalation
description: "Transfers to a human representative"
available when @variables.verified == True and @variables.is_business_hours == True
When a customer isn't verified, the Order Management transition simply isn't offered to the LLM, so it can't route there however the customer phrases the request.
Filters vs. instructions
| Requirement | Weak approach | Deterministic approach |
|---|---|---|
| Only verified customers can see orders | "Never show orders to unverified users" | Filter the subagent or action with @variables.verified == True |
| Returns allowed only within 60 days | "Only offer returns within 60 days" | Logic sets return_eligibility from days_since_order, and a filter gates create_return |
| Escalate only during business hours | "Don't transfer after hours" | Filter the escalation action on a business-hours variable |
In the legacy builder, filters are created on the Filters tab of the Context panel from context, conversation, and custom variables, then applied to topics or actions. The concept is the same.
Template Expressions
A template expression uses {! } to resolve a value into prompt text at runtime:
| Refer to the customer as {!@variables.member_name}.
| Their current order status is {!@variables.order_status}.
If member_name is "Priya" and order_status is "shipped," the LLM receives "Refer to the customer as Priya. Their current order status is shipped." The model sees concrete values instead of being asked to look them up or remember them. Template expressions can also contain expressions built with supported operators.
| Construct | Purpose | Example |
|---|---|---|
@variables.x in logic | Read a variable in conditions, with, or set | if @variables.count > 0: |
{!@variables.x} in prompt text | Insert the value into what the LLM reads | | Your order {!@variables.order_id} ... |
... in action inputs | Ask the LLM to slot-fill from the conversation | with order_id = ... |
A common mistake is using ... as a variable's default value. The slot-fill token belongs only in action inputs.
Security Pattern: Identify vs. Authorize
Context variables such as MessagingSession.ContactId tell the agent who the user is but don't control data access. For Service agents running as the agent user, Salesforce recommends this pattern:
- Verify the customer, for example with the standard Customer Verification subagent or a custom verification action.
- Store the verified ID in the
VerifiedCustomerIdvariable. - Add filters such as
VerifiedCustomerId is not Noneto subagents and actions that expose private data. - Build the same check into the flows and Apex behind those actions, for example
WHERE ContactId = VerifiedCustomerId.
Filters stop the agent from offering a sensitive action. Logic inside the action enforces the data scope.
Follow-Up Actions and Deterministic Chaining
In actions available for reasoning, a follow-up action ("After this action is run:") defines what always happens after the LLM chooses and runs an action. It can save outputs to variables, transition to another subagent, or run another action. For example, after validate_user_ready runs, the agent always transitions to the analyze_issue subagent.
Exam Traps
- Stronger wording isn't a filter. "ALWAYS" and "NEVER" still leave the decision to the LLM.
- A context variable doesn't enforce security. Knowing a Contact ID doesn't restrict what the agent user can query.
- Run-in-instructions requires explicit bindings. Without
withandset, the action has no inputs and its outputs aren't stored. - System variables are read-only. Store derived values in a custom variable.
A Service agent should offer the Order Management subagent only after the customer passes identity verification. Which configuration enforces this deterministically?
What does the template expression {!@variables.order_status} do inside prompt text?
A developer needs an external web app using the Agent API to pass a customer's loyalty tier into an agent session. How should the custom variable be configured?
A Service agent reads MessagingSession.ContactId into a context variable. Which statement about that variable is accurate?