上个月把自动化代码评审那条流水线的月度账单拉出来对了一遍,结果挺意外:整条链路里真正需要“读懂上下文”的判断不到三成,剩下七成全在干格式检查、命名规范、拼写纠正、提交信息润色这类活儿,而这些活儿当时是同一个旗舰档模型一路跑下来的。这就是典型的“一个模型写到底”——从任务拆解到结论输出,全程一个档位扛着。改成多模型编排之后,同样一批评审任务,官方口径的评测成本降了 67%。这个数字背后不是玄学,是一笔能一行行复算的账。这篇就把 HydraFusion 这套编排思路拆开讲:它解决的到底是什么问题、路由和级联怎么设计、接到代码评审流水线上要做哪些脏活、实测中会在哪儿翻车,以及怎么把一个漂亮的降本数字变成每天都能稳定复现的收益。刚接触自动化评审的新手能照着搭出最小可跑版本,已经在上多模型的老手可以直接跳到第四、第五节看踩坑和调参。
1. 一个模型跑到底亏在哪:把账算到 token 级别
1.1 全流程单模型的三处隐性开销
先说清楚“一个模型写到底”为什么贵。很多人第一反应是“旗舰模型单价高”,这只是表层。真正的开销有三处,而且后两处往往比单价更致命。
第一处是输入冗余。单模型流水线的典型写法是:把整个 diff、相关文件全文、历史评论、规范文档一股脑塞进同一次调用。为了省事,每做一步都重新塞一遍完整上下文。一次评审做五步,同一份 12k token 的上下文就被重复计费五次。这部分钱花得毫无信息增量,纯粹是工程偷懒的代价。
第二处是能力错配。检查一个缩进是不是四个空格、变量名是不是蛇形命名、提交信息有没有超过 72 字符,这些用规则引擎就能干,最差也该是轻量档的活儿,结果交给了能写论文的档位。这就像请一位资深架构师去逐行核对括号是否配对——他不是干不了,是贵得离谱,而且慢。
第三处是串行重试。单模型出错时没有退路,只能原样重跑或者换个提示词重跑,一重跑就是整段上下文重新计费。更麻烦的是遇到模型“自信地胡说”时,你手上没有任何交叉验证信号,只能靠人工复核兜底,人工成本最后又摊回到整条链路上。
提示:先别急着优化单价,把过去一周每次调用的输入 token 分布导出来看一眼。绝大多数团队的浪费,第一大头永远是重复塞进去的长上下文,而不是模型选贵了。
1.2 67% 的降幅拆解:把数字还原成一笔能复算的账
官方说成本降低 67%,这个数听着夸张,但只要把调用分布拆开,它是经得起算的。我按自己复现时用的一组口径演算一遍,方便你套到自己的场景里。
基线假设:一次评审平均输入 12k token、输出 1.5k token,全部走旗舰档;旗舰档计费按每千输入 token 1 个单位、每千输出 token 3 个单位。单次成本 = 12×1 + 1.5×3 =16.5 个单位。
编排之后,任务被切成小块并按难度分流。实际跑下来比较典型的分流比例是:三成落在轻量档、四成半落在中档、两成半仍然要旗舰档。各档的输入输出量和单价都不一样,具体如下表。
| 档位 | 流量占比 | 平均输入 | 平均输出 | 输入单价 | 输出单价 | 单次成本 |
|---|---|---|---|---|---|---|
| 轻量档 | 30% | 5k | 0.5k | 0.1 | 0.3 | 0.65 |
| 中档 | 45% | 7k | 0.8k | 0.5 | 1.5 | 4.70 |
| 旗舰档 | 25% | 9k | 1.2k | 1.0 | 3.0 | 12.60 |
加权成本 = 0.30×0.65 + 0.45×4.70 + 0.25×12.60 = 0.195 + 2.115 + 3.15 =5.46 个单位。相对基线的 16.5,降幅 66.9%,四舍五入就是那个 67%。
这个账里有两个细节值得注意。一是输入 token 变小了,不是模型变便宜了。切片之后,轻量档只看到自己负责的那一小块 diff 和对应的规范片段,5k 就够了,而不是 12k 全量硬塞。二是输出 token 也变小了,因为一个模型只回答一个问题,不需要在长回答里顺带把整份评审意见都复述一遍。
但账不能只算收益。编排层自身也要花钱:路由判定如果用模型做,得算进去;汇总收敛如果再做一次调用,也得算进去。我实测这部分额外开销在 0.3 到 0.5 个单位之间,计入后总成本约 5.9,降幅回落到 64% 左右。官方口径大概率不含编排自身开销,或者把缓存命中带来的收益一块算了进去。
注意:看到“降本 X%”这类数字,第一件事是问清楚分母是什么。是只算推理费用,还是含编排层、含重试、含人工复核?口径不同,同一个系统能算出三个不同的百分比。
1.3 任务分层:哪些活儿压根不配用旗舰档
把任务按“需要多少理解力”分层,是多模型编排能成立的前提。代码评审场景里,我习惯分成四层,每层的判据不是任务名字,而是这件事出错后的后果严重程度。
第一层是确定性检查。缩进、行宽、命名风格、许可证头缺失、明显的空指针解引用。这类活儿规则引擎加正则就能覆盖八成,剩下的交给轻量档。它们的特点是答案唯一、可验证、不需要推理。
第二层是局部语义检查。变量遮蔽、未使用参数、异常吞掉、日志里打印了敏感字段。这类需要看懂几行到几十行上下文,中档模型足够,偶尔误判也能靠规则兜底。
第三层是跨文件推理。改了接口签名但没改调用方、改了配置项但没改文档、并发路径上的锁顺序问题。这类必须看到多个文件的关系,属于中档到旗舰的交界地带。
第四层是架构级判断。这次改动是否引入了循环依赖、是否违背了分层约束、是否应该拆成两个提交。这类必须旗舰档,而且往往需要多轮对话或者多模型交叉验证。
分层做完你会发现,能落到第一、二层的任务量远超直觉。我统计过自己这边的分布:确定性和局部语义加起来占了七成以上。这七成就是降本的主要来源,也是“一个模型写到底”最亏的地方——它把这七成和最后那三成用同一个价格处理了。
2. HydraFusion 的编排骨架:路由、级联、汇总三件套
2.1 三层结构:调度层、执行层、收敛层
HydraFusion 这个名字听着唬人,拆开看结构其实很朴素,就是三层。
调度层负责一件事:拿到任务之后,判断它该由谁来干、要不要拆、拆几块。这一层是整个系统里最便宜也最关键的,因为它决定了后面所有的钱怎么花。调度层做错了,后面再优化都是给错误的方向加速。
执行层是真正跑模型的地方。它的特点不是“支持多个模型”,而是支持同一个任务在多条路径上并行或串行地跑。并行是为了拿多个视角做交叉验证,串行是为了让便宜的先上、贵的兜底。这两种模式在实现上是一套东西,区别只在编排描述文件里怎么写依赖关系。
收敛层负责把执行层的多路结果合成一个结论。它要处理三种情况:多路一致(直接采纳)、多路冲突(裁决)、部分失败(降级输出并标记待人工)。收敛层是最容易被低估的一层,因为它的失败模式非常隐蔽——它不会报错,它只会给你一个看起来很合理但其实是拼接出来的结论。
三层之间的数据流是单向的:调度层输出一个执行计划 DAG,执行层按 DAG 跑完把结果塞进上下文,收敛层读上下文出最终结论。单向的意思是,执行层不能回头改调度决策。这个约束很重要,它保证了整条链路可回放、可审计。
2.2 路由器怎么判:特征工程比模型选择更重要
很多人做路由的第一反应是“再调一个小模型来判断任务难度”。这个思路能跑,但不是最优解,因为路由本身也要花钱,而且引入了新的不确定性。
我的做法是规则优先、模型兜底。先用一批廉价的确定性特征做初筛,判不出来的才交给分类模型。有效的特征大概有这么几类:
- 改动规模:diff 行数、涉及文件数、新增/删除比例。改动小于 20 行且只碰一个文件,绝大多数情况不需要旗舰档。
- 路径特征:改的是测试文件、文档、配置文件还是核心业务代码。改文档和改测试的路由策略可以激进得多。
- 语法信号:是否包含并发原语、是否涉及序列化、是否改了公开接口签名。这些信号一出现,直接跳旗舰。
- 历史信号:这个文件过去三个月的评审意见密度、被回退次数。高回退率的文件自动升档。
- 相似度信号:当前 diff 和缓存里已有的评审记录有多像。相似度高的直接复用结论,连模型都不用调。
这五类特征里,前两类几乎零成本,第三类需要一次轻量解析,后两类依赖历史数据积累。冷启动阶段只靠前两类就能拿到七成左右的准确率,剩下的靠模型兜底,随着历史数据积累再逐步把规则覆盖率提上去。
提示:路由规则不要一上来就追求准确率。先追求可解释——每一条路由决策都要能打印出“因为 diff 行数 < 20 且路径在 docs/ 下,所以判定为轻量档”。等出了问题,这句话能帮你省掉半天排查。
2.3 级联的两条升级路径:置信度不足与答案冲突
级联(cascade)是多模型编排里性价比最高的一个机制,核心就一句话:让便宜的模型先答,答得不够好再升级。但“不够好”这个判断标准,才是真正见功力的地方。
HydraFusion 里我用了两条升级路径。
第一条是置信度不足。轻量档输出结论的同时,要求它给出一个自评置信度(或者用输出长度、是否包含“不确定”“需要更多上下文”这类措辞作为代理信号)。低于阈值就升级到中档,中档再判一次,还不够就上旗舰。这条路的好处是快,坏处是模型的自我评估经常虚高——它错的时候往往特别自信。
第二条是答案冲突。同一个任务让两个不同档位并行跑(或者同档位跑两次),如果结论不一致,自动升级到更高档做裁决。这条路成本更高,但它抓的是模型自评抓不到的那类错误。我一般只用在高风险任务上,比如涉及权限校验、金额计算的改动。
两条路径可以叠加。实际配置里,我给的策略是:确定性检查只走第一条,局部语义检查两条都走,架构级判断直接跳过级联上旗舰。这个分档策略让绝大多数请求在第一跳就收敛了,只有不到两成会触发升级。
2.4 汇总层:多数投票、加权合并与冲突裁决
收敛层听起来像是把几个答案拼一拼,实际上它是整个系统里最容易出幺蛾子的地方。
多数投票适合答案离散的场景,比如“这段代码有没有空指针风险”,答案就三个:有、没有、不确定。三路结果里两路说有,就采纳。但要注意平票怎么处理——我的做法是平票时一律判为“不确定”并降级到人工,宁可漏报不误报。
加权合并适合答案连续或者可以合并的场景,比如给出一份评审意见列表。这时候不是投票,而是把多路结果取并集再去重,然后按来源档位加权排序。旗舰档提出的意见排前面,轻量档的排后面,重复的合并成一条并保留最高权重的措辞。
冲突裁决是最麻烦的。两路结果直接矛盾(一路说有风险一路说没有),这时候要么再调一次更高档位,要么按预设的优先级直接采信某一路。我倾向于前者,但要加预算帽——裁决调用超过一次还没收敛,就直接输出“存在分歧,建议人工复核”,把问题暴露出来而不是强行合并。
这里有个反直觉的经验:汇总层的输出质量,很大程度上取决于它敢不敢说“我不知道”。我见过太多编排系统,收敛逻辑写得特别“聪明”,任何输入都能合成一个像模像样的结论。这种系统在演示时很好看,上线后就是灾难,因为它把模型的不确定性全部抹平了,让下游误以为结论是可靠的。
2.5 一份最小可跑的编排描述文件
说了这么多概念,落到配置上其实不长。下面这份是我自己用的最小版本,跑通了路由、级联、汇总三个环节。字段名按你自己的框架调整就行。
pipeline: code-review version: 3 stages: - id: route type: classifier strategy: rules_first rules: - if: "diff_lines < 20 and paths_all_under('docs/')" then: tier-light - if: "touches_public_api or contains_concurrency_primitive" then: tier-flagship - if: "diff_lines < 200 and single_file" then: tier-medium fallback: tier-medium budget_units: 0.05 - id: review type: execute depends_on: [route] mode: cascade ladder: [tier-light, tier-medium, tier-flagship] escalate_on: confidence_below: 0.72 conflicting_answers: true max_upgrades: 2 timeout_seconds: 45 - id: merge type: converge depends_on: [review] strategy: weighted_union weights: {tier-flagship: 3, tier-medium: 2, tier-light: 1} on_conflict: escalate_once on_unresolved: emit_manual_flag budgets: per_task_units: 8 per_pipeline_run_units: 200配置里有几个字段是踩坑之后才加上的,值得单独说。max_upgrades: 2是防止级联无限升级——最早没限制,遇到一个特别刁钻的 diff,它在轻量、中档、旗舰之间来回升了七次,单次任务成本直接翻了三倍。on_unresolved: emit_manual_flag是上面说的“敢说不知道”。per_pipeline_run_units是整条流水线的总预算帽,超过直接熔断,避免某次大批量提交把当月额度打穿。
3. 接到评审流水线上的落地步骤
3.1 先切任务再选模型:diff 分块与上下文裁剪
新手最容易犯的错是先选模型再想任务怎么切。顺序反了。正确的顺序是:先把一次评审拆成若干个自包含的小问题,再看每个问题需要什么档位。
diff 分块的原则是按语义边界切,不按行数切。一个函数、一个类、一个配置段落是天然的边界。按固定行数切会把一个完整的逻辑切两半,两边都看不懂。我用的切分规则是:以 hunk 为最小单位,合并同一文件内相邻且属于同一语法块的 hunk,单个块超过 400 行再按函数边界二次切分。
上下文裁剪的原则是只带必要的东西。一个检查命名规范的子任务,需要的是这个文件的命名风格约定和改动的那几行,不需要整个仓库的目录树。一个检查接口兼容性的子任务,需要的是被改动的签名和所有调用点,不需要改动文件的完整内容。
这里有个具体数字可以参考:切片裁剪做到位之后,轻量档子任务的输入从平均 12k token 降到 5k 左右,中档从 12k 降到 7k。这不是靠压缩算法,纯粹是靠“不给模型看它用不上的东西”。很多人优化成本只知道换模型,其实裁剪上下文带来的收益往往更大,而且不影响质量。
3.2 路由规则表的写法与冷启动
路由规则不建议写成一堆 if-else 散在代码里,也不建议一上来就训练分类器。我的做法是维护一张显式的规则表,每条规则有优先级、条件、目标档位和一个可选的置信度修正。
| 优先级 | 条件 | 目标档位 | 说明 |
|---|---|---|---|
| 1 | 涉及鉴权、金额、加密相关路径 | 旗舰 | 出错代价过高,不做成本优化 |
| 2 | 改动公开接口签名 | 旗舰 | 跨文件影响面不可控 |
| 3 | 仅文档或注释改动 | 轻量 | 只需事实性检查 |
| 4 | 仅测试文件改动 | 轻量 | 断言逻辑简单 |
| 5 | 单文件且改动小于 200 行 | 中档 | 主力档位 |
| 6 | 其余情况 | 中档 | 兜底 |
冷启动阶段,这张表的前三条规则就能挡掉大部分高风险误判,剩下的全走中档,虽然省得不多但不会出事。跑上两周,把每次路由决策和最终的人工采纳结果对一遍,就能算出每条规则的实际准确率,然后针对性调整。
我踩过一个坑:一开始把“改动小于 20 行”直接路由到轻量档,结果漏了好几个小而致命的改动——比如把!=改成==、把>改成>=,一共就两行。后来加了“包含比较运算符变更”这个特征,强制升档才堵住。教训是行数少不等于简单,越是精炼的改动越可能藏着逻辑陷阱。
3.3 缓存、去重与指纹设计
降本的第二大来源是缓存,而且这块的收益经常被完全忽略。
缓存的键不能简单用 diff 内容的哈希。同一个逻辑改动,格式化工具跑一遍、变量重命名一次,哈希就变了,但语义没变。我用的是三层指纹:
第一层是文件路径 + 语法块类型 + 归一化后的 AST 摘要。归一化指的是把变量名替换成占位符、去掉注释和空白。这一层能抓住“换了名字但逻辑一样”的情况。
第二层是规则集版本号。规则改了,缓存必须失效,否则你会拿到用旧规则评出来的结论。
第三层是模型档位 + 提示词版本。同一个子任务在不同档位下的输出不能互相复用,提示词改了同理。
三层拼起来做缓存键,命中率在我们这条流水线上稳定在 30% 到 40%。这部分请求完全不产生推理费用,是实打实的净收益。而且它还顺带解决了另一个问题:同一份代码反复提交时,评审意见不会因为模型的随机性而变来变去,用户体验好很多。
注意:缓存别设太长的过期时间。代码库的规范会演进,三个月前的评审结论套到今天可能已经过时。我给的是 21 天,超过就重新评估。
3.4 灰度与质量对齐:怎么证明没降质
降本这件事,最怕的是“成本降了 67%,然后没人发现质量也降了”。上线前必须有一套质量对齐机制,否则你没法回答“这个 67% 是不是靠偷工减料换来的”。
我的做法是准备一个固定回放集:从历史评审记录里挑 300 条,覆盖四个难度层级,每条都有人工确认过的标准答案。每次改动路由规则或者升级阈值,先在这个集合上跑一遍,输出三个指标:召回率(该报的问题报出来没有)、精确率(报出来的问题有多少是真问题)、平均成本。
只有三个指标同时满足条件才允许上线:召回率不比基线低超过 2 个百分点,精确率不比基线低超过 3 个百分点,平均成本确实下降。这三条里,召回率的权重最高——漏报一个真问题,比多报十个假问题严重得多。
灰度阶段我按流量比例分三档:5%、20%、100%。每档至少观察三天,重点看两个信号:人工复核的驳回率有没有异常上升,以及“不确定”标记的比例有没有暴涨。后者特别重要,如果系统开始大量输出“建议人工复核”,说明级联在频繁失败,成本可能没降反升。
4. 实测中的四类翻车:误判、抖动、汇总失真、预算失控
4.1 路由器误判的三种形态
路由误判是编排系统最常见的故障源,我遇到过的可以归成三类。
第一类是低估难度。一个看起来人畜无害的配置改动,实际上影响了一条关键链路的重试策略。这类误判的根因通常是路由只看局部特征,看不到全局影响。补救办法是加一层“影响面分析”:解析改动的符号被哪些地方引用,引用数超过阈值就强制升档。这个分析本身很便宜,一次符号表查询的事。
第二类是高估难度。大批量格式化提交,改了两千行但全是空白和换行,路由器一看行数直接甩给旗舰档,结果旗舰档认真分析了半天告诉你“这只是格式调整”。这类误判纯粹是浪费钱,解决办法是在路由前加一道预处理器:剥离纯格式改动,如果剥离后 diff 为空,直接短路返回。
第三类是摇摆。同一个提交,今天判成中档,明天判成旗舰,因为某个特征刚好卡在阈值边缘。这类问题最烦人,因为它让成本变得不可预测。解决办法是给每个阈值加一个滞回区间,比如“改动 180 到 220 行之间保持上一次的判定结果”,减少抖动。
4.2 升级阈值设错引发的连锁反应
级联的阈值是个牵一发动全身的参数。我最早把置信度阈值设成 0.9,想着宁可多升级也别漏判。结果是什么呢?九成请求都升级到了中档,中档又有六成升级到旗舰,整体成本比单模型还高,因为同一个任务被跑了两次甚至三次。
后来把阈值降到 0.72,又出了另一个问题:轻量档在一批涉及异常处理的代码上给出了错误的“无风险”结论,因为它自评置信度是 0.8,刚好过线。这类错误的共同特点是模型不知道自己不知道。
最终的解法不是找一个完美阈值,而是分层设阈值 + 按任务类型调整。确定性检查用 0.65(错了也影响不大),局部语义用 0.78(需要一定谨慎),跨文件推理不用置信度路径、直接走答案冲突路径。这套组合让升级率从九成降到两成以下,同时漏报率没有明显上升。
还有一点:升级次数必须设上限。我见过一次没有上限的级联,在一个特别复杂的 diff 上,轻量→中档→旗舰→中档→旗舰来回折腾,单次任务跑了七轮,花了正常情况的四倍钱,最后还是输出“不确定”。加上max_upgrades之后,这种情况会在第三跳直接熔断并转人工。
4.3 汇总层的虚假一致
这一类故障最隐蔽,也最值得单独拿出来讲。
有一次线上反馈,说系统对一批“资源未释放”的问题集体漏报了。查日志发现,那批任务走的是三路并行的配置:轻量两路、中档一路。三路结果里,轻量两路都说“没有资源泄漏风险”,中档那一路说“存在风险”。按多数投票,两票对一票,判为无风险。
问题在于,那两路轻量档跑的是同一个模型、同一个提示词、只是温度不同,它们的结论高度相关,根本不是独立投票。用两个相关的信号去做多数投票,等于把同一个错误算了两遍。这就是我说的“虚假一致”——看起来是三路共识,实际上是一路意见被复制了。
修复方案有两个层次。浅层是改投票权重:不同档位的票不等权,旗舰的票权重 3、中档 2、轻量 1,这样两路轻量的总权重 2 干不过一路中档的 2 加个平票规则。深层是改并行配置:多样性要来自真正的差异(不同档位、不同提示词策略、不同上下文切法),而不是同一个模型的温度扰动。
提示:如果你的编排系统里多路并行都是同一档位,那投票机制基本是自欺欺人。要么换成真正的异构组合,要么干脆取消投票,只跑一次。
4.4 一条可复现的排查链路
遇到“降本了但质量出问题”的情况,我一般是这么查的,顺序别乱。
第一步,定位问题任务的编排轨迹。每条任务都应该有完整记录:走了哪条路由规则、触发了几次升级、每路的输出是什么、汇总层为什么这么合。没有这条轨迹,后面全是猜。我最早没做这个记录,出了事只能重跑,重跑结果又不一样,排查效率极低。
第二步,判断问题出在路由、执行还是汇总。方法是把轨迹里每一路的原始输出单独拿出来,人工判断哪一路是对的。如果每一路单独看都对,合起来错,那就是汇总层的锅;如果某一路本身就是错的,且它本该被升级却没升,那是路由或阈值的问题。
第三步,在回放集上复现。把这条任务加进固定回放集,然后针对怀疑的原因改一个参数,跑一遍看是否修复。这一步必须离线做,别在线上试。
第四步,确认没有引入新的回退。改完参数后,整个回放集重跑一遍,看召回率、精确率、成本三个指标的完整变化,而不是只看那一条修复了没有。我吃过亏:为了修一个召回问题,调高了升级阈值,结果精确率掉了 6 个点。
5. 把 67% 变成稳定收益的调参手册
5.1 阈值、预算帽、超时的三角联动
这三个参数必须一起调,单独调任何一个都会出问题。
升级阈值决定级联的收敛速度。阈值高,升级多,成本高但质量稳;阈值低,升级少,成本低但风险大。
预算帽决定单次任务最多能花多少钱。没有预算帽,一次异常任务就能把当天的额度吃掉一大半。我设置的口径是正常任务平均成本的 4 到 5 倍,超了就熔断转人工。
超时决定级联最多能等多久。级联的每一跳都要时间,三跳下来可能超过一分钟。在评审这类准实时场景里,超过 45 秒用户就开始刷新了。我的做法是给每一跳设独立的超时,前两跳卡紧一点(10 秒、15 秒),最后一跳放宽(25 秒),总时长控制住。
三者的联动关系是这样的:预算帽决定了最多能升几次级,超时决定了在限定时间内最多能升几次级,实际升几次级由阈值决定。所以调参的顺序是:先定超时(由业务场景决定,不商量),再定预算帽(由日额度倒推),最后调阈值让大部分请求在超时和预算之内收敛。
5.2 离线回放:把线上试错搬到本地
线上调参是大忌。一来样本量太小,改一个参数要观察好几天才能看出效果;二来出了问题影响真实用户;三来线上流量是变化的,同一个参数在周一和周五的表现可能完全不同。
回放集的做法前面提过,这里补充几个建设要点。
样本要分层。四个难度层级各占一定比例,不能全挑简单的,否则测不出级联的效果。我按 20%、35%、30%、15% 分配,和高难度任务的真实分布接近。
样本要留出时间跨度。全挑最近一周的,会过拟合当前的代码风格。我一般取最近六个月,均匀采样。
要有对抗样本。专门挑那些历史上有过争议、被人工推翻过的案例。这类样本最能测出系统的边界。
定期刷新。代码库在演进,半年前的回放集可能已经不代表现在的场景。我每季度更新三分之一。
回放的执行方式很简单:一个脚本读样本、跑完整编排链路、记录三指标、和上次结果做 diff。每次改配置前跑一遍,改完再跑一遍,对比着看。这套流程跑顺了之后,调参从“拍脑袋 + 观察一周”变成了“改配置 + 等十分钟”。
5.3 帕累托曲线与“够用就好”的拐点
多模型编排最容易陷入的误区是无限追求“成本更低”。实际上成本和质量的权衡是有一条帕累托曲线的,越往左下走越难,收益递减非常明显。
我的做法是扫一遍阈值,把每个阈值下的(平均成本,召回率)点画出来,连成曲线。曲线的左端是“极低成本 + 中等召回”,右端是“接近单模型成本 + 最高召回”。大多数团队会本能地想往右端靠,觉得质量最重要。
但看曲线你会发现,从左往右走,前期成本涨一点、召回涨很多,到了某个点之后,成本继续涨、召回几乎不动。那个点就是拐点。我的经验是这个拐点大概在“成本是理论最优的 1.3 到 1.5 倍”的位置,具体取决于任务类型。
找到拐点之后,还要做一件事:确认拐点左侧的质量损失是可接受的。这需要业务方参与判断——漏报一个格式化问题无所谓,漏报一个并发死锁就可能出事故。把任务按后果严重程度分类,不同类别取曲线上的不同点,这才是负责任的优化方式。
我们最后落地的配置是:确定性检查压在曲线最左端,局部语义取拐点,跨文件和架构级直接给到曲线右端不做优化。整体成本降幅落在 62% 到 68% 之间浮动,取决于当批提交的难度分布。这个区间比官方那个 67% 更真实——降本数字从来不是一个固定值,是跟着流量结构飘的。
6. 三种不该上多模型编排的情形
6.1 任务没有分层空间
编排能降本的前提是任务本身难度有分布。如果你们的场景里每一次任务都是硬骨头——比如专门做架构评审、专门做安全审计——那分层就是伪命题,全部得走旗舰档,加了编排层只是多了一次路由开销和一层延迟。
判断方法很简单:把过去一个月的任务抽一百条,人工标一遍“这条用轻量档能不能做对”。如果有八成以上答“不能”,那就别上编排,老老实实用单模型,把精力放到上下文裁剪和缓存上,这两块的收益更确定。
6.2 编排层的运维成本被低估
多模型编排不是配完就完事的,它需要持续的运维投入:路由规则要跟着代码库演进调整,回放集要定期刷新,阈值要跟着模型版本更新重调,出了问题要能读懂编排轨迹。这套东西的隐性成本,对一个小团队来说可能比省下来的推理费用还高。
我见过一个三人小组上了编排,结果每个月花在维护规则和排查故障上的时间,抵得上省下的那点钱。后来他们退回到“单模型 + 缓存 + 上下文裁剪”的简单方案,成本降了四成,维护成本几乎为零。
一个粗略的判断标准:如果你们团队每周花在评审流水线上的运维时间超过半天,先别加编排的复杂度,把现有链路做扎实。
6.3 强一致性场景
有些场景要求同一个输入必须得到同一个输出,比如合规检查、审计留痕。多模型编排天然带随机性——路由可能抖动、级联可能升或不升、汇总可能因为一路超时而改变结果。虽然可以靠缓存强制对齐,但一旦缓存失效或者规则更新,历史结论和当前结论就可能不一致,这在审计场景里是硬伤。
这类场景要么固定走单一档位,要么在输出里明确记录“本次结论由哪一路产生、用了什么配置”,把不确定性显式化。后者更现实一些,但需要下游系统能接受这种带版本的结论。
最后分享一个我个人一直在用的小技巧:给每条评审结论加一个极简的来源标记,比如结论末尾跟上档位代号和是否经过升级。这个标记对人来说几乎不可见,但排查问题时它能把定位时间从半小时压缩到两分钟。我在实际操作中的体会是,多模型编排的价值不在于省下的那点钱,而在于它逼着你去想清楚每个任务到底需要多少理解力——这个思考过程本身,往往比降本数字更值钱。