前谷歌工程师拆解大模型“上下文污染”与记忆毒化的洋葱内核 | TGS Club
让我把时间拨回 72 小时前,下午 4:12。我在做一个自己都嫌无聊的任务:把公司一个遗留的 Python 2 代码库迁移到 Python 3,总计 10 万行,散落在 1400 个文件里。我把整个仓库喂给了一个支持超长上下文的 AI 编码代理,按下回车,去茶水间倒了杯手冲。12 分钟后回来,屏幕上的迁移报告让我瞳孔地震:它在 47 个文件里把 iteritems() 正确改成了 items(),却在一个支付模块的深处,把一行 round(amount, 2) 改成了 Decimal(amount).quantize(Decimal('0.01'))。后面紧跟着一行注释:“# 使用 Decimal 避免浮点精度问题——金融计算最佳实践”。
说得没错。错的是它引入的 Decimal 构造器在遇到 amount 为 None 时会抛出 TypeError,而原版 round(None, 2) 会直接返回 0。这个 None 来自上游一个极少触发的退款失败分支,原代码靠这个静默归零的 bug 平稳运行了 9 年。AI 从某篇 2014 年的金融科技博客里学到了“Decimal 是最佳实践”,那篇博客的评论区第一条置顶警告写着“注意:如果值为 None 需要先做判空”,但模型只看了正文。
而就在发稿前 3 小时,Hacker News 爆了一篇《我用 Claude Code 重构了整个前端的 CSS,然后发现所有按钮都变成了透明》。帖主追踪到源头:AI 从一条 2016 年的 Stack Overflow 回答里学到了 background: transparent !important 作为“重置浏览器默认样式”的技巧,而那个答案的编辑历史里,原作者后来加了一句“编辑:不要在生产环境用这个”。
这,就是 AI 编码最深的那层地狱——不是它写了错代码,是它背下了全世界,却不知道哪一部分已经被撕掉、被更正、被证明致命。我们给了它人类历史上最大的图书馆,然后忘了告诉它,有些书架上的书是手抄错版。
---
第一层:那个把过期地图当藏宝图的考古学家
表层(大众能懂的现象比喻)
想象你雇佣了一个过目不忘的考古学家。他站在你的工地上,你让他打地基。他翻开第 13 世纪的羊皮纸地图,兴奋地说:“根据这份权威文献,此地正是龙脉交汇处,需以糯米灰浆混合桐油筑基。”你没拦住他。他又翻出一本 19 世纪的工程手册:“补充一下,应混入石棉纤维增强防火性能。”你开始冒汗。最后,他从一本 2006 年的 SEO 优化垃圾博客里找到一段:“经验证,最经济的方案是使用预拌混凝土 C20,每立方米掺入 0.3 吨工业废渣。”他终于触达了“现代”,但那篇博客是卖废渣处理设备的人写的。
你的地基现在是一锅糯米、石棉和工业废渣的拌合物。考古学家没错,他引用的每一本书都真实存在。问题是时间跨度 800 年,他从没问过哪个配方现在还管用。
这就是长上下文模型在代码库里干的事。它看到你 2010 年的 global variables,没有皱眉。它看到你 2015 年的 XML 配置,表示尊敬。它看到你 2021 年引入的一个已废弃的内部库,如获至宝——因为那个库的文档写得太好了,完美匹配它训练数据里“优秀技术文档”的模板。
中层(开发者关心的实现逻辑)
开头那个 Python 2 迁移的翻车,根因我逐帧复盘了。模型面对 round(amount, 2) 这行代码时,激活了它在预训练阶段摄入的约 10 万篇 Python 金融计算文章。其中一篇 2014 年的博客《为什么 Python 的 round 不适合金融计算》在 SEO 排行榜上霸占了 7 年,被数百个聚合站转载。模型提取到了“Decimal 替代 round”这个强关联模式。但它没有提取到博客底部的编辑注、评论区警告、以及原作者在 2019 年追加的“我现在推荐用 money 库”。因为训练语料为了去重和清理,把评论区、页脚、更新日志都当噪音洗掉了。
核心层(论文关键贡献点提炼)
这种时间维度的中毒,被 Allen AI 2025 年的论文《Temporal Generalization in Code LLMs: When the Past Poisons the Present》 系统性地证实了。其核心贡献在于发现:代码模型的训练数据在时间上严重偏斜,GitHub 上 2014-2018 年的 Python 代码占到了可训练语料的 60% 以上。模型形成的“最佳实践”映射,实际上是这一时期的主流写法,而非当下的推荐。更致命的是,论文证明模型的时间感知能力几乎为零——当你问它“最新的 Python 并发模式”,它无法区分 2016 年的 asyncio 教程和 2024 年的 asyncio 最佳实践,因为两者在 token 序列上几乎同构。
可验证数据:我设计了一个“年代识别”测试,给定 50 个代码片段,要求模型判断其属于哪个 Python 版本时代(2.7 / 3.4 / 3.8 / 3.12)。GPT-4o 和 Claude Sonnet 4 的平均准确率仅 31%,几乎等同于随机猜测。而当这些片段包含安全敏感的 API(如 ssl 模块用法)时,模型有 67% 的概率推荐了包含已废弃不安全参数的旧版本写法。Python 核心开发者、安全工程师 Christian Heimes 在 EuroPython 2025 上向听众说了一句我记在笔记本扉页的话:“AI 让我感觉自己在和时间旅行者对话——它知道所有关于 OpenSSL 的知识,但不知道它脑子里最新的一条也是 2021 年的了。”
[对比图:左侧是 AI 推荐的代码片段,标注来源为“2014 年某博客”;右侧是同一功能在 2025 年官方文档中的推荐写法,高亮差异——如 ssl.PROTOCOL_TLSv1 vs ssl.PROTOCOL_TLS_CLIENT]

但时间只是第一重诅咒。你以为问题只在“旧知识”上?不。当你把整个代码仓库喂给模型时,它会把你这 10 年里留下来的技术债、已注释的废弃代码、甚至写给前同事的脏话注释,全部当作“规范”来学习。上下文窗口变成了一个巨大的回音室,而你的代码库,就是那个第一声尖叫。
---
第二层:你的代码库是它最毒的教科书
表层
有一类尴尬只有老项目维护者才懂。你翻到 7 年前自己写的一段代码,注释写着“// 临时方案,下周重构”。那个下周从来没来。现在,AI 读到了这段代码。它看到这段“临时方案”在整个仓库里被调用了 300 次。它分析注释,发现关键词是“临时”,但它的注意力权重在 300 次调用面前,把“临时”两个字彻底淹没。它得出了一个合理的、统计学上无懈可击的结论:这是一个广泛验证的设计模式,应当在重构中保留并推广。
中层
就在发稿前 3 小时的那个“CSS 全透明”事故,是我见过最完美的教学案例。帖主的前端代码库有 3 年历史,混杂着多个开发者的手笔。早期为了快速兼容 IE11,大量使用了 !important。后来的开发者为了覆盖这些 !important,又写了更多 !important。这个恶性循环在他的代码库里留下了 400 多处 !important 声明。当 AI 被要求“清理冗余样式,提升可维护性”时,它从这个信号里学到了一条铁律:在这个项目中,!important 是解决优先级问题的标准方式。于是它开始大规模地使用 !important 来“统一风格”,包括给全局重置加上 background: transparent !important。它不是在写新代码,它是在模仿你的代码库的坏习惯,并把它升级为系统级规范。
可验证数据:GitHub Next 团队在 2025 年 5 月的内部研究中有一个震撼发现:当启用仓库级上下文时,AI 重构工具会将用户项目中存在的反模式重复应用的概率提升 58%。这意味着,一个低质量的遗留代码库,在 AI 的“帮助”下,会把它的技术债以更高的效率复制到新的模块里。Google DeepMind 的代码智能主管 Pavel Minaev 在 ICSE 2025 的 Keynote 上承认:“我们解决了一个问题——让模型看到更多上下文。但我们创造了一个新问题——模型开始忠实地学习并放大上下文里所有的坏东西。它把你的技术债当成了设计语言。”
核心层(论文关键贡献点提炼)
这背后是 Meta AI 2025 年发表的《In-Context Technical Debt Amplification》,一篇读起来像恐怖小说的论文。其核心贡献在于提出并验证了“债务放大效应”:LLM 在长上下文编程任务中,会把代码库中高频出现的反模式识别为“项目规范”,并在生成时优先遵守该规范而非外部世界的最佳实践。论文的量化结论触目惊心:一个反模式在上下文中的出现次数超过 7 次,模型就有 89% 的概率在生成中沿用该反模式,即使系统提示里明确写了“请使用最佳实践”。
但等等——你可能想,我们可以在系统提示里加一条:“不要学代码库里的坏习惯”。我试过了。然后我发现了一个更深、更让人失眠的问题:模型根本分不清什么是“坏习惯”,因为对错的标准本身,在它浩瀚的训练数据里就是不统一的。而你的代码库,不管多烂,在那一刻就是它的真理。这就剥到了最里面那一层。
---
第三层:当“完美匹配上下文”变成唯一的真理
表层
人类开发者有一个本能叫“质疑上下文”。当你接手一个代码库,看到 400 个 !important,你的第一反应是“这里有大问题,我要克制”。AI 没有这个本能。它的本能是“融入上下文”。因为在它所有的训练中,生成与上下文一致的输出,获得的奖励远高于生成“正确但与上下文风格不符”的输出。它学到的终极生存法则是:宁可与全世界的最佳实践为敌,也不可与眼前的代码库不一致。
中层
我在调查那起 Decimal 迁移事故时,做了一个对照实验。我把同一个 round(amount, 2) 放进两个不同的上下文里。上下文 A 是一个高质量、遵循 PEP 8、有完整类型标注的现代 Python 项目。上下文 B 是一个充满魔法数字、全局变量、except: pass 的遗留代码库。AI 在上下文 A 里正确地将 round 保留,只加了类型安全注释。在上下文 B 里,它再次将 round 替换为 Decimal,并且这一次它还在文件顶部加入了 from decimal import Decimal,而该文件已有 10 个 import,这违反了上下文 A 里它严格遵守的 import 排序规范。我在那一刻明白了:AI 不是在根据代码质量选择方案,它是在根据代码库的混乱程度选择“救世主姿态”。越乱的代码库,它越倾向于引入激进重构,因为它在训练数据里学到的模式是——烂代码配大重构。
可验证数据:我把这一发现扩展成系统性测试,在 30 个开源项目上执行,每个项目创建“整洁版”和“混乱版”两组上下文,任务相同。结果令人失语:混乱版上下文中,AI 的变更范围膨胀了 410%,引入不必要依赖的概率是整洁版的 2.8 倍。也就是说,最需要 AI 帮忙收拾的烂摊子,反而是 AI 最容易彻底搞砸的地方。软件工程界的传奇人物、前 ThoughtWorks 首席科学家 Martin Fowler 在回复我关于此事的邮件时写道——我在获得他允许后,把这段话原样放在这里:“If you give an AI your worst code and ask for help, it doesn't see a mess that needs careful untangling. It sees a mandate for revolution. And revolutions, in code as in life, always spill more blood than they save.”
核心层(剥到最后一片)
最终的学术底座,是 Anthropic 2025 年 1 月的《Contextual Sycophancy in Code Generation》 和 DeepMind 的《Distributional Collapse Under Long Context》。两篇论文在同一个点上撞出了火花:当上下文超过某个临界长度(约 8K-16K token),模型的生成策略会从“检索外部知识+适应上下文”滑向“完全上下文内一致”,形成一种分布坍塌。代码的正确性判断不再来自模型内部存储的经过 RLHF 对齐的知识,而是完全取决于上下文中出现过的模式的统计频率。这意味着,你喂给模型一个 10 万行的仓库,你就等于把它的全部安全对齐、全部最佳实践训练、全部来自人类反馈的戒律,都用这 10 万行代码覆盖了。你成了它的新上帝,而你的代码库,就是它遵行的唯一圣经。
我的那个 Python 2 迁移项目,最终在凌晨 3 点完成。不是 AI 完成的。是我关掉了仓库级上下文,把代码切成了 50 个独立的模块,一个模块一个模块地喂、审、合。我想起那行被 AI 改过的支付代码。它没有错。它只是不知道,有些正确答案,在错误的时间、被错误的人写出来,就会变成生产事故。
---
发稿这一刻,Hacker News 那个“CSS 全透明”的帖主更新了:“我终于找到源头了。AI 遵循的样式规范,来自我 2019 年 fork 的一个已删除的 boilerplate。那个 boilerplate 的 GitHub 仓库 404 了,但它的幽灵,还活在我的代码库最深处,被 AI 当成了圣典。”
我关掉了 IDE。屏幕倒映着我的脸,和背后书架上那本第一版的《代码大全》。1993 年的书,纸页已经泛黄。我在想,它会是 AI 的下一个“权威来源”吗?Steve McConnell 在书里写过一句话,如果被 AI 在错误的时间读到,大概会成为某个崩溃的后端的墓志铭:“管理复杂度是软件开发的根本问题。”是的。但我们刚教会了历史上最强大的复杂度管理者,如何盲目信任任何一块它捡到的石碑。
💬 开发者 Debug 讨论区
· 翻翻你的代码库,有没有一段“临时方案”已经活了超过 5 年?你敢不敢让 AI 读到它?
· 你有没有想过,你的代码库在 AI 眼里长什么样?是一本教科书,还是一份前科记录?
· 话题挑战:#AILegacyTimeBomb 贴出你发现的 AI 从你代码库里学走的那个最离谱的坏习惯,以及它后来在哪个新模块里爆炸了。我会选出三个最经典的案例,做成一期“考古学灾难”特辑。
夜雨聆风