导语
2024 年底到 2025 年初,Coding Agent 赛道迎来了一次范式跳跃。
从最早的 LangChain 式”显式编排”,到 AutoGPT 式”自主循环”,再到如今以 Claude Code 为代表的 Harness 范式——AI 编程助手的架构设计在短短两年内经历了三次根本性的转变。
昨晚 Claude Code 的源码意外泄露,我第一时间通读了其完整架构,试图回答一个核心问题:为什么同样是”让 LLM 写代码”,不同架构的体验差距如此之大?
这篇文章不是一篇源码导读,而是我对 Coding Agent 架构演进的思考总结。我会从以下几个维度展开:
- 🔄 Agent 架构的三次范式转变
- 🏗️ Harness 范式的工程设计哲学
- 🧠 如何让 Agent 不丢失上下文
- 🎯 如何让模型减少幻觉
- 🔧 工具系统的精妙设计
- ⚡ Edge Case 的工程化处理
一、Agent 架构的三次范式转变
1.1 第一代:显式编排时代(LangChain / LangGraph)
2023 年,LangChain 几乎是所有 LLM 应用的标准起点。它的核心思想是显式编排:开发者用 Chain、Pipe、DAG 把 LLM 的调用流程”画”出来。
1 | # LangChain 的典型模式:开发者预定义工作流 |
这个模式的问题在于:开发者必须预先知道所有可能的执行路径。每当你想支持一个新场景,就必须回去修改 chain 定义。这在面对代码编辑这种高度动态的场景时,几乎不可能穷举。
更致命的是状态管理。LangChain 把状态管理的责任完全交给了开发者——你需要手动管理对话历史、手动处理上下文窗口溢出、手动定义错误恢复路径。当你的 Agent 需要在一个 10 万行的项目里做跨文件重构时,这种手动管理几乎是不可能完成的任务。
1.2 第二代:自主循环时代(AutoGPT / BabyAGI)
2023 年中,AutoGPT 的出现让所有人兴奋了一阵子。它的思路很简单:让 LLM 自己决定下一步做什么,循环执行直到任务完成。
1 | while not done: |
这个方向是对的,但当时的执行有严重缺陷:
- 上下文快速爆炸:没有压缩策略,几轮循环就塞满了上下文窗口
- 幻觉失控:缺乏环境反馈的 grounding,LLM 经常在想象中”完成”了任务
- 没有安全边界:
rm -rf /说执行就执行,没有任何防护 - 工具原始:只有最基本的文件读写和 shell 执行
AutoGPT 的方向正确但时机不对——当时的模型能力不足以支撑自主决策,而工程基础设施也远远不够。
1.3 第三代:Harness 范式(Claude Code / Cursor / Codex CLI)
到了 2025 年,一种新的范式逐渐成型,我称之为 Harness 范式。它的核心理念用一句话概括就是:
不要编排 LLM,要为 LLM 构建一个丰富的运行时环境,然后让它自己决定怎么做。
“Harness” 这个词很精妙——它既有”驾驭、利用”的意思,也有”安全带、保护装置”的含义。一个好的 Harness 既要赋能(给 LLM 足够的工具和信息),又要约束(确保安全、防止失控)。
Claude Code 是这个范式目前最极致的实现。它的 Agent Loop 核心逻辑简洁得令人惊叹:
1 | while (模型未停止): |
就这么几行伪代码,支撑起了一个能做跨文件重构、多代理协作、无限对话的编程助手。所有的”智能”不是来自复杂的编排逻辑,而是来自三个方面:
- 模型本身的推理能力
- 精心设计的系统提示
- 丰富且高质量的工具反馈
1.4 三代对比
| 维度 | 显式编排 (LangChain) | 自主循环 (AutoGPT) | Harness (Claude Code) |
|---|---|---|---|
| 控制流 | 开发者定义 DAG | LLM 自主但粗糙 | LLM 自主 + 精密环境 |
| 状态管理 | 手动管理 | 几乎没有 | 多层自动管理 |
| 安全性 | 开发者负责 | 几乎没有 | 多层权限 + 沙箱 |
| 扩展性 | 改 chain 定义 | 改代码 | 加工具/MCP 服务器 |
| 上下文 | 固定窗口 | 快速溢出 | 多层压缩 + 无限对话 |
| 核心瓶颈 | 开发者的想象力 | 模型的自控力 | 模型的推理能力 |
这个演进的本质是:随着模型能力的提升,控制权从开发者逐渐转移到模型本身。LangChain 时代,开发者不信任模型,所以要用 DAG 来”管”它;Harness 时代,模型已经足够强,开发者要做的是构建好环境,然后 get out of the way。
二、Harness 范式的工程设计哲学
理解了范式差异之后,让我们深入看看 Harness 范式在工程层面是怎么落地的。
2.1 “操作系统”而非”框架”
Claude Code 的定位不是一个”调用 LLM 的应用”,而是一个为 LLM 构建的完整操作系统。就像 Linux 为用户程序提供文件系统、进程管理、网络栈一样,Claude Code 为 LLM 提供:
- 文件系统访问:读/写/精确编辑任意文件
- 进程执行:运行 Shell 命令,支持超时和中断
- 代码搜索:文件名匹配 + 内容正则搜索
- 网络访问:搜索互联网 + 抓取网页
- 子进程(子代理):递归启动子代理处理复杂任务
- 外部协议:通过 MCP 协议接入任意外部工具
- 语言服务器:实时获取代码诊断、定义跳转
这些能力不是简单的 API wrapper,每一个都经过了精心的工程设计。比如文件编辑不是简单的”覆写文件”,而是支持精确的行级编辑,并且会自动检测冲突、处理并发。
2.2 Agent Loop 的极简之美
整个系统的心脏是一个流式的 Agent Loop。它的设计体现了一个深刻的工程洞察:复杂的行为应该涌现自简单的规则,而不是被硬编码。
1 | 用户输入 |
注意这里没有任何”if 用户要求重构 then 走重构流程”这样的分支逻辑。所有的任务路由、工具选择、执行策略都由模型在运行时动态决定。
2.3 流式执行:与时间赛跑
传统的工具执行是”请求-响应”模式——等 API 返回完整响应,解析出工具调用,再逐个执行。Claude Code 的做法更激进:
在 API 流式返回的过程中就开始执行工具。
第一个 tool_use block 的 JSON 一完成解析,就立刻开始执行。后续的 block 继续接收,继续排队。这意味着网络传输和工具执行是重叠进行的,整体延迟大幅降低。
这种设计在编程场景下尤其重要——当模型决定同时读取 5 个文件时,你不需要等 5 个 tool_use block 全部接收完才开始读文件。第一个文件名一确定,文件读取就开始了。
三、如何让 Agent 不丢失上下文
上下文管理是 Coding Agent 最核心也最容易被忽视的问题。一个典型的编程会话可能持续数小时,涉及几十个文件、上百次工具调用。如果每隔 20 分钟就”忘了”之前在干什么,体验就会断崖式下跌。
3.1 问题的本质
LLM 的上下文窗口是有限的。即使是 200K token 的窗口,在一个复杂的编程会话中也会很快填满——每次文件读取、每次命令输出、每条对话消息都在消耗 token。
传统方案很粗暴:要么截断历史消息(丢失上下文),要么限制对话长度(限制能力)。Claude Code 的方案精妙得多:多层压缩 + 持久记忆。
3.2 六层上下文压缩策略
1 | ┌──────────────────────────────────────────────┐ |
这套系统的精妙之处在于层层递进、各有侧重:
- Auto Compact 是常规操作,温和地压缩历史
- Micro Compact 解决单次工具返回结果过大的问题
- Reactive Compact 是紧急兜底
- Session Memory 确保即使压缩了,关键信息也不丢失
3.3 Session Memory:跨越压缩边界的记忆
这是我认为最精妙的设计之一。
想象一个场景:你和 Agent 讨论了一个小时,做了很多决策——“我们决定用 Strategy 模式重构这个模块”、”这个 API 的返回格式必须保持向后兼容”、”测试覆盖率要达到 80%”。然后上下文满了,触发了自动压缩。
如果只做简单的摘要压缩,这些关键决策很可能被淹没在”读取了 file_a.ts”这样的工具调用记录中。
Session Memory 的做法是:在压缩之前,提取并持久化关键的上下文信息——用户的偏好、重要决策、当前任务状态。即使历史消息被大幅压缩,这些”记忆”仍然会被注入到后续的对话中。
这就像人类的记忆系统:你可能忘了上周二下午 3 点读了哪些代码,但你记得”我们决定用 Redis 做缓存”。
3.4 System Prompt 缓存:被忽视的成本杀手
上下文管理不只是关于”不丢信息”,还关于成本。
Claude Code 的系统提示词有近千行,每次 API 调用都要发送。如果不做优化,光系统提示就要消耗大量 token。
它的解决方案是将系统提示分为两部分:
- 静态部分:工具定义、行为规范、安全规则——这些跨会话不变
- 动态部分:当前 Git 状态、项目配置、用户自定义规则——这些每次可能不同
通过在两部分之间插入一个特殊的分隔标记,API 层可以对静态部分做全局缓存,Prompt Cache 命中率最大化。据分析,仅仅将代理列表从工具描述移到附件中,就减少了 10.2% 的 cache_creation tokens。
这种”抠 token”的精神贯穿了整个系统。
四、如何让模型减少幻觉
幻觉(Hallucination)是 LLM 的天敌,在编程场景下尤其致命——模型”幻觉”出一个不存在的 API、一个错误的文件路径,就可能浪费用户大量时间。
Claude Code 的防幻觉策略可以归纳为一个核心原则:用环境事实来 ground 模型的每一步决策。
4.1 Tool-Grounded Reasoning:用工具结果锚定推理
这是最根本的防幻觉机制。模型不是在”想象”代码库长什么样,而是通过工具调用实际查看代码库。
当模型需要修改一个文件时,它不会凭记忆”猜”文件内容,而是:
- 先用
GrepTool搜索相关代码 - 用
FileReadTool读取完整文件 - 基于实际读取的内容生成编辑
- 用
FileEditTool执行精确编辑(而不是覆写整个文件)
每一步都有真实的文件系统反馈。如果文件不存在,工具会返回明确的错误信息,而不是让模型在幻觉中继续。
4.2 环境上下文注入:让模型知道自己在哪
Claude Code 会在每次对话中自动注入丰富的环境上下文:
| 上下文类型 | 注入内容 |
|---|---|
| Git 状态 | 当前分支、最近提交、未提交的修改 |
| 项目配置 | CLAUDE.md 项目级规则、技术栈信息 |
| LSP 诊断 | 实时的编译错误、类型警告 |
| 文件结构 | 项目目录树的概览 |
这些信息让模型在开始推理之前就对”当前环境的真实状态”有准确的认知,而不是基于训练数据中的通用知识来猜测。
4.3 精确编辑 vs 全文覆写
这是一个常被忽视但极其重要的设计决策。
很多 Coding Agent 的文件编辑方式是”生成新的完整文件内容然后覆写”。这有两个严重问题:
- 幻觉风险高:模型需要”记住”文件的所有内容,很容易遗漏或修改不该改的部分
- Token 浪费:一个 1000 行的文件,改了 3 行,却要重新生成 1000 行
Claude Code 的 FileEditTool 采用的是精确编辑模式——指定要替换的旧字符串和新字符串。模型只需要关注需要修改的部分,大幅降低了幻觉的概率。
4.4 LSP 集成:编译器级别的事实核查
当模型完成代码编辑后,LSP(Language Server Protocol)会立刻提供诊断信息——类型错误、语法错误、未解析的引用等。
这相当于给模型配了一个”编译器级别的事实核查员”。如果模型幻觉出了一个不存在的函数调用,LSP 会立刻报告 Cannot find name 'xxx',模型可以据此自我修正。
五、工具系统的精妙设计
工具系统是 Harness 的核心——它决定了 LLM 能做什么、做得多好。Claude Code 的工具系统有几个令我印象深刻的设计。
5.1 智能并发编排
当模型在一次回复中调用了多个工具时,系统不是简单地顺序执行,而是根据工具的特性自动决定并行还是串行:
1 | 模型一次性发出工具调用: |
每个工具自己声明”我是否可以安全并发”,系统据此自动编排。读操作之间自由并行(最高 10 并发),遇到写操作立刻切换为串行,写操作完成后再恢复并行。
这种设计在大型项目中效果显著——当模型需要同时读取 5 个文件来理解一个功能的实现时,5 次 IO 操作是并行的,而不是串行等待。
5.2 元工具:ToolSearchTool
当 Agent 接入了大量 MCP 服务器后,可用工具数量可能达到上百个。把所有工具的 Schema 都塞进每次 API 调用会浪费大量 token。
Claude Code 的解决方案是引入一个**”元工具”**——ToolSearchTool:
1 | 工具数量 > 阈值时: |
这是”延迟加载”思想在工具系统中的体现——不预加载所有能力,而是按需发现和加载。
5.3 子代理系统:分而治之
AgentTool 是整个工具系统中最复杂也最强大的工具。它允许主代理启动子代理来处理子任务,支持多种模式:
- 同步子代理:阻塞主对话,等待子任务完成
- 后台异步子代理:不阻塞主对话,子任务在后台执行
- Fork 子代理:继承父代理的完整对话上下文,共享 Prompt Cache
- Worktree 隔离:在独立的 Git worktree 中执行,避免冲突
其中 Fork 子代理的设计尤其精妙——子代理不从零开始,而是”站在父代理的肩膀上”。它继承了父代理积累的所有上下文(项目结构、已做的决策、当前任务状态),同时共享 Prompt Cache,几乎不产生额外的缓存成本。
5.4 Coordinator 模式:生产级多代理协作
在更复杂的场景下,Claude Code 可以进入 Coordinator 模式——一个主代理编排多个 Worker 的完整工作流:
1 | Coordinator (主代理) |
注意一个关键设计:Synthesis 阶段由 Coordinator 自己完成,而不是委托给 Worker。这确保了主代理始终掌握全局视图,不会在信息传递中丢失关键细节。
Worker 之间通过消息路由系统通信,支持直接消息、广播、关闭请求等多种消息类型,并有完整的计划审批流程。
六、Edge Case 的工程化处理
一个 Coding Agent 从”能用”到”好用”,差距往往在 Edge Case 的处理上。Claude Code 在这方面的工程投入令人尊敬。
6.1 max_output_tokens 截断恢复
LLM API 有输出 token 的限制。当模型的回复太长时(比如在生成一个很长的文件),输出会被截断。
大多数 Agent 遇到这种情况要么报错,要么丢失截断的部分。Claude Code 的做法是自动恢复循环:
1 | API 返回 stop_reason = "max_output_tokens" |
对用户来说,这个过程完全透明——你看到的只是一段很长的输出流畅地生成出来。
6.2 prompt_too_long 紧急恢复
当消息历史过长导致 API 返回 prompt_too_long 错误时,不是直接报错,而是触发 Reactive Compact——紧急压缩上下文,然后自动重试。
这个设计的价值在于:用户永远不会遇到”对话太长了,请开一个新会话”这种体验断裂。系统在后台悄悄处理了所有的复杂性。
6.3 Shell 命令的多层安全验证
BashTool 是安全风险最高的工具——它可以执行任意 Shell 命令。Claude Code 为它构建了极其严格的多层验证:
- 路径验证:确保命令不操作项目目录之外的路径
- sed 命令解析:专门解析 sed 命令判断是否修改文件(因为
sed -i是一种隐蔽的写操作) - 破坏性命令检测:识别
rm -rf、git push --force、chmod 777等危险模式 - 只读验证:在只读模式下拦截所有写操作
- 沙箱限制:在沙箱环境中限制网络访问和文件系统权限
仅这个安全验证模块就超过了 100KB 的代码量。
6.4 权限的分层设计
安全不只是”拦截危险命令”,还需要一个完整的权限体系:
1 | 第一层: Permission Mode (权限模式) |
这种分层设计让安全策略可以根据场景灵活调整——本地开发可以宽松一些,CI/CD 环境需要严格锁定,企业环境需要审计日志。
6.5 优雅的中断与恢复
当用户按下 Ctrl+C 中断一个正在执行的操作时,系统不是粗暴地终止一切,而是通过 AbortController 体系实现优雅的中断:
- 正在进行的 API 调用被取消
- 正在执行的工具收到中断信号
- 已分配的资源被正确释放
- 对话状态保持一致
用户可以在中断后继续对话,之前积累的上下文不会丢失。
6.6 Token 预算控制
在 SDK 模式下(作为其他程序的子组件运行时),支持 API 级别的 Token Budget 控制。超过预算时自动停止,而不是无限消耗。
这对于在 CI/CD 中使用 Agent、或者按成本计费的商业场景来说至关重要。
七、MCP 协议:开放的扩展体系
值得单独提一下的是 MCP(Model Context Protocol) 的角色。它是 Claude Code 扩展能力的核心通道。
Claude Code 同时扮演两个角色:
- 作为 MCP Client:连接外部的 MCP Server,获取新的工具能力
- 作为 MCP Server:将自己的所有工具暴露出去,供其他程序调用
这意味着:
- 想让 Agent 操作数据库?接入一个数据库 MCP Server
- 想让 Agent 管理 Kubernetes?接入一个 K8s MCP Server
- 想让 Agent 调用内部 API?写一个自定义 MCP Server
无需修改 Claude Code 的任何代码,通过协议扩展能力。这是真正的”开放架构”。
八、总结:好的 Agent 框架就是没有框架
写到这里,我想回到文章开头的问题:为什么同样是”让 LLM 写代码”,不同架构的体验差距如此之大?
答案是:差距不在 LLM 本身,而在 Harness 的深度。
一个优秀的 Coding Agent 需要同时做好六件事:
- 🔧 丰富的工具:让 LLM 有足够的”手脚”
- 🧠 智能的上下文管理:让 LLM 不会”失忆”
- 🎯 充分的环境反馈:让 LLM 不会”幻觉”
- ⚡ 高效的执行:让交互不会”卡顿”
- 🔒 严密的安全:让操作不会”失控”
- 🌐 开放的扩展:让能力不会”封顶”
LangChain 时代,我们试图用复杂的编排逻辑来弥补模型能力的不足。如今,模型已经足够强大,最好的策略是:
构建一个极其丰富的 Harness——工具、上下文、安全、协作——然后让 LLM 自己决定怎么做。
这就是 Harness 范式的核心,也是 Claude Code 的成功之处。它证明了一个反直觉的结论:最好的 Agent 框架,就是没有框架。
不需要 DAG,不需要 Chain,不需要 Pipe。只需要一个简洁的 Agent Loop + 精心设计的环境,剩下的交给模型自己。
参考资料
- Claude Code 源码分析(基于 claude-code/src 目录)
- Anthropic: Building Effective Agents
- Model Context Protocol (MCP) 规范
- Google AI Agents Technical Guide
这篇文章是我基于 Claude Code 源码研究的思考总结。在 AI 编程助手快速演进的今天,理解架构设计背后的取舍,比知道某个具体功能怎么用更有长期价值。