越聊越忘:长对话被压缩后关键约束丢失该怎么排查

2026-07-28

数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。

绝大多数「越聊越忘」不是模型变笨了,而是你把约束放错了地方——它只存在于对话历史里,而对话历史是一种会被裁剪、被摘要、随时可能改写的易失介质。 把这件事归因成「今天降智了」,你接下来的动作就全是徒劳的:换个说法再强调一遍、加重语气、多敲几个感叹号,然后在下一次压缩里又丢一次。真正要改的是存放位置,而不是措辞强度。

这篇只讲一件事:约束在长会话中蒸发的排查与加固。如果你想先补机制层面的基础,看 [上下文窗口是什么?为什么 AI 会「忘记」前](/learn/shangxiawen-chuangkou-shi-shenme/);日常会话怎么切分、什么时候该主动清空,在 [Claude Code 上下文管理](/learn/claude-code-shangxiawen-guanli/);规则文件本身怎么组织、写多长,在 [CLAUDE.md 怎么写?给 AI 立项目](/learn/claude-md-zenme-xie/);如果症状是它压根不了解你这个仓库的既有写法,那是另一类问题,看 [仓库太大塞不进上下文,先别急着换模型](/learn/dacangku-shangxiawen-buzu/)。本篇的位置是这几篇的中间层:现象已经出现了,怎么定位到具体成因,再决定加固还是止损。

一、先分清是忘了,还是从来没记住

排查的第一步不是加固,是分因。三种成因看起来一模一样,处置动作完全相反。

成因 A:摘要有损。 会话变长后,早期轮次会被压成一段概要。概要保留的是「在做什么」,最容易丢的是三类东西:否定式约束(别动某个目录)、一次性口头补丁(这个字段特殊处理)、以及已经推翻过的决策链(先定了甲,后来改成乙,摘要里只剩甲)。各家工具的压缩时机和策略不同且会调整,以官方最新说明为准,但有损这个性质是共通的。

成因 B:窗口挤压。 约束还在历史里,但被大块内容顶到了注意力的边缘。典型触发是工具调用返回了整个文件、整份日志、整页搜索结果。这类会话的表现是回答开始泛化,不再点具体文件名和函数名。

成因 C:约束本身不成立。 你写的是「保持风格一致」「注意性能」这种无法判真假的句子。它从第一轮就没被当成约束执行过,只是前几轮你恰好没碰到冲突的场景。这种情况和会话长度无关——换个干净会话立刻重现。

区分方法只有一个动作:让它把当前生效的约束逐条复述出来,你比对清单。 复述里没有这一条,是 A;复述出来了但动手时不执行,多半是 B 或 C;在全新会话里只给这一条约束做最小复现,如果照样违反,就是 C。这一步花不到两分钟,能省掉后面半小时的瞎试。

二、判别表:从现象直接查到动作

现象大概率成因怎么验证处置动作
改了明确说过别动的文件/目录A 摘要丢掉了否定式约束让它复述生效约束清单,看是否缺这条把约束落成仓库文件 + pre-commit 或 CI 拦截
参数、命名回退到早期版本A 摘要留下早期决策、丢了后期修正git diff 看是否退回某个旧值,并追问该值的依据最终决策写进代码注释或决策记录文件,不靠对话承载
反复追问你已经回答过的细节A 具体细节没进概要追问「这条我在哪一轮说过、原话是什么」:完全不认曾拿到过是 A,承认拿到过但要你再贴一次更偏 B开新会话,用一段结构化交接重启,别在旧会话里续
输出变泛,不再引用具体文件与行号B 引用内容被大块工具输出挤出让它列出此刻它能看见的文件路径清掉冗余上下文,只重新读入必要片段
长会话里始终稳定,唯独某类任务一直做错C 约束表述不可判定新建干净会话,只给这条约束做最小复现重写约束为可检查的正向句子,别靠加长历史
实现方式和仓库既有写法冲突、重复造轮子与压缩无关,是仓库上下文不足问它同类功能现有实现在哪个文件走 [仓库太大塞不进上下文,先别急着换模型](/learn/dacangku-shangxiawen-buzu/) 的路子
输出中途截断、工具调用失败后行为变怪网络或服务侧问题被误读成遗忘看有没有 429、500、ETIMEDOUT、ECONNRESET 一类错误先修连接与重试,别改约束(详见第五节)

表里最后一行是最常见的误判。请求在传输层断了、或者被限流挡了,会话状态可能只落了一半,下一轮的行为自然对不上。这跟摘要一点关系没有。

三、把约束搬出对话历史:三层落点

确认是 A 或 B 之后,加固思路是同一个:约束的寿命,取决于它存放介质的寿命。 对话历史寿命最短,往下依次是仓库文件、可执行检查。三层都用上,才算真的固定住。

第一层,落在版本化文件里。 仓库根目录的规则文件(各家工具约定的文件名不同,以你用的工具说明为准)是最低成本的落点。写的时候守两条:每条都能判真假,整份控制在一屏内。写成长篇反而稀释权重,也更容易在长会话里被截。具体写法参考 [CLAUDE.md 怎么写?给 AI 立项目](/learn/claude-md-zenme-xie/)。

第二层,落成机器能执行的检查。 这是唯一不会遗忘的一层。凡是能用文件路径划出来的边界,都能变成提交前拦截:

#!/bin/sh
# .git/hooks/pre-commit
# 规则:已提交的迁移文件不许再改,schema 变更只能新增文件
# --diff-filter=MD 只挑出被修改(M)和被删除(D)的路径,新增(A)不拦
if git diff --cached --name-only --diff-filter=MD | grep -q '^db/migrations/'; then
  echo "db/migrations 下的已有文件受保护:schema 变更请新增迁移文件" >&2
  exit 1
fi

这里的 --diff-filter 是关键。如果省掉它,钩子会把新增迁移文件也一起拦下来,规则就从「不许改历史」变成了「不许做任何 schema 变更」,用两天你就会自己加 --no-verify 绕过它——一条被绕过的检查等于没有检查。写机器检查时先想清楚要拦的是哪一类改动,再决定过滤条件。

同样的检查也该放进 CI,本地钩子可以被绕过,流水线不能。风格类约束交给 lint 与格式化工具,目录归属交给 CODEOWNERS。让工具去挡,你就不必在每一轮里重复叮嘱。

第三层,落在每次会话的开场交接段。 不是把上一轮历史整段贴过来,而是压成一段固定结构:当前目标一句、已完成的事实几条、硬约束逐条列、本轮只做哪一件。开场时要求它先复读约束再动手——复读这个动作本身就是一次自检,你能立刻看出有没有漏项。

顺序上先做第二层。第一层和第三层降低违规概率,第二层决定违规了会不会流到主干。

四、会话中途怎么救,开新会话怎么防

已经发现约束丢了,中途的救法按代价从低到高:

  1. 先看 diff,不看解释。 git diff --stat 拿到改动面,git diff -- <path> 逐个确认。注意一个常见的空手而归:如果工具已经替你把改动 git add 进了暂存区,上面两条什么都不显示,得换成 git diff --cached;不确定的时候先 git status --short 看一眼哪些在暂存区、哪些还在工作区。它对自己做过什么的叙述,本身也在那段有损历史里,不可靠。
  2. 把丢掉的约束重新固定一次,同时说明它已被违反的具体位置。 只说「你忘了别动 X」等于又往历史里放一份同样易失的信息;带上文件路径和错误改法,它才有可校验的锚点。
  3. 改动范围超出预期就直接回滚,不做增量修补。 让模型去撤销自己刚做的一堆改动,通常会产生第二轮偏移。范围失控的判断标准和处置见 [一句需求换来 30 个文件的改动,AI 的手](/learn/ai-gaidong-fanwei-shikong/)。
  4. 压缩已经发生过、任务还没做完,就换会话。 在一段有损历史上继续叠加,只会让后面每一轮都更贵、更飘。

预防端,真正有效的只有两件事。一是缩短单会话的任务跨度:一个会话只吃一个可验证的小步,做完提交,然后开新的。二是每一小步都留回滚点

git add -A && git commit -m "step: 只做 X,约束:不动 db/migrations"

提交消息里带上本步的约束,这份记录比对话历史活得久得多,下次交接时直接从 git log 里抄。至于把大任务铺开成多会话并行,注意别让两个会话同时改同一分支,工作区互相覆盖的现象非常像「它把我的改动忘了」。

五、什么情况下别再折腾

工程上更值钱的是及时停手的判断线。下面几条到了就停:

  • 同一条约束提醒到第三次仍被违反。 不要再提第四次。这条约束该下沉成第二层的机器检查,或者拆细到无法误解的粒度。继续在对话里较劲,纯粹是拿你的时间换概率。
  • 同一任务的会话已经压缩过两轮以上。 剩下的历史里,早期决策基本已经不可信。停下来,把已完成的事实抄进交接段,开新会话。
  • diff 跨出了你预期的目录范围。 立刻停,把工作区收起来,回到最近那个干净提交重来。收的时候用 git stash -u:不带 -u 只会收走已跟踪文件的改动,AI 新建的那些未跟踪文件会原地留下,你以为回到干净状态了其实没有。带着一个已经跑偏的工作区往下走,后面每一步都在污染判断依据。
  • 验证成本超过实现成本。 当你为了检查它有没有违反约束,需要通读的代码比自己写这段还多,就自己写。关键的那几十行由人写、其余交给工具,是完全正当的分工。
  • 怀疑是服务侧或网络侧问题时,先排除再谈约束。 429 是限流,401 与 403 是凭据或权限,500 是服务端,ETIMEDOUT 与 ECONNRESET 是连接层;公司内网走自签证书的代理时,还会出现证书链校验失败。这些都不该用「改约束写法」去治。
  • 依赖的是海外工具,且症状是间歇性失败。 需要清楚一点:多数海外 AI 编程工具的服务条款并未把中国大陆列入支持地区,具体以各家官方最新条款与支持地区说明为准。把一条本身就不被官方支持的通路当成稳定基础设施,排查会变成无底洞——你分不清这次失败是模型的、是你约束写法的,还是通路的。市面上确实存在第三方中转,但可靠性、合规与数据流向都由提供方决定,我不为任何渠道背书,也不建议把生产工作押在上面。要么用官方支持你所在区域的工具,要么在选型阶段就把这条限制算进成本。

换条路的信号也在这里:如果一个任务反复在同一处失手,通常不是模型的问题,而是任务描述缺了验收标准。把「做成什么样算完成」写清楚再开工,比强化约束的语气有用得多。

六、避坑清单

  • 用否定句写约束。 为什么会踩:否定句最贴近口语,写起来顺手。怎么避:否定式在摘要中优先被压掉,而且无法直接验证。改成正向可检查句——把「别动迁移目录」写成「已提交的迁移文件不可编辑,所有 schema 变更一律新增一个迁移文件」。这句的好处是它和上面那个钩子逐字对应,人和机器判的是同一件事,不会出现文件里写着一套、CI 拦的是另一套。
  • 把约束堆在会话最开头一大段。 为什么会踩:开场交代得越全越安心。怎么避:越靠前的内容在裁剪时越先被处理。开头只留短清单和指向仓库规则文件的路径,细则放文件里。
  • 规则文件越写越长。 为什么会踩:每次踩坑就追加一条,只增不删。怎么避:长文件会被截断,且每条的相对权重被摊薄。定期合并同类项,删掉已经由 lint 或 CI 覆盖的条目——机器已经在管的事,不必再写一遍。
  • 让工具把整个大文件读进来。 为什么会踩:省事,一次性给全上下文看着更稳。怎么避:大块返回会把约束挤到边缘。指定行范围或先检索定位,只喂需要的片段。
  • 约束失效后靠加重语气补救。 为什么会踩:直觉认为强调不够。怎么避:语气不改变存放介质。把这条约束下沉成机器检查,同时重开会话。
  • 长会话全程一次都不提交。 为什么会踩:想等功能完整了再一起交。怎么避:没有回滚点,一旦跑偏只能整体丢弃,连哪一步开始错的都查不出。每个可验证小步一个提交。
  • 直接得出模型退化的结论。 为什么会踩:这个解释最省心,也最难被证伪。怎么避:归因错了就不会去修存放位置。在干净会话里做最小复现,能稳定重现才谈模型层面的差异。
  • 多个会话并行改同一分支。 为什么会踩:以为各自改的文件不重叠。怎么避:工作区是共享状态,覆盖后表现得像遗忘。用独立分支或独立工作目录隔离,冲突时按正常的 git 冲突标记人工合。
  • 把交接段写成对话摘要。 为什么会踩:复制上一段历史最快。怎么避:摘要里混着过程性讨论和已被推翻的方案,等于把污染搬进新会话。交接段只写结论与事实,不写心路。

收束

这类问题的解法不复杂,难的是第一步归因不走偏。约束会蒸发,不是因为模型不听话,而是因为你把它放在了一个会蒸发的地方。判别表跑一遍确定成因,加固走三层落点,越过止损线就回滚重开——这套流程对哪家工具都成立,也不会因为产品迭代而失效。

开工前扫一遍这份自检清单:

  1. 本次会话的硬约束,有几条只存在于对话里?
  2. 其中哪几条能变成提交前或 CI 里的检查?今天就能落的先落。
  3. 上一个可回滚的提交是什么时候?如果超过一小步,先提交。
  4. 现在这个会话已经压缩过吗?如果是,先写交接段再继续。
  5. 出问题时,你手上有没有 diff 作为客观依据,而不是只有它的自述?

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。