afv-library/skills/agentforce-generate/assets/patterns/procedural-instructions.agent

109 lines
4.8 KiB
Plaintext

# Procedural Instructions Pattern
# Run a required read-only action before the model handles the current turn.
#
# ★ When To Use This Pattern:
# - The response must use fresh external context on every reasoning turn
# - A trusted action result deterministically changes the available response
# - Stale cached data would be worse than the extra read-only action call
#
# ★ Do Not Use This Pattern:
# - For optional lookups the model should call only when the user asks
# - To cache external facts for the rest of a conversation
# - To represent dialogue stages, current intent, or questions already asked
#
# The mutable values below are a turn-scoped bridge from trusted action output
# to deterministic instructions. The same block resets and overwrites them on
# every reasoning turn; there is no conversation-long `data_loaded` latch.
system:
instructions: |
Help customers with their current account-support request. Use the
freshly loaded account result for account facts. If no account is
found, explain that limitation without inventing customer details.
messages:
welcome: "Hello! How can I help with your account today?"
error: "I couldn't retrieve the account context. Please try again."
access:
default_agent_user: "agent@company.salesforce.com"
config:
developer_name: "Fresh_Account_Context_Agent"
agent_label: "Fresh Account Context Agent"
description: "Demonstrates a required read-only action inside procedural instructions"
variables:
ContactId: linked string
source: @MessagingEndUser.ContactId
description: "Trusted contact identifier used by the account lookup"
account_found: mutable boolean = False
description: "Reset and overwritten from lookup output on every reasoning turn; read by the response branches"
customer_name: mutable string = ""
description: "Reset and overwritten from lookup output on every reasoning turn; used only in the current response"
customer_tier: mutable string = ""
description: "Reset and overwritten from lookup output on every reasoning turn; used only in the current response"
language:
default_locale: "en_US"
start_agent account_support:
label: "Account Support"
description: "Handles account questions using freshly loaded customer context"
actions:
load_customer:
description: "Read the current customer profile for this turn"
inputs:
contact_id: string
description: "Trusted contact ID to look up"
is_required: True
outputs:
found: boolean
description: "Whether a matching customer profile was found"
name: string
description: "Current customer display name"
tier: string
description: "Current customer service tier"
target: "flow://Get_Customer_Profile"
reasoning:
instructions: ->
# Reset first so an empty or failed lookup cannot reuse prior facts.
set @variables.account_found = False
set @variables.customer_name = ""
set @variables.customer_tier = ""
# This read-only lookup runs on every reasoning turn. Its outputs
# overwrite the turn-scoped bridge values above.
run @actions.load_customer
with contact_id=@variables.ContactId
set @variables.account_found = @outputs.found
set @variables.customer_name = @outputs.name
set @variables.customer_tier = @outputs.tier
if @variables.account_found == False:
| Explain that no current account profile was found. Ask the
user to verify their channel identity or request a human.
else if @variables.customer_tier == "vip":
| Address {!@variables.customer_name} by name and provide
priority help for the latest account-support request.
else:
| Address {!@variables.customer_name} by name and help with the
latest account-support request.
actions:
escalate_to_human: @utils.escalate
description: "Transfer when the user explicitly requests a human or the account cannot be resolved"
# ★ Procedural vs. Declarative Actions
#
# `run @actions.load_customer` above is procedural: the runtime executes it
# before assembling the model-visible response instructions.
#
# An action exposed only under `reasoning.actions` is declarative: the model
# decides whether to call it for the current request.
#
# Prefer declarative actions for optional work. Use procedural execution only
# when the fresh result is required on every pass through the block.