# 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.