事情的起因很朴素一条自动化评测流水线全量跑一遍账单出来的时候团队里没人愿意认领。单条用例上下文动辄五六千 token输出还要一千多两千条跑一轮就是一笔不小的开销而更让人难受的是——其中相当一部分用例用最小的模型就能做对。这就是我后来一头扎进多模型编排的原因。标题里说的“不再让一个模型写到底”本质上就是把一条端到端的任务链拆开让不同规模的模型各管一段中间用路由、级联、缓存和融合层串起来。我们内部把这套编排层叫 HydraFusion取自“多头 融合”的意思。它解决的问题很具体在保证评测通过率不下降的前提下把单位评测成本压下去实测下来整体降幅约 67%同时 p50 延迟还变短了。这篇东西适合两类人看一类是正在为模型调用账单发愁、想知道钱到底花在哪儿的工程师另一类是想在自己项目里落地多模型协作、但不想一上来就搞复杂 Agent 框架的开发者。我会把架构取舍、路由阈值怎么定、缓存怎么防污染、成本账怎么复算全部摊开讲。1. 单模型端到端在评测流水线里到底卡在哪很多人第一反应是“换更便宜的模型不就行了”。我试过直接换小模型成本确实下来了但通过率掉得比成本还快最后人工复核的人力成本反而更高。问题的根子不在模型贵不贵而在于我们让一个模型同时干了四件性质完全不同的事读懂仓库上下文、定位要改的地方、写出正确的补丁、判断这个补丁对不对。这四件事对模型能力的要求差异极大用一个模型统一扛就等于按最难的那件事付费。1.1 任务异质性一条流水线塞了四种活我大概统计过我们那条流水线的 token 分布真正用于“推理出正确改法”的部分占比不到四成剩下的六成消耗在阅读理解上——把几十个文件的上下文塞进去、把历史讨论塞进去、把规范文档塞进去。而阅读理解这件事中小模型的表现和大模型的差距远没有“写出正确补丁”这件事的差距那么大。换句话说我们花了最贵的钱去干了一件相对便宜的事。这就是任务异质性带来的浪费。举个生活里的类比你请一位资深架构师帮你改一行配置文件的缩进他当然能改对但你付的是架构师的时薪。多模型编排的第一层价值就是把“缩进”这类活识别出来交给一个便宜的角色去做把资深角色的时间留给真正需要判断力的部分。这个识别动作就是后面要讲的路由。还有一个常被忽略的点单模型端到端意味着上下文必须一次性全给。因为模型没有“回头再看一眼”的能力你只能在一次调用里把所有可能相关的信息都塞进去宁可塞多了浪费也不敢塞少了漏掉。这种“防御性塞满”在长尾用例上尤其致命——少数超长用例的输入能到两三万 token一条就顶几十条普通用例的成本。1.2 成本曲线不是线性的长尾才是账单刺客把两千条用例的成本按单条排序画出来你会发现它不是均匀分布的而是典型的长尾头部 70% 的用例单条成本只占总额的两成多尾部 10% 的用例能吃掉将近一半的预算。原因是这些长尾用例往往同时具备三个特征仓库上下文特别大、需要多轮修正、输出特别长。这里有个反直觉的结论优化长尾的绝对成本不如优化头部的相对成本收益高。因为头部用例数量多哪怕单条只省一点点乘上基数也很可观而长尾用例你费很大劲优化省下的绝对金额有限还容易把通过率搞崩。我们最后采用的策略是“头部做极致压缩长尾做有条件的升级”这个思路直接决定了后面的级联设计。顺带说一句成本统计的口径一定要提前定死。我们最开始是按“调用次数”估的结果实际账单差了将近一倍因为忽略了输出 token 的单价通常是输入的好几倍而长尾用例恰恰是输出最长的。后来改成按“输入 token × 输入单价 输出 token × 输出单价”逐条累加账单才对得上。这个坑看着低级但我见过不止一个团队踩。1.3 自己评自己单模型评测的可信度问题这一条跟成本没关系但跟“官方评测”四个字直接相关。当生成答案和评判答案的是同一个模型时评测结果天然带有自洽偏差——它倾向于认为自己写的风格是对的。我们在早期做过一个小实验让同一个模型先写补丁再让它按评分标准给自己打分结果它给自己的分数比人工评分平均高出 12 个百分点而且偏差集中在“风格偏好”这一类主观维度上。多模型编排顺手解决了这个问题生成用一个模型审查用另一个不同来源的模型打分再由第三个独立配置的实例来做。三个角色的提示词、温度、甚至上下文截断策略都不一样。这不是为了“ ensemble 一定更强”这种玄学而是为了让错误不同源——同一个模型犯的错往往高度相关换一个来源错误的概率分布就变了融合层才有价值。2. HydraFusion 的编排骨架把“写到底”拆成四段把整条链路拆开之后我们的划分是这样的规划段负责读懂 diff 和上下文输出“要改哪几个位置、每个位置大概改什么”生成段负责按规划写具体补丁审查段负责挑毛病只输出定位和建议不直接改写验证段跑静态检查和可执行测试给出通过/不通过以及失败原因。四段之间用统一的结构化契约传递数据每一段都可以独立换模型、独立调参、独立计费。2.1 四段流水线与三个角色这里有个关键设计决定审查段不直接产出最终代码。我一开始图省事让审查模型直接把改好的完整文件吐出来结果发现两个问题一是输出 token 暴涨审查一次的成本快赶上生成了二是审查模型经常“顺手”改动一些跟本次任务无关的地方导致 diff 变得不可控。后来改成审查只输出“行号 问题类型 建议片段”由融合层决定采纳哪几条输出量降了一个数量级。三个角色的分工也要说清楚。路由角色最便宜的那档不参与内容生成只做分类决策执行角色分 S/M/L 三档按难度选用裁判角色是固定配置的独立实例负责按评分标准给分它的配置在整个评测周期内不允许随意改动否则历史数据就没法比了。这个“裁判配置冻结”的规矩是我们踩坑之后立的后面会细说。段落之间传的东西我建议用严格的 JSON 结构字段名固定缺字段直接报错而不是让模型自由发挥。原因很实际自由文本在段间传递下游模型得先“理解”上游在说什么这一步的理解偏差会累积而且没法定位是哪一段出的问题。结构化契约让每一段的输入输出都能被程序直接校验出问题时一眼就能看出是哪一段的锅。2.2 路由器的三种实现与取舍路由器是整个方案的核心它决定了每条用例走哪条路。我实际试过三种实现各自的适用场景差别很大。第一种是纯规则路由靠特征阈值判断比如 diff 行数小于 20、只涉及单文件、语言在支持列表内、没有触碰依赖清单文件就判为简单档。优点是零成本、零延迟、完全可解释、调试极其方便。缺点是需要人工维护规则遇到新模式就得补。我们上线第一周用的就是这个覆盖了大约六成的流量效果已经不错。第二种是小模型分类器用几百条人工标注的样本微调或者直接 few-shot输入是精简后的特征描述不是完整上下文输出是难度档位加置信度。它的好处是能捕捉规则覆盖不到的组合特征比如“文件数不多但涉及接口签名变更”这种。代价是每条多一次调用成本不高但不能忽略而且它的判断质量依赖标注样本的代表性。第三种是级联置信度不用单独分类直接让 S 档模型先做同时输出一个自评置信度低于阈值就升级。这个方案最省事但我不太推荐作为主力因为它有个隐蔽问题小模型的自评置信度很不准尤其在它“自认为做对了但其实错了”的样本上置信度反而偏高。这种自信的错误最难拦。我们最终采用的是规则打底 分类器兜底 级联校验的混合方式先用规则把明确简单的和明确复杂的分出来中间那段灰度区间交给分类器分类器置信度低于 0.6 的直接升到 M 档高于 0.85 且规则也认为简单的才降到 S 档。这个组合把误判率压到了 4% 以内。2.3 融合层不是投票是契约下的合并很多资料一讲多模型就讲投票、讲多数一致。这在选择题上管用在代码生成上基本没用——两个模型写出来的补丁可能逻辑都对但变量命名、缩进风格、错误处理方式完全不同投票没法投。我们用的是“骨架 补丁建议”的合并策略L 档模型产出的完整补丁作为骨架审查段提出的建议以“行号区间 替换片段 严重级别”的形式返回融合层按严重级别从高到低依次尝试应用每应用一条就重新跑一次轻量校验不通过就回滚这条。这套机制的关键在于严重级别的定义必须是可执行的不能是“严重/一般/轻微”这种主观词。我们把它拆成了四类语法错误、类型不匹配、可执行测试失败、逻辑与需求描述不一致。前两类机器能判第三类靠测试跑第四类才需要模型判断。实际数据是前三类占了审查建议的八成以上也就是说绝大部分审查价值是可以被程序直接验证的这让整个融合过程相当可靠。融合层还有一个职责是冲突消解。当两条建议改同一个位置时按严重级别优先同级别按建议来源的可信度排序这个可信度是离线统计出来的不是拍脑袋。如果两条建议互相矛盾且都通过不了校验就直接放弃自动融合标记为“需要升级”交给更高档位重做。宁可多花一次钱也别强行合出一个四不像。3. 省钱的关键细节级联、缓存与预算护栏架构画出来只是第一步真正把成本压下去的是三个工程细节级联早退、多层缓存、预算护栏。这三样东西单独看都不复杂但组合起来的效果是乘法的而且每一样都有自己的污染边界处理不好反而会拖累通过率。3.1 级联早退把升级当成异常而不是常态级联的核心思想是“先用便宜的试不行再升级”。听起来简单但工程上有两个容易做错的地方。第一个是升级条件的判定。千万别只用模型自评置信度。我们现在的升级触发有四类模型自评低于阈值、输出没能通过结构化校验比如 JSON 解析失败、补丁格式不对、静态检查报错、可执行测试失败。四类里只要命中一类就升级。这四类信号里后两类是硬信号几乎不会误判前两类是软信号主要用来兜住那些“格式对但内容明显跑偏”的情况。第二个是最大升级次数。这个必须设上限我们设的是 2 次也就是 S→M→L 之后就不再升了直接标记失败进入人工队列。不设上限的后果我深有体会有一条用例在 S 和 M 之间来回振荡了五次最后一次跑到了 L单条成本是普通用例的二十多倍。虽然这种用例占比不到 1%但它们贡献了将近 20% 的总成本——这就是前面说的长尾。还有一个优化点升级时不要重跑全流程。规划段的结果在 S 档和 M 档之间通常是通用的升级只重跑生成段和审查段就行。我们算过光是这一条就让升级路径的成本降了三成多。当然前提是规划段本身没被判为不可靠如果规划都错了那就得从头来。3.2 三层缓存与它们的污染边界缓存是成本优化的最大杠杆但也是最容易出事故的地方。我们做了三层每层的命中判据和失效策略都不一样。L1 精确缓存键是提示词全文 模型标识 采样参数的哈希。这层最安全理论上不可能误命中因为输入完全一致。它的命中率取决于评测集里有多少重复用例我们这边大概 8%主要来自同一个 bug 在多分支上重复出现。L2 语义近似缓存把 diff 和上下文的关键部分做嵌入相似度超过阈值就认为可以复用。这层收益最大但风险也最大。我们的阈值定得比较保守0.95 以上才命中而且加了两个硬约束必须是同一门语言、必须是同一个仓库路径前缀。这两个约束看起来粗暴但确实把误命中率压下来了。早期我们只用了相似度结果把两个不同模块里长得像但语义相反的改动搞混了出了几次错之后才加的约束。L3 片段缓存针对高频的小模式比如补 import、加空值判断、修典型的拼写错误。这层不缓存完整答案只缓存“建议片段”由融合层决定用不用。它的价值在于把一些几乎确定的小修小补提前干掉减少主模型的输出长度。提示语义缓存一定要有“污染回收”机制。我们每周会从命中样本里随机抽 30 条人工核对一旦发现误命中除了删掉对应条目还要把该仓库路径下的缓存整体失效一次。别心疼误命中带来的通过率损失远大于缓存省下的钱。3.3 预算护栏与降级顺序没有预算护栏的编排系统在遇到异常流量时会以你意想不到的速度烧钱。护栏要分三层设全局单轮预算、单用例预算、单阶段预算。全局超了直接终止本轮单用例超了强制返回当前最好结果并标记未完成单阶段超了跳过该阶段的可选步骤。超预算之后的降级顺序必须提前定死不能临时决定。我们的顺序是先关审查段的交叉验证省得最多质量损失最小再把灰度区间的用例整体下调一档最后才动缓存策略。这个顺序不是拍脑袋的是拿历史数据离线模拟出来的——按这个顺序降级在同样的预算削减幅度下通过率损失最小。另外强烈建议给每个阶段单独记账而不是只记总账。我们最早只看总数结果发现某个月成本异常排查了两天才定位到是审查段的平均输出长度翻倍了原因是某次提示词改版不小心去掉了“输出不超过 200 字”的约束。有了分阶段记账这种问题在日报里就能看出来。4. 一套能跑起来的实现配置、代码与成本复算理论讲完落到能跑的东西上。我尽量把代码写成与具体供应商无关的形式你可以直接对着改成自己环境的版本。核心抽象只有三个模型提供方接口、编排配置、预算计数器。4.1 编排配置长什么样配置文件我坚持用声明式因为编排策略会频繁调整写在代码里每次都要改逻辑很容易引入回归。下面是我们现在用的配置骨架删掉了环境相关部分。pipeline: stages: [plan, generate, review, verify] tiers: s: model: m-small max_input_tokens: 3200 max_output_tokens: 700 unit_price: {input: 0.8, output: 2.0} m: model: m-mid max_input_tokens: 5200 max_output_tokens: 1000 unit_price: {input: 1.5, output: 6.0} l: model: m-large max_input_tokens: 6400 max_output_tokens: 1300 unit_price: {input: 3.0, output: 15.0} router: model: m-tiny unit_price: {input: 0.2, output: 0.6} routing: rules: - name: trivial when: diff_lines 20 and files 1 and not touches_manifest target: s confidence: 0.95 - name: heavy when: files 6 or diff_lines 300 or touches_manifest target: l confidence: 0.98 classifier: fallback_tier: m low_confidence_threshold: 0.60 high_confidence_threshold: 0.85 cascade: max_upgrades: 2 upgrade_on: - low_self_confidence - schema_violation - static_check_failed - test_failed reuse_plan_on_upgrade: true cache: l1_exact: {enabled: true} l2_semantic: enabled: true similarity_threshold: 0.95 require_same_language: true require_same_path_prefix: true l3_snippet: {enabled: true, max_entries: 500} budget: per_run: 30000 per_case: 80 per_stage: {plan: 12, generate: 45, review: 15, verify: 8} degrade_order: [disable_cross_review, downgrade_mid, relax_cache]这份配置里最值得琢磨的是per_case: 80这个数。它是怎么来的我们取了正常用例成本的 95 分位数再乘 2而不是平均值。用平均值做上限等于把长尾用例直接掐死用 99 分位数又太松起不到护栏作用。95 分位乘 2 是个折中既能兜住绝大多数正常的长尾又能拦住那些真正失控的。4.2 路由与级联的代码骨架下面这段是核心逻辑的骨架去掉了重试、日志、指标上报这些基础设施只保留决策路径方便你直接看懂数据怎么流动。from dataclasses import dataclass from typing import Optional dataclass class Decision: tier: str reason: str confidence: float def route(case, rules, classifier, budget) - Decision: # 1. 硬规则优先规则命中直接返回不花一分钱 for rule in rules: if rule.matches(case): return Decision(rule.target, rule.name, rule.confidence) # 2. 灰度区间交给轻量分类器 features extract_features(case) # 只取特征不送全文 out classifier.classify(features) # 返回 (tier, confidence) budget.charge(router, out.usage) # 3. 低置信度统一向上兜宁可多花钱也别猜 if out.confidence 0.60: return Decision(m, low_conf_floor, out.confidence) if out.confidence 0.85 and out.tier s: return Decision(s, high_conf_small, out.confidence) return Decision(out.tier, classifier, out.confidence) def run_case(case, decision, cfg, budget): tier_order [s, m, l] idx tier_order.index(decision.tier) plan None trace [] for attempt in range(cfg.cascade.max_upgrades 1): tier tier_order[min(idx attempt, len(tier_order) - 1)] # 升级时复用规划避免重复付费 if plan is None or not cfg.cascade.reuse_plan_on_upgrade: plan call_stage(plan, tier, case, budget) patch call_stage(generate, tier, case, plan, budget) review call_stage(review, tier, case, plan, patch, budget) \ if tier ! s else None merged fuse(patch, review) verdict verify(merged, case) # 静态检查 可执行测试 trace.append({tier: tier, verdict: verdict.status, cost: budget.case_spent()}) if verdict.ok: return merged, trace if budget.case_exceeded() or verdict.fatal: break return None, trace这段代码里有三个设计点值得单独说。第一规则命中时完全不调用模型连路由器都不调这是最省的一档。第二verdict.fatal是个逃生口语法彻底炸掉或者触发安全校验的用例直接放弃升级因为升级也救不回来硬升只是烧钱。第三trace全程记录每一档的花费和判定结果这是后面做消融分析和故障定位的唯一依据千万别省。4.3 成本账67% 是怎么算出来的数字不能拍脑袋给我把假设摊开你可以直接换成自己的价格表重算。口径是2000 条用例跑一轮单价按上表假设值每千 token不含人工复核成本。重试率按 3% 计入。项目单条输入单条输出单条成本条数小计基线全量 L 档端到端6000120036.00200072000路由判定全部用例800600.202000392缓存命中仅付路由--0.2024048S 档直出占有效量 50%30006003.608803168M 档占有效量 33%500090012.905817492L 档 独立审查占有效量 17%60002500120040042.1529912611重试与失败回放3%----712编排方案合计---200024423基线 72000编排后 24423降幅 66.1%我们对外取整说 67%。平均单条成本从 36 降到 12.2。这个结构里最重要的不是绝对数字而是比例接近一半的用例被最低档吃掉了只花了总成本的 13%而 17% 的高难用例花了超过一半的钱。这个分布是健康的说明路由确实把简单活分出去了。单条成本的计算方法是输入token/1000 × 输入单价 输出token/1000 × 输出单价。拿 L 档加审查举例生成部分 6×3 1.2×15 36审查部分 2.5×1.5 0.4×6 6.15合计 42.15。审查用的是 M 档而不是 L 档这是我们做过对比后定的——用 L 档审查检出率只高了 3 个百分点成本却翻了快一倍不划算。延迟方面的账也一并报了基线 p50 是 16.8 秒编排后整体 p50 降到 5.1 秒因为一半流量走了 S 档三秒出头就返回了。p95 反而从 31 秒涨到 33 秒原因是 L 档多了串行的审查和校验。这个取舍我们认为可以接受评测场景对 p50 敏感、对 p95 相对宽容。4.4 上线的灰度节奏别一次全量切。我们的灰度分了四步每步观察两天。第一步只开规则路由不启用分类器和缓存观察路由的覆盖率和你对规则质量的信心。这一步的意义是验证“简单用例真的简单”如果发现规则判定的简单用例通过率掉得厉害说明规则写得有问题得回去改。第二步打开级联但仍然不全量只对规则命中率为“中等”的那批用例生效占比大约三成。这一步主要看升级率和升级后的通过率提升幅度如果升级率超过 25%说明路由整体偏保守钱没省到点子上。第三步开 L1 和 L3 缓存这两种缓存风险低先跑起来积累信心。第四步才开 L2 语义缓存而且阈值先设 0.97跑一周没问题再降到 0.95。L2 是我见过最容易翻车的一环慢一点没坏处。5. 评测体系要跟着改否则数字会骗人编排上线之后原来的评测口径就不够用了。因为一条用例可能被尝试了多次也可能部分走了缓存如果只报一个“通过率”你根本不知道这个数字是怎么来的。这一节讲我们怎么把指标体系重做了一遍。5.1 五个必须同时看的指标只报一个数一定会被误导我们现在的日报固定看五个首次通过率、最终通过率、平均单条成本、人工采纳率、p95 延迟。首次通过率是第一次尝试就过的比例它反映的是路由和首档模型的质量最终通过率包含升级后成功的部分反映的是整条链路的上限。这两个数的差就是级联的贡献。如果差值很大说明路由偏保守可以往下调如果差值很小但成本很高说明升级次数太多得查是不是某个档位的提示词退化了。人工采纳率是抽样人工复核后“认为可以直接用”的比例这个指标不能省。我们遇到过最终通过率 82% 但人工采纳率只有 61% 的情况仔细看是模型学会了讨好自动校验器——测试能过但写法完全不符合项目规范。平均单条成本要按档位分开报光报总数看不出结构变化。最后一个 p95 延迟主要是为了防回归编排链路变长之后某一档的超时很容易把长尾拖垮。5.2 消融实验的最小集合要证明“成本下降不是靠牺牲质量换来的”消融实验是必须做的。我们固定做四组对照每组跑同样的 2000 条用例。对照项关闭的模块关注指标我们实测的结论A 组关闭全部路由全量走 M 档通过率、成本成本降 61%通过率降 4.3 个点不划算B 组关闭级联升级最终通过率成本再降 9%通过率降 6.8 个点长尾崩了C 组关闭 L2 语义缓存成本、误命中成本升 11%通过率升 0.4 个点说明缓存基本安全D 组关闭独立审查段人工采纳率通过率几乎不变人工采纳率降 9 个点审查不能省C 组那个 0.4 个点的通过率提升特别值得说。它说明语义缓存确实存在极少量误命中但代价很小。如果这个数字超过 1.5 个点就得考虑把阈值调回去了。做消融最忌讳只看总账一定要把“质量变化”和“成本变化”放在一起看否则很容易得出“关掉某个模块更省钱”这种自欺欺人的结论。5.3 可复现清单评测结果最怕的就是“上周还是 82%这周变 79%但没人知道为什么”。我们立了一份强制记录清单每次跑评测必须落盘编排配置的完整快照、每个模型的版本标识、所有提示词的版本号和哈希、路由规则的版本、缓存状态是否预热、命中率、随机种子、以及每条用例的完整 trace。有了这份清单任何一次指标波动都能在半小时内定位到是哪次改动引起的。我们靠它抓到过一次很隐蔽的问题某次常规的模型版本升级接口没变、价格没变但输出风格变保守了导致审查段的建议数量下降了三成最终通过率掉了 2 个点。如果没有版本快照这种问题根本查不出来。还有一条规矩裁判配置在整个对比周期内冻结。评分标准、裁判模型版本、温度参数一个都不许动。想调可以调完之后所有历史数据作废重新建立基线。这条规矩立起来的时候挺多人有意见但坚持下来之后我们的数据可信度上了一个台阶。6. 踩坑实录与速查表这一段是我觉得最有价值的部分因为下面这些问题在文档里基本看不到全是拿账单和事故换来的。6.1 故障速查表现象最可能的根因排查动作处理办法成本突然翻倍通过率没变某档输出长度暴涨看分阶段 token 统计对比历史均值检查该档提示词是否被改补上长度约束升级率从 12% 涨到 30%路由阈值漂移或分类器退化用金标集重跑路由看混淆矩阵重新校准阈值必要时补标注样本少数用例反复升级长尾用例触发全链路按单条成本排序看 top 20设max_upgrades2超限直接进人工通过率高但人工不认可模型在讨好自动校验器抽样人工复核 50 条加强规范类检查引入风格维度评分语义缓存误命中相似度阈值过低或跨模块复用抽查命中样本看是否同语言同路径提高阈值加路径前缀约束失效该路径缓存同一用例两次结果不同采样参数未固定或缓存状态不同对比 trace 里的种子和缓存标记评测路径强制 temperature0禁用随机采样账单和自记成本对不上只统计了输入 token核对输出 token 和重试次数改成逐条计价重试单独记账某阶段超时率升高该档模型限流或长尾输入看 p95 延迟分布和错误码加超时降级超时直接返回上一档结果6.2 三个印象最深的坑第一个坑把“格式正确”当成“内容正确”。我们早期在生成段加了严格的 JSON 校验结果发现模型学会了输出格式完美但内容空洞的结果——字段全在值全是占位符。后来加了字段级的语义校验比如补丁字段必须包含至少一个有效行号区间描述字段不能是“修改相关代码”这类空话问题才算解决。这个教训是通用的任何校验一旦被模型感知到它就会针对校验做优化所以校验要分层硬校验管格式软校验管语义人工抽检管规范。第二个坑缓存把评测集“记住”了。这个最阴。我们的评测集是固定的跑了几轮之后缓存命中率一路涨到 35%成本好看得不像话。后来做了一次留出集验证发现真实场景的通过率比评测集低了 7 个点。原因是缓存把评测集的答案固化了等于开卷考试。解决办法是评测时强制区分“冷缓存跑”和“热缓存跑”两个数字报指标一律用冷缓存的结果热缓存只用来估算线上成本。这个区分非常关键不然所有优化数字都是虚的。第三个坑升级路径上的上下文丢失。我们从 S 升到 M 的时候为了省 token把规划段的输出压缩了一段再传下去结果 M 档拿到的规划信息不完整反而比直接用 M 从头做还差。这种现象在小模型上尤其明显因为它对输入完整性的敏感度更高。后来定的规矩是升级时规划段结果原样传递一个字段都不裁要省就省在别处。升级本来就是个低频动作在这上面抠 token 属于捡芝麻丢西瓜。注意编排链路越长越要警惕“每段都优化了但整体变差”的情况。我们每加一个环节都会在全量集上对比“加之前”和“加之后”的最终通过率和总成本只要出现通过率下降超过 1 个点就要重新审视这个环节是不是真的有必要。多一个模型不等于多一份保险也可能多一个甩锅的地方。最后分享一个我个人的体会多模型编排这件事真正难的不是调度而是判断每个环节到底值不值。我们前后加过又砍掉过三个环节——一次额外的自我反思、一次基于历史相似用例的检索注入、一次双重审查。砍掉的理由都一样成本涨了 8% 到 15%通过率提升不到 1 个点人工采纳率一点没变。反而是几个看起来最不起眼的改动收益最大把规划段的输出改成结构化、把审查建议从文本改成行号加片段、把升级时的规划复用起来。这三个改动加起来成本又降了将近两成。所以如果你也想动这个方向我的建议是先别急着设计复杂的编排图先把你现有流水线的 token 分布和长尾成本摊开看一遍钱花在哪儿、哪儿花了冤枉钱看完那张表该拆几段、该设几个档位答案基本自己就浮出来了。