工作流
工作流是你的代理应逐步执行的可视化地图——谁先开口、要问什么、何时分支、何时转移通话,以及何时将通话交给另一个代理。你在画布上通过放置节点并连接它们来构建它。
当单一提示词不够用时,就使用工作流——当通话有明确的阶段("先资格审核,再收集详情,然后要么预约要么转接")、当你需要根据来电者的回答进行确定性路由(而不是"AI 通常会做对的事"),或者当一个代理应把一部分工作交给专家时。
提示词是一条长指令。代理读一次并在整通电话中使用它。 工作流是许多条小指令,每个节点一条,节点之间有明确的转换。图驱动通话——每个节点上的代理只负责它自己那一部分。
如果你的代理用单一提示词已经工作良好,你就不需要工作流。当你不断往提示词里添加"如果 X 就 Y"的规则、而它们开始变得不可靠时,就该考虑工作流了。
编辑器
工作流编辑器位于代理页面上专门的一个标签页中。默认情况下你会看到一张画布,上面有一个 Start 节点和一个 End 节点。你从工具栏添加节点,把它们拖到画布上,并用边把它们连起来。
右侧面板会随你选择的内容切换:
- 点击一个节点——它的设置面板打开(提示词、字段、检索变量等)。
- 点击一条边——它的触发设置打开(AI 决定 / 条件 / 始终)。
- Variables 面板(工具栏按钮)列出工作流声明或使用的每个变量,并可一键跳转到设置或读取每个变量的节点。
直接从编辑器运行浏览器内测试通话——一只发光的萤火虫追踪实时位置,让你能准确看到每一刻哪个节点处于活动状态。
撤销/重做 (v2.8) —— 编辑器保留最多 20 步的历史记录,因此你可以回退一个错误操作(或重做它)而无需重建。删除节点时会先要求确认 (v2.8),这样你就不会因误点而丢失一步。
你不必从空白画布开始。"build as workflow" 入口为代理配置一个工作流并直接把你带进构建器——一个动作即创建工作流并打开编辑器,随时可以添加节点。
工作流有三种状态:
- Draft —— 可编辑,尚未上线。使用测试通话来试用它。
- Active —— 对真实通话上线。代理从下一通电话起使用此工作流。
- Template —— 可复用的蓝图,从不直接关联到某个代理。你可以将它实例化到多个代理上。
节点
每个节点都是对话中的一步。不同的节点类型做不同的事——有的说话,有的倾听,有的路由,有的运行工具。
代理
代理节点是工作流阶段的核心。当图到达一个代理节点时,该代理接管话语权并驱动对话,直到某件事触发转换。
你就在画布上配置代理节点的人设和任务(点击节点时会打开 Prompt 标签页)——这是针对对话那一部分的提示词,而不是整通电话。你还可以按节点附加知识库和工具。
Greeting —— 开场白。如果这是入口节点(Start 之后的第一站),这就是来电者最先听到的内容。
当一张图掌管通话时,活动节点的提示词会叠加在代理基础提示词(Model 标签页中的那个)之上。图拥有每个阶段的个性和任务,而代理的基础指令作为基底保持不变。
子代理
子代理是一个内联运行的助手——它接管对话去完成一个专注的任务,然后把结果交回给调用它的一方。
当某一部分工作可复用或感觉独立时,子代理很好用:"核实来电者身份"、"收集送货地址"、"用 SPIN 方法为线索做资格审核"。父代理调用子代理,子代理做它的事并报告一个结果,父代理从它离开的地方继续。
子代理节点与代理节点有相同的标签页(Prompt、Knowledge、Tools、Actions)——编辑器会自动为你创建一个隐藏的支撑代理。你不会在主代理列表中看到这个隐藏代理,它也不计入你套餐的代理上限。
Return variable —— 存储子代理结果的槽位(例如 identity_verified、lead_score)。
谁先开口
默认情况下,子代理在接管时会先开口说话——它在接管的那一刻就问候或问出第一个问题。两个开关可以改变这一点:
- Wait for the caller (don't speak first) —— 子代理进入时保持安静,只有在来电者说了些什么之后才回应。当子代理是通话中的第一站、你希望来电者先开口(一句真正的 "Hello?")而不是代理先开口时,开启它——或者在通话中途,当上一步已经说完了所有内容、子代理应该只倾听答案时。
- Silent step (don't speak) —— 子代理完全不说话。它读取目前为止的对话,判定一个结果,然后直接交回控制权。把它当作一个隐形路由器:"读一下来电者问了什么,判断这属于哪个部门"——不说一个字,只做一个供图分支的决定。
允许的结果值
子代理通常以它判断为合适的任意值结束。如果你打算用条件边在该结果上分支,请把它固定到一个允许值的封闭列表上:
- 在 Allowed result values 下列出每个允许值(例如
sales、support、billing)。 - 子代理必须以其中恰好一个结束——它不能编造新的。
- 为每个值配对一条条件边(
result is "sales"→ …),以实现干净、可预测的路由。
这是让子代理决策并路由的可靠方式:结果保证匹配你的某个分支。
子代理完成时交回单个结果。用该结果上的条件边或始终边来接线下一步该发生什么——子代理的出口不使用"AI 决定"边。这就是为什么 Allowed result values + 条件边是路由型子代理的推荐模式。
消息
消息节点说出一句固定的话,没有 AI 轮次,没有即兴发挥。用它来做过渡("稍等,正在为您转接…")、法律要求的免责声明,或措辞很重要的固定确认。
Text —— 要说的内容。支持 {{variable}} 标记来注入收集到的值,例如 "Thanks {{first_name}}, I've booked you for {{appointment_time}}."
消息节点会消耗该轮次——代理不会在其上再添加任何内容。
收集输入
收集输入节点问一个问题并把来电者的回答捕获到一个变量中。
Prompt —— 要问的问题。支持 {{tokens}}。
Variable —— 存储答案的槽位名称。
Max silence (v2.8) —— 针对每个问题的上限,规定在继续前等待来电者回答的最长时间。为每个收集输入节点单独设置,这样一个简单的是/否问题可以比要求来电者读出长账号的问题更早超时。
当你在继续之前需要一个特定值时就使用它——电子邮件、账号、人数、问题类别。来电者的下一个轮次即成为该值;你可以紧接着用一条条件边在它上面分支。
更新状态
更新状态节点在不说话也不提问的情况下把一个或多个值写入工作流的变量。用它来预设默认值、标记进度标志,或暂存从先前步骤计算出的值。
Variable / Value —— 一个键值对。 More variables (optional) —— 额外的键值对。
键和值都支持 {{tokens}},因此你可以从现有的值组合出新值。
条件
条件节点是一个只做路由的节点——它不说话、不提问,只是把通话沿着任何匹配的出边发送出去。
条件本身位于每条出边上,而不在节点上——参见下面的条件边。
当你想要在图中有一个清晰的"扇出"点时使用条件节点:许多依赖于某一个变量值的分支。相比把条件放在代理节点的边上,一个专门的条件节点让路由逻辑在视觉上一目了然。
工具
工具节点把代理已连接的某个工具作为图中的一步来运行——不给 AI 任何决定是否调用它的余地。当你想要一个事实而非一场对话时使用它:"查一下这个客户"、"查库存"、"GET 当前天气"。
你可以:
- 选择代理的某个现有工具(日历查询、知识库搜索、某个集成的特定动作),或者
- 就在节点上配置一个内联 HTTP 调用(URL、方法、请求头、参数、请求体)。
工具的响应会对所有下游节点可用。如果响应是一个 JSON 对象,则每个顶层字段会成为它自己的变量——所以像 {"is_known": true, "tier": "gold"} 这样的响应会为你写入 is_known 和 tier 两个变量以供分支。
工具节点上的每个文本字段——URL、请求头的值、请求体——都支持 {{tokens}}。无需编写任何代码即可把变量拉入 URL 路径、请求头或请求体。
如果工具调用失败(HTTP 错误、超时、网络问题),节点会记录该失败,图会继续——但没有数据。规划你的下游条件以处理"无值"的情况。
知识(RAG)
知识(RAG)节点是专注于知识库检索的工具变体。选择代理的某个知识库,写一个搜索查询(如果需要可带 {{tokens}}),节点就会把查询作为图中的一步来运行。
当你想让工作流在某个代理节点开口之前查找一些东西时使用它——例如取出客户最近的订单,这样下一个代理节点无需询问就能提到它。检索到的内容落入一个变量,你可以把它拉入提示词和消息中。
集成
集成节点是工具的一个专注变体,用于已连接的外部服务——Google Calendar、Outlook、HubSpot。你选择提供方和具体动作(例如 check_availability、create_event),设置参数,节点便确定性地调用该集成。
如果该集成未在此代理上连接,节点会直接通过而不做任何事——你的图继续。请检查下游条件以应对"未运行"的情况。
电话转接
电话转接节点把通话温转接到一个外部电话号码。转接成功后,通话已离开你的工作流——你的代理不再在线上。
Number —— 采用国际格式、带前导 + 的目的地(例如 +15551234567)。支持 {{tokens}},因此目的地可以来自更早的一步(例如"要为您接到哪个办公室?"的答案)。
如果转接未接通(号码无效、无人接听、未设置电话连接),图会回滚,当前代理留在线上。对话正常继续——代理可以道歉并尝试另一条路径。
结束通话
结束通话节点收尾——代理说出配置好的告别语,然后挂断。把它用作"一切完成"分支的干净终点,或用作来自难以处理的条件的"我们帮不上忙,抱歉"分支。
除了接一条边进去,无需任何配置。
复用工作流(子工作流)
子工作流节点保留用于把另一个工作流作为子图嵌入。这个节点在编辑器中,但尚未完全接线——图会直接通过它而不做任何事。不要在生产通话中依赖它。等我们发布可复用子图后会将其启用。
边(连接)
你用一条边把一个节点连到下一个。边的触发决定转换何时触发。
有三种触发:
| 触发 | 谁决定 | 何时触发 |
|---|---|---|
| AI decides | AI | 当对话使其成为自然的下一步时。AI 获得一个可调用的"转换工具"。 |
| Condition | 运行时 | 当某个变量匹配你写的规则时。在 AI 回复之前评估。 |
| Always | 运行时 | 无条件——源节点一完成就触发。无需来电者输入,不涉及 AI。 |
AI decides(意图驱动)
当只有 AI 才能判断何时切换时使用 AI decides——"如果来电者询问价格,转接给销售代理"、"如果听起来像投诉,运行道歉子代理"。
你在边上写一段简短的意图描述(例如 "Caller asks about pricing or wants to upgrade")。AI 会把它看作一个以该意图为标签的工具,并根据对话决定是否调用它。如果存在多条 AI-decides 边,AI 每轮最多挑选一条。
Condition(确定性分支)
当你希望由图——而不是 AI——根据一个已知事实来决定时使用 Condition:"如果 lead_score is more than 7,送给成交专员;否则,礼貌地结束通话"。
条件在 AI 回复之前评估,因此它们在那一轮上总是胜过 AI。AI 无法覆盖一个匹配的条件。
你可以用这些运算符构建一个条件:
| 运算符 | 含义 | 示例 |
|---|---|---|
| is | 精确匹配(文本不区分大小写) | intent is "billing" |
| is not | 不匹配 | status is not "active" |
| contains | 子串匹配(不区分大小写) | feedback contains "broken" |
| is more than | 数值 > | lead_score is more than 7 |
| is less than | 数值 < | wait_minutes is less than 5 |
你还可以用 All(每个子句都必须匹配——AND)或 Any(至少一个必须匹配——OR)来组合子句,并可嵌套。因此像 "来电者是已知的且其等级为 gold 或 premium" 这样的规则变成:
All:
- is_known is true
- Any:
- tier is gold
- tier is premium
如果你引用一个尚未设置的变量(例如 AI 从未收集它),基于它的条件总是失败——它们不会报错,只是不匹配。所以如果 loyalty_tier 从未被设置,loyalty_tier is "gold" 返回 false。设计你的分支来处理这种情况——通常用一个 else 分支(见下文)。
Else 分支——当无条件匹配时
如果有若干条条件边离开一个节点而它们都不匹配,通话会落到优先级最低的非条件边(Always,或一条 AI-decides 边)。如果根本没有兜底,AI 重新获得控制权并可以自由继续。
一个常见模式:为熟知的路径设置若干条件,外加一条通往兜底的 Always 边(Message 一些内容,然后 End call,或转接)。
Always(自动前进)
当下一步是无条件时——无决策、无来电者输入——使用 Always。两种自然用法:
- 串联步骤:Message → Update state → Message → Tool → Condition。每一步完成后 Always 边立即前进。
- 在代理说完后自动前进:一个带 Always 出边的代理节点意味着*"AI 一停止说话就前进"*——无需来电者回应。适合独白式流程,比如一个先讲解、再过渡到下一节的演示代理。
条件和始终(确定性的那些)总是胜过 AI decides。 如果某个条件匹配,AI 的转换工具在那一轮会被忽略。这正是让你能写出 AI 无法绕过的规则的原因。
分支顺序
当若干条边离开同一个节点时,编辑器让你通过上下拖动行来设置优先级。优先级较低的分支会先被检查。这只有在两个分支可能在同一轮都触发时才重要——通常是两条规则重叠的条件边。第一个匹配的分支胜出。
携带上下文——下一个代理记住多少
每条边都有一个 Carry context 设置,控制下一个节点看到多少对话转录:
- Full transcript(默认)—— 下一个代理看到目前为止说过的所有内容。
- Summary only —— 平台总结先前的对话,并把一段简短摘要交给下一个代理。
- Nothing —— 下一个代理从头开始,没有先前的转录。
当交接给一个不需要每个字的专家代理时使用 Summary only——它节省 token 并让新代理保持专注。对于新代理人设本应独立的分支使用 Nothing(例如通话结束时的满意度调查)。
变量
变量是工作流的记忆。工作流中的大多数事情不是在设置一个变量,就是在读取一个变量。
声明变量
Variables 面板(工具栏按钮)显示工作流中的每个变量以及设置或读取它的步骤。点击一个变量跳转到某个设置者;这是找出哪里出错的最快方式。
你可以在面板中声明一个带默认值的变量——对于像 escalated=false 这样的标志很有用,它们需要从一开始就存在,这样你的条件永远不会因"缺失"而悄悄失败。
内置通话变量
有几个变量在每通电话最开始就为你填好——你不声明也不设置它们,它们就在那里供读取:
| 变量 | 它保存什么 |
|---|---|
{{call_from}} | 来电者的电话号码 |
{{call_to}} | 被拨打的号码(你的号码) |
{{call_direction}} | inbound 或 outbound |
因为它们从第一刻起就存在,你可以在最开始的节点上就对它们分支——例如,用一个基于 call_from 的条件来称名问候一位已知的 VIP、按国家代码路由,或在任何事情发生之前把未知号码送上一条核实路径。
变量如何被设置
| 来源 | 示例 |
|---|---|
| 默认值(在 Variables 面板中声明) | escalated = false 在通话开始前设置 |
| 收集输入 | 来电者说出的答案被捕获到节点的变量中 |
| 更新状态 | 你直接写入一个值(字面量或由 {{tokens}} 组合而成) |
| 工具/集成结果 | 整个响应存储在节点的名称下;如果响应是 JSON 对象,每个顶层字段也成为它自己的变量 |
| 子代理返回 | 子代理结束时的任何值都存储在子代理节点的返回变量中 |
读取变量——{{tokens}}
在节点上任何你能输入文本的地方——代理提示词、消息文本、收集输入提示、工具 URL / 请求体 / 请求头、电话转接号码、更新状态的值、条件值——你都可以放入 {{variable}},它会在运行时被替换。
点分路径也适用于嵌套数据——例如,如果某个工具响应是 {"customer": {"name": "Anna", "tier": "gold"}},你可以读取 {{customer.name}} 或 {{customer.tier}}。
如果一个标记引用了不存在的变量,字面的 {{name}} 文本会保留原样(不会被清空)。这是有意为之——它让损坏的模板在测试通话中易于发现,而不是悄悄地把它们吞掉。
常见模式
资格审核 → 分支 → 路由
Start
└─ Agent (greeting + qualify)
└─ Gather input (intent)
└─ Condition: intent == "sales" → Agent (sales)
└─ Condition: intent == "support" → Subagent (triage)
└─ Always (catch-all) → Message "Let me connect you" → Phone transfer
查询 → 个性化
Start
└─ Tool (CRM lookup, writes is_known, name, tier)
└─ Condition: is_known == true → Agent (warm greeting with {{name}})
└─ Always (else) → Agent (cold greeting)
可复用助手(子代理旁路)
Agent (main conversation)
└─ AI decides: "Caller mentions an address" → Subagent (address collector)
└─ on finish: result = full_address
└─ Agent continues with {{full_address}}
工作流通话期间被锁定的内容
当一张图掌管通话时,有几个默认值会改变:
- 语言被锁定到工作流第一个代理使用的语言。AI 无法在通话中途切换语言。这防止模型在嘈杂的一轮中漂移到错误的语言。
- 代理的主提示词被替换为活动节点的提示词。这就是"图拥有人设"规则——参见代理下的警告。
- 第一条消息来自入口节点的问候语,而不是代理主"Begin Message"字段。
循环安全——步数预算
每个工作流都有每通电话的最大转换次数(默认 25)。每次图移动到一个新节点,计数器减一。当它到达零时,工作流停止在节点之间跳转,以防止失控循环。
你可以为这种情况挑选一个兜底节点(通常是电话转接或结束通话)——预算用尽后通话即改道到那里。否则 AI 重新获得控制权,图在通话剩余时间内实际上冻结。
你几乎不需要考虑这一点——它是为意外循环的图准备的安全网。如果你在正常使用中触及它,那你很可能在某处有一个环。
测试
编辑器中的 Test Call 按钮用当前工作流草稿打开一个浏览器内通话。通话运行时,一只发光的萤火虫在画布的节点之间移动,让你能准确看到通话进行到哪里。测试通话期间编辑器被锁定,以防止编辑与实时状态失去同步。
使用测试通话来验证:
- 你的条件匹配你期望的值(观察萤火虫跳过一个你以为会触发的分支)。
- 你的标记正确替换(如果某个值尚未设置,留意漏出的
{{name}})。 - 工具失败能优雅降级(你的兜底路径确实触发)。
值得了解的失败模式
| 情况 | 会发生什么 |
|---|---|
| 工具/集成调用失败 | 图在没有数据的情况下继续。你的下游条件应处理缺失的变量。 |
| 电话转接失败(号码无效、无人接听、缺少电话连接) | 图回滚,当前代理留在线上,对话正常继续。 |
| 子代理启动失败 | 旁路被取消,父代理留在线上,并说出一句简短的失败提示。 |
文本中出现 {{missing_variable}} | 在输出中保留原样——在测试通话中易于发现。 |
| 子工作流节点 | 当前直接通过而不做任何事。暂时不要依赖它。 |
| 步数预算耗尽 | 若设置了兜底节点则改道到它,否则 AI 重新获得控制权。 |
何时不使用工作流
工作流很强大,但它们并非对每个代理都是正确答案。在以下情况坚持使用单一提示词:
- 对话是开放式的("回答来电者关于我们产品的问题")。
- 你没有清晰明确的分支逻辑——大多数决策是 AI 能很好处理的判断。
- 你会只写一个代理节点和一个结束通话节点——那不过是一个多此一举的提示词。
在以下情况考虑工作流:
- 你能把通话勾勒成一张有三个或更多不同阶段的流程图。
- 你至少需要一个基于变量的确定性分支(一个条件)。
- 你想在多个代理之间复用对话的一部分(一个子代理)。
- 你已经在往提示词里堆积"如果来电者说 X 就做 Y"的规则,而它们不可靠。