辩论太长喂不进去——综合前的一次压缩
Open Council 复盘:多模型 × 多轮辩论产生的文本会撑爆主席的上下文。核心保全、外围截断、代码块绝不动,以及一个「先上确定性、把 LLM 摘要留到二期」的诚实取舍。
Open Council 的一场 debate 是这样的:几个模型各自作答,互相评审,分歧大就再交叉质询一轮,最后交给一个「主席」模型综合成最终答案。问题是——几个模型 × 好几轮,喂给主席的文本量线性膨胀,可能直接超出它的上下文窗口。 综合前得先压一压。
什么时候压:一道 6 万字符的门
压缩不是每次都做。综合前先估一下所有回答的总字符数,不超过 60000 字符就直接跳过,原样喂给主席。
这个 6 万的门是算出来的:假设主席的上下文窗口约 10 万字符(混合内容大约 2.5 万 token),取 0.6 的比例作为触发线,0.6 × 100000 = 60000。留 40% 的余量给主席自己的输出——压缩的目标从来不是「塞满窗口」,而是「留够空间让它还能好好回答」。
压什么、保什么:按评审分排座次
超过门槛,就要决定哪些回答留全文、哪些压。排序的依据很直接:
- debate 模式有评审分 → 按评审总分降序;
- compare 模式没评审分 → 按模型
priority升序(优先级高的靠前)。
排完之后,前 2 名整篇保留(全文不动),其余的才压。 逻辑是:主席综合时,评价最高的两个回答值得逐字读;排在后面的,留个要点就够了。把有限的上下文预算,优先花在最可能被采纳的观点上。
怎么压:代码块绝不动,头尾留住,中间抽要点
真正的压缩不是把长文一刀切。对每一篇要压的回答:
- 先把代码块抠出来存好。用正则把 ``` 围栏代码块换成占位符,代码块永远逐字保留——技术回答里,一段被截断的代码就是废的,宁可别的地方多压一点;
- 按行切,保头 15 行、保尾 10 行。开头通常是立场和结论,结尾常是收束,这两头信息密度最高;
- 中间不是直接扔,而是抽关键行:标题(
#)、项目符号(-*)、编号、加粗行,取前 3 条、每条截到 60 字,拼成一句[……N 行已压缩。要点:……]塞回中段; - 最后把代码块放回原位。
压缩结果另存在 response_compressed 字段,不覆盖原文 response_raw。综合阶段用 response_compressed ?? response_raw 消费——压过就用压缩版,没压过用原文;而原文一直在,审计和回放时能看到模型到底说了什么。压缩是给主席看的一次性视图,不是对原始数据的破坏性修改。
一个诚实的落差:LLM 摘要还没接线
这里要说一件实话。「外围回答做摘要」这个说法,容易让人以为是让模型把长文智能压成三五条核心论点。代码里确实设计了这条 LLM 自摘要的路径——有现成的摘要 prompt,让原模型把自己的回答压成「核心论点 3-5 条 / 关键结论 / 差异点 / 风险」。
但目前真正接线跑着的,只有上面那套结构化截断(保头尾 + 抽要点 + 护住代码块)。LLM 摘要那条路写好了,还没接到编排流程里。
这是个有意识的工程排序,值得说清楚:先上确定性的那一版——截断零额外 LLM 调用、零额外延迟、结果可预测,先把「文本撑爆上下文」这个真问题挡住;LLM 摘要更聪明,但要多花一次调用、多一层不确定(模型可能摘错重点),留作二期。把「能确定解决问题的笨办法」先上,把「更优雅但更贵更不可控的办法」排后面,是长辩论这种成本敏感场景里合理的取舍。写文章时把这个现状讲清楚,比把设计稿当成已实现更诚实。
小结
给一场多轮多模型辩论的输出做「喂给主席前」的压缩:
- 一道算出来的门:总字符超 60000(= 0.6 × 10 万上下文)才压,留 40% 余量给主席输出;
- 按评审分保 Top-2 全文:把有限预算花在最可能被采纳的观点上;
- 代码块绝不动:技术回答里截断的代码就是废的,先护住它再压别处;
- 压缩不毁原文:压缩版另存,原文保留供审计回放;
- 先上确定性,LLM 摘要留二期:截断是零延迟零调用的笨办法,先挡住真问题,聪明但更贵的方案排后面。
一句话:压缩的难点不在「怎么压得更短」,而在「压的时候,先想清楚哪些信息一个字都不能丢」。
留言