sf-ai-agentscript

安装量: 199
排名: #4321

安装

npx skills add https://github.com/jaganpro/sf-skills --skill sf-ai-agentscript

"Prompt engineering is like writing laws in poetry - beautiful, but not enforceable."

Agent Script transforms agent development from prompt-based suggestions to code-enforced guarantees. This skill guides you through writing, debugging, testing, and deploying Agentforce agents using the Agent Script DSL.

⚠️ CRITICAL WARNINGS

API & Version Requirements

| API Version | 65.0+ | Required for Agent Script support

| License | Agentforce | Required for agent authoring

| Einstein Agent User | Required | Must exist in org for default_agent_user

| File Extension | .agent | Single file contains entire agent definition

MANDATORY Pre-Deployment Checks

  • default_agent_user MUST be valid - Query: SELECT Username FROM User WHERE Profile.Name = 'Einstein Agent User' AND IsActive = true

  • No mixed tabs/spaces - Use consistent indentation (2-space, 3-space, or tabs - never mix)

  • Booleans are capitalized - Use True/False, not true/false

  • Exactly one start_agent block - Multiple entry points cause compilation failure

⛔ SYNTAX CONSTRAINTS (Validated via Testing + Official Spec)

| No nested if statements | if x: then if y: (nested) | if x and y: (compound) OR flatten to sequential ifs

| No top-level actions: block | actions: at root level | Actions only inside topic.reasoning.actions:

| No inputs:/outputs: in actions | inputs: block inside action | Use with for inputs, set for outputs

| One available when per action | Two available when clauses | available when A and B

| Avoid reserved action names | escalate: @utils.escalate | escalate_now: @utils.escalate

| ... is slot-filling only | my_var: mutable string = ... | my_var: mutable string = ""

| No defaults on linked vars | id: linked string = "" | id: linked string + source:

| Linked vars: no object/list | data: linked object | Use linked string or parse in Flow

| Post-action only on @actions | @utils.X with set/run | Only @actions.X supports post-action

| agent_name must match folder | Folder: MyAgent, config: my_agent | Both must be identical (case-sensitive)

| Reserved field names | description: string, label: string | Use descriptions, label_text, or suffix with _field

🔴 Reserved Field Names (Breaking in Recent Releases)

Common field names that cause parse errors:

❌ RESERVED (cannot use as variable/field names):
description, label, is_required, is_displayable, is_used_by_planner

✅ WORKAROUNDS:
description  → descriptions, description_text, desc_field
label        → label_text, display_label, label_field

🔴 Features NOT Valid in Current Release (TDD Validated 2026-01-20)

These features appear in documentation or recipes but do NOT compile in Winter '26.

| label: on topics | agentforce.guide | Unexpected 'label' | ❌ NOT valid anywhere

| label: on actions | agentforce.guide | Unexpected 'label' | ❌ NOT valid anywhere

| always_expect_input: | Some docs | Unexpected 'always_expect_input' | ❌ NOT implemented

| require_user_confirmation: on transitions | Recipes | Unexpected 'require_user_confirmation' | ❌ NOT valid on @utils.transition

| include_in_progress_indicator: on transitions | Recipes | Unexpected 'include_in_progress_indicator' | ❌ NOT valid on @utils.transition

| output_instructions: on transitions | Recipes | Unexpected 'output_instructions' | ❌ NOT valid on @utils.transition

| progress_indicator_message: on transitions | Recipes | Unexpected 'progress_indicator_message' | ❌ May only work on flow:// targets

What DOES work on @utils.transition actions:

actions:
   go_next: @utils.transition to @topic.next
      description: "Navigate to next topic"   # ✅ ONLY this works

Note: Some of these may work on flow:// action targets (not validated). The @utils.transition utility action has limited property support.

🔴 complex_data_type_name Mapping Table (Critical for Actions)

"#1 source of compile errors" - Use this table when defining action inputs/outputs in Agentforce Assets.

| string | (none needed) | Primitive type

| number | (none needed) | Primitive type

| boolean | (none needed) | Primitive type

| object (SObject) | lightning__recordInfoType | Use for Account, Contact, etc.

| list[string] | lightning__textType | Collection of text values

| list[object] | lightning__textType | Serialized as JSON text

| Apex Inner Class | @apexClassType/NamespacedClass__InnerClass | Namespace required

| Custom LWC Type | lightning__c__CustomTypeName | Custom component types

| Currency field | lightning__currencyType | For monetary values

Pro Tip: Don't manually edit complex_data_type_name - use the UI dropdown in Agentforce Assets > Action Definition, then export/import the action definition.

⚠️ Canvas View Corruption Bugs

CRITICAL: Canvas view can silently corrupt Agent Script syntax. Make complex edits in Script view.

| == | {! OPERATOR.EQUAL } | Breaks conditionals

| if condition: | if condition (missing colon) | Parse error

| with email = | with @inputs.email = | Invalid syntax

| 4-space indent | De-indented (breaks nesting) | Structure lost

| @topic.X (supervision) | @utils.transition to @topic.X (handoff) | Changes return behavior

| A and B | A {! and } B | Breaks compound conditions

Safe Workflow:

  • Use Script view for all structural edits (conditionals, actions, transitions)

  • Use Canvas only for visual validation and simple text changes

  • Always review in Script view after any Canvas edit

⚠️ Preview Mode Critical Bugs

CRITICAL REFRESH BUG: Browser refresh required after every Agent Script save before preview works properly.

| Linked vars in context, not state | "Cannot access 'X': Not a declared field in dict" | Convert to mutable + hardcode for testing

| Output property access fails | Silent failure, no error | Assign to variable first, then use in conditional

| Simulate vs Live behavior differs | Works in Simulate, fails in Live | Test in BOTH modes before committing

Pattern for Testing Linked Variables:

# ❌ DOESN'T WORK IN PREVIEW (linked var from session):
RoutableId: linked string
   source: @MessagingSession.Id

# ✅ WORKAROUND FOR TESTING (hardcode value):
RoutableId: mutable string = "test-session-123"
   description: "MessagingSession Id (hardcoded for testing)"

# After testing, switch back to linked for production

Output Property Access Pattern:

# ❌ DOESN'T WORK IN PREVIEW (direct output access):
if @actions.check_status.result == "approved":
   | Approved!

# ✅ CORRECT (assign to variable first):
set @variables.status = @outputs.result
if @variables.status == "approved":
   | Approved!

No Nested if - Two Valid Approaches

# ❌ WRONG - Nested if (causes SyntaxError)
if @variables.software_cost > 0:
   if @variables.software_cost <= 500:
      | Auto-approve this software request.

# ✅ CORRECT Approach 1 - Compound condition (when logic allows)
if @variables.software_cost > 0 and @variables.software_cost <= 500:
   | Auto-approve this software request.

# ✅ CORRECT Approach 2 - Flatten to sequential ifs (for separate messages)
if @variables.order_verified == False or @variables.payment_confirmed == False:
   | ❌ **PROCESSING BLOCKED**
   | Missing requirements:

if @variables.order_verified == False:
   | - Order verification pending

if @variables.payment_confirmed == False:
   | - Payment confirmation pending

When to use each: Use compound conditions when logic permits (single condition block). Use flattening when you need separate conditional outputs that can't be combined.

... is Slot-Filling Syntax (LLM Extracts from Conversation)

# ❌ WRONG - Using ... as default value
order_id: mutable string = ...

# ✅ CORRECT - Use ... only in action parameter binding
reasoning:
   actions:
      search: @actions.search_products
         with query=...           # LLM extracts from user message
         with category=...        # LLM decides based on context
         with limit=10            # Fixed value

Post-Action Directives: Only on @actions.*

# ❌ WRONG - @utils does NOT support set/run/if
go_next: @utils.transition to @topic.main
   set @variables.visited = True   # ERROR!

# ✅ CORRECT - Only @actions.* supports post-action
process: @actions.process_order
   with order_id=@variables.order_id
   set @variables.status = @outputs.status        # ✅ Works
   run @actions.send_notification                 # ✅ Works
   if @outputs.needs_review:                      # ✅ Works
      transition to @topic.review

Helper Topic Pattern (For Demo Agents Without Flows/Apex)

When you need to set variables without backend actions, use dedicated "helper topics":

# Main topic offers LLM-selectable action
topic verify_employee:
   reasoning:
      actions:
         complete_verification: @utils.transition to @topic.verification_success
            description: "Mark employee as verified"
            available when @variables.employee_verified == False

# Helper topic sets variables in instructions, then returns
topic verification_success:
   description: "Set verified state and return"
   reasoning:
      instructions: ->
         set @variables.employee_verified = True
         set @variables.employee_name = "Demo Employee"
         | ✓ Identity verified!
         transition to @topic.verify_employee  # Return to parent

Why this works: set statements ARE valid inside instructions: -> blocks. The topic loop pattern lets you change state without Flows/Apex.

💰 PRODUCTION GOTCHAS: Billing, Determinism & Performance

Credit Consumption Table

Key insight: Framework operations are FREE. Only actions that invoke external services consume credits.

| @utils.transition | FREE | Framework navigation

| @utils.setVariables | FREE | Framework state management

| @utils.escalate | FREE | Framework escalation

| if/else control flow | FREE | Deterministic resolution

| before_reasoning | FREE | Deterministic pre-processing (see note below)

| after_reasoning | FREE | Deterministic post-processing (see note below)

| reasoning (LLM turn) | FREE | LLM reasoning itself is not billed

| Prompt Templates | 2-16 | Per invocation (varies by complexity)

| Flow actions | 20 | Per action execution

| Apex actions | 20 | Per action execution

| Any other action | 20 | Per action execution

✅ Lifecycle Hooks Validated (v1.3.0): The before_reasoning: and after_reasoning: lifecycle hooks are now TDD-validated. Content goes directly under the block (no instructions: wrapper). See "Lifecycle Hooks" section below for correct syntax.

Cost Optimization Pattern: Fetch data once in before_reasoning:, cache in variables, reuse across topics.

Lifecycle Hooks: before_reasoning: and after_reasoning:

TDD Validated (2026-01-20): These hooks enable deterministic pre/post-processing around LLM reasoning.

topic main:
   description: "Topic with lifecycle hooks"

   # BEFORE: Runs deterministically BEFORE LLM sees instructions
   before_reasoning:
      # Content goes DIRECTLY here (NO instructions: wrapper!)
      set @variables.pre_processed = True
      set @variables.customer_tier = "gold"

   # LLM reasoning phase
   reasoning:
      instructions: ->
         | Customer tier: {!@variables.customer_tier}
         | How can I help you today?

   # AFTER: Runs deterministically AFTER LLM finishes reasoning
   after_reasoning:
      # Content goes DIRECTLY here (NO instructions: wrapper!)
      set @variables.interaction_logged = True
      if @variables.needs_audit == True:
         set @variables.audit_flag = True

Key Points:

  • Content goes directly under before_reasoning: / after_reasoning: (NO instructions: wrapper)

  • Supports set, if, run statements (same as procedural instructions: ->)

  • before_reasoning: is FREE (no credit cost) - use for data prep

  • after_reasoning: is FREE (no credit cost) - use for logging, cleanup

❌ WRONG Syntax (causes compile error):

before_reasoning:
   instructions: ->      # ❌ NO! Don't wrap with instructions:
      set @variables.x = True

✅ CORRECT Syntax:

before_reasoning:
   set @variables.x = True   # ✅ Direct content under the block

Supervision vs Handoff (Clarified Terminology)

| Handoff | @utils.transition to @topic.X | Control transfers completely, child generates final response | Checkout, escalation, terminal states

| Supervision | @topic.X (as action reference) | Parent orchestrates, child returns, parent synthesizes | Expert consultation, sub-tasks

# HANDOFF - child topic takes over completely:
checkout: @utils.transition to @topic.order_checkout
   description: "Proceed to checkout"
# → @topic.order_checkout generates the user-facing response

# SUPERVISION - parent remains in control:
get_advice: @topic.product_expert
   description: "Consult product expert"
# → @topic.product_expert returns, parent topic synthesizes final response

KNOWN BUG: Adding ANY new action in Canvas view may inadvertently change Supervision references to Handoff transitions.

Action Output Flags for Zero-Hallucination Routing

Key Pattern for Determinism: Control what the LLM can see and say.

When defining actions in Agentforce Assets, use these output flags:

| is_displayable: False | LLM cannot show this value to user | Preventing hallucinated responses

| is_used_by_planner: True | LLM can reason about this value | Decision-making, routing

Zero-Hallucination Intent Classification Pattern:

# In Agentforce Assets - Action Definition outputs:
outputs:
   intent_classification: string
      is_displayable: False       # LLM cannot show this to user
      is_used_by_planner: True    # LLM can use for routing decisions

# In Agent Script - LLM routes but cannot hallucinate:
topic intent_router:
   reasoning:
      instructions: ->
         run @actions.classify_intent
         set @variables.intent = @outputs.intent_classification

         if @variables.intent == "refund":
            transition to @topic.refunds
         if @variables.intent == "order_status":
            transition to @topic.orders

Action Chaining with run Keyword

Known quirk: Parent action may complain about inputs needed by chained action - this is expected.

# Chained action execution:
process_order: @actions.create_order
   with customer_id = @variables.customer_id
   run @actions.send_confirmation        # Chains after create_order completes
   set @variables.order_id = @outputs.id

KNOWN BUG: Chained actions with Prompt Templates don't properly map inputs using Input:Query format:

# ❌ MAY NOT WORK with Prompt Templates:
run @actions.transform_recommendation
   with "Input:Reco_Input" = @variables.ProductReco

# ⚠️ TRY THIS (may still have issues):
run @actions.transform_recommendation
   with Reco_Input = @variables.ProductReco

Latch Variable Pattern for Topic Re-entry

Problem: Topic selector doesn't properly re-evaluate after user provides missing input.

Solution: Use a "latch" variable to force re-entry:

variables:
   verification_in_progress: mutable boolean = False

start_agent topic_selector:
   reasoning:
      instructions: ->
         # LATCH CHECK - force re-entry if verification was started
         if @variables.verification_in_progress == True:
            transition to @topic.verification

         | How can I help you today?
      actions:
         start_verify: @topic.verification
            description: "Start identity verification"
            # Set latch when user chooses this action
            set @variables.verification_in_progress = True

topic verification:
   reasoning:
      instructions: ->
         | Please provide your email to verify your identity.
      actions:
         verify: @actions.verify_identity
            with email = ...
            set @variables.verified = @outputs.success
            # Clear latch when verification completes
            set @variables.verification_in_progress = False

Loop Protection Guardrail

Agent Scripts have a built-in guardrail that limits iterations to approximately 3-4 loops before breaking out and returning to the Topic Selector.

Best Practice: Map out your execution paths - particularly topic transitions. Ensure testing covers all paths and specifically check for unintended circular references between topics.

Token & Size Limits

| Max response size | 1,048,576 bytes (1MB) | Per agent response

| Plan trace limit (Frontend) | 1M characters | For debugging UI

| Transformed plan trace (Backend) | 32k tokens | Internal processing

| Active/Committed Agents per org | 100 max | Org limit

Progress Indicators

Add user feedback during long-running actions:

actions:
   fetch_data: @actions.get_customer_data
      description: "Fetch customer information"
      include_in_progress_indicator: True
      progress_indicator_message: "Fetching your account details..."

VS Code Pull/Push NOT Supported

# ❌ ERROR when using source tracking:
Failed to retrieve components using source tracking:
[SfError [UnsupportedBundleTypeError]: Unsupported Bundle Type: AiAuthoringBundle

# ✅ WORKAROUND - Use CLI directly:
sf project retrieve start -m AiAuthoringBundle:MyAgent
sf agent publish authoring-bundle --source-dir ./force-app/main/default/aiAuthoringBundles/MyAgent

Language Block Quirks

  • Hebrew and Indonesian appear twice in the language dropdown

  • Selecting from the second set causes save errors

  • Use adaptive_response_allowed: True for automatic language adaptation

language:
   locale: en_US
   adaptive_response_allowed: True  # Allow language adaptation

Cross-Skill Orchestration

| Before Agent Script | /sf-flow - Create Flows for flow:// action targets | ⚠️ REQUIRED

| After Agent Script | /sf-ai-agentforce-testing - Test topic routing and actions | ✅ RECOMMENDED

| For Deployment | /sf-deploy - Publish agent with sf agent publish | ⚠️ REQUIRED

📋 QUICK REFERENCE: Agent Script Syntax

Block Structure (CORRECTED Order per Official Spec)

config:        # 1. Required: Agent metadata (agent_name, default_agent_user)
variables:     # 2. Optional: State management (mutable/linked)
system:        # 3. Required: Global messages and instructions
connections:   # 4. Optional: Escalation routing
knowledge:     # 5. Optional: Knowledge base config
language:      # 6. Optional: Locale settings
start_agent:   # 7. Required: Entry point (exactly one)
topic:         # 8. Required: Conversation topics (one or more)

Naming Rules (All Identifiers)

  • Only letters, numbers, underscores

  • Must begin with a letter

  • No spaces, no consecutive underscores, cannot end with underscore

  • Maximum 80 characters

Instruction Syntax Patterns

| instructions: | | Literal multi-line (no expressions) | instructions: | Help the user.

| instructions: -> | Procedural (enables expressions) | instructions: -> if @variables.x:

| | text | Literal text for LLM prompt | | Hello + variable injection

| if @variables.x: | Conditional (resolves before LLM) | if @variables.verified == True:

| run @actions.x | Execute action during resolution | run @actions.load_customer

| set @var = @outputs.y | Capture action output | set @variables.risk = @outputs.score

| set @var = value | Set variable in instructions | set @variables.count = 0

| {!@variables.x} | Variable injection in text | Risk score: {!@variables.risk}

| {!expr if cond else alt} | Conditional interpolation | {!@variables.status if @variables.status else "pending"}

| available when | Control action visibility to LLM | available when @variables.verified == True

| with param=... | LLM slot-filling (extracts from conversation) | with query=...

| with param=value | Fixed parameter value | with limit=10

Transition vs Delegation (CRITICAL DISTINCTION)

| @utils.transition to @topic.X | Permanent handoff | ❌ No | Checkout, escalation, final states

| @topic.X (in reasoning.actions) | Delegation | ✅ Yes | Get expert advice, sub-tasks

| transition to @topic.X (inline) | Deterministic jump | ❌ No | Post-action routing, gates

# Delegation - returns to current topic after specialist finishes
consulting: @topic.expert_topic
   description: "Get expert advice"

# Transition - permanent handoff, no return
checkout: @utils.transition to @topic.checkout
   description: "Proceed to purchase"

Expression Operators (Safe Subset)

| Comparison | ==, <> (not-equal), <, <=, >, >=, is, is not |

| Logical | and, or, not |

| Arithmetic | +, - | ❌ *, /, %

| Access | . (property), [] (index) |

| Conditional | x if condition else y |

Variable Types

| mutable | Read/write state | string, number, boolean, object, date, timestamp, currency, id, list[T] | ✅ Yes

| linked | Read-only from source | string, number, boolean, date, timestamp, currency, id | ❌ No (has source:)

⚠️ Linked variables CANNOT use object or list types

Variable vs Action I/O Type Matrix

Critical distinction: Some types are valid ONLY for action inputs/outputs, NOT for Agent Script variables.

| string | ✅ | ✅ | Universal

| number | ✅ | ✅ | Universal

| boolean | ✅ | ✅ | Universal

| date | ✅ | ✅ | Universal

| currency | ✅ | ✅ | Universal

| id | ✅ | ✅ | Salesforce IDs

| list | ✅ (mutable only) | ✅ | Collections

| object | ✅ (mutable only) | ✅ | ⚠️ Not for linked vars

| datetime | ❌ | ✅ | Actions only

| time | ❌ | ✅ | Actions only

| integer | ❌ | ✅ | Actions only

| long | ❌ | ✅ | Actions only

Source: AGENT_SCRIPT.md rules document from trailheadapps/agent-script-recipes

Action Target Protocols

| flow | flow:// | Data operations, business logic | ✅ TDD

| apex | apex:// | Custom calculations, validation | ✅ TDD

| prompt | generatePromptResponse:// | Grounded LLM responses | ✅ TDD

| api | api:// | REST API calls | ✅ TDD

| retriever | retriever:// | RAG knowledge search | ✅ TDD

| externalService | externalService:// | Third-party APIs via Named Credentials | ✅ TDD

| standardInvocableAction | standardInvocableAction:// | Built-in SF actions (email, tasks) | ✅ TDD

| datacloudDataGraphAction | datacloudDataGraphAction:// | Data Cloud graph queries | 📋 Spec

| datacloudSegmentAction | datacloudSegmentAction:// | Data Cloud segment operations | 📋 Spec

| triggerByKnowledgeSource | triggerByKnowledgeSource:// | Knowledge article triggers | 📋 Spec

| contextGrounding | contextGrounding:// | Context grounding for LLM | 📋 Spec

| predictiveAI | predictiveAI:// | Einstein prediction models | 📋 Spec

| runAction | runAction:// | Execute sub-actions | 📋 Spec

| external | external:// | External service calls | 📋 Spec

| copilotAction | copilotAction:// | Salesforce Copilot actions | 📋 Spec

| @topic.X | (inline) | Topic delegation (returns to parent) | ✅ TDD

Legend: ✅ TDD = Validated via deployment testing | 📋 Spec = Documented in AGENT_SCRIPT.md spec (requires specific org setup to test)

Connection Block (Full Escalation Pattern)

connections:
   # Messaging channel escalation
   connection messaging:
      escalation_message: "One moment, I'm transferring our conversation to get you more help."
      outbound_route_type: "OmniChannelFlow"
      outbound_route_name: "<flow://Escalate_Messaging_To_Live_Agent>"
      adaptive_response_allowed: False

   # Voice channel escalation
   connection voice:
      escalation_message: "Please hold while I transfer you to a specialist."
      outbound_route_type: "Queue"
      outbound_route_name: "Support_Queue"
      adaptive_response_allowed: True

   # Web chat escalation
   connection web:
      escalation_message: "Connecting you with a live agent now."
      outbound_route_type: "OmniChannelFlow"
      outbound_route_name: "<flow://Web_Chat_Escalation>"

Key Properties:

| escalation_message | ✅ | Message shown to user during handoff

| outbound_route_type | ✅ | OmniChannelFlow, Queue, or Skill

| outbound_route_name | ✅ | Flow API name or Queue name

| adaptive_response_allowed | ❌ | Allow LLM to adapt escalation message

🔄 WORKFLOW: Agent Development Lifecycle

Phase 1: Requirements & Design

  • Identify deterministic vs. subjective logic

Deterministic: Security checks, financial thresholds, data lookups, counters

  • Subjective: Greetings, context understanding, natural language generation

  • Design FSM architecture - Map topics as states, transitions as edges

  • Define variables - Mutable for state tracking, linked for session context

Phase 2: Agent Script Authoring

  • Create .agent file with required blocks

  • Write topics with instruction resolution pattern:

Post-action checks at TOP (triggers on loop)

  • Pre-LLM data loading

  • Dynamic instructions for LLM

  • Configure actions with appropriate target protocols

  • Add available when guards to enforce security

Phase 3: Validation (LSP + CLI)

AUTOMATIC: LSP validation runs on every Write/Edit to .agent files. Errors are reported with line numbers and autofix suggestions.

LSP Validation Loop (Find Error → Autofix)

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ Write/Edit  │ ──▶ │ LSP Analyze │ ──▶ │ Report      │
│ .agent file │     │ (automatic) │     │ Errors      │
└─────────────┘     └─────────────┘     └──────┬──────┘
                                               │
      ┌────────────────────────────────────────┘
      ▼
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ Claude      │ ──▶ │ Apply Fix   │ ──▶ │ Re-validate │
│ Suggests Fix│     │ (Edit tool) │     │ (loop)      │
└─────────────┘     └─────────────┘     └─────────────┘

LSP Checks (Automatic)

| Mixed tabs/spaces | ❌ Error | Convert to consistent spacing

| Lowercase booleans (true/false) | ❌ Error | Capitalize to True/False

| Missing required blocks | ❌ Error | Add missing block template

| Missing default_agent_user | ❌ Error | Add placeholder with comment

| Mutable + linked conflict | ❌ Error | Remove conflicting modifier

| Undefined topic references | ⚠️ Warning | Create topic stub

| Post-action check position | ⚠️ Warning | Move to top of instructions

CLI Validation (Before Deploy)

# Validate authoring bundle syntax
sf agent validate authoring-bundle --source-dir ./force-app/main/default/aiAuthoringBundles/MyAgent

Manual Checks

  • default_agent_user exists and is active Einstein Agent User

  • All topic references resolve to existing topics

  • Action targets (flow://, apex://, etc.) exist in org

Phase 4: Testing (Delegate to /sf-ai-agentforce-testing)

  • Batch testing - Run up to 100 test cases simultaneously

  • Quality metrics - Completeness, Coherence, Topic/Action Assertions

  • LLM-as-Judge - Automated scoring against golden responses

Phase 5: Deployment

⚠️ CRITICAL: Use sf agent publish authoring-bundle, NOT sf project deploy start

  • Create bundle directory: force-app/main/default/aiAuthoringBundles/AgentName/

  • Add files:

AgentName.agent - Your Agent Script

  • AgentName.bundle-meta.xml - Metadata XML (NOT .aiAuthoringBundle-meta.xml)

  • Publish: sf agent publish authoring-bundle --source-dir ./force-app/main/default/aiAuthoringBundles/AgentName

  • Monitor - Use trace debugging for production issues

Phase 6: CLI Operations

# Retrieve from org
sf agent retrieve --name MyAgent --target-org sandbox

# Validate syntax
sf agent validate authoring-bundle --source-dir ./force-app/main/default/aiAuthoringBundles/MyAgent

# Publish to org (NOT sf project deploy!)
sf agent publish authoring-bundle --source-dir ./force-app/main/default/aiAuthoringBundles/MyAgent

Bundle Structure (CRITICAL)

force-app/main/default/aiAuthoringBundles/
└── MyAgent/
    ├── MyAgent.agent              # Agent Script file
    └── MyAgent.bundle-meta.xml    # NOT .aiAuthoringBundle-meta.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>

📊 SCORING SYSTEM (100 Points)

Categories

| Structure & Syntax | 20 | Block ordering, indentation consistency, required fields present

| Deterministic Logic | 25 | Security via available when, post-action checks, proper conditionals

| Instruction Resolution | 20 | Correct use of -> vs |, template injection, action execution

| FSM Architecture | 15 | Clear topic separation, explicit transitions, state management

| Action Configuration | 10 | Correct protocols, input/output mapping, error handling

| Deployment Readiness | 10 | Valid default_agent_user, no compilation errors, metadata complete

Scoring Rubric Details

Structure & Syntax (20 points)

| 20 | All required blocks present, consistent indentation, valid identifiers

| 15 | Minor issues (e.g., inconsistent spacing within tolerance)

| 10 | Missing optional blocks that would improve clarity

| 5 | Block ordering issues or mixed indentation

| 0 | Missing required blocks or compilation failures

Deterministic Logic (25 points)

| 25 | All security actions guarded with available when, post-action patterns used

| 20 | Most guards present, minor gaps in deterministic enforcement

| 15 | Some security logic relies on prompts instead of guards

| 10 | Critical actions lack available when guards

| 0 | Security logic entirely prompt-based (LLM can bypass)

Instruction Resolution (20 points)

| 20 | Arrow syntax for complex logic, proper template injection, correct action execution

| 15 | Mostly correct, minor syntax issues

| 10 | Uses pipe syntax where arrow needed, template injection errors

| 5 | Incorrect phase ordering (data loads after LLM sees instructions)

| 0 | Fundamental misunderstanding of resolution order

FSM Architecture (15 points)

| 15 | Clear topic boundaries, explicit transitions, appropriate escalation paths

| 12 | Good structure with minor redundancy

| 9 | Topics too broad or transitions unclear

| 5 | Monolithic topic handling multiple concerns

| 0 | No topic separation, all logic in start_agent

Action Configuration (10 points)

| 10 | Correct protocols, proper I/O mapping, descriptions present

| 8 | Minor issues (missing descriptions)

| 5 | Wrong protocol for use case

| 2 | Input/output mapping errors

| 0 | Actions don't compile

Deployment Readiness (10 points)

| 10 | Valid user, clean validation, metadata complete

| 8 | Minor warnings

| 5 | Validation errors that need fixing

| 2 | Missing metadata files

| 0 | Cannot deploy

Score Thresholds

| 90-100 | ⭐⭐⭐⭐⭐ Excellent | Deploy with confidence

| 80-89 | ⭐⭐⭐⭐ Very Good | Minor improvements recommended

| 70-79 | ⭐⭐⭐ Good | Review flagged issues before deploy

| 60-69 | ⭐⭐ Needs Work | Address issues before deploy

| <60 | ⭐ Critical | BLOCK - Fix critical issues

Score Report Format

📊 AGENT SCRIPT SCORE REPORT
════════════════════════════════════════

Score: 85/100 ⭐⭐⭐⭐ Very Good
├─ Structure & Syntax:    18/20 (90%)
├─ Deterministic Logic:   22/25 (88%)
├─ Instruction Resolution: 16/20 (80%)
├─ FSM Architecture:      12/15 (80%)
├─ Action Configuration:   9/10 (90%)
└─ Deployment Readiness:   8/10 (80%)

Issues:
⚠️ [Deterministic] Missing `available when` on process_refund action
⚠️ [Resolution] Post-action check should be at TOP of instructions
✓ All Structure & Syntax checks passed
✓ All Action Configuration checks passed

🔧 THE 6 DETERMINISTIC BUILDING BLOCKS

These execute as code, not suggestions. The LLM cannot override them.

| 1 | Conditionals | if/else resolves before LLM | if @variables.attempts >= 3:

| 2 | Topic Filters | Control action visibility | available when @variables.verified == True

| 3 | Variable Checks | Numeric/boolean comparisons | if @variables.churn_risk >= 80:

| 4 | Inline Actions | Immediate execution | run @actions.load_customer

| 5 | Utility Actions | Built-in helpers | @utils.transition, @utils.escalate

| 6 | Variable Injection | Template values | {!@variables.customer_name}

📐 ARCHITECTURE PATTERNS

Pattern 1: Hub and Spoke

Central router (hub) to specialized topics (spokes). Use for multi-purpose agents.

       ┌─────────────┐
       │ topic_sel   │
       │   (hub)     │
       └──────┬──────┘
    ┌─────────┼─────────┐
    ▼         ▼         ▼
┌────────┐ ┌────────┐ ┌────────┐
│refunds │ │ orders │ │support │
└────────┘ └────────┘ └────────┘

Pattern 2: Verification Gate

Security gate before protected topics. Mandatory for sensitive data.

┌─────────┐     ┌──────────┐     ┌───────────┐
│  entry  │ ──▶ │ VERIFY   │ ──▶ │ protected │
└─────────┘     │  (GATE)  │     │  topics   │
                └────┬─────┘     └───────────┘
                     │ 3 fails
                     ▼
                ┌──────────┐
                │ lockout  │
                └──────────┘

Pattern 3: Post-Action Loop

Topic re-resolves after action completes - put checks at TOP.

topic refund:
  reasoning:
    instructions: ->
      # POST-ACTION CHECK (at TOP - triggers on next loop)
      if @variables.refund_status == "Approved":
        run @actions.create_crm_case
        transition to @topic.success

      # PRE-LLM DATA LOADING
      run @actions.check_churn_risk
      set @variables.risk = @outputs.score

      # DYNAMIC INSTRUCTIONS FOR LLM
      if @variables.risk >= 80:
        | Offer full refund to retain customer.
      else:
        | Offer $10 credit instead.

🐛 DEBUGGING: Trace Analysis

The 6 Span Types

| ➡️ topic_enter | Execution enters a topic

| ▶ before_reasoning | Deterministic pre-processing

| 🧠 reasoning | LLM processes instructions

| ⚡ action_call | Action invoked

| → transition | Topic navigation

| ✓ after_reasoning | Deterministic post-processing

Debugging Workflow

  • Interaction Details - Quick understanding of what happened

  • Trace Waterfall - Technical view with exact prompts, latencies

  • Variable State - Entry vs Exit values reveal when state was ignored

  • Script View - Red squiggles show syntax errors

Common Debug Patterns

| Wrong policy applied | Variable Entry values | Change mutable to linked with source:

| Action executed without auth | available when presence | Add guard clause

| LLM ignores variable | Instruction resolution order | Move data load before LLM text

| Infinite loop | Transition conditions | Add exit condition

⚠️ COMMON ISSUES & FIXES

| Internal Error, try again later | Invalid default_agent_user | Query: sf data query -q "SELECT Username FROM User WHERE Profile.Name = 'Einstein Agent User'" -o TARGET_ORG

| Default agent user X could not be found | User doesn't exist in target org | Query the specific target org (user formats vary: some use user@orgid.ext)

| No .agent file found in directory | agent_name doesn't match folder | Make agent_name identical to folder name (case-sensitive)

| SyntaxError: cannot mix spaces and tabs | Mixed indentation | Use consistent spacing throughout

| SyntaxError: Unexpected 'if' | Nested if statements | Use compound condition: if A and B: or flatten to sequential ifs

| SyntaxError: Unexpected 'actions' | Top-level actions block | Move actions inside topic.reasoning.actions:

| SyntaxError: Unexpected 'inputs' | inputs: block in action | Use with param=value syntax instead

| SyntaxError: Unexpected 'outputs' | outputs: block in action | Use set @variables.x = @outputs.y instead

| SyntaxError: Unexpected 'set' | set after @utils.setVariables | Use Helper Topic Pattern (set in instructions: ->)

| Duplicate 'available when' clause | Multiple guards on action | Combine: available when A and B

| Unexpected 'escalate' | Reserved action name | Rename to escalate_now or escalate_to_human

| Transition to undefined topic | Typo in topic reference | Check spelling, ensure topic exists

| Variables cannot be both mutable AND linked | Conflicting modifiers | Choose one: mutable for state, linked for external

| Required fields missing: [BundleType] | Using wrong deploy command | Use sf agent publish authoring-bundle, NOT sf project deploy start

| Cannot find a bundle-meta.xml file | Wrong file naming | Rename to AgentName.bundle-meta.xml, NOT .aiAuthoringBundle-meta.xml

| ValidationError: Tool target 'X' is not an action definition | Action references non-existent Flow/Apex | Create the action definition first, or use Helper Topic Pattern

| LLM bypasses security check | Using prompts for security | Use available when guards instead

| Post-action logic doesn't run | Check not at TOP | Move post-action check to first lines

| Wrong data retrieved | Missing filter | Wrap retriever in Flow with filter inputs

| Variables don't change | Using @utils.setVariables with set | Post-action set only works on @actions.*, use Helper Topics

Deployment Gotchas (Validated by Testing)

| AgentName.aiAuthoringBundle-meta.xml | AgentName.bundle-meta.xml

| sf project deploy start | sf agent publish authoring-bundle

| sf agent validate --source-dir | sf agent validate authoring-bundle --source-dir

| Query user from wrong org | Query target org specifically with -o flag

Einstein Agent User Format (Org-Specific)

Einstein Agent User formats vary between orgs:

  • Production/Partner orgs: Often use username@orgid.ext format (e.g., resort_manager@00dak00000gdgwu480119933.ext)

  • Dev orgs: May use username.suffix@orgfarm.salesforce.com format

MANDATORY: Ask user to confirm which Einstein Agent User to use when creating a new agent.

Always query the specific target org:

# Query target org specifically
sf data query -q "SELECT Username FROM User WHERE Profile.Name = 'Einstein Agent User' AND IsActive = true" -o YOUR_TARGET_ORG

Present the results to the user and ask them to select which user to use for default_agent_user.

⚠️ A user existing in one org does NOT mean it exists in another. Always verify in the deployment target.

📚 DOCUMENT MAP (Progressive Disclosure)

Tier 2: Resource Guides (Comprehensive)

| Syntax reference | resources/syntax-reference.md | Complete block & expression syntax

| FSM design | resources/fsm-architecture.md | State machine patterns & examples

| Instruction resolution | resources/instruction-resolution.md | Three-phase execution model

| Data & multi-agent | resources/grounding-multiagent.md | Retriever actions & SOMA patterns

| Debugging | resources/debugging-guide.md | Trace analysis & forensics

| Testing | resources/testing-guide.md | Batch testing & quality metrics

Tier 3: Quick References (Docs)

| CLI commands | docs/cli-guide.md | sf agent retrieve/validate/deploy

| Patterns | docs/patterns-quick-ref.md | Decision tree for pattern selection

🔗 CROSS-SKILL INTEGRATION

MANDATORY Delegations

| Create Flows for flow:// targets | /sf-flow | Flows must exist before agent uses them

| Test agent routing & actions | /sf-ai-agentforce-testing | Specialized testing patterns

| Deploy agent to org | /sf-deploy | Proper deployment validation

Integration Patterns

| /sf-ai-agentscript | /sf-flow | Create Flow, then reference in agent

| /sf-ai-agentscript | /sf-apex | Create Apex class, then use apex:// protocol

| /sf-ai-agentscript | /sf-integration | Set up Named Credentials for externalService://

✅ DEPLOYMENT CHECKLIST

Configuration

default_agent_user is valid Einstein Agent User agent_name uses snake_case (no spaces)

Syntax

No mixed tabs/spaces Booleans use True/False Variable names use snake_case

Structure

Exactly one start_agent block At least one topic block All transitions reference existing topics

Security

Critical actions have available when guards Session data uses linked variables (not mutable)

Testing

sf agent validate --source-dir ./my-agent passes Preview mode tested before activation

🚀 MINIMAL WORKING EXAMPLE

config:
  agent_name: "simple_agent"
  agent_label: "Simple Agent"
  description: "A minimal working agent example"
  default_agent_user: "agent_user@yourorg.com"

system:
  messages:
    welcome: "Hello! How can I help you today?"
    error: "Sorry, something went wrong."
  instructions: "You are a helpful customer service agent."

variables:
  customer_verified: mutable boolean = False

topic main:
  description: "Main conversation handler"
  reasoning:
    instructions: ->
      if @variables.customer_verified == True:
        | You are speaking with a verified customer.
        | Help them with their request.
      else:
        | Please verify the customer's identity first.
    actions:
      verify: @actions.verify_customer
        description: "Verify customer identity"
        set @variables.customer_verified = @outputs.verified

start_agent entry:
  description: "Entry point for all conversations"
  reasoning:
    instructions: |
      Greet the customer and route to the main topic.
    actions:
      go_main: @utils.transition to @topic.main
        description: "Navigate to main conversation"

📖 OFFICIAL RESOURCES

📚 SOURCES & ACKNOWLEDGMENTS

This skill draws from multiple authoritative sources:

| trailheadapps/agent-script-recipes | 20 reference recipes across 4 categories, AGENT_SCRIPT.md rules document, variable patterns, action target catalog

| Salesforce Official Documentation | Core syntax, API references, deployment guides

| TDD Validation (this skill) | 13 validation agents confirming current-release syntax compatibility

| Tribal knowledge interviews | Canvas View bugs, VS Code limitations, credit consumption patterns

| agentforce.guide | Unofficial but useful examples (note: some patterns don't compile in current release)

⚠️ Note on Feature Validation: Some patterns from external sources (e.g., always_expect_input:, label: property, certain action properties on transitions) do NOT compile in Winter '26. The before_reasoning:/after_reasoning: lifecycle hooks ARE valid but require direct content (no instructions: wrapper) - see the Lifecycle Hooks section for correct syntax. This skill documents only patterns that pass TDD validation.

🏷️ VERSION HISTORY

| 1.3.0 | 2026-01-20 | Lifecycle hooks validated: Added full documentation for before_reasoning: and after_reasoning: with CORRECT syntax (content directly under block, NO instructions: wrapper). Added "Features NOT Valid in Current Release" section documenting 7 features that appear in docs/recipes but don't compile (label on topics/actions, always_expect_input, action properties on transitions). Updated validation_agents count to 13. Confirmed @utils.transition only supports description: property.

| 1.2.0 | 2026-01-20 | Gap analysis vs agent-script-recipes: Expanded Action Target Protocols from 7 to 16 (with validation status indicators), added Variable vs Action I/O Type Matrix, added lifecycle hooks note with TDD validation caveat, added Sources & Acknowledgments section, documented future/planned features notice. TDD validation confirmed label: IS reserved (SKILL.md was correct), before_reasoning:/after_reasoning: syntax from recipes does NOT compile in current release

| 1.1.0 | 2026-01-20 | "Ultimate Guide" tribal knowledge integration: Added complex_data_type_name mapping table, Canvas View corruption bugs, Reserved field names, Preview mode workarounds, Credit consumption table, Supervision vs Handoff clarification, Action output flags for zero-hallucination routing, Latch variable pattern, Loop protection guardrails, Token/size limits, Progress indicators, Connection block escalation patterns, VS Code limitations, Language block quirks. Added 4 new templates: flow-action-lookup, prompt-rag-search, deterministic-routing, escalation-pattern

| 1.0.4 | 2026-01-19 | Progressive testing validation (Quiz_Master, Expense_Calculator, Order_Processor): Added constraints for no top-level actions: block, no inputs:/outputs: in reasoning.actions, expanded nested-if guidance with flattening approach, added new SyntaxError entries to common issues

| 1.0.3 | 2026-01-19 | Added Einstein Agent User interview requirement - mandatory user confirmation when creating new agents

| 1.0.2 | 2026-01-19 | Major corrections from GitHub reference: Fixed block order (config→system), added Helper Topic Pattern, transition vs delegation, expression operators (+/- only), naming rules (80 char max), slot-filling ... syntax, post-action directives (@actions.* only)

| 1.0.1 | 2026-01-19 | Added syntax constraints from 0-shot testing: no nested if, one available when per action, reserved action names

| 1.0.0 | 2026-01 | Initial release with 8-module coverage

返回排行榜