上下文被污染:一次错误假设留在历史里,后面全跟着错
数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。
大多数人把这个问题归成”模型变笨了”,其实是历史里躺着一句错的前提,它每一轮都被当成既定事实重新读一遍。 你在第 3 轮说”这个项目用的是 A 方案”,模型信了;第 9 轮你发现其实是 B,你打了一句”不对,是 B”,它回你”抱歉,我更正一下”,然后第 11 轮生成的代码里依然按 A 的假设写。不是它不听话,是那句错的前提还在历史里,你的更正只是又一条对话,而不是把前面那条删掉。模型每轮做的事是读一遍能读到的全部内容再往下推,读到两条互相矛盾的陈述时,它并不天然知道哪条该赢。
这类故障最坑的地方是它不报错。额度不够会给你提示,网络超时会在客户端抛出一个网络层错误码(Node 环境下常见的是 ETIMEDOUT),鉴权失败会返回 401 这类状态码,只有上下文污染完全静默:输出格式正常、语气自信、代码能跑,只是建在一个错的地基上。你越往下追加需求,错的地基埋得越深,最后你花两小时在调一个从第 3 轮就注定的错。
先把边界说清楚,站内三篇讲的是三件不同的事。如果你的症状是”越聊到后面它越不记得开头说过什么”,那是遗忘,属于容量与截断的问题,看多轮对话越聊越忘;如果症状是”同一个 bug 改了六遍,每次都换个说法,最后绕回第一版”,那是修复循环,属于反馈信号不足的问题,看AI 修不好的思维循环。本篇只管一种:历史里存在一条明确错误的前提,之后所有推理都在它之上,你需要识别它、定位它、把它清掉。想先补机制底子的,可以看上下文窗口是什么。
一、确认是污染:一次对照实验,两分钟出结论
不要靠猜。污染有一个极其干净的判别方法:开一个全新会话,把当前这个任务用最短的话重新问一遍,不带任何历史。
- 新会话答对了,老会话答错 —— 是污染。差异只可能来自历史。
- 新会话也答错 —— 不是污染。是能力边界、是你的描述本身有歧义、或者是任务真的难。别再清场,清场解决不了。这里有一个容易漏的前提:你在新会话里的提问不能夹带那条错的假设,否则你只是把污染手动复制了一份,得到的”也答错”是假阴性。所以重问的时候,只描述目标和现状,不要复述你之前的结论。
- 新会话答对但你把老会话前几轮原文粘过去又错了 —— 恭喜,你已经定位到了污染源就在那几轮里。
这个实验值得成为你的第一反应。它的成本是一次提问,收益是把”模型不行”和”会话脏了”这两条完全不同的路分开。我自己的判断是:在多轮编码会话里,被归因成”模型今天不聪明”的问题,相当一部分实际是这一类,因为它的表现和能力不足高度相似。
污染源通常是这四类东西之一,按出现频率排:
一是你自己给错的事实。项目结构说错了、字段名记错了、说”我们没用 TypeScript”结果用了。这是最常见的,也最容易被忽略,因为你不会怀疑自己那句话。
二是模型自己编出来后被你默认接受的东西。它猜了一个不存在的函数名,你没纠正,只说了”继续”,那个函数名从此就是这个会话里的事实。
三是过时的中间状态。第 4 轮它给了一版实现,你在编辑器里手改了一半,但会话里躺着的还是那一版原文。它后面所有的改动都是基于旧版本算 diff,你合并的时候就会看到莫名其妙的冲突标记。
四是被否决的方案没被明确注销。你说”这个思路先放一放”,“放一放”在语义上不等于”这个方案是错的、以后不要再用”。后面它会重新拾起来。
二、从现象反查成因:判别表
| 现象 | 大概率成因 | 怎么验证 | 处置动作 |
|---|---|---|---|
| 你纠正后它口头认错,下一轮输出照旧 | 错误前提仍在历史中,更正只是新增而未覆盖 | 直接问它”现在你认为这个项目用的是哪个方案,依据是哪一句” | 定点更正 + 要求它重述前提(见第三节等级一) |
| 反复引用一个不存在的文件名/函数名 | 早前的编造被默认接受,成了会话内事实 | 在本地执行搜索确认该标识符是否存在 | 明确注销该标识符,并给出真实清单替代 |
| 生成的补丁打不上,冲突标记指向你早已改过的代码 | 会话里的代码版本是旧的 | 打开该文件看当前内容,再用 git diff HEAD -- 文件路径 看它相对上次提交改了哪些行,跟模型引用的片段逐段对 | 重发当前真实文件内容,或直接清场重开 |
| 越改越复杂,每轮都在给上一轮补丁打补丁 | 早期方向性错误未被清除,后续都是修补 | 数一下:从第几轮开始改动只是在兜底而不是在解决 | 回滚到那一轮之前的代码,新会话重做 |
| 明确否决过的方案又冒出来 | 否决表述太软,未构成硬约束 | 检查你当时的原话是不是”先放一放""暂时不考虑” | 用”禁止使用 X,原因是 Y”重新表述并写进项目约定 |
| 换新会话同样答错 | 不是污染 | 上一节的对照实验 | 停止清场,改去拆解任务或换模型 |
表里最后一行是防止你把时间花在错的方向上,别跳过。
三、动作分级:从定点更正到彻底清场
按成本从低到高,依次尝试,不要一上来就重开——重开会丢掉那些真正有用的历史。
等级一,定点更正加回述确认。 适用于污染源单一且你知道它在哪。做法不是说”不对,是 B”,而是三句话:指出哪条陈述是错的、给出正确的、要求它把当前认定的前提列一遍。第三句是关键,你要看到它把前提复述出来,才算确认覆盖生效。它复述出来的东西和你以为的不一样,是很常见的情况。
等级二,摘要重启。 适用于污染源分散、历史又太长不想全丢。做法是让它先输出一份”当前已确认的事实 + 已完成的改动 + 待办”的清单,你逐条审一遍,把错的划掉,然后开新会话,只带这份审过的清单进去。注意顺序:先审再带,不审就带等于把污染搬了个家。这一步的手工审核不能省,它是整个流程里唯一防止污染继承的环节。
等级三,彻底清场。 适用于你已经分不清哪些前提是对的。清场不只是开新会话,代码侧也要一起清,否则你带着一堆半成品改动进新会话,等于换了个地方继续脏。可参考的做法:
# 先把当前改动存起来,别直接丢
# 加 -u 是因为默认只收已跟踪文件的改动,新建的文件会被留在工作区
git stash push -u -m "wip: 污染会话产出,待复核"
# 确认工作区回到干净基线:这条应该没有任何输出
git status --short
# 想留档但不想污染主分支时,另起一个分支重做
git switch -c redo/clean-context
新会话的开场只带三种东西:真实的代码现状(贴文件或指路径)、可验证的目标(跑什么命令、期望什么输出)、硬约束(禁止用什么、必须用什么)。不带任何”上次它说过”。
结构性预防。 反复出现的前提别靠每次口述,写进项目里的约定文件,让它成为每个会话的起点,这样至少地基是同一个。工程化的做法和长会话的分段策略,可以看Claude Code 上下文管理;如果你跑的是长时间自动执行的流程,污染的破坏面会更大,因为没人在中间看着,见Agent 上下文管理。
顺带说清楚一件事:一部分海外工具与模型的官方服务对中国大陆有区域限制、不支持直连,市面上确实存在第三方中转,但我不背书也不给具体渠道,你自己判断合规与数据风险。
四、什么情况下别再折腾了
排错要有止损点,否则你会在一个脏会话里耗掉一整个下午。以下任一条命中,立刻停手换路:
- 同一个前提你纠正过两次,第三次它还在错。 这不是表述问题,是历史里的矛盾陈述太多,谁赢已经不可控。直接跳等级三。
- 你开始不确定哪个版本的代码是真的。 一旦你需要来回滚屏找”它说的那个文件到底是哪一版”,这个会话的信息价值已经是负的。
- 回滚点还在,且重做估时短于继续排错。 只要 stash 或某个分支上有一个干净基线,从那里重做的预估时间又小于继续在脏会话里纠缠的预估时间,就回滚。判断依据很朴素:数一下从哪一轮开始,你的每条消息都在纠正而不是在推进,那一轮之前就是回滚点。
- 新会话对照实验证明不是污染。 前面说过,这时候清场是无用功。该做的是把任务切小、把验收条件写成可执行的命令、或者换个模型试。
- 你已经改了三处以上无关文件。 污染常见的次生伤害是改动范围扩散。范围一散,回滚成本每分钟都在涨,趁早收。
反过来,有一种情况不该清场:会话里积累了大量正确且难以重建的信息(比如你花了半小时才讲清楚的业务规则)。这时优先走等级二,把资产捞出来再重开。
五、避坑清单
坑一:把”抱歉,我更正一下”当成修复完成。 为什么会踩——那句话读起来像确认,人天然接受道歉即修正。怎么避——不看态度看事实,要求它复述当前前提,或者直接看下一轮输出里那个错的假设有没有消失。它认错是零成本的,改前提不是。
坑二:用软话否决方案。 为什么会踩——口语里”先不考虑”就等于否决,但在文本历史里它是一个弱信号,和更强的技术论证并列时会被压过去。怎么避——否决写成硬约束句式:禁止用 X,原因是 Y,替代是 Z。带原因是为了让它在遇到边界情况时不会”善意”地退回去。
坑三:手改了代码却不同步回会话。 为什么会踩——你在编辑器里改是顺手的事,回头贴给它是额外动作,很容易漏。怎么避——凡是你手动改过的文件,下一轮要么重贴当前内容,要么明确说”这个文件我改了,以你之前那版为准会出错”。别让它拿旧版算增量。
坑四:清场时只换会话不换代码基线。 为什么会踩——注意力都在”重开一个干净会话”上,忘了工作区里还堆着上一轮的半成品。怎么避——清场动作固定成一对:会话新开 + 工作区确认干净(或明确列出保留哪些改动)。
坑五:带着未审核的自动摘要重开。 为什么会踩——让模型自己总结再继续,操作上最省事,但摘要是从被污染的历史里提炼的,错前提往往会被浓缩成一句更简洁、更像结论的话,反而更难被发现。怎么避——摘要必须人过一遍,尤其看”已确认事实”那几条,每条问自己一句:这是我核实过的,还是它推出来的。
坑六:一次会话里塞多个不相关任务。 为什么会踩——切任务比开会话方便。怎么避——任务之间的前提是不相通的,A 任务里的错假设会污染 B 任务的推理。按任务开会话,这是最省事的隔离手段。
坑七:把污染当成模型退化去换模型。 为什么会踩——症状确实像”今天不太行”。怎么避——换模型之前先做对照实验。省下的不只是时间,还有你对工具的错误认知,那个认知会影响你后面很多决策。
六、收尾
上下文污染的本质是:会话历史是追加的,而你的认知是修订的,两者不同步的那部分就是错误的来源。所以处理它的核心动作只有两个——让错前提显性化(问出来、看它复述),以及在必要时果断丢掉历史(清场,连代码基线一起)。
给你一份可以贴在手边的自检清单,出问题时从上往下走:
- 开新会话原样问一遍,还错吗?错 → 不是污染,去拆任务。
- 问它”你现在认定的前提有哪些”,逐条比对,有错的吗?
- 它引用的文件名、函数名,在本地真实存在吗?
- 它引用的代码片段,和工作区文件里的当前内容一致吗(
git diff HEAD -- 文件路径能看出你手改了哪几行)? - 从哪一轮开始,我的消息只在纠正、不在推进?那一轮之前是回滚点。
- 同一个前提我纠正过几次?到第三次就别修了,清场。
- 清场前,有没有值得捞出来的信息资产?捞出来的清单我审过吗?
排错的顺序比排错的技巧更值钱。先花两分钟做对照实验,能省掉后面两小时的方向性浪费。