Advanced Action Patterns
Context-aware descriptions, instruction references, and binding strategies
Related: SKILL.md — Action Chaining | action-prompt-templates.md
1. Context-Aware Action Descriptions
The same underlying action can have different descriptions across topics. This improves LLM action selection by matching the description to the user's expertise level or business context.
Beginner vs. Advanced Mode
topic beginner_help:
reasoning:
actions:
search_help: @actions.search_knowledge_base
description: "Search for help articles"
topic advanced_help:
reasoning:
actions:
search_help: @actions.search_knowledge_base
description: "Execute knowledge base query with advanced filters (type, date range, tags). Returns paginated results with relevance scoring. Supports Boolean operators and wildcards."
Result: Beginner users see simple language; advanced users see technical capabilities.
available when + Description Override
Combine conditional availability with contextual descriptions for stronger control:
topic business_hours:
reasoning:
actions:
create_case: @actions.create_support_case
available when @variables.during_business_hours == True
description: "Create a support case for immediate assignment to available agents"
topic after_hours:
reasoning:
actions:
create_case: @actions.create_support_case
available when @variables.during_business_hours == False
description: "Create a support case for next-business-day follow-up"
When to use description overrides
| Scenario | Use Override? |
|---|---|
| Same action serves different user expertise levels | ✅ Yes |
| Same action has different context in different topics | ✅ Yes |
| Action is always used the same way | ❌ No — single description suffices |
| Action name alone is already clear | ❌ No — don't over-engineer |
2. Instruction Action References
The {!@actions.action_name} syntax embeds a reference to the full action definition (not just the name) into reasoning instructions. This gives the LLM richer context about the action's inputs, outputs, and purpose — improving selection accuracy when multiple actions could apply.
Basic Syntax
topic business_hours_routing:
data:
@variables.next_open_time: ...
reasoning:
instructions: ->
if @variables.next_open_time:
| We are currently OUTSIDE business hours.
The next available time is {!@variables.next_open_time}.
Create a support case using {!@actions.create_case} to ensure
follow-up during business hours.
else:
| We are currently WITHIN business hours.
Connect the customer to live support using {!@actions.transfer_to_agent}.
Conditional Instruction Patterns
Guide the LLM toward specific actions based on state:
topic identity_verification:
reasoning:
instructions: ->
if @variables.verification_attempts < 3:
| Verify identity using {!@actions.verify_email_code}
or {!@actions.verify_phone_code}.
else:
| Maximum attempts reached. Escalate using {!@actions.escalate_to_security}.
topic order_inquiry:
reasoning:
instructions: ->
if @variables.order_status == "shipped":
| Track shipment using {!@actions.get_tracking_info}.
if @variables.order_status == "processing":
| Check status using {!@actions.get_fulfillment_status}.
if @variables.order_status == "cancelled":
| Process refund using {!@actions.initiate_refund}.
Combining with available when
For maximum control, use both instruction references AND deterministic guards:
topic secure_operations:
reasoning:
instructions: ->
if @variables.verified == True:
| You can now access sensitive operations using {!@actions.view_account_details}.
else:
| Please verify your identity first.
actions:
view_account_details: @actions.get_account_info
available when @variables.verified == True
description: "View sensitive account information"
Result: Instruction guidance steers the LLM, while available when provides a deterministic safety net.
Descriptions vs. References: When to Use Which
| Approach | When to Use |
|---|---|
| Description overrides | Action needs different descriptions per topic/context |
| Instruction references | Need to explicitly guide LLM toward an action in instructions |
| Both | Maximum control — override description AND reference in instructions |
3. Input Binding Decision Matrix
Agent Script supports four input binding approaches. Use this matrix to choose:
| Binding | Syntax | Use When | Example |
|---|---|---|---|
| LLM slot-filling | ... |
User provides the value in conversation | with query=... |
| Variable binding | @variables.X |
Data exists from prior turns or actions | with id=@variables.customer_id |
| Fixed value | literal | System constant or business rule | with format="pdf" |
| Mixed | combination | Complex actions needing multiple sources | See below |
Mixed Binding Example
process_refund: @actions.process_refund_request
with order_id=@variables.order_id # Variable — from previous action
reason=... # Slot-fill — user explains why
amount=@variables.order_total # Variable — stored value
refund_method="original_payment" # Fixed — business rule
require_approval=True # Fixed — policy constant
Combined Pattern: Capture → Reuse → Extend
# Step 1: Capture data from first action
search: @actions.search_products
with query=...
set @variables.product_id = @outputs.top_result_id
# Step 2: Reuse captured value + collect new input
add_to_cart: @actions.add_to_cart
with product_id=@variables.product_id # Reuse from step 1
quantity=... # New user input
apply_discount=True # Business rule
4. Callback Behavior Notes
These supplement the "Action Chaining with run Keyword" section in SKILL.md.
Callbacks only execute if the parent action succeeds
verify_payment: @actions.verify_payment_method
with payment_method_id=...
set @variables.verified = @outputs.success
run @actions.process_payment # Only runs if verify succeeds
with payment_method_id=...
If verify_payment_method fails (Flow error, Apex exception), process_payment will not execute. Design your conversation flow to handle the failure case separately.
Flatten, don't nest
# ❌ WRONG — nested callbacks (not allowed)
action1: @actions.first
run @actions.second
run @actions.third # ERROR: Nested callbacks not supported
# ✅ CORRECT — sequential callbacks (flat structure)
action1: @actions.first
run @actions.second
run @actions.third
Practical callback chain: Create → Notify → Log
create_case: @actions.create_support_case
with subject=...
description=...
priority=@variables.priority
set @variables.case_id = @outputs.id
run @actions.notify_team
with case_id=@variables.case_id
priority=@variables.priority
run @actions.log_case_creation
with case_id=@variables.case_id
5. Additional Error Patterns
These supplement the "Common Issues" table in SKILL.md.
| Error | Cause | Fix |
|---|---|---|
| Missing colon after action name | my_action instead of my_action: |
Add colon: my_action: |
| Missing type annotation on input | email: with no type |
Add type: email: string |
| Wrong target protocol | flows://MyFlow |
Use flow://MyFlow (no trailing s) |
Input: without quotes |
with Input:email=... |
Quote it: with "Input:email"=... |
... as variable default |
my_var: mutable string = ... |
Use "" for defaults; ... is slot-filling only |
| Vague action description | description: "Does a search" |
Be specific: description: "Searches KB for articles matching the query" |
6. JSON Parsing Pattern
Agent Script cannot parse JSON strings inline. When an action returns a JSON string that needs to be decomposed into individual fields, use a Flow or Apex action to parse it.
Problem
# This will NOT work - cannot parse JSON inline in Agent Script
set @variables.name = @outputs.json_response["name"] # Not supported
set @variables.email = @outputs.json_response.email # May work for objects, not JSON strings
Solution: Flow/Apex JSON Parser
# Step 1: Call the action that returns JSON
get_data: @actions.fetch_external_data
with endpoint = "customer_profile"
set @variables.raw_json = @outputs.response_body
# Step 2: Pass JSON to a parser action (Flow or Apex)
parse: @actions.parse_json_response
with json_string = @variables.raw_json
set @variables.customer_name = @outputs.name
set @variables.customer_email = @outputs.email
set @variables.customer_tier = @outputs.tier
Apex Parser Example
public class JsonParserAction {
public class ParseRequest {
@InvocableVariable(required=true)
public String jsonString;
}
public class ParseResult {
@InvocableVariable public String name;
@InvocableVariable public String email;
@InvocableVariable public String tier;
}
@InvocableMethod(label='Parse Customer JSON')
public static List<ParseResult> parse(List<ParseRequest> requests) {
List<ParseResult> results = new List<ParseResult>();
for (ParseRequest req : requests) {
Map<String, Object> parsed = (Map<String, Object>) JSON.deserializeUntyped(req.jsonString);
ParseResult result = new ParseResult();
result.name = (String) parsed.get('name');
result.email = (String) parsed.get('email');
result.tier = (String) parsed.get('tier');
results.add(result);
}
return results;
}
}
Tip: For complex nested JSON, create typed Apex wrapper classes instead of using
deserializeUntyped.
Consolidated from @kunello's PR #20 research on Agent Script Recipes action configuration patterns.