提示词优化会把多智能体管道"优化崩"?这篇论文用一个老思想解决了新问题

先讲一个我自己的真实经历。之前做一个多 Agent 评审流水线的时候,我接了个 TextGrad 风格的提示词优化器,想着让 leader Agent 的分派质量能自动涨一涨。跑了三轮迭代,任务分数确实在涨——然后第四轮,整个管道直接挂了。排查了半天才发现:优化器把 leader 提示词里那段"输出 JSON,action 字段取值 send/stop"的格式说明给改写得更"自然"了,模型开始输出一段优美的散文,Python 控制器解析失败,整个 episode 全废。

说实话当时我以为是自己的工程实现太脆。直到看到这篇 EMNLP 2026 Findings 的论文(arXiv: 2609.00621),我才意识到这是一个普遍现象:提示词在多 Agent 系统里其实扮演两个角色,而现有优化器根本不区分它们

核心摘要:多 Agent LLM 系统里,提示词既负责生成任务相关内容(评论、摘要、中间结果),又负责规定执行关键协议(消息路由、输出格式、终止信号)。用 TextGrad 这类方法直接优化整段提示词,等于让一个只关心任务分数的优化器去改系统的"接口定义"——改着改着就把协议改坏了。这篇论文提出 control-data flow separation(控制-数据流分离):把执行协议抽成带类型、可校验的程序对象(控制流),任务内容留在自由文本里(数据流),优化器只能碰后者。结果是在四个任务上协议有效性稳定保持 100% 的水平,而 Naive TextGrad 在多智能体评审任务上稳定性直接崩到 。这不是底层突破,是一个工程界早该有的设计原则被正式化——但我觉得这恰恰是它值钱的地方。


📖 论文信息

  • 标题:Control-Data Flow Separation: Stable Prompt Optimization in Multi-Agent LLMs
  • 链接:https://arxiv.org/abs/2609.00621
  • 作者:Wentao Zhang、Syed Shariyar Murtaza、Junaid Ahmad Bhatti、Utkarsh Soni、Yifan Nie、Eugene Wen、Yuntian Deng
  • 机构:University of Waterloo(Wentao Zhang、Yuntian Deng);Manulife(其余五位作者)
  • 发表:EMNLP 2026 Findings,2026 年 9 月 1 日提交
  • 代码:https://github.com/yuntian-group/cdsep (Python 库 cdsep,完整流水线 40 行以内)

🎯 问题动机:提示词的双重身份

先把问题讲清楚。一个多 Agent 系统大概是这么跑的:Python 控制器每一步挑一个 Agent,把上下文塞给它,LLM 输出一段文本,控制器解析这段文本来决定下一步——追加消息、路由给另一个 Agent、还是终止。

注意最后这一步。控制器在"解析"LLM 的输出。这意味着 LLM 输出的一部分其实是给代码看的:JSON 字段、路由目标、action 标签、stop 信号。而另一部分是给其他 Agent 看的:评论内容、推理过程、最终答案。

论文把这两种角色拆得很直白:

角色 读者 典型内容 表征形式
数据流(data flow) 其他 Agent、提示词优化器 评论、摘要、论证、中间结果 非结构化自然语言
控制流(control flow) Python 控制器 action、target_agent、终止标志 结构化字段

问题来了:这两种内容通常混在同一段文本输出里,由同一段提示词描述。你让 TextGrad 去优化这段提示词,它的目标函数只有任务损失——它才不在乎哪句话是"给模型的建议"、哪句话是"给解析器的合同"。改着改着,格式指令就被"优化"没了。

这不是假想的麻烦。论文实测:在 MARG 多智能体评审生成任务上,Naive TextGrad 跑几轮迭代之后,系统稳定性从正常直接掉到 ——不是分数变差,是没有任何一个 episode 能正常跑完。

我的第一反应是:这个痛点太真实了。做过 Agent 工程的人大概率都踩过类似的坑,只不过多数人是用 try-catch 加正则硬糊过去的。


🧠 方法核心:控制面与数据面分离

一句话讲清核心思路:执行协议用类型化的程序对象表达,且放在优化器碰不到的冻结槽位里;任务内容留在自由文本里,随便优化。

具体地,每个 Agent 的输出不再是单一字符串,而是一个二元组:

\[y_{i}^{t}=\big(c_{i}^{t},\,m_{i}^{t}\big)\]

其中 \(c_i^t\) 是控制流对象(control object),\(m_i^t\) 是数据流消息(data message)。规则很简单:

  • 控制器只读 \(c_i^t\)。路由、终止、状态更新全部由校验过的控制对象决定,控制器永远不去解析 \(m_i^t\) 来决定执行行为。
  • 优化器只能改数据面的提示词。控制 schema 对应的格式脚手架(schema scaffolding)放在一个冻结的 prompt slot 里,优化器读不到也改不了。
  • 控制对象必须过运行时校验。schema 用 Python dataclass 或 Pydantic 声明,枚举字段用 Literal 封闭取值集合;解析失败就有限次重试,再不行回退到默认控制对象或受控失败,但非法控制永远不会到达路由器

这个设计的漂亮之处在于,Agent 的自主性没有受损——leader 仍然可以决定"下一步找哪个 worker"、"什么时候停",只是这个决定必须通过一个带类型的接口表达出来。打个比方:以前是员工在报告里随手写一句"我觉得接下来该小王干",老板靠阅读全文来领会意图;现在是员工必须填一张带下拉菜单的审批单,报告正文爱怎么写怎么写。

论文还给了一个正式的协议稳定性引理(Lemma 1,附录 A)。条件就三条:

  • C1:路由函数只接受类型化控制对象,返回值落在 Agent 集合 ∪ {terminate} 里;
  • C2:schema 脚手架位于优化器不可读写的冻结槽位;
  • C3:解析/校验失败的候选控制对象有限重试,之后回退默认对象或受控失败。

满足这三条,任意优化器选出的提示词、任意输入,episode 轨迹里都不会出现未处理的解析违规、校验违规或路由违规。证明思路很直接:优化器碰不到脚手架(C2)所以破坏不了解析合同;模型偶尔抽风输出畸形控制块有 C3 兜底;路由只吃校验过的对象(C1)所以不存在"路由到不存在的 Agent"。

值得一提的是这个引理的自我设限——作者明说:这只保证协议不崩,不保证输出语义正确。一个协议完全合法的流水线照样可以产出垃圾答案。这个坦诚我很喜欢,比那些动不动"我们的方法保证了 xxx"的论文清醒得多。它做的事其实是把灾难性失败(崩溃、乱路由)降级为普通的任务性能失败——后者可以测量、可以优化,前者只能半夜爬起来修。

开发者接口长什么样

论文实现的 cdsep 库抽象很简单:Agent、control schema、episode 三件套。论文里的 Figure 1 是两个代码清单,核心代码长这样:

from cdsep import Agent

@dataclass
class LeaderControl:
    target: Literal["w1", "w2", "w3"]

leader = Agent(
    name="leader",
    control_schema=LeaderControl,
    system_prompt="You coordinate a review team of three workers..."
)

路由函数的类型签名概念上是 route(ControlType) -> NextAction——控制器拿到的永远是校验过的 LeaderControl 实例,target 只能是那三个合法值。模型输出不合规?校验器直接拒掉,触发重新提示或受控回退,根本轮不到路由逻辑看到脏数据。


🧪 实验:四个任务,稳定性与性能两头都要

实验围绕三个问题:任务性能打得过 baseline 吗(RQ1)?分离设计能不能阻止协议崩溃(RQ2)?到底是哪个组件在起作用、跨模型家族成立吗(RQ3)?

四个任务难度递增:

  1. BBH:BIG-Bench Hard 的四个子任务,单 Agent 推理;
  2. MARG 评审生成:leader + 三个 worker 的多 Agent 流水线,leader 每步要输出 action ∈ {send, stop} 和 target_agent 来路由;
  3. 合成保险核保:extractor → rater → aggregator 三 Agent 顺序链,extractor 要从封闭的 12 类病症 taxonomy 里选 primary_chapter;
  4. 行业验证保险核保:同样的流水线,但用行业合作伙伴的核保专家生成并评级的 91 份合成医疗摘要(测试抽 20 条),taxonomy 扩到 28 章,输出是 15 档评级阶梯。

Baseline 包括 Fixed(手工提示词)、Naive TextGrad、DSPy 三个配置(不编译、BootstrapFewShot、MIPROv2),行业核保任务上还有一个 Partner-Fixed(行业合作伙伴提供的 40+ 行专家提示词)。所有数字都是 3 个随机种子的均值。

主结果

方法 BBH Acc BBH Stab MARG Jaccard MARG Stab 核保(合成) Acc 核保(合成) Stab 核保(行业) Acc 核保(行业) Stab
Fixed prompts 51.7% - 31.0% - 36.7% - 20.0% -
Naive TextGrad 45.0% 92.0% 0.0% 0.0% 47.8% 100% 18.3% 56.7%
DSPy (no compile) 66.3% 100% 41.8% 100% 26.7% 98.9% 11.7% 93.3%
DSPy + BootstrapFewShot 74.3% 100% 39.6% 100% 34.4% 100% 23.3% 83.3%
DSPy + MIPROv2 71.3% 100% 43.2% 100% 32.2% 98.9% 18.3% 83.3%
Partner-Fixed - - - - - - 31.7% -
Ours 78.3 100 44.4 100 50.0 100 36.7 100

三个关键观察:

第一,Ours 在四个任务上全部领先。 BBH 78.3% vs 最好的 DSPy 配置 74.3%;MARG Jaccard 44.4% vs MIPROv2 的 43.2%;行业核保 36.7%,比 Partner-Fixed 那份 40 多行的专家手工提示词还高 5 个点——这个数字挺能打的,说明优化器在分离架构内确实学到了东西,而不是靠"不崩"混分。

第二,Naive TextGrad 的崩溃模式和路由复杂度强相关。 单 Agent 的 BBH 上稳定性还有 92%(只在 CausalJudgement 这种 Yes/No 任务上翻车——优化器把输出引导成"Probably yes"这种字符串,解析器不认);到了 leader-worker 路由的 MARG 直接归零;行业核保(28 章封闭集合)掉到 56.7%。优化器重写的就是内联 JSON 说明或章节名格式指令,解析器一拒,整个 episode 报废。

第三,DSPy 的"稳定"有水分,值得细看。 DSPy 在 MARG 上 100% 稳定性,但论文指出它的 collapsed forward pass 里根本没有路由决策需要校验——等于绕开了问题而不是解决了问题。在两个核保任务上,DSPy 的原始输出在 snap-to-bucket 修复前并不是全都 schema-valid(行业任务上 BootstrapFewShot 和 MIPROv2 都只有 83.3% 的严格 schema 有效性),表格里那个 100% 是修复后的数字。这个对比方式我觉得挺诚实的,作者专门在表注里讲清楚了。

逐迭代稳定性:崩溃是怎么发生的

逐迭代 episode 稳定性

图 2:四个任务上随优化迭代的 episode 稳定性。绿线(Ours,分离架构)全程钉在 100%;橙线(Naive TextGrad)在 Review 任务上随编辑累积一路崩到 0%,BBH 上也有明显塌陷;Synthetic 和 Insurance 这类路由面小的任务在当前优化预算内没有崩溃。虚线是 Fixed prompts 基线。

这张图把"崩溃是渐进发生的"这件事展示得很清楚——不是某一步突然暴雷,而是提示词漂移一点点侵蚀控制面,到某个临界点解析器开始批量拒绝。

BBH 逐迭代测试准确率

图 3:BBH 四个子任务的逐迭代测试准确率(3 种子均值 ± 标准差)。Ours(绿)与 Naive(橙)对比,虚线是固定提示词。LogicalDeduction 和 TrackingShuffledObjects 上 Ours 直接冲到 100%,WordSorting 最难只有 45.3%,CausalJudgement 上 Naive 因格式漂移出现明显抖动。

合成核保任务逐迭代准确率与 MAE

图 4:合成核保任务上准确率与 MAE 随迭代的变化。Ours(绿)准确率稳步爬升到 50% 左右,同时 MAE 下降,全程保持 100% 稳定性;Naive(橙)虽然这个任务上没崩,但准确率天花板更低。

消融:三个组件各管一件事

消融实验在 MARG 评审和合成核保上做,三个正交开关逐个打开:

变体 Schema 脚手架 解析重试 逐样本反馈 MARG Jaccard MARG Stab 核保 Acc 核保 Stab
Naive 0.0% 0% 44.4% 97.8%
Schema-only 26.8% 100% 34.4% 98.9%
Schema + retry 26.9% 100% 37.8% 100%
Ours (full) 38.0 100 51.1 100

这个分解真的漂亮,三个组件的贡献几乎是正交的:

  • Schema 脚手架管稳定。评审任务上从 Naive 到 Schema-only 这一个开关,稳定性 0% → 100%。一步到位。
  • 解析重试管最后一点边角。评审上 Jaccard 只动不到 1 个点,但核保上把最后约 1% 的不稳定 episode 收干净(98.9% → 100%)。
  • 逐样本反馈管性能。schema 和重试固定不动,只把优化器信号从标量 batch loss 换成逐样本反馈,评审 Jaccard 从 26.9% 拉到 38.0%,核保准确率 37.8% → 51.1%。

说实话看到这个消融我才完全信服——稳定性和性能确实是两个独立的轴,被两个不同的组件分别解决,而不是某种笼统的"我们的框架更好"。

提示词漂移的定量证据

作者还做了个挺有意思的统计:diff 优化器第 0 轮和最终轮的提示词,数有多少比例的编辑行碰到了控制相关 token("JSON"、"Output format"、action、target_agent 这类 schema 字段名)。

任务 方法 编辑字符数 控制面触碰率
Review Naive TextGrad 6638 16.6
Review Ours 6269 4.2%
Insurance Naive TextGrad 5368 22.6%
Insurance Ours 8528 12.2%
BBH Naive TextGrad 1589 22.9%
BBH Ours 1472 18.6%

评审任务上 Naive 的控制面触碰率是 Ours 的约 4 倍。Ours 那残留的 4.2% 也不是真在改协议——作者解释那只是优化器在讨论推理的句子里碰巧写了"output"这类词,token 重叠而已,因为可编辑槽位里根本没有控制面可碰。这个统计方法虽然粗糙(纯 token 匹配),但作为证据链的一环挺有说服力。

跨模型家族鲁棒性

在 MARG 上用 OpenAI(gpt-5.4-nano 系)、Anthropic(claude-haiku-4-5 Agent + claude-sonnet-4-5 优化器)、Google(gemini-2.5-flash Agent + gemini-2.5-pro 优化器)三个家族各跑一遍:

方法 OpenAI J OpenAI Stab Anthropic J Anthropic Stab Google J Google Stab
Fixed 43.4 100% 25.1 100% 33.7 100%
Naive 0.0 0% 0.0 0% 0.0 0%
Ours 42.2 100% 33.5 100% 40.3 100%

Naive 在三个家族上全部崩到 0%,Ours 全部 100%。注意这块只有 1 个种子、N=12 篇论文的子集,证据强度弱一些,但作为方向性验证够了。另外 Anthropic 上 Ours 的 Jaccard(33.5)明显高于 Fixed(25.1),Google 上 40.3 vs 33.7,说明优化收益也跨家族成立——OpenAI 上 42.2 反而略低于 Fixed 的 43.4,作者没解释,我猜是预算缩减后优化器没收敛到位,这种小波动在 1 种子下说明不了什么。


🔬 相关工作坐标:这到底是新东西吗?

坦率讲,控制面/数据面分离是网络系统和软件工程里活了几十年的老思想。类型化 LLM 输出这条线也很热闹:instructor、LangChain output parsers、guidance、OpenAI structured output API,学术上有 PICARD、LMQL、Outlines、XGrammar 这一串约束解码工作。DSPy 的 signature 机制其实也在做类似的事。

那这篇论文的增量在哪?我的判断是:它不是发明了分离,而是把分离在多 Agent 提示词优化场景下"正式化 + 强制化"了。具体三层:

  1. 问题定义层面的贡献:明确指出多 Agent 提示词里"内容生成"和"执行协议"两个角色的纠缠,是提示词优化特有的失败模式。之前大家要么当工程 bug 修,要么靠 structured output 硬约束,没人把"优化器会漂移协议"当成一个独立问题来形式化。
  2. 机制层面的贡献:schema 不是仅仅用来约束输出,而是放进优化器不可见的冻结槽位。约束解码保证单步输出合法,冻结槽位保证优化过程不会把约束本身改掉——后者才是 TextGrad/DSPy 这类迭代优化场景的关键。
  3. 性质层面的贡献:Lemma 1 把"协议不崩"写成了可检验的程序安全性质,三条条件清晰可审计。

作者自己的定位也很克制:"遵循已有的软件工程原则,而不是引入新的类型系统或分离原则"。跟 TextGrad、DSPy、GEPA 这些优化器是互补关系——cdsep 管边界,优化器管边界内的策略。这个定位我认为是准确的。


🤔 我的判断

亮点:问题抓得准且真实,任何拿提示词优化器怼过多 Agent 系统的人都会会心一笑;消融实验干净,三个组件的贡献正交分解,方法论上是这篇论文最扎实的部分;cdsep 库 40 行能跑完整流水线,工程可用性看起来不错;跨三模型家族的验证虽然样本小但方向一致。

疑问和局限

  • 任务规模偏小。MARG 消融用 12 篇论文子集,行业核保测试只有 20 条/种子,BBH 只取 4 个子任务。100% 稳定性是构造性保证(冻结槽位 + 校验),这个不需要大样本背书,但"性能领先 DSPy"那几个点(44.4 vs 43.2)在小样本下的统计意义我其实不太确定,论文也没给显著性检验。
  • schema 要预先声明,这是最大的适用边界。固定角色、固定路由面、固定终止条件——真实世界里动态创建 Agent、运行时演化协议的系统(比如开放式 Agent 社会模拟)这套就不直接适用了。作者在 Limitations 里承认了这点,把它留给未来工作。
  • Stability ≠ correctness,作者自己反复强调。协议不崩只是及格线,数据面产出的内容质量仍然要靠优化器和评估器保证。
  • 跟约束解码(XGrammar 这类)的关系可以更紧密。现在 schema 校验是生成后做的(配重试),如果底层直接上 grammar-constrained decoding,重试开销能省掉——作者说互补,但没实测这条组合路径。

对工程的启发,这块我觉得是最值得带走的:

如果你在用 TextGrad/GEPA/MIPROv2 这类方法优化任何"LLM 输出要被代码解析"的系统,记住三条——路由只信类型化对象(C1)、格式脚手架冻结且优化器不可见(C2)、解析失败有界重试加默认回退(C3)。就算你不用 cdsep 这个库,这三条原则拿 Pydantic + 一个冻结的 system prompt 段落也能自己拼出来。反过来,如果你发现你的 Agent 管道在自动优化后莫名崩溃,先去 diff 一下前后提示词里格式指令被动了多少——大概率答案就在那里。


📝 收尾

这篇论文不会改变谁的研究方向,但它可能会进入不少人的工程 checklist。有些论文的价值在于提出新问题,有些在于给出新解法,这篇属于第三类:把一个大家隐约知道但一直在用创可贴糊的问题,正式命名、形式化、并给出带保证的解法。EMNLP Findings 的收稿位置也挺符合它的定位——不是主会那种"开新坑"的论文,而是那种你工程上真会回头引用的论文。

最后一个更本质的追问:当 Agent 系统从"固定角色固定路由"走向"动态组队动态协议"的那天,控制面本身也要被优化、被演化——到那时候,分离原则还成立吗?schema 会不会变成另一种需要被优化的提示词?这个问题这篇论文没回答,但我觉得是下一步真正有意思的地方。


觉得有启发的话,欢迎点赞、在看、转发。跟进最新AI前沿,关注我