OpenDots 被叫作「AI 同事」时,最容易踩的坑是把接入点放在聊天窗口上:前端拿到一段回复就完事。判断标准其实很朴素——如果用户说完话之后,系统还需要去查数据、改状态、发通知,这部分职责就不该由对话框承担,而应落到任务编排层。反过来,只要产出是一段给人看的文字、后续动作由人自己完成,普通对话接口就够,硬塞一套编排只会扩大调试面。
把 OpenDots 定位成任务编排层,意味着由它持有任务步骤状态、调用工具、回收结果并写回业务侧,聊天窗口只是展示面。适合的场景是任务需要多步、多个工具、步骤随输入变化,并且失败可观测、可重试。判断动作是先跑一遍自检问题,确认需求不停留在文本生成;验证方式是看调用日志里有没有工具调用与步骤状态流转,而不是只看最终回复像不像人话。风险边界:状态被前端和编排层同时改写会出现重复执行或数据不一致,接入前先定清楚谁写、谁只读。
从一次完整请求看它和纯问答助手的差别
拿同一句用户输入做对照会清楚很多。假设用户说:「把今天还没处理的退款工单挑出来,按金额从高到低排,超过 48 小时的标一下。」
纯问答助手的处理路径通常是:一次模型调用,把这句话连同上下文交给模型,模型输出一段文字或一个表格,请求结束。产出是文本,用户需要自己再去后台系统里找工单、自己打标记。整个过程没有外部副作用,重试一次也就是重生成一段话。
任务编排型的处理路径不一样:先解析目标,识别出这需要查询工单数据,生成带参数的调用请求;执行调用拿到结构化结果;对结果做排序和筛选;再决定是否需要写回标记;最后才把「做了什么、结果如何」组织成给用户看的表述。产出不只是文本,还包括可核验的副作用——调用记录、被改动的状态字段。
| 环节 | 纯问答助手 | 任务编排型同事 |
|---|---|---|
| 模型往返次数 | 通常一次 | 按步骤多次,取决于任务分支 |
| 是否触碰业务数据 | 不触碰,除非另写代码 | 通过工具读写,读和写可分开授权 |
| 最终产出 | 一段可读文本 | 文本 + 工具调用记录 + 状态变更 |
| 失败后的表现 | 答得不好,重问即可 | 可能停在某一步,需要看步骤状态定位 |
验证方式很直接:在调用日志里看有没有工具调用条目、有没有多轮步骤状态流转。如果日志里只有模型请求,那不管回复写得多像「同事」,实际承担的仍是问答角色。
把任务拆解、工具调用、结果回收三层分开看
把编排层拆成三层,出问题时定位会快很多。每层的输入输出和依赖都不一样,故障表现也不一样。
任务拆解层
输入是用户原话、会话上下文和一份可用工具清单;输出是结构化步骤,例如意图加参数,或者一个步骤数组。这一层依赖的是意图与工具描述的质量——工具描述写得含糊,拆解结果就会挑错工具或漏掉必填参数。常见故障表现:本该查数据的问题被当成纯问答回答;生成的调用参数缺字段,到下一层才报错。
工具调用层
输入是带参数的调用请求,输出是工具原始返回或错误对象。这一层依赖工具适配器、鉴权凭据、超时与重试策略。常见故障表现:超时、鉴权失败、返回空结果集。需要特别注意「返回空」和「调用失败」要区分开,否则上层会把空结果当成成功继续跑完流程。
结果回收层
输入是工具原始返回,输出是两样东西:给用户看的表述,以及需要写回的状态变更。这一层依赖结果校验、字段映射和幂等控制。常见故障表现:同一步骤被重复执行导致重复标记;页面显示已完成但业务数据没变;多次重试叠加出多条记录。
下面是一份通用的步骤结构示意,用来对照日志排查,字段名接入时按实际接口替换:
{
"task_id": "t-0001",
"intent": "查询并标记超时退款工单",
"steps": [
{"id": 1, "tool": "ticket.query", "args": {"type": "refund", "status": "open"}},
{"id": 2, "tool": "ticket.tag", "args": {"rule": "age_over_48h", "tag": "overdue"}}
],
"state": "planned"
}
排查时先看 state 停在哪一步,再看该步的输入参数是否完整,最后核对工具返回与写回结果是否一致。三层里最容易看着像「模型变笨了」的其实是工具调用层,因为错误信息往往只在日志里出现一次。
判断自己的场景落在哪一层
引入整套编排能力的成本主要在调试和状态管理上,所以值得先花几分钟自检。下面几个问题按顺序问自己,结论倾向写在问题后面。
- 用户说完之后,系统需要改点什么吗?要写库、发消息、建单、改状态,说明有执行诉求;如果只是要一段可读文本、后续由人复制执行,倾向不引入编排层。
- 这个任务是否稳定需要两个以上的工具或数据源?只有一个固定查询,直接在业务代码里调接口更可控;跨系统、需要组合结果时,编排层的价值才显现。
- 步骤顺序是否随输入变化?如果每次都是同一套固定流程,用作业调度或业务代码写死顺序更易测;如果不同用户走不同分支,才需要动态拆解。
- 失败时能不能接受人工接手?可以人工兜底的场景,先接只读工具、单步调用就够;要求自动重试和状态回溯的场景,才需要完整的步骤状态机。
自检的目的不是证明该用,而是避免为了一个问答需求把整套编排能力拉进来。四个问题里前两个都答「否」,通常就可以先停在直接调模型接口这一步。
和现有前端组件共存时的职责切分
同一份会话状态被两处同时改写,是接入后最常见也最难查的问题。切分原则只有一条:状态只有一个持有者,其余角色只读或只触发。
| 状态类型 | 持有方 | 谁可读 | 谁可写 |
|---|---|---|---|
| 任务步骤状态(进行中/已完成/失败) | 编排层 | 前端组件只渲染 | 编排层 |
| 会话消息列表 | 编排层 | 前端组件只渲染 | 编排层追加 |
| 业务数据(工单、订单等) | 业务后端 | 编排层通过工具读 | 仅工具调用写 |
| 输入框内容 | 前端组件本地 | 前端 | 前端 |
触发点也要收敛到一处:要么由用户提交入口触发,要么由编排层回调触发,不要让前端在提交失败时自行补发一次。写业务数据只走工具调用,前端不做「看着像成功」的乐观写入;如果为了体验必须即时反馈,就把它标成待确认状态,等编排层回收结果后再改。
请求里带上幂等键是低成本的保护措施,下面是一个可替换的接入骨架:
POST /orchestrate
{
"request_id": "调用方生成的唯一值",
"conversation_id": "会话标识",
"turn": 3,
"input": "把今天没处理的退款工单挑出来"
}
验证方式:让前端重复提交同一个 request_id,观察服务端是否只产生一条步骤记录;再查看页面刷新后,任务状态是否由编排层返回的数据还原,而不是前端本地记忆。
什么时候不该用它
有几类场景引入编排层之后收益不明显,反而增加排查难度,可以先排除:
- 只需要一段文字产出,比如摘要、改写、翻译、客服话术草拟。这类需求用对话接口加人工确认即可,替代方向是把提示词和输出格式固定下来。
- 流程固定、每次顺序都一样,比如每晚跑一次的对账或导出。替代方向是用作业调度或业务代码写死调用顺序,出错时堆栈清楚。
- 对响应时间有硬约束的同步路径,例如用户点击后必须立刻给出结果。编排要经历多轮步骤,耗时不确定,替代方向是先返回「已受理」,把执行放到异步任务里。
- 合规上不允许模型自主选择工具或写入数据的场景。替代方向是只开放只读工具,写操作仍由人工在原有后台完成。
- 团队还没有把步骤日志和调用记录收集起来的场景。编排层出了问题必须能看出停在哪一步,缺少可观测性时,先做日志再谈接入。
判断顺序建议是:先确认需求里有执行动作,再看是否需要多步与多工具,最后才评估要不要用编排层承载。如果只是把 OpenDots 放在聊天窗口里当输出端,那和直接调对话接口的差别,通常并没有描述里说得那么大。