Agent 编排升级:从“能跑”到“能持续演化”

Agent Orchestration Evolution Cover

这篇文章想复盘的,不是某一个 bug 的修复过程。

我更想讨论一个更底层的问题:

当一个企业智能助手同时要处理聊天、知识问答、数据分析和混合查询时,Agent 编排层到底应该承担什么责任?

在这次升级之前,OpsMind 的很多链路其实已经“能跑”了。

它能回答问题。

它能调用工具。

它能画图。

它也能勉强处理一些混合流程。

但我后来越来越确定:

能跑和能持续演化之间,隔着一整层架构能力。

为什么这次升级不是“修几个 bug”那么简单

最容易让人产生错觉的一件事,就是系统看起来已经能工作。

可当我重新审视这套 Agent 编排时,看到的不是几个孤立问题,而是一组高度相关的系统性信号:

  • Router 已经识别出的能力,在 Workflow 层被静默吞掉
  • 工具执行阶段产出的状态,没有进入跨轮可复用记忆
  • 某些失败没有显式暴露,只伪装成“看似成功但内容空洞”
  • 某些请求链路最危险的问题不是报错,而是无期限挂起

如果拆开看,每个都像普通缺陷。

但合起来看,它们指向的是同一个事实:

Agent 编排不是把模型和工具连起来就结束了,它本质上是在维护一个有状态、有预算、有退路的执行系统。

我重新定义了 Agent 编排层的职责

在更早的实现里,编排层更像“调用顺序的胶水层”:

  • Router 决定 primary intent
  • Workflow 按 intent 选分支
  • Agent 在需要时做 ReAct
  • Tool 返回结果,模型再做总结

这样的结构当然能跑。

但它有一个很大的隐患:

编排层只知道“下一步调用谁”,却没有真正管理“当前系统处于什么状态”。

这次升级之后,我更倾向于把 Agent 编排层看成六类责任的组合:

  1. 路由解释责任:Router 给出的不是命令,而是建议
  2. 状态承接责任:一轮分析里产出的关键状态要能跨轮复用
  3. 执行预算责任:调用多久、循环多久、整条链路多久,都要显式管理
  4. 失败显性化责任:真失败就应该返回失败,而不是伪装成空结果
  5. 路径选择责任:混合请求不该被硬编码顺序静默压平
  6. 语义保真责任:知识库、上下文和计划都要以语义友好的方式保存

这六个职责,基本也对应了这次升级里最关键的六个问题。

六个问题,其实对应六个编排原则

1. 多轮数据分析无记忆

原来的系统里,如果用户接着上一轮说:

  • “换成折线图”
  • “沿用上次分析”
  • “加上环比”

Agent 其实并不知道“上次分析”具体是什么。

因为会话历史里虽然保留了自然语言回答,但真正关键的分析状态并没有被当作资产保存下来,比如:

  • chart_plan
  • table_plan
  • 列映射
  • 数据形状
  • 领域与分析逻辑

这件事暴露了一个很核心的原则:

在 Agent 系统里,可复用的不是上一轮说过的话,而是上一轮形成的结构化执行状态。

所以这次我做的不是“给 Agent 更多聊天历史”。

而是把最近一次分析配置从执行结果里透传出来,保存进会话消息 metadata,再在下一轮进入 Agent 时注入成一条 system memory。

这样系统获得的就不是“对话连续”,而是“任务连续”。

2. agent_decides 被静默吞掉

Router 其实已经能识别出某些混合请求不是固定顺序的。

比如:

  • 先看数据再核规则
  • 先查规则再看数据
  • 顺序不明,交给 Agent 自主判断

问题在于,如果 Workflow 只支持固定分支,那么 agent_decides 这种信号就会在中途被压平成某个硬编码路径。

这说明一件很典型的事:

如果 Router 给出的能力不能被下游消费,Router 越聪明,系统的“假智能”就越重。

所以这次我把原则收得很明确:

  • 显式顺序,交给显式编排
  • 不确定顺序,交给 Agent ReAct

这不是多写一个 if 那么简单。

它意味着编排系统必须承认一件事:

某些问题的最优执行顺序,只有在运行期才会被确定。

3. Router confidence 原来几乎没意义

如果一个带 confidence 的路由结果,不管是 0.95 还是 0.50 都走一样的路径,那这个字段本质上就只是日志装饰。

这次我没有把它做成一套复杂的交互式追问。

我先做了最保守也最实用的一步:

当模型给出低置信 CHAT,但当前会话里已经有文件,query 又明显带数据词时,就自动把它从 CHAT 降级成 DATA

为什么先做这个?

因为真实系统里最危险的低置信误判,不是“错了一点点”,而是:

它把请求送进了一个能力明显不足的分支。

所以我现在越来越认同,confidence 的作用不一定是让系统更复杂。

它首先应该让系统更保守。

4. execute_analysis 空 plan 静默成功

这是一个非常典型的工程问题:

  • plan_analysis 生成了空图表计划
  • execute_analysis 没报错
  • 后面的 LLM 依然吐出一段“分析完成”的话
  • 用户最终看到的是“像成功一样的失败”

比直接报错更糟糕的,往往就是这种假成功。

它会让系统表面稳定,实际却在持续输出低可信结果。

所以这里对应的原则很清楚:

中间态如果已经失去执行前提,就必须在中间层显式失败,不能把脏状态继续往后传。

编排层不仅要会“衔接成功路径”。

它还要会“阻断无意义路径”。

5. 所有 LLM 调用都没有超时

这类问题一开始很容易被忽略。

因为大多数时候模型都能在可接受时间内返回。

但只要链路复杂一点:

  • Router 一次
  • Agent 多轮 ReAct
  • KB 改写一次
  • RAG 回答一次
  • Data engine 再调用若干次

就会发现“单次调用没问题”不等于“整条链路安全”。

这次我把超时拆成了三层:

  1. 单次模型调用超时
  2. Agent 总预算
  3. API 总请求超时

这样做的关键,不是为了显得严谨,而是为了让系统始终知道自己还剩多少时间可以花。

一个没有时间预算概念的 Agent,本质上不是智能体,而是概率性阻塞源。

6. KB 纯字符分块会截断语义

很多 RAG 系统一开始都会先用字符滑窗。

实现简单,性能也直接。

但对制度、流程、规则文档来说,这种方式有个很明显的问题:

  • 一条审批规则可能被从中间切断
  • 一个条件句可能和结果句分离
  • 一个列表项可能只剩半句

这会带来一种非常难排查的质量退化:

检索看起来命中了,但命中的其实是语义残片。

所以这次我把分块策略换成了:

  • 段落优先
  • 超长段落按句子聚合
  • 仍然过长才退回字符滑窗

背后的原则很朴素:

RAG 质量不只取决于 embedding 和 rerank,还取决于你有没有先把文档切成“人类还能理解的最小单元”。

这次升级后,我对 Agent 编排有了三个更稳定的判断

判断一:状态比过程更重要

很多系统会把大量精力花在“让 Agent 更会推理”上。

但更基础的问题其实是:

系统有没有把高价值状态留下来。

如果没有,每轮都在重新规划、重新读环境、重新猜上一次做了什么。

这不是智能。

这是昂贵的失忆。

判断二:编排系统要允许“不确定”

很多工程实现喜欢把所有路径在设计期写死:

  • 这种走 A
  • 那种走 B
  • 混合就是 A 再 B

但真实请求经常不是这样。

有些问题最合理的顺序,本来就应该由 Agent 在看到局部结果后再决定。

所以一个成熟的编排层,至少应该允许三种模式并存:

  • 静态路径
  • 半静态路径
  • 运行时决策路径

agent_decides 的价值就在这里。

它不是放弃架构设计。

它是承认:某些问题的最优流程,只能在执行期被确定。

判断三:失败设计是编排设计的一部分

如果一个系统只设计 happy path,它就不是真的编排系统。

它只是一串乐观调用。

真正的编排层,必须回答这些问题:

  • 哪些失败应该立即中断
  • 哪些失败可以降级继续
  • 哪些失败必须向用户暴露
  • 哪些失败应该转成 Agent 的反思上下文

从这个角度看:

  • 空 plan 早失败
  • KB 查询失败但保留数据结果
  • ReAct 超时后阶段性收尾
  • API 超时后显式 SSE error

这些都不是边角料。

它们构成的是系统的可恢复性。

我现在更认同的一种 Agent 编排观

如果要用一句话总结这次升级后的思考,我会说:

Agent 编排层不是“提示词 + 工具调用”的包装层,而是一个负责管理状态、预算、置信度和语义边界的执行控制层。

它至少应该做到四件事:

  1. 把有价值的中间结果变成长期可复用状态
  2. 把不确定的路由结果变成显式策略选择
  3. 把模糊失败变成可诊断、可恢复的失败
  4. 把所有外部依赖纳入统一的时间预算体系

只有做到这些,Agent 才不是“偶尔表现得像个智能体”。

它才开始具备稳定的工程行为。

这次升级后,系统真正变强的地方是什么

如果只从 diff 看,这次改动里有很多具体点:

  • chart_plan / table_plan 持久化
  • agent_decides 回到 Agent
  • 低置信路由降级
  • 空 plan early return
  • LLM timeout + budget
  • 段落级 KB chunking

但从架构层面看,真正变强的是另一件事:

OpsMind 的 Agent 编排开始从“流程驱动”转向“状态驱动”。

这意味着之后系统更容易承接的会是:

  • 多轮分析迭代
  • 混合查询的动态策略选择
  • 会话级任务恢复
  • 更细粒度的失败恢复
  • 更稳的长期记忆与知识边界

这也是我觉得这次升级最值得记录的地方。

不是因为它修了六个问题。

而是因为它让系统往“可持续演化的 Agent 架构”迈进了一步。

一个仍然需要记住的后续动作

这次 KB 分块策略虽然已经改成段落优先,但旧文档数据不会自动重切分。

所以如果要让知识库检索质量真正受益,后续还需要:

  • 清理旧的向量数据
  • 对文档重新 ingest
  • 观察召回质量和答案稳定性变化

这类动作很容易在“代码改完以后”被忘掉。

但它其实是架构升级真正闭环的一部分。

结语

如果说之前的系统是在证明“Agent 编排能跑起来”。

那这次升级更像是在回答另一个问题:

当系统开始变复杂时,我们怎样让 Agent 编排继续保持可信、可控、可演化。

这比把一次回答做对更难。

但它决定了一个 Agent 系统到底只是 demo,还是能变成长期产品。