装得下不等于用得好
百万 token 上下文铺开的第一周,我做了一件很蠢的事:把一个中型前端仓库整包塞进去,让它读完再改。结果它非常自信地调用了一个不存在的 store 方法,还顺手修复了几处上一轮我自己写错的地方------修复的方向也是错的。
窗口从 32k 涨到 128k 再到 1m,很多团队把它当成再也不用做检索的理由。但上下文长度解决的是能塞多少,没有解决该看哪一段。这两个问题的难度完全不在一个量级。
我的混合方案:先检索压缩,再让长窗口交叉比对
现在的链路是三段:
- 向量检索把候选文件从几百个压到十个左右,这一步保证召回的性价比;
- 把这十个片段连同调用链一起丢进长窗口,让模型在大约一千行的规模里做交叉比对,这是长上下文真正擅长的活儿,它能在同一次推理里同时看见 useEffect 和那个被遗漏的依赖;
- 最后要求它在三百字以内给出结论与改动点,防止它把检索到的原文重新复述一遍。
第二步是关键。窗口小的时候只能做单文件问答,跨文件的一致性检查根本做不了,全靠人肉 grep。现在这块确实可以交出去。
位置偏置比容量更早成为瓶颈
有个现象困扰了我两天:同一个提示词,偶尔会完全无视工具说明。后来我把工具的 schema 从对话中段挪到系统段的第一个位置,异常从此消失。
这不是玄学。长上下文里的中间遗忘是公开结论,模型对开头和结尾的注意力显著高于中部。所以真正的工程手段不是塞更多,而是把最想让它遵守的东西放在首尾,中间留给可以被复述的材料。Coze 和阿里云百炼公开的实践里,长上下文也基本都是放在检索之后做二次精读,而不是替代检索。
提示词反而写短了
窗口变大之后,我的系统提示词从一千多字删到不足四百字,只留三类东西:
- 任务边界:什么情况必须停下来问,而不是自己猜;
- 输出格式:给 diff 还是整文件,要不要一起补测试;
- 硬性禁止:不许改的目录、不许引入的依赖。
以前那些请你仔细思考、你是一个资深工程师的填充句,在短窗口时代是用来提高遵循率的,在长窗口时代只是稀释注意力。至于具体示例,我把它挪到了用户消息的最后一轮,效果比写在系统段好得多。
一点结论
先建可观测性,再扩窗口。至少要能区分没检索到和看到了但没采纳,否则你只会得出模型不行这个既昂贵又无用的结论。
上下文长度是个好特性,但它替代不了工程里的老手艺:切分、召回、排序、约束输出。这几件事做得越好,长窗口才越值钱。