从零手搓一个 AI 编程助手:当你理解了 Claude Code 的 50 行核心代码
做一个思想实验:如果明天 Claude Code 停服了,你的工作会怎样?
不只是 Claude Code。假设 Cursor、Copilot、Windsurf——所有 AI 编程工具,同时消失 24 小时。
如果你的第一反应是"那我回去手写呗,也没什么"——说明你还没真正依赖这些工具。但如果你停顿了一下,心里开始盘算哪些任务会被卡住——恭喜你,你已经是 AI 编程时代的原住民了。
但这个思想实验的重点不是让你焦虑。重点是:如果这些工具突然都不能用了,你有能力自己搓一个吗?
答案可能比你想象的要乐观。因为 Claude Code、Codex CLI、Aider 这些看起来很神奇的工具,它们的核心架构惊人地简单。Braintrust 的工程团队总结得好:最成功的 AI Agent,本质上就是一个 while 循环加一堆工具调用。没有 DAG,没有分类器,没有 RAG。
今天我就来带你拆解这个 while 循环,从架构到实现,从核心组件到踩坑记录。读完这篇文章,你不一定能造出下一个 Claude Code,但你一定能理解它为什么能工作,以及——自己动手搓一个够用的版本。
一个 AI 编程助手的本质是什么?
在开始写代码之前,我们先搞清楚一件事:AI 编程助手跟 ChatGPT 那种聊天机器人,到底有什么本质区别?
ChatGPT 是一个问答系统:你问一句,它答一句。即使聊了很多轮,它本质上还是在"回答问题"。
但 Claude Code 不一样。你说"帮我把这个项目的测试覆盖率从 60% 提到 85%",它会:
- 先搜索项目里所有的测试文件,理解现有测试结构
- 跑一遍测试,看哪些模块覆盖率低
- 读源码,理解没被覆盖的逻辑分支
- 写新测试,一个文件一个文件地补
- 跑测试验证,失败了就改,直到通过
发现区别了吗?它不是在"回答问题",它是在执行任务。它会规划、行动、观察结果、然后调整策略。这个"规划—行动—观察"的循环,就是 AI Agent 的核心范式,学术界叫它 ReAct(Reasoning + Acting)。
打个比方:ChatGPT 像一个顾问——你问它怎么做,它给你建议,但活儿还是你干。Claude Code 像一个实习生——你告诉它目标,它自己去查资料、写代码、跑测试、改 bug,直到把活儿干完。
input → output,一来一回。AI 编程助手的核心是 while (没完成) { 思考 → 行动 → 观察 },一个持续运转的循环。这个循环有多少行代码?答案是:大约 50 行。真正的复杂度不在循环本身,而在循环里调用的那些工具。
用一个公式概括:
AI 编程助手 = LLM + 工具系统 + Agent Loop + 上下文管理
LLM 提供"大脑",工具系统提供"手脚",Agent Loop 让大脑指挥手脚干活,上下文管理确保大脑不会忘掉关键信息。就这四样东西,排列组合一下,你就能做出一个 AI 编程助手。
50 行代码就是全部秘密。剩下的几万行,都是在让这 50 行跑得更稳、更快、更安全。
架构长什么样?
知道了本质,接下来看架构。一个 AI 编程助手从用户输入到最终输出,要经过哪些环节?
我画了一张图,这是几乎所有主流 AI 编程助手共享的架构模式:
这个架构看起来简单,但每一层都有讲究。让我逐层拆解:
第一层:终端 UI。用户在命令行里输入指令,看到 AI 的思考过程和操作结果。Claude Code 用的是 React + Ink(没错,React 不只能写网页,还能写终端 UI),实现了流式输出、语法高亮、diff 展示等功能。但如果你自己搓,一个简单的 readline 就够了。
第二层:CLI 解析 + 权限检查。解析用户命令,检查安全权限。比如用户要执行 rm -rf /,这一层要拦住它。Claude Code 有一套完整的 Shell AST 解析器,能在执行前分析命令的危险程度。
第三层:Agent Loop。这是整个系统的心脏,后面会详细展开。
第四层:LLM API + 工具系统 + 上下文管理。Agent Loop 调度的三个核心资源——大模型负责思考,工具负责执行,上下文管理负责记忆。
有意思的是,这个架构跟 Unix 的设计哲学惊人地相似:每一层做一件事,通过简单的接口串联。没有花哨的微服务,没有复杂的消息队列。一条管道,从头到尾。
AI 编程助手的竞争力,不是 AI 多聪明,而是管道多顺畅。
Agent Loop 怎么转起来?
好,到了最核心的部分。Agent Loop 到底是怎么工作的?
我先给你看代码,再解释。这是一个可以工作的 Agent Loop,用 Python 写的,20 多行:
import anthropic
client = anthropic.Anthropic()
messages = []
tools = [read_tool, edit_tool, bash_tool, grep_tool] # 工具定义
def agent_loop(user_input: str):
messages.append({"role": "user", "content": user_input})
while True:
response = client.messages.create(
model="claude-sonnet-4-6",
system="你是一个编程助手,可以读写文件、执行命令。",
messages=messages,
tools=tools,
max_tokens=4096,
)
messages.append({"role": "assistant", "content": response.content})
# 如果没有工具调用,说明任务完成
if response.stop_reason == "end_turn":
return response.content
# 执行所有工具调用,把结果喂回去
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
messages.append({
"role": "user",
"content": [{"type": "tool_result",
"tool_use_id": block.id,
"content": result}]
})
关键在第 12 行到第 15 行。模型返回的 stop_reason 有两种情况:如果是 end_turn,说明模型觉得活儿干完了,可以停下来跟你汇报了;如果是 tool_use,说明模型还需要调用工具——读个文件、跑个命令——拿到结果后再继续思考。
打个比方:这就像一个厨师做菜的过程。
厨师(LLM)看了菜谱(你的需求),心想"我先看看冰箱里有什么食材"(调用 Read 工具读文件)。看完之后发现缺点盐(发现一个 bug),于是"去柜子里拿盐"(调用 Edit 工具修改代码)。加完盐之后"尝一口看看咸淡"(调用 Bash 工具跑测试)。测试没过——"还差点味精"——继续调整。直到尝一口觉得"好了,可以上菜了"——返回 end_turn。
整个过程就是一个思考 → 行动 → 观察 → 再思考的循环,直到模型认为任务完成。
你可能会问:就这么简单?那为什么 Claude Code 有几万行代码?
因为这个 while 循环只是骨架。要让它在真实场景中可靠地工作,你还需要处理一大堆"脏活":
- 安全控制:设置最大循环次数(通常 15-25 次),防止 Agent 陷入死循环烧掉你的 API 额度
- 错误处理:区分可重试的错误(429 限流、500 服务器错误)和致命错误(401 认证失败),用指数退避策略重试
- 循环检测:对每次工具调用的名称和结果做哈希,发现重复模式就强制终止,避免"Ralph Wiggum 漂移"——Agent 看起来在忙,实际上在兜圈子
- 流式输出:用户不想盯着空白屏幕等 30 秒,你需要把模型的思考过程实时流到终端上
- 费用控制:设置单次会话的花费上限(比如 2 美元),超过就停下来问用户要不要继续
让模型"做事 + 看结果",比只让它"想清楚再说"更有效。这就是 Agent 范式的核心洞察。
工具系统怎么设计?
Agent Loop 是心脏,工具系统就是四肢。一个 AI 编程助手到底需要哪些工具?
你可能以为越多越好。100 个工具覆盖所有场景,多全面!
错了。反直觉的是:少而精的工具远胜大而全的军火库。
原因很简单——当你给模型 100 个工具的描述,光工具定义就要吃掉几千个 token,模型还要在 100 个选项里做选择,出错概率大增。Steve Kinney 的建议很精辟:如果一个人类工程师都分不清该用哪个工具,AI 也做不到。
Claude Code 的做法值得学习。它只有 8 个核心工具:
| 工具 | 职责 | 为什么需要它 |
|---|---|---|
| Read | 读文件 | 理解代码的第一步 |
| Edit | 编辑文件 | 精确修改,不重写整个文件 |
| Write | 创建新文件 | 只在需要新文件时使用 |
| Bash | 执行 shell 命令 | 万能适配器——跑测试、装依赖、查 git 日志 |
| Grep | 搜索文件内容 | 在大项目里快速定位代码 |
| Glob | 搜索文件路径 | 找到特定文件名模式的文件 |
| Task | 启动子 Agent | 复杂任务拆分给子进程 |
| TodoWrite | 管理任务清单 | 跟踪多步骤任务的进度 |
8 个工具,覆盖了 99% 的编程场景。注意 Bash 工具——它是一个"万能适配器"。你不需要专门做一个"运行测试"工具、一个"安装依赖"工具、一个"查看 git 日志"工具。一个 Bash 工具,全搞定。
那工具的接口长什么样?每个工具本质上就是一个函数,对外暴露三样东西:名字、描述、参数 schema。
# 工具定义示例(Anthropic Tool Use 格式)
read_tool = {
"name": "read_file",
"description": "读取指定路径的文件内容。返回带行号的文件内容。",
"input_schema": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "文件的绝对路径"
},
"offset": {
"type": "integer",
"description": "从第几行开始读(可选)"
},
"limit": {
"type": "integer",
"description": "最多读几行(可选)"
}
},
"required": ["file_path"]
}
}
关键在 description。这不是写给人看的文档——这是写给模型看的指令。模型靠这段描述来决定什么时候调用这个工具、传什么参数。写得越精确,模型用得越准。
notify_customer 工具,比一个通用的 send_message 工具好用 10 倍。因为前者的描述告诉模型"这是用来通知客户的",模型立刻知道什么场景该用它。后者呢?"发消息给某人"——发给谁?用什么渠道?什么格式?模型一脸懵。工具描述的精确度,直接决定了 Agent 的智商。
再分享一个设计原则:工具返回错误字符串,不要抛异常。
比如用户要读一个不存在的文件,不要让程序崩溃,而是返回 "Error: 文件 /foo/bar.py 不存在"。这样模型能看到错误信息,自己调整策略——"哦,文件名可能拼错了,让我 grep 搜一下"。如果你直接抛异常,整个 Agent Loop 就断了。
少而精的工具,加上精确的描述,胜过大而全的军火库。这是工具设计的第一性原理。
文件编辑这个硬骨头怎么啃?
工具系统里,最难的工具是哪个?
不是 Bash(执行命令很直接),不是 Read(读文件谁不会),而是文件编辑。
这件事的难度远超你的想象。2026 年初,Can Boluk 发了一篇论文叫《The Harness Problem》,核心发现是:只改变编辑格式(不换模型),编码性能平均提升 8%。换句话说,你用什么方式让 AI 修改文件,比你用什么 AI 模型更影响成功率。
为什么文件编辑这么难?因为大模型是文本生成器,它不是 IDE。它不能"移动光标到第 47 行,选中第 3 个单词,替换为另一个"。它只能输出一段文字来描述"我想做什么改动"。然后,你的代码要根据这段描述,精确地修改文件。
这就像让一个外科医生不能亲自动手,只能对着电话口述"把第三根肋骨旁边那条血管缝合一下"——手术能不能成功,完全取决于你的口述格式够不够精确,以及接电话那边的人能不能准确理解。
目前业界有五种主流的编辑策略,各有优劣:
策略一:整文件重写
最暴力的方式——让模型输出修改后的完整文件。优点是简单粗暴,不存在"匹配不上"的问题。缺点?一个 500 行的文件,你只改了第 42 行的一个变量名,模型要把 500 行全输出一遍。浪费 token 不说,还经常"手抖"改了不该改的地方。
策略二:Search/Replace 块
这是 Aider 和 Claude Code 的方案。模型输出一对"搜索/替换"块:
<<<<<<< SEARCH
def calculate_total(items):
return sum(item.price for item in items)
=======
def calculate_total(items):
tax_rate = 0.08
subtotal = sum(item.price for item in items)
return subtotal * (1 + tax_rate)
>>>>>>> REPLACE
你的代码在文件里搜索 SEARCH 块的内容,找到后替换成 REPLACE 块的内容。直觉上很好理解,对吧?
但坑在哪?模型必须完全精确地复现原文。多一个空格、少一个缩进,搜索就匹配不上。所以实际实现时,你需要一套级联匹配策略:先精确匹配,失败了就忽略空白符匹配,再失败就保留缩进匹配,最后兜底用模糊匹配(Levenshtein 距离)。
策略三:Unified Diff
标准的 diff 格式,程序员都熟悉。但让模型生成格式正确的 diff 出奇地难——行号经常出错,上下文行也容易对不上。
策略四:LLM 辅助应用
Cursor 和 OpenHands 的方案——分两步走。第一步:大模型生成一个"改动草稿"(不需要格式精确);第二步:一个专门训练的小模型(Fast Apply Model)负责把草稿精确地合并到文件里。
这有点像:大模型是主任医师,负责决策"这个地方要改";小模型是手术机器人,负责精确执行。各司其职。
策略五:Hashline 编辑(2026 年新秀)
给每行代码加一个内容哈希前缀,让模型"看到"行锚点。比如 47:a3f2| def main():。这让模型能精确定位要修改的位置,不需要复现原文。效果惊人:某些模型的编辑成功率从 6.7% 直接跳到 68.3%。
如果你是第一次搓,我的建议是从 Search/Replace 开始,加一套三级模糊匹配。投入产出比最高。
编辑格式的选择,比模型选择更影响成功率。这可能是 AI 编程领域最反直觉的结论。
上下文爆了怎么办?
到目前为止,你已经有了 Agent Loop、工具系统、文件编辑方案。看起来可以开工了。
但还有一个隐藏 boss:上下文窗口。
你可能知道大模型有一个"上下文窗口"——就是它一次能"看到"的文本量。Claude 的上下文窗口是 200K token,GPT-4.1 是 1M token。听起来很大对吧?
但你算算一个真实的 Agent 会话消耗多少 token。Anthropic 的数据显示:工具返回的结果占了总 token 量的 67.6%,而系统提示词只占 3.4%。也就是说,每次 Read 一个文件、每次 Bash 执行一个命令,返回的结果都在疯狂吃你的上下文。
一个复杂的编程任务——比如"把这个项目从 JavaScript 迁移到 TypeScript"——可能需要读几十个文件,跑几十次命令。用不了多久,200K 的上下文就塞满了。
打个比方:上下文窗口就像会议室的白板。一开始白板是空的,你在上面写需求、画架构、贴便签。但白板面积有限,写着写着就满了。这时候你有两个选择:要么换一块更大的白板(更大的上下文窗口,但贵),要么把不重要的内容擦掉,在旁边写个摘要(上下文压缩)。
AI 编程助手选的是第二种。这就是上下文压缩(Compaction)。
压缩策略的核心思路是:最近几轮对话保持原文,更早的内容压缩成摘要。就像会议纪要——你不需要逐字记录"张三说了什么、李四反驳了什么",只需要记录"讨论结果是用方案 B,原因是性能更好"。
具体实现上,当上下文使用量超过 70% 时触发压缩:
def maybe_compact(messages, max_tokens):
used = count_tokens(messages)
if used < max_tokens * 0.7:
return messages # 还没满,不用压缩
# 保留最近 N 轮完整对话
recent = messages[-10:]
older = messages[:-10]
# 用 LLM 把旧对话压缩成摘要
summary = llm_summarize(older, prompt="""
总结之前的对话,保留:
1. 用户的原始需求
2. 已完成的修改和文件列表
3. 遇到的错误和解决方案
4. 当前进度和下一步计划
丢弃:具体的文件内容、命令输出等细节。
""")
return [{"role": "user", "content": summary}] + recent
关键在第 12-17 行的 prompt。你要告诉压缩用的 LLM:哪些信息必须保留(需求、进度、决策),哪些可以丢弃(文件内容、命令输出)。这其实就是在做信息的"优先级排序"。
除了压缩,还有两个常用的上下文管理手段:
分页读取:不要一次读完整个文件。Read 工具支持 offset 和 limit 参数,一次只读 200 行。这就像看书时只翻到你需要的那一页,而不是把整本书塞进工作记忆里。
子 Agent 隔离:对于复杂任务,Claude Code 会把子任务派给一个独立的子 Agent。子 Agent 有自己的上下文窗口,完成后只返回一个精简的总结。这就像"你去调研一下这个问题,回来给我一页纸的报告",而不是"你去调研,把所有原始资料都搬回来"。
最好的上下文管理,不是让模型记住一切,而是让它忘掉该忘的。
现在,轮到你了
回到开头那个思想实验:如果所有 AI 编程工具都停服了,你能自己搓一个吗?
读完这篇文章,你已经知道了答案的骨架:
- 核心是一个 while 循环——思考、行动、观察,周而复始
- 工具系统贵精不贵多——8 个工具覆盖 99% 的场景
- 文件编辑是最大的技术难点——编辑格式的选择比模型选择更重要
- 上下文管理决定了能跑多远——该忘的信息要果断丢掉
- 安全控制不能省——最大迭代次数、费用上限、循环检测,一个都不能少
现在给你一个挑战:
今天回去,用 50 行代码实现一个最小的 Agent Loop。
就用上面那段 Python 代码作为起点,加上两个工具:一个 Read(读文件),一个 Bash(执行命令)。然后对它说:"读一下当前目录的 README,告诉我这个项目是干什么的。"
如果它能正确完成——恭喜,你已经手搓出了一个 AI 编程助手的胚胎。接下来要做的,就是一点点加工具、一点点加安全控制、一点点优化上下文管理。
如果它失败了——更好。记录下它卡在哪里,这就是你需要解决的下一个问题。而解决问题的过程,才是你真正理解这项技术的开始。
Claude Code 不是魔法。它只是一个 while 循环,加上足够多的细节处理。
而细节这种东西——只要你愿意动手,就一定能搞定。
参考资料
- The Canonical Agent Architecture: A While Loop with Tools - Braintrust
- The Anatomy of an Agent Loop - Steve Kinney
- Code Surgery: How AI Assistants Make Precise Edits to Your Files - Fabian Hertwig
- How Claude Code Works - Anthropic
- Effective Context Engineering for AI Agents - Anthropic
- Diff Format Explained: Search-Replace Blocks & AI Code Editing Limits - Morph
- Inside Claude Code: An Architecture Deep Dive - Zain Hasan
- How to Build Your Own AI Coding Agent from Scratch - Morph
- Building Your Own CLI Coding Agent with Pydantic-AI - Martin Fowler