结构化程序

智能体每次都以相同方式执行的一组固定类型化步骤

概览

结构化程序是一种程序,会执行一组固定步骤。自由形式程序是由智能体根据具体情况理解并调整的自然语言指引。结构化程序则是一组有序的类型化步骤,每次适用时智能体都会依次执行。

当特定步骤必须在每次通话中以相同方式完成时,可使用结构化程序,例如验证来电者身份、升级工单或收取付款。你只需将其编写为简短的自然语言步骤列表。

与所有程序一样,结构化程序也有一个用于说明何时适用的触发条件。对话符合触发条件时,智能体会按顺序执行程序步骤,然后返回继续其余对话。

结构化程序
编辑器

何时使用结构化程序

当特定步骤必须每次以相同方式执行,同时你仍希望快速用简单步骤编写时,可使用结构化程序。结构化程序比工作流更易编写,但表达能力较弱。若要了解它与自由形式程序、工作流和系统提示词的对比,请参阅何时使用程序。

结构化程序的组成

结构化程序包含三部分:名称、触发条件和有序步骤列表。

名称

用于在控制台中识别程序的简短标签。名称绝不会发送给 LLM,因此不会影响智能体行为。

触发条件

用于描述智能体应在何时运行此程序的自然语言说明,例如 当用户要求为订单退款时。智能体会将用户意图与每个程序的触发条件进行比较,并运行匹配的程序,因此触发条件应具体且彼此不同。智能体只能看到触发条件文本,无法看到程序名称或 ID。触发条件的工作方式与其他任何程序相同;请参阅编写触发条件。

将触发条件留空,可使该程序成为仅在被其他程序调用时才运行的子程序。

步骤

程序正文是一组有序的类型化步骤。可使用多种步骤类型,并将它们组合起来描述任务。

步骤作用
询问向用户询问信息并等待回答。在用户作答前会持续询问。这是唯一会暂停等待用户的步骤。
告知让智能体用自己的话传达信息,然后继续下一个步骤。
说出让智能体逐字说出准确消息,然后继续下一个步骤。对于智能体支持的每种语言,说出步骤都可设置不同的翻译。
工具调用特定工具。你可以用自然语言指示 LLM 如何调用工具;若需要最高确定性,也可以明确固定参数值。还可以定义工具调用失败时要执行的步骤。
如果按顺序评估一个或多个条件,并执行第一个匹配项的步骤。没有匹配项时,可选的 Else 会运行。
子程序运行另一个结构化程序。其步骤完成后,控制权会返回此(调用方)程序的下一步。
系统工具执行内置系统操作。目前仅支持结束通话。
重试最多重新运行工具步骤的失败处理程序(包括工具调用)3 次。仅可在工具步骤的失败处理程序中使用。

结构化程序步骤类型
菜单

并非所有步骤都能出现在任何位置。在 If 分支中,可以使用除另一个 If 或 Retry 以外的任何步骤。在工具步骤的失败处理程序中,可以使用除 If 或另一个工具以外的任何步骤。

API 步骤参考

结构化 procedure 的 content 是一个 JSON 编码文档,其中包含 steps 数组。每个步骤都是通过其 type 标识的对象。触发条件是 procedure 中独立的顶级字段,不属于 content。API 和 SDK 载荷中,procedure 本身使用 type: "deterministic"。

Ask

Ask 步骤会指示智能体请求信息,并等待用户提供合适的回复。

  • API 类型:ask
  • instruction:必填,非空字符串。
{
"type": "ask",
"instruction": "Ask the user for their order ID."
}

Tell

Tell 步骤会指示智能体用自己的话生成一条消息。它不会等待用户回复,而是继续执行。

  • API 类型:tell
  • instruction:必填,非空字符串。
{
"type": "tell",
"instruction": "Explain that the refund normally takes five to ten business days."
}

Say

Say 步骤会完全按照提供的文本朗读,然后继续执行。通过 message_translations 为智能体支持的每种其他语言提供准确消息,并以语言代码为键。

  • API 类型:say
  • message:必填,非空字符串。
  • message_translations:可选对象,将语言代码映射到 { "value": "..." }。
{
"type": "say",
"message": "Your refund has been submitted.",
"message_translations": {
"es": { "value": "Su reembolso ha sido enviado." }
}
}

If、else if 和 else

If 步骤包含一个或多个有序条件分支。第一个匹配的分支会执行。可选的 fallback 数组为 Else 分支。

  • API 类型:branch
  • branches:必填,非空条件分支列表。
  • fallback:可选的 Else 步骤列表。
  • 每个分支都需要 condition 和非空的 steps 列表。
{
"type": "branch",
"branches": [
{
"condition": {
"type": "llm",
"condition": "The user is on an annual plan."
},
"steps": [
{
"type": "say",
"message": "Your annual plan is eligible for a prorated refund."
}
]
}
],
"fallback": [
{
"type": "tell",
"instruction": "Explain that the account's plan could not be determined."
}
]
}

其行为类似 if/else-if/else:

  1. 按顺序评估条件。
  2. 执行第一个匹配的分支。
  3. 如果没有条件匹配,则执行 fallback。
  4. 分支完成后,procedure 会重新汇入主序列。

上述示例使用文本条件,由模型通过自然语言评估。条件也可以是动态变量表达式:

{
"type": "expression",
"expression": {
"type": "eq_operator",
"left": {
"type": "dynamic_variable",
"name": "plan_tier"
},
"right": {
"type": "string_literal",
"value": "annual"
}
}
}

表达式条件用于测试动态变量,这些变量由工具结果填充,或在对话开始时设置。它无法读取用户最近一次回复。若要根据用户所说内容分支,请使用文本条件。

一个 If 步骤中的所有分支必须使用相同条件类型:llm 或 expression。

Tool

Tool 步骤会调用指定工具。

  • API 类型:tool_call
  • tool_id:必填,非空工具 ID。该工具必须已附加到智能体。
  • tool_name:必填工具名称,必须与工具匹配。
  • instruction:可选,用于说明如何调用工具。
  • schema_overrides:可选,用于为工具参数指定固定值。
  • on_failure:可选,失败处理程序。
{
"type": "tool_call",
"tool_id": "tool_abc123",
"tool_name": "lookup_order",
"instruction": "Look up the order using the order ID provided by the user."
}

固定参数值

当参数必须始终采用特定值时,使用 schema_overrides。模型看不到也不会选择被覆盖的参数。键是工具 schema 中的参数路径;每个值指定一个来源:

source字段行为
constantconstant_value始终发送给定值。
dynamic_variabledynamic_variable发送指定动态变量的当前值。
llmprompt(可选)让模型选择值,可选择覆盖提示词。
omit调用时省略该参数。
{
"type": "tool_call",
"tool_id": "tool_abc123",
"tool_name": "update_ticket",
"schema_overrides": {
"request_body.status": { "source": "constant", "constant_value": "pending" },
"request_body.ticket_id": { "source": "dynamic_variable", "dynamic_variable": "ticket_id" }
}
}

失败处理

如果没有 on_failure,工具调用失败会结束对话。添加 on_failure 可改为执行恢复步骤。

  • fallback:必填,工具失败时执行的非空步骤列表。
  • branches:预留用于条件失败处理。请保持为空。
{
"type": "tool_call",
"tool_id": "tool_abc123",
"tool_name": "lookup_order",
"on_failure": {
"branches": [],
"fallback": [
{
"type": "tell",
"instruction": "Explain that the order could not be retrieved and offer to connect the user with support."
}
]
}
}

失败处理程序可包含 Ask、Tell、Say、子 procedure、系统工具和 Retry 步骤。不能包含 Tool 或 If 步骤。处理程序运行后,procedure 会继续执行 Tool 步骤后的步骤。

Retry

Retry 步骤会重新运行包含它的失败处理程序,包括工具调用。每次尝试都会再次调用工具;如果再次失败,也会再次运行处理程序中的所有步骤。尝试次数用尽后,对话结束。

  • API 类型:retry
  • max_retries:可选整数,范围为 1 到 3。默认为 1。
  • 该值计算原始工具调用后的尝试次数。
  • Retry 仅可在 on_failure 中使用。
  • Retry 必须是其失败处理程序中的最后一步,因为后续步骤将无法执行。
{
"type": "retry",
"max_retries": 2
}

子 procedure

子 procedure 步骤会运行另一个结构化 procedure。该 procedure 的步骤完成后,执行会返回到子 procedure 步骤后的步骤。

  • API 类型:sub_procedure
  • procedure_id:必填,非空 procedure ID。
  • 目标必须存在于同一个智能体中。
  • 目标必须是结构化 procedure。
  • procedure 不能调用自身。
{
"type": "sub_procedure",
"procedure_id": "agtprc_6qbpwdq8n01bxhk44bgjy6f10ck3"
}

系统工具

系统工具步骤会执行内置系统操作。

  • API 类型:system_tool
  • system_tool_name:必填系统工具名称。
  • 目前仅支持 end_call。未来可能会添加更多系统工具。
  • 由于 end_call 是终止操作,它必须是所在序列、分支或失败处理程序中的最后一步。
{
"type": "system_tool",
"system_tool_name": "end_call"
}

验证规则

发布智能体或保存智能体草稿时,若结构化 procedure 违反以下任一规则,操作将被拒绝。错误会通过路径指明出错步骤。

  • 两个 If 步骤不能连续放置。
  • If 步骤不能嵌套。
  • 使用表达式条件的 If 步骤不能直接位于 Ask 步骤之后。
  • 一个 If 步骤中的所有条件必须是相同类型:llm 或 expression。
  • Retry 只能出现在 on_failure 中,并且必须是其中的最后一步。
  • end_call 必须是其所在列表的最后一步。
  • 失败处理程序的 fallback 至少要包含一个步骤。
  • 子 procedure 必须指向同一智能体中已有的结构化 procedure,且不能指向自身。
  • tool_id 必须是该智能体上的工具,tool_name 必须匹配,且 schema_overrides 必须与工具 schema 匹配。
  • steps 列表、每个 instruction 和每个 message 都不能为空。

有关如何重构触发这些规则的 procedure,请参阅最佳实践。

完整 API 示例

此示例根据发货状态处理订单取消。它固定一个工具参数,从失败的工具调用中恢复,调用另一个结构化 procedure,然后结束通话。

{
"steps": [
{
"type": "ask",
"instruction": "Ask the user for their order ID."
},
{
"type": "branch",
"branches": [
{
"condition": {
"type": "llm",
"condition": "The user says the order has already shipped."
},
"steps": [
{
"type": "tell",
"instruction": "Explain that shipped orders must be returned before they can be refunded."
}
]
},
{
"condition": {
"type": "llm",
"condition": "The user says the order has not shipped."
},
"steps": [
{
"type": "tool_call",
"tool_id": "tool_abc123",
"tool_name": "cancel_order",
"instruction": "Cancel the order using the order ID provided by the user.",
"schema_overrides": {
"request_body.notify_customer": { "source": "constant", "constant_value": true }
},
"on_failure": {
"branches": [],
"fallback": [
{
"type": "tell",
"instruction": "Apologize that the cancellation did not go through and say you will try once more."
},
{
"type": "retry",
"max_retries": 1
}
]
}
}
]
}
],
"fallback": [
{
"type": "ask",
"instruction": "Ask whether the order has already shipped."
}
]
},
{
"type": "sub_procedure",
"procedure_id": "agtprc_6qbpwdq8n01bxhk44bgjy6f10ck3"
},
{
"type": "say",
"message": "Thank you for contacting us. Goodbye.",
"message_translations": {
"es": { "value": "Gracias por contactarnos. Adiós." }
}
},
{
"type": "system_tool",
"system_tool_name": "end_call"
}
]
}

结构化 procedure 的运行方式

将结构化 procedure 的步骤转换为智能体执行的形式称为编译。发布时,平台会编译每个结构化 procedure;无需自行编译任何内容。目前可以在 Workflow 标签页中以只读节点形式查看编译结果。

当用户请求与 procedure 的触发条件匹配时,智能体会进入该 procedure 并按顺序执行其步骤。在结构化 procedure 中,智能体会单独处理每个步骤。执行到末尾时,它会返回对话的其余部分。

以下规则说明步骤在运行时的行为。

除 Ask 外的每个步骤都会立即执行,控制权会在同一轮中转到下一步。Tell 或 Say 步骤会发送消息后继续执行。除 Ask 外,没有其他步骤会暂停对话,也没有步骤会结束当前轮次。如果需要用户输入,请使用 Ask 步骤。如果应结束对话,请使用 end_call 系统工具。

最后一步完成时,procedure 结束,智能体会返回对话的其余部分,当前轮次仍保持开放,因此它可能会继续说话。子 procedure 完成时,控制权会返回调用它的 procedure 中的下一步。

Tool 步骤无法根据状态码或响应正文进行分支。工具成功时,procedure 会继续执行。工具失败且该步骤没有失败处理程序时,对话会结束。如果有失败处理程序,则执行处理程序中的步骤,然后 procedure 继续执行下一步。处理程序中的 Retry 会重新运行工具;如果工具再次失败,也会重新运行处理程序中的所有步骤,直到工具成功或尝试次数用尽。如果次数用尽,对话会结束。

条件按顺序评估,第一个匹配项会执行。没有匹配项时执行 Else 分支。如果没有 Else 且没有任何匹配项,procedure 会继续执行 If 后的步骤。未处理的情况不是错误。

If 分支内确定的任何内容都不会被后续步骤记住。如果分支中获知的内容之后仍需要,请通过工具调用或动态变量显式持久化。

只有 Tool 步骤可以调用工具。无需告知 Ask、Tell 或 Say 步骤不要调用工具;它们无法调用。

管理结构化 procedure

在控制台中打开智能体,然后选择 Procedures。 使用 + 创建结构化 procedure。添加触发条件,为每个步骤选择类型,然后发布智能体更改。

编辑时,控制台会验证结构化 procedure。如果 procedure 违反验证规则,Publish 按钮会显示错误状态,Procedures 标签页会显示错误徽标,并且在修复 procedure 前无法启动预览。选择错误指示器可查看受影响的 procedure 和步骤。

结构化 procedure 编辑器,其中 Publish 按钮处于错误状态,并显示 1 Error 徽标

验证详情对话框,列出失败的 procedure 及需要填写消息的步骤

最佳实践

每种步骤类型都会执行自身行为,因此通常无需额外说明。写明每一步的目的,其余交给步骤类型处理。以下指南涵盖了值得注意的场景。

选择步骤类型

Ask 步骤会等待一个回答。如果在一条指令中包含多个问题, 智能体往往会跳过其中一些或将它们合并。每条信息使用一个 Ask 步骤。

Tell 步骤会传达消息后继续执行,不会等待。以问题形式表述的 Tell 不会得到回答。如果步骤需要用户回复,应使用 Ask。

Ask 步骤在获得合适回答后继续。如果仅凭问题无法明确什么算作回答, 请在指令中说明,例如 询问订单 ID;有效 ID 为 8 位数字。

当智能体应自行组织消息时使用 Tell 步骤;措辞必须逐字一致或需要翻译时使用 Say 步骤。两者都只发送一条消息,因此无需 指示步骤发送单条消息。

Ask、Tell 和 Say 步骤无法调用工具。向其中写入 不要调用任何工具 不会改变行为,只会增加指令噪声。

构建流程

不能连续放置两个 If 步骤。为了满足此规则而在中间插入无关的 Tell 或 Say, 会让智能体说出不该说的话。应将第二个判断作为额外的 Else if 分支 合并到第一个 If 中,或移入子流程。

If 步骤不能嵌套。当一个判断依赖于另一个判断时,将内部判断放入 单独的结构化流程,并从需要它的分支通过 Sub-procedure 步骤调用。

没有 Else 的 If 步骤在没有任何条件匹配时会继续执行下一步。如果未匹配的 情况应采用不同处理方式,请为其添加 Else 分支。

在 If 分支中做出的决定之后不会被记住。如果后续步骤依赖于 在某个分支中获取的信息,请在该分支内通过工具调用或动态变量记录它。

表达式条件会检测动态变量。使用这些变量的 If 步骤应紧跟在设置这些变量的 Tool 步骤之后。要根据用户所说的内容分支,请使用文本条件。

当多个结构化流程共享相同序列(如转接人工客服)时, 请将其放入一个触发器为空的结构化流程,并从各流程中调用它。复制的序列 会随时间逐渐产生差异。

使用工具

Tool 步骤始终会调用其工具。写入指令的条件(例如 如果工单已添加标签则跳过此步骤) 无法阻止调用。如果并非总需要执行调用,请在 Tool 步骤前添加 包含该条件的 If 步骤。

当参数必须始终采用特定值时,请在 schema_overrides 中使用 constant 覆盖项进行设置。 像 始终将 status 设为 pending 这样的指令只是要求模型 遵循;覆盖项会被强制执行,无法跳过。

如果没有 on_failure,任何工具失败都会结束对话。添加一个处理程序,告知用户 发生了什么,并进行重试、升级处理或继续执行。

Tool 步骤只运行工具;智能体无法在执行期间说话或做出决定。要与 用户交谈,或根据工具返回结果分支,请在 Tool 步骤前后使用单独的步骤。

编写指令

流程控制下一步执行的内容,智能体执行当前步骤时并不知道后续步骤。 让步骤顺序负责控制流程。

写在步骤指令中的句子(如 这是本轮的最后一条消息)会要求 智能体强制执行平台并不支持的边界。使用 Ask 步骤等待用户,或使用 end_call 系统工具结束对话。

语气、格式、结束语和拒绝策略应放在系统 提示词中。步骤指令应只说明 该步骤特有的内容。

组合流程

组合流程的一般指南同样适用于结构化流程;请参阅自由格式流程页面中的组合流程。

有一种模式专用于混合类型:自由格式流程可以引用结构化流程。将开放式处理保留在自由格式流程中,并将每次都必须以相同方式执行的部分(如身份验证或升级处理)交给结构化流程。

限制

  • If 步骤不能嵌套,也不能连续放置两个 If 步骤。
  • 唯一支持的系统工具是 end_call。
  • 结构化流程不能引用知识库文档。
  • 流程完成时无法结束当前轮次;智能体会保持当前轮次开启,并可能继续说话。
  • 无法从控制台中的特定 workflow 节点启动结构化流程。
  • 启动流程会增加延迟:智能体会通过工具调用进入流程,然后依次执行生成的 workflow。

模型提供商支持

进入子流程和完成流程时,结构化流程会强制进行内部工具调用。主要的 OpenAI、Anthropic、Gemini 和 Grok 模型系列均支持强制选择工具。其他模型或自定义提供商可能无法保证此功能,这会降低子流程切换或流程完成的可靠性。使用其他模型提供商时,请确认其支持强制选择工具。

请参阅流程,了解适用于所有流程的限制,包括内容大小上限以及结构化流程与自由格式流程的差异。