What makes Claude Code so damn good (and how to recreate that magic in your agent)!?
作者: vivek
发布日期: 2025-08-21
Claude Code 是我目前使用过的最令人愉悦的 AI 代理/工作流。它不仅让定向编辑或氛围编码的临时工具不再那么烦人,使用 Claude Code 本身也让我感到快乐。它有足够的自主性来做有趣的事情,同时又不会像其他工具那样带来突兀的失控感。当然,大部分繁重的工作都是由新的 Claude 4 模型完成的(特别是交错思维)。但我发现,即使在相同的底层模型下,Claude Code 在客观上也比 Cursor 或 Github Copilot agents 更不烦人!是什么让它如此出色?如果你正在阅读这段话并点头认同,我将尝试提供一些答案。
注意:这不是一篇泄露 Claude Code 架构的博客文章(已经有一些不错的文章了)。这篇博客旨在作为构建令人愉悦的 LLM 代理的指南,基于我过去几个月使用和调试 Claude Code 的经验(以及我们拦截和分析的所有日志)。你可以在附录部分找到 prompts 和 tools。这篇帖子约 2000 字,所以请做好准备!如果你想要一些快速要点,TL;DR 部分是个不错的起点。
你可以清楚地看到不同的 Claude Code 更新。
Claude Code (CC) 感觉很好用,因为它就是能正常工作。CC 是基于对 LLM 擅长什么和不擅长什么的深刻理解而精心打造的。它的 prompts 和工具弥补了模型的愚蠢,帮助它在自己的强项领域发光发热。控制循环极其简单易懂,调试起来也很简单。
我们在 CC 发布后立即在 MinusX 开始使用它。为了深入了解,Sreejith 编写了一个记录器,可以拦截和记录每个网络请求。以下分析基于我过去几个月的广泛使用。本文试图回答这个问题——"是什么让 Claude Code 如此出色,以及你如何在自己的基于聊天式 LLM 的代理中提供类似 CC 的体验?" 我们已经将大部分这些内容整合到 MinusX 中,我很兴奋看到你也能做到!
Edit 是最常用的工具,其次是 Read 和 ToDoWrite
如何构建类似 Claude Code 的代理:TL;DR
如果只能从中学到一点,那就是——保持简单,傻瓜。LLM 已经足够难以调试和评估。你引入的任何额外复杂性(多代理、代理交接或复杂的 RAG 搜索算法)只会让调试难度增加 10 倍。如果这样一个脆弱的系统真的能工作,你也会害怕以后对它做大的改动。所以,把所有内容保持在一个文件中,避免过度的样板代码脚手架,并至少完全重写几次 :)
以下是 Claude Code 的主要要点,可以用于在你自己的系统中实现。
1. 控制循环
1.1 保持一个主循环(最多一个分支)和一个消息历史
1.2 为所有事情使用更小的模型。所有。该死的。时间。
2. Prompts
2.1 使用 claude.md 模式来协作和记住用户偏好
2.2 使用特殊的 XML 标签、Markdown 和大量示例
3. 工具
3.1 LLM 搜索 >>> 基于 RAG 的搜索
3.2 如何设计好的工具?(高级 vs 低级工具)
3.3 让你的代理管理自己的待办事项列表
4. 可控性
4.1 语气和风格
4.2 "请记住这很重要" 不幸地仍然是最先进的技术
4.3 编写算法,包含启发式方法和示例
Claude Code 在每个节点都选择了架构简单性——一个主循环、简单的搜索、简单的待办列表等。抵制过度工程的冲动,为模型构建好的挽具,让它发挥!这又是端到端自动驾驶的重演吗?痛苦的教训又来了?
1. 控制循环设计
1.1 保持一个主循环
可调试性 >>> 复杂的手工调优的多代理 lang-chain-graph-node 大杂烩。
尽管多代理系统正风靡一时,Claude Code 只有一个主线程。它定期使用几种不同类型的 prompts 来总结 git 历史,将消息历史压缩成一条消息,或者提出一些有趣的 UX 元素。但除此之外,它维护着一个扁平的消息列表。它处理分层任务的一个有趣方式是将自己作为子代理生成,但没有能力生成更多子代理。最多有一个分支,其结果作为"工具响应"添加到主消息历史中。
如果问题足够简单,主循环就通过迭代工具调用来处理。但如果有一个或多个复杂任务,主代理会创建自身的克隆。最多 1 个分支和待办事项列表的组合确保了代理能够将问题分解为子问题,同时还能关注最终期望的结果。
我非常怀疑你的应用需要一个多代理系统。每一层抽象都会让你的系统更难调试,更重要的是,你会偏离通用模型的改进轨迹。
1.2 为一切使用更小的模型
CC 产生的所有重要 LLM 调用中超过 50% 都是调用 claude-3-5-haiku。它用于读取大文件、解析网页、处理 git 历史和总结长对话。它还用于提出单字处理标签——字面上是为了每次击键!较小的模型比标准模型(Sonnet 4、GPT-4.1)便宜 70-80%。随便使用它们!
2. Prompts
Claude Code 有极其精心设计的 prompts,充满了启发式方法、示例和重要(提醒)的提醒。系统 prompt 长约 2800 个 token,工具占用惊人的 9400 个 token。用户 prompt 总是包含 claude.md 文件,通常还有另外 1000-2000 个 token。系统 prompt 包含关于语气、风格、主动性、任务管理、工具使用策略和执行任务的章节。它还包含日期、当前工作目录、平台和操作系统信息以及最近的提交。
2.1 使用 claude.md 来协作用户上下文和偏好
大多数编码代理创作者已经确定的主要模式之一是上下文文件(aka Cursor Rules / claude.md / agent.md)。Claude Code 在有无 claude.md 的情况下的表现简直是天壤之别。这是开发者提供无法从代码库推断的上下文和编码所有严格偏好的好方法。例如,你可以强制 LLM 跳过某些文件夹,或使用特定的库。CC 在每次用户请求时都发送 claude.md 的全部内容
我们最近在 MinusX 中引入了 minusx.md,它正快速成为我们代理编码用户和团队偏好的事实上下文文件。
2.2 特殊的 XML 标签、Markdown 和大量示例
XML 标签和 Markdown 是构建 prompt 的两种方式,这一点已经相当确定。CC 大量使用两者。以下是 Claude Code 中一些值得注意的 XML 标签:
<system-reminder>:这在许多 prompt 章节的末尾使用,以提醒 LLM 那些它否则可能会忘记的事情。示例:
<system-reminder>提醒你,待办事项列表当前为空。不要明确向用户提及此事,因为他们已经知晓。如果你正在处理可以从待办事项列表中受益的任务,请使用 TodoWrite 工具创建一个。如果没有,请随时忽略。再次不要向用户提及此消息。</system-reminder>
<good-example>、<bad-example>:这些用于编码启发式方法。当有多条看似合理的路径/tool_calls 可供模型选择时,它们特别有用。示例可以用来对比案例,非常清楚地说明哪条路径更可取。示例:
尝试在整个会话过程中保持当前工作目录,使用绝对路径避免使用 `cd`。如果用户明确要求,你可以使用 `cd`。
<good-example>
pytest /foo/bar/tests
</good-example>
<bad-example>
cd /foo/bar && pytest tests
</bad-example>
CC 还使用 markdown 在系统 prompt 中划分清晰的章节。示例 markdown 标题包括:
- 语气和风格
- 主动性
- 遵循约定
- 代码风格
- 任务管理
- 工具使用策略
- 执行任务
- 工具
3. 工具
去读完整的工具 prompt - 它长达惊人的 9400 个 token!
3.1 LLM 搜索 >>> 基于 RAG 的搜索
CC 与其他流行编码代理的一个显著不同之处在于它拒绝 RAG。Claude Code 搜索你的代码库就像你一样,使用非常复杂的 ripgrep、jq 和 find 命令。由于 LLM 非常理解代码,它可以使用复杂的正则表达式找到它认为相关的任何代码块。有时它最终会用更小的模型读取整个文件。
RAG 理论上听起来是个好主意,但它引入了新的(更重要的是,隐藏的)失败模式。使用什么相似性函数?什么重排序器?如何分块代码?如何处理大型 JSON 或日志文件?使用 LLM 搜索,它只需查看 json 文件的 10 行来理解其结构。如果需要,它会再看 10 行——就像你会做的那样。最重要的是,这是 RL 可学习的——BigLabs 已经在研究这个。模型承担了大部分繁重工作——正如它应该的,大幅减少了代理中的活动部件。此外,以这种方式连接两个复杂的智能系统只是很丑陋。我最近和朋友开玩笑说这是 LLM 时代的相机 vs 激光雷达,我只是半开玩笑。
3.2 如何设计好的工具?(低级 vs 高级工具)
这个问题让任何构建 LLM 代理的人夜不能寐。你应该给模型通用任务(如有意义的操作)还是低级任务(如打字、点击和 bash)?答案是有关系的(你应该两者都用)。
Claude Code 有低级(Bash、Read、Write)、中级(Edit、Grep、Glob)和高级工具(Task、WebFetch、exit_plan_mode)。CC 可以使用 bash,那为什么还要单独的 Grep 工具?这里真正的权衡在于你期望代理使用工具的频率与代理使用工具的准确性。CC 非常频繁地使用 grep 和 glob,所以将它们做成单独的工具是有意义的,但同时,它也可以为特殊场景编写通用的 bash 命令。
类似地,还有更高级的工具,如 WebFetch 或 'mcp__ide__getDiagnostics',它们的功能极其确定性。这节省了 LLM 进行多次低级点击和输入,并保持其正轨。帮帮可怜的模型,好吗!?工具描述有精心设计的 prompts,包含大量示例。系统 prompt 包含关于"何时使用工具"或如何在两个可以执行相同任务的工具之间进行选择的信息。
Claude Code 中的工具:
- Task
- Bash
- Glob
- Grep
- LS
- ExitPlanMode
- Read
- Edit
- MultiEdit
- Write
- NotebookEdit
- WebFetch
- TodoWrite
- WebSearch
- mcp__ide__getDiagnostics
- mcp__ide__executeCode
3.3 让代理管理待办事项列表
这样做有很多好处。上下文腐烂是长期运行的 LLM 代理中的常见问题。它们热情地开始处理困难问题,但随着时间推移迷失方向,退化为垃圾。当前的代理设计有几种方法来解决这个问题。许多代理已经尝试了显式待办事项(一个模型生成待办事项,另一个模型实现它们)或多代理交接 + 验证(PRD/PM 代理 -> 实现者代理 -> QA 代理)
我们已经知道多代理交接不是个好主意,有很多很多原因。CC 使用显式的待办事项列表,但是由模型维护的。这保持了 LLM 正轨(它被大量提示要频繁参考待办事项列表),同时也给予模型在实施中途纠正路线的灵活性。这也有效地利用了模型的交错思维能力来即时拒绝或插入新的待办事项。
4. 可控性
4.1 语气和风格
CC 明确尝试控制代理的美学行为。系统 prompt 中有关于语气、风格和主动性的章节——充满了指令和示例。这就是为什么 Claude Code 在其评论和热情方面"感觉"很有品味。我建议将这些大部分原样复制到你的应用中。
语气和风格的一些示例
-
重要:你不应该用不必要的前言或后言(如解释你的代码或总结你的行动)来回答,除非用户要求你。 除非用户要求,否则不要添加额外的代码解释摘要。
-
如果你不能或不愿意帮助用户某事,请不要说原因或可能导致什么,因为这显得说教和烦人。
-
只有在用户明确要求时才使用表情符号。避免在所有交流中使用表情符号,除非被要求。
4.2 "这很重要"仍然是最先进的技术
不幸的是,在要求模型不要做某事方面,CC 并不更好。重要、非常重要、从不和总是似乎是将模型引导避开地雷的最佳方式。我期望模型在未来变得更加可控,避免这种丑陋。但现在,CC 大量使用这个,你也应该这样做。一些示例:
-
重要:除非被要求,否则不要添加任何注释
-
非常重要:你必须避免使用像
find和grep这样的搜索命令。而是使用 Grep、Glob 或 Task 来搜索。你必须避免像cat、head、tail和ls这样的读取工具,使用 Read 和 LS 来读取文件。 -
如果你仍然需要运行
grep,停止。总是先使用rg的 ripgrep -
重要:除非你确信这些 URL 是为了帮助用户编程,否则绝不能为用户生成或猜测 URL。你可以使用用户在消息或本地文件中提供的 URL。
4.3 编写算法(包含启发式方法和示例)
识别 LLM 需要执行的最重要的任务并为其编写算法极其重要。尝试扮演 LLM 的角色并处理示例,识别所有决策点并明确写出它们。如果以流程图的形式表达会很有帮助。这有助于构建决策制定并帮助 LLM 遵循指令。绝对要避免的是一大堆混杂的该做和不该做。它们更难跟踪,而且相互排斥。如果你的 prompt 长达几千个 token,你将无意中有冲突的该做和不该做。在这种情况下,LLM 变得极其脆弱,变得不可能包含新的用例。
Claude Code 系统 prompt 中的任务管理、执行任务和工具使用策略章节清楚地演示了要遵循的算法。这也是添加大量启发式方法和 LLM 可能遇到的各种场景示例的地方。
奖励:为什么要关注 BigLab 的 prompts?
引导 LLM 的很多努力都是试图逆向工程它们的训练后 / RLHF 数据分布。你应该使用 JSON 还是 XML?工具描述应该在系统 prompt 中还是只在工具中?你应用的当前状态如何?看看它们在自己的应用中做什么并用它来指导你的应用会很有帮助。Claude Code 设计非常有主见,这有助于形成你自己的观点。
结论
主要要点再次是保持简单。极端的脚手架框架弊大于利。Claude Code 真的让我相信一个"代理"可以简单而极其强大。我们已经将其中许多教训整合到 MinusX 中,并继续整合更多。
如果你有兴趣为自己的 LLM 代理进行 Claude 化,我很想聊聊——在 twitter 上联系我!如果你想要可训练的类似 Claude Code 的数据代理用于你的 Metabase,查看 MinusX 或与我 这里 设置演示。愉快的(Claude)编码!