第一次使用Claude Code重构整个模块时,我几乎产生了宗教般的震撼体验。描述需求后喝了杯咖啡回来,就看见14个文件变更的PR,测试用例全更新,提交信息也像模像样。“这简直是魔法”,我当时这样想。

但根本不是魔法。就是个while循环。

OpenAI的Michael Bolin最近发文拆解了Codex CLI的内部机制。原来这些AI编程助手背后的秘密既不是革命性算法,也不是神秘神经网络,而是一个循环调用LLM、执行工具直到任务完成的简单流程。

让我们来庖丁解牛。

状态机:5阶段循环结构

所有编程助手——Codex、Claude Code、Cursor等都遵循相同的基础模式。Michael Bolin将其描述为5阶段循环:

flowchart TD
    A["1. 提示词组装"] --> B["2. 模型推理"]
    B --> C{需要工具调用?}
    C -->|是| D["3. 工具执行"]
    D --> E["4. 工具结果反馈"]
    E --> B
    C -->|否| F["5. 生成最终响应"]
    F -->|新输入| A

用开发者能懂的话说:

  1. 提示词组装:构建包含系统指令、可用工具、读取文件、对话历史等完整上下文的提示词
  2. 模型推理:将提示词token化后传给模型,获取思维链/工具调用/文本响应
  3. 工具调用:若模型请求工具(读文件/执行命令等),则运行对应操作
  4. 工具反馈循环:将工具执行结果作为新增上下文再次传给模型,重复2-4阶段
  5. 结果生成:当模型判定任务完成时输出最终响应

就这么简单。没有知识图谱,没有符号规划器,没有复杂架构。本质上就是个封装了LLM的while循环。

优秀助手与平庸助手的差异不在循环结构——它们完全一致——而在于每个阶段的实现细节。

阶段1:提示词工程的艺术

第一阶段是核心所在。在LLM看到任何代码前,助手需要构建包含以下要素的提示词:

flowchart LR
    subgraph 提示词组件["提示词组装"]
        方向 TB
        SP["系统指令\n(角色设定/规则)"]
        Tools["可用工具\n(读/写/Bash等)"]
        Ctx["已读取文件/图像"]
        Inst["CLAUDE.md/AGENTS.md\n(项目规范)"]
        Env["环境信息\n(OS/git状态等)"]
        Hist["完整对话历史"]
        User["用户最新消息"]
    end

    SP --> 最终提示
    Tools --> 最终提示
    Ctx --> 最终提示
    Inst --> 最终提示
    Env --> 最终提示
    Hist --> 最终提示
    User --> 最终提示

这里有个关键设计决策:组件顺序至关重要。提示词按稳定性降序排列:系统指令最前(永不变动),工具定义次之(很少变动),最后是动态增长的文件内容和对话历史。

这种排序是为了最大化提示词缓存命中率。由于缓存机制基于前缀匹配,将稳定内容前置可以显著提升缓存利用率。我在提示词缓存优化一文中详细讨论过,核心思路是:提示词顺序直接影响计算成本。

此外还有CLAUDE.md和AGENTS.md文件。它们就像是给维修工留的便条:“总闸在橱柜下层,别碰蓝色管道”。助手启动时会读取这些文件并注入到每次提示中,是实现上下文延续的巧妙设计。

平方级膨胀:上下文增长的雪球效应

接下来是残酷的现实。每次循环迭代都会把完整对话历史重新传给模型。服务端不保存状态,每个请求都是独立的。

这种无状态设计虽然能保证"零数据留存"的隐私承诺,但代价巨大:

flowchart LR
    subgraph 迭代1["第1次迭代"]
        S1["系统指令\n10K tokens"] --> U1["用户消息\n500 tokens"]
    end

    subgraph 迭代5["第5次迭代"]
        S5["系统指令\n10K tokens"] --> H5["历史记录\n40K tokens"] --> U5["用户消息\n500 tokens"]
    end

    subgraph 迭代20["第20次迭代"]
        S20["系统指令\n10K tokens"] --> H20["历史记录\n180K tokens"] --> U20["用户消息\n500 tokens"]
    end

第一次迭代发送10K tokens,第五次50K,第二十次高达190K。由于Transformer的自注意力机制计算复杂度与token数量成平方关系,上下文膨胀带来的不仅是数据传输量增长,更是计算成本的指数级上升。

压缩技术:去芜存菁

Codex和Claude Code都采用上下文压缩技术应对这个问题。当历史记录接近上下文窗口限制时,助手会将完整历史发送到专用端点生成压缩摘要:

flowchart TD
    完整历史["180K tokens完整历史"] --> 检查{接近限制?}
    检查 -->|否| 继续["正常继续"]
    检查 -->|是| 压缩["调用压缩端点"]
    压缩 --> 摘要["生成20K tokens摘要"]
    摘要 --> 新上下文["新上下文=系统指令+摘要+最新消息"]
    新上下文 --> 继续循环

    style 完整历史 fill:#3a1a1a,stroke:#ed4a4a,color:#fff
    style 摘要 fill:#283d28,stroke:#4aed5c,color:#fff

关键点:压缩并非无损。模型将无法查看第七步的详细diff,只能看到"重构了认证模块"这样的摘要。多数场景下够用,但精确调试时可能造成信息缺失。

沙箱环境:安全的牢笼

两款助手都在沙箱环境中执行工具操作——这个受限环境默认禁止网络和文件系统访问。

这种设计至关重要。没有沙箱保护,模型幻觉产生的rm -rf /可能会摧毁你的系统。而在沙箱中,最坏情况也只是在允许范围内造成破坏。

Claude Code会对每个危险操作要求二次确认(除非你明确授权),Codex CLI也采用类似的权限管理模式。这里的启示是哲学层面的:一个无所不能的助手本质上不可信任。限制不是缺陷,而是安全保障。

Codex CLI vs Claude Code:同源异流

虽然核心循环相同,但两款产品在设计哲学上存在有趣分歧:

flowchart TB
    subgraph Codex["Codex CLI (OpenAI)"]
        方向 TB
        CG["桌面GUI\n(控制中心)"]
        CS["通用Shell\n(bash/终端)"]
        CA["自动化任务\n(原生调度)"]
        CD["带批注的代码差异"]
    end

    subgraph Claude["Claude Code (Anthropic)"]
        方向 TB
        CC["CLI优先\n(原生终端)"]
        CT["专用工具\n(读/编辑/搜索)"]
        CK["技能指令\n(/blog, /improve)"]
        CF["对话式反馈"]
    end

工具策略:通用 vs 专用

Codex为模型提供通用shell访问。要读取文件?模型执行cat 文件.py。要搜索文本?执行grep -r "模式" .。

Claude Code则采用专用工具策略:Read读取文件,Edit编辑文件(精确字符串替换而非重写),Grep搜索内容,Glob模式匹配文件。

孰优孰劣?通用shell灵活性更高,专用工具则更安全高效。Edit仅发送差异部分比cat > 文件.py的全量重写更快速可靠。实测表明90%场景下专用工具更优,但通用shell更适合边缘用例。

GUI vs CLI 之争

Codex主打桌面GUI(控制中心),提供类PR的差异对比界面,支持行内批注和可视化任务追踪。

Claude Code坚持CLI路线。纯终端操作,变更以文本形式展示,反馈通过对话完成。

个人更倾向CLI方案。并非出于极客情结,而是因为CLI能与tmux、脚本、CI流水线、SSH远程管理等无缝集成。GUI虽然直观,但会限制自动化能力。

任务调度:原生 vs 自主

Codex提供原生自动化功能,可设置GitHub事件触发、定时任务等。

Claude Code则将此交给用户实现。需要定时任务?用cron或systemd timer。需要响应Webhook?自己搭建集成。

这方面Codex明显占优,但Claude Code的DIY方案有个隐性优势:你完全掌控基础设施。即使Anthropic变更API,你的cron任务仍能持续运行。如果OpenAI改动自动化机制,你只能被动接受。

本质洞察

拆解至此,结论简单得几乎令人失望:

所谓AI编程助手,不过是个循环组装提示词、调用LLM、执行工具的while循环。 仅此而已。

真正的魔法不在于循环结构,而在于三个关键要素:

  1. 模型质量。用GPT-3的循环做不了有用工作,但Claude Opus或GPT-4o就能重构完整模块。循环相同,内在智慧决定一切。

  2. 上下文管理。提示词不能无限增长。信息排序策略、压缩时机的把握、关键数据的保留,这些工程细节决定助手是否犯低级错误。

  3. 工具设计。给LLM无限制的bash访问权就像让新手直接开车上路。良好的工具设计(包含校验、约束和明确错误反馈)区分了实用助手和危险玩具。

下次你的编程助手展现"魔法"时请记住:它不过是个封装了LLM的while True循环。精巧吗?确实。强大吗?毋庸置疑。但要说魔法嘛…恐怕还差得远呢。


资料来源: 核心观点来自OpenAI工程师Michael Bolin的《AI编程助手的内部真相》,Claude Code的分析基于官方文档和实际使用体验。想深入了解上下文缓存机制,可阅读提示词缓存优化的秘密和LLM缓存的双重成本。

本文原文为西班牙语,借助AI翻译。