Claude Code 并没有“消灭幻觉”,它做的是把模型尽量放进一个受约束、可验证、可回退的执行环境里,让模型少靠猜、多靠读、少空想、多试错。
这和普通聊天式大模型最大的区别在于:
- 普通聊天:模型主要靠参数记忆和上下文推断
- Claude Code:模型被要求先读真实文件、执行真实命令、拿到真实结果,再继续下一步
所以它的抗幻觉能力,本质上不是来自“模型更聪明”,而是来自系统设计。
一、先理解什么叫“代码场景里的幻觉”
在编程场景里,常见幻觉主要有 5 类:
1. 假装知道项目结构
例如模型没读代码,就说:
- “你这里用的是 Express”
- “这个函数在 utils 目录里”
- “测试框架是 Jest”
但实际上项目可能根本不是这样。
2. 假装知道文件内容
例如没真正读文件,就编造:
- 某个函数签名
- 某个类型定义
- 某个配置项名字
3. 假装知道命令执行结果
例如没跑测试,就说:
- “测试已经通过”
- “构建没问题”
- “这个命令输出说明依赖缺失”
4. 假装编辑成功
例如模型说“我已经改好了”,但实际上:
- 没改到正确位置
old_string不匹配- 文件根本没写成功
5. 假装自己理解了全局上下文
例如只看了一小段代码,就对整个系统架构下结论。
Claude Code 的设计重点,基本都在对抗这 5 类问题。
二、Claude Code 的核心思路:不要让模型直接“相信自己”
它主要通过 6 类机制降低幻觉:
- 工具优先,而不是记忆优先
- 读-改-验闭环,而不是一次性生成
- 局部编辑协议,而不是整文件想象式重写
- 上下文压缩和预算控制,减少错误记忆污染
- 权限和执行结果显式反馈,避免“假装成功”
- transcript / tool_result 持久化,保证后续轮次基于真实历史
下面逐个讲。
三、先读真实世界,再继续推理
这是最核心的一点。
Claude Code 不是鼓励模型“直接回答代码问题”,而是鼓励模型先调用工具:
ReadGrepGlobBash
它的工作方式更像:
- 先看文件
- 再看搜索结果
- 再决定怎么改
- 再执行验证
- 再根据真实反馈修正
例子:避免“假装知道路由框架”
用户说:
“帮我加一个 /health 接口。”
普通聊天模型很可能直接假设:
- 项目是 Express
- 入口文件叫
server.ts - 路由写法是
app.get('/health', ...)
这就容易幻觉。
Claude Code 更合理的流程是:
Glob("src/**")Read("package.json")Grep("express|fastify|koa|router", "src")Read命中的文件片段
这样模型不是靠猜,而是靠真实文件决定:
- 到底是 Express 还是 Fastify
- 入口文件在哪里
- 路由风格是什么
这里的反幻觉本质:把“我以为”变成“我看见了”。
四、多轮 agent loop,让模型必须面对真实反馈
Claude Code 的核心不是单次生成,而是一个循环:
- 模型提出工具调用
- 系统执行工具
- 工具结果回灌给模型
- 模型再决定下一步
这很重要,因为很多幻觉来自“模型说完就结束”,没人逼它面对现实。
例子:避免“假装测试通过”
用户说:
“帮我修这个测试。”
普通聊天模型可能只看报错描述,就直接给出一个修复建议,然后说“应该可以了”。
Claude Code 的更可靠路径是:
Read测试文件Read被测实现Edit代码Bash("npm test")- 把真实测试结果作为
tool_result回给模型 - 如果失败,再继续修
也就是说,模型不能只说“我觉得这样能过”,它必须看到:
- 真正执行后的 stdout/stderr
- 哪个断言还在失败
- 哪个模块没有导出
- 哪个类型不兼容
这里的反幻觉本质:让模型对现实结果负责。
五、编辑不是“重写整文件”,而是局部替换
Claude Code 的 Edit 工具不是让模型直接输出一个新文件全文,而是要求它提供:
file_pathold_stringnew_stringreplace_all
这套协议非常重要。
为什么这能降低幻觉
因为它逼模型回答两个问题:
- 你到底要改哪一段?
- 你准备把它改成什么?
如果模型其实没读懂文件,它就很容易在这里暴露:
old_string在文件里找不到- 找到多个位置,不够唯一
- 缩进不对
- 上下文不匹配
工具层会直接报错,而不是“默默接受一个想象中的修改”。
例子:避免“假装知道函数实现位置”
用户说:
“把这个 service 的超时时间从 5 秒改成 10 秒。”
如果模型只靠猜,可能会直接说:
- “已修改常量
TIMEOUT_MS为 10000”
但如果它没读文件,实际可能:
- 常量名不是这个
- 它在配置文件里
- 有多个 5000
- 实际单位是毫秒还是秒都不确定
在 Claude Code 里,如果它真要改,就得给出明确的 old_string。
一旦 old_string 不存在,编辑就失败,模型必须回去继续读文件。
这里的反幻觉本质:把“抽象上的修改意图”变成“精确的文本定位”。
六、Read 工具有局部读取和大小限制,逼模型精确定位
这点看起来像性能优化,实际上也是反幻觉设计。
Read 不鼓励无脑整文件读取,而是支持:
offsetlimit
同时对过大的文件有:
- 字节大小限制
- token 限制
这带来两个效果。
1. 减少“读了一堆没用内容后误解上下文”
上下文太大时,模型容易:
- 丢掉局部细节
- 混淆不同位置的相似代码
- 错把旧逻辑当新逻辑
局部读取反而更清晰。
2. 逼模型先定位,再阅读
当文件很大时,模型通常得先:
Grep- 再
Read(offset, limit)
这会让它形成“先找位置,再看内容”的习惯,而不是凭印象下结论。
例子:6000 行文件里的 bug 修复
假设一个文件有 6200 行,用户说:
“修一下 websocket 重连逻辑。”
更容易幻觉的做法是:
- 把前面几百行看一遍
- 然后假设重连逻辑写法
- 开始改代码
Claude Code 更合理的做法是:
Grep("reconnect|retry|websocket|backoff", file)- 根据命中位置
Read(offset=3000, limit=120) - 如果还需要上下文,再补读附近片段
这样模型看到的是“真正的相关代码”,不是整文件里一堆相似概念。
这里的反幻觉本质:减少无关上下文,提升定位精度。
七、工具结果不是随便说,而是结构化回灌
Claude Code 里,工具执行结果会被编码成结构化 tool_result,再进入下一轮上下文。
这意味着下一轮模型看到的不是“我刚才可能做了什么”,而是:
- 某个文件真实内容
- 某条命令真实输出
- 某次编辑真实成功或失败
- 某次权限被拒绝
例子:避免“把失败当成功”
比如模型发起一次 Edit,如果编辑失败,系统不会假装继续,而是把失败信息喂回去:
String to replace not foundFound multiple matchesCannot create new file - file already exists
下一轮模型必须基于这个失败继续决策。
这比很多简单 agent 强很多。很多 agent 框架会让模型“以为”工具成功了,或者把失败吞掉,后面就一路幻觉下去。
这里的反幻觉本质:真实结果必须显式进入后续推理。
八、权限系统让“做不到”变成显式事实
Claude Code 不是让模型默认拥有无限能力。工具可能被:
- allow
- ask
- deny
一旦权限不允许,系统会明确产生拒绝结果。
这能降低一类非常常见的幻觉:
- “我已经修改好了”
- “我已经运行了命令”
- “我已经读了某个文件”
实际上可能只是权限没过。
Claude Code 会把这些拒绝记录下来,并在最终结果里携带 permission_denials。
例子:避免“假装已经修改系统文件”
用户说:
“帮我改一下 /etc/hosts。”
如果权限不允许,Claude Code 不会假装改了。
它只能得到一个明确事实:这次 edit 被拒绝。
于是模型后续更可能诚实地说:
- 当前权限不足
- 需要用户授权
- 可以先给出修改建议,但不能宣称已经完成
这里的反幻觉本质:把能力边界显式写进执行环境。
九、transcript 持久化,让后续轮次基于真实历史而不是模糊记忆
Claude Code 会把消息、工具结果、compact 边界等记录到 transcript。
这有两个反幻觉价值。
1. 中断恢复时不丢真实历史
如果系统中途停了,恢复时不是靠模型“重新回忆”,而是靠 transcript 继续。
2. 后续轮次依赖的是可回放历史
不是“我印象里之前改过”,而是“系统记录里确实改过/读过/跑过”。
例子:避免恢复后说错上下文
如果一个长任务中途停止,普通聊天模型恢复后很容易:
- 忘了刚才读过哪些文件
- 忘了测试失败在哪一步
- 忘了已经尝试过哪些修复
Claude Code 因为有 transcript 和相关状态,恢复后更容易延续真实执行轨迹,而不是重新猜。
这里的反幻觉本质:让历史来自记录,而不是来自模型记忆。
十、上下文压缩不是简单总结,而是尽量保留可运行事实
Claude Code 会做上下文压缩,但不是粗暴地“总结一下前文”。
它会分层处理:
- 先缩小 tool result
- 再 snip / microcompact
- 最后才做 autocompact
并且压缩后还会补回运行时附件。
这对反幻觉的意义是:
尽量不要让模型只依赖模糊摘要继续做精确编程任务。
如果压缩做得太粗糙,模型后面就容易:
- 混淆之前看过的代码
- 错记某次编辑内容
- 错记某个测试输出
Claude Code 的压缩策略,本质上是在“节省上下文”和“保持事实精度”之间做平衡。
十一、一个完整例子:Claude Code 如何降低“修 bug”过程中的幻觉
假设用户说:
“这个登录接口 500 了,你帮我修一下,并验证通过。”
一个容易幻觉的模型可能会这样:
- 猜问题在数据库连接
- 猜项目用 Express
- 猜测试命令是
npm test - 给出一个修改方案
- 说“应该修好了”
Claude Code 更可能这样做:
第一步:定位
Grep("login|signin|auth", src)Read命中的 controller / service / route 片段Read(package.json)看脚本和依赖
第二步:确认错误来源
Bash跑用户给的复现命令或测试命令- 拿到真实 500 堆栈或测试输出
第三步:修改
- 用
Edit精确替换相关逻辑 - 如果
old_string不匹配,立即失败并重试
第四步:验证
- 再次
Bash跑测试或启动命令 - 读取真实输出
第五步:必要时继续修
- 如果还有错误,模型必须基于真实日志继续
这个过程中,模型几乎每一步都被现实约束:
- 先读
- 再改
- 再验
- 失败就回退
所以它不像在“凭空写答案”,更像在“操作一个真实系统”。
十二、但要诚实一点:Claude Code 不是不幻觉,只是更难幻觉
它依然会出错,常见残留问题包括:
1. 读了局部后,对全局做过度推断
比如只看一个函数,就猜整个模块意图。
2. grep 命中不准,导致读错位置
这属于定位偏差,不是完全消除的。
3. old_string 选得不够稳
尤其在重复代码多的文件里。
4. 测试覆盖不完整
即使跑通了一个命令,也不代表整体没问题。
5. 压缩后仍可能丢部分细节
尤其在超长复杂任务中。
所以更准确的说法不是:
- “Claude Code 解决了幻觉”
而是:
- Claude Code 把幻觉从“随时随地乱猜”压缩成“在受约束环境里的局部判断错误”
这已经是巨大的进步。
十三、如果把 Claude Code 的抗幻觉能力总结成一句话
我会这样总结:
Claude Code 降低幻觉的核心,不是让模型更自信,而是让模型更少有机会靠想象完成任务。
它通过:
- 真实读取
- 结构化工具调用
- 局部编辑协议
- 真实命令执行
- 明确权限边界
- transcript 持久化
- 分层上下文治理
把模型从“回答机器”变成了“在真实反馈回路中工作的执行代理”。
十四、最值得借鉴的设计原则
如果你想自己做一个 agent 系统,我建议直接记这 6 条:
- 不要让模型直接猜项目结构,先读文件
- 不要让模型直接宣布成功,先跑验证
- 不要让模型整文件重写,优先局部替换
- 不要把失败吞掉,失败也要进入上下文
- 不要让超大上下文长期堆积,及时做预算和压缩
- 不要依赖模型记忆会话,重要历史要持久化