乐于分享
好东西不私藏

30分钟手写一个 AI Agent:揭秘 Claude Code 的核心循环

30分钟手写一个 AI Agent:揭秘 Claude Code 的核心循环

你有没有想过:Claude Code、Cursor、Copilot 这些 AI 编程助手,它们的核心到底是什么?

是不是一大堆复杂的规则引擎?是不是层层嵌套的决策树?是不是精心设计的 prompt chain?

都不是。

它们的内核,只有一个简单的循环。

今天,我用 30 分钟带你手写一个最小化的 AI Agent。读完这篇,你会明白:为什么"智能"不在代码里,而在模型里。


一个让很多人困惑的问题

先问一个问题:当你对 Claude Code 说"帮我重构这个函数"时,是谁在决定先做什么、后做什么?

很多人会以为是代码里的流程控制。但实际上:

所有的决策,都在模型内部发生。

代码只是执行器。模型说"先读文件",代码就读文件。模型说"再改代码",代码就改代码。模型说"完成了",代码就停止。

这不是比喻。这是事实。


用一个类比理解

想象你在玩一个游戏:

玩家(模型)坐在驾驶舱方向盘、油门、刹车(工具)都在手里路(代码库)在眼前展开玩家决定往哪开车(代码)负责真的开过去

Agent = 模型 + 工具 + 循环

模型是驾驶员,工具是方向盘,循环是发动机。


最小 Agent:只需要 20 行代码

我们用 TypeScript 写一个最小 Agent:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line// 核心循环:就这么简单async function agentLoop(messages: Message[]) {  while (true) {    // 1. 调用模型    const response = await client.messages.create({      modelMODEL,      systemSYSTEM,      messages: messages,      toolsTOOLS,  // 告诉模型它有什么工具    })    // 2. 记录模型的回复    messages.push({ role'assistant'content: response.content })    // 3. 关键判断:模型想停止吗?    if (response.stop_reason !== 'tool_use') {      return  // 模型说"够了",退出循环    }    // 4. 模型想用工具?那就执行    const results = await executeTools(response.content)    // 5. 把执行结果喂回给模型    messages.push({ role'user'content: results })    // 继续循环,模型会看到结果,然后决定下一步...  }}

这就是全部。

没有决策树,没有流程图,没有复杂的状态机。


stop_reason:模型在说话

重点来了。stop_reason 这个字段,是模型告诉代码"我接下来想干什么":

stop_reason
含义
代码行为
tool_use
"我想用工具"
执行工具,继续循环
end_turn
"我说完了"
退出循环,返回结果

这不是代码决定的。是模型决定的。

模型 trained 了无数次,学会了什么时候该用工具、什么时候该停下。代码只是听话。


一个工具就够了

我们给这个 Agent 一个最简单的工具:Bash。

const TOOLS = [{  name: 'bash',  description: 'Run a shell command.',  input_schema: {    type'object',    properties: {      command: { type'string' }    },    required: ['command'],  },}]

就这一个。模型想列出文件?它会调用 bash 执行 ls。模型想创建文件?它会调用 bash 执行 touch

一个工具,无限可能。


安全第一

当然,我们不能让模型执行任意命令:

const DANGEROUS_COMMANDS = [  'rm -rf /',  'sudo',  'shutdown',]functionrunBash(command: string{  // 安全检查  for(const dangerous of DANGEROUS_COMMANDS) {    if(command.includes(dangerous)) {      return 'Error: Dangerous command blocked'    }  }  // 执行命令  const result execSync(command, { cwd: WORKDIR })  return result}

这是 Harness Engineering 的核心原则之一:给模型能力,但要设边界。


动手试试

现在我们把这个 Agent 跑起来。

环境准备

# 克隆项目git clone https://github.com/OPBR/build-claude-codecd build-claude-code# 安装依赖pnpm install# 配置 API Keycp .env.example .env# 编辑 .env,填入你的 ANTHROPIC_API_KEY

运行

ounter(linepnpm s01

你会看到:

╔════════════════════════════════════╗║  s01 - Agent Loop                  ║║  "One loop & Bash is all you need" ║╚════════════════════════════════════╝s01 >> 列出当前目录的文件$ ls -la...执行结果...s01 >> 创建一个 hello.txt,内容是 "Hello World"$ echo "Hello World" > hello.txt...执行结果...

这只是第一步

这个最小 Agent 只有一个工具(Bash)。但它已经展示了 Agent 的核心模式:

ounter(line模型决策 → 代码执行 → 结果反馈 → 循环继续

接下来,我们会逐步添加:

  • s02:添加更多工具(读文件、写文件、编辑文件)
  • s03:任务规划(TodoWrite)
  • s04:子代理(干净的上下文隔离)
  • s05:技能加载(按需注入知识)
  • ...一直到完整的多代理协作系统

FAQ

Q:为什么不用复杂的流程控制?

A:因为模型的智能已经足够。你不需要用代码去"指导"模型怎么做。模型 trained 了亿万次,它知道。你只需要给它工具和边界。

Q:stop_reason 还有其他值吗?

A:有,比如 max_tokens(超出长度限制)、stop_sequence(遇到预设停止词)。但在 Agent 场景,我们主要关注 tool_use 和 end_turn

Q:能不能用 OpenAI 的 API?

A:完全可以。核心模式是一样的。只是 API 格式略有不同(OpenAI 用 finish_reason,Anthropic 用 stop_reason)。

Q:为什么不把所有工具都加进去?

A:渐进式学习。先理解核心循环,再逐步添加复杂性。你会发现,添加工具从不改变循环本身——只是增加 handler。


小结

今天我们实现了:

✅ 理解了 Agent 的核心是一个循环 ✅ 手写了 20 行代码的最小 Agent ✅ 理解了 stop_reason 的关键作用 ✅ 跑起来了一个能执行 Bash 命令的 Agent

关键洞察

代码不决策,只执行。 模型决策,代码服从。 这就是 Agent 的本质。


下一步

想继续深入?

  1. 阅读 s02:看看如何添加更多工具,而不改变循环
  2. 阅读源码src/core/agent-loop.ts 只有 100 行
  3. 动手改造:尝试添加一个新工具(比如 read_file

项目地址:https://github.com/opbr/build-claude-code


Agency comes from the model. The harness makes agency real.

你在构建 Agent 时遇到过什么问题?评论区聊聊!


相关阅读

  • Claude Code 官方文档
  • learn-claude-code - Python 实现