# AI 编程助手的灵魂：Think-Act-Observe 循环


<!-- more -->


如果你把 Claude Code 的源码翻一遍，你会发现一个反直觉的事实：

**整个程序的核心，是一个 while 循环。**

不是复杂的状态机，不是精巧的管道，不是分布式协调——就是一个朴素的 `while` 循环，反复做三件事：让模型想、让模型动手、把结果喂回去。

```
while (用户没退出) {
  思考 = 调用LLM(对话历史 + 工具定义)
  if (思考.要调用工具) {
    结果 = 执行工具(思考.工具调用)
    对话历史.push(结果)
  } else {
    输出(思考.回复)
  }
}
```

就这？一个 AI 编程助手的全部秘密就藏在这个循环里？

是的。而且这个秘密并不新——它叫 **ReAct 模式**（Reason + Act），2022 年就被提出来了。但 ReAct 从论文到产品，中间隔着的不是算法突破，而是**工程判断**：什么时候该让模型想、什么时候该让它动、动了之后怎么把结果塞回去让它继续想。

## 从 Chatbot 到 Agent：一个循环的距离

ChatGPT 的模式很简单：你说一句，它回一句。模型是**被动的**——它等你提问，然后生成一段文本。这段文本可能是代码、可能是解释、可能是建议，但它的本质是**文本**。文本不会改变你的文件系统，不会运行命令，不会读取你项目的实际状态。

Claude Code 做的第一件事，就是把这个单向通道变成**双向闭环**。

模型不再只是"回答者"，它变成了"操作者"。它可以说"让我先看看这个项目结构"，然后真的去读文件；它可以说"这个测试失败了，我来跑一下"，然后真的执行命令；它可以看到命令输出，然后基于真实结果做下一步判断。

这个转变的关键不是模型变聪明了——模型还是那个模型。关键是多了一个**行动通道**，以及一个让思考和行动交替发生的**循环机制**。

## Think-Act-Observe：三步拆解

### Think（思考）

循环的第一步总是思考。模型接收当前的对话历史——包括用户的请求、之前的工具调用结果、系统提示中注入的项目上下文——然后做出判断：

- 我现在需要什么信息？（→ 调用工具）
- 我现在能做出什么结论？（→ 回复用户）
- 我之前的操作结果是否符合预期？（→ 调整策略）

这一步没有技术门槛。它就是普通的 LLM 调用，只不过 prompt 里多了一组**工具定义**——每个工具的名字、描述、参数 schema。模型通过自然语言理解来决定"我现在该用什么工具"。

### Act（行动）

如果模型决定调用工具，循环就进入行动阶段。这是 Claude Code 源码里最"有实感"的部分：模型生成的不是文本，而是一个**结构化的工具调用请求**。

```json
{
  "type": "tool_use",
  "name": "read_file",
  "input": { "path": "src/main.ts" }
}
```

Claude Code 的 agent 模块解析这个请求，找到对应的工具实现，执行它。工具可能是读文件、写文件、执行 shell 命令、搜索代码……每个工具都是独立模块，有输入验证、执行逻辑、权限控制和输出格式化。

这一步的核心设计决策是：**工具调用是同步阻塞的**。模型发出调用请求后，循环暂停，等工具执行完毕、结果返回，才进入下一步。这不是性能最优的选择（异步并行显然更快），但它是**认知最清晰**的选择——模型一次只做一个动作，看到结果再决定下一步，这和人类编程的工作方式一致。

### Observe（观察）

工具执行完毕后，结果被格式化为一段文本，作为"工具响应"注入对话历史。然后循环回到 Think 阶段——模型看到了它行动的结果。

这才是真正的魔法所在。

模型不是"盲操作"。它读了文件，看到了实际内容；它跑了命令，看到了真实输出；它改了代码，然后跑测试，看到了测试结果。基于这些**真实世界的反馈**，它调整下一步行动。

如果测试失败了，它会读错误信息然后修复代码。如果文件结构和预期不同，它会适应实际情况。如果命令输出揭示了新的问题，它会改变策略。

**这个"行动→观察→调整"的闭环，就是 AI 编程助手和代码补全工具的本质区别。** TabAutocomplete 是在猜你接下来要写什么；Claude Code 是在理解你项目的实际状态然后做出判断。

## 循环的终止条件

一个循环如果不能停下来，就是死循环。Think-Act-Observe 循环的终止条件是什么？

在 Claude Code 里，终止发生在模型决定**不调用任何工具**的时候。当模型判断"我已经有了足够的信息，可以回复用户了"，它生成的就不再是工具调用，而是一段自然语言回复。这段回复被展示给用户，当前回合结束。

用户再发一条消息，循环重新开始。

这里有一个微妙的设计：模型可能在同一个回合里循环多次——读文件、改文件、跑测试、再改文件——直到它觉得自己搞定了，才给用户最终回复。这意味着用户看到的不是过程，而是**结果**。过程被折叠在循环内部，对用户透明。

## 从 ReAct 到生产：工程判断

ReAct 论文里的循环和 Claude Code 源码里的循环，看起来一样，差得很远。差在工程判断：

**上下文窗口是有限的。** 每次循环迭代都在消耗 token——工具调用的请求、工具返回的结果、模型的思考。如果循环跑了 20 轮，上下文可能被撑爆。Claude Code 的解决方案是分层的：消息裁剪（trimming）、对话压缩（compaction）、上下文窗口管理。这些不是 ReAct 论文里写的，是生产环境逼出来的。

**工具调用可能失败。** 文件不存在、命令执行超时、权限被拒绝——这些在论文里不存在，在生产里是常态。每个工具都有错误处理，错误信息被格式化后注入对话历史，让模型"看到"失败然后自己调整。

**模型可能 hallucinate。** 它可能调用一个不存在的工具、传入错误的参数、或者在不需要工具的时候硬调。Claude Code 有工具名验证、参数 schema 校验、以及 system prompt 里的行为约束。

**用户需要控制权。** 不是每次工具调用都应该被执行——删文件、推代码、改配置，这些操作需要确认。权限系统（permission model）在 Act 阶段插入用户确认环节，循环暂停，等人拍板。

这些工程判断加起来，才把一个学术概念变成了每天能用的产品。

## 延伸阅读：BYF 的 TurnFlow —— 同一个循环的另一种实现

当我用同样的思想去审视自己的开源项目 [BYF（Be Your Friend）](https://github.com/ByronFinn/byf) 时，发现它的 Agent 引擎核心也是同一个循环，但做了自己的工程取舍。

BYF 将 Think-Act-Observe 循环封装在 `TurnFlow` 模块中，由它驱动无状态的 `loop/runTurn()`。与 Claude Code 最大的不同在于：**BYF 的 `TurnFlow` 不跨 turn 持有状态**——每次循环迭代都是独立的，状态由外层的 `Session` 容器管理（`ContextMemory._history`、`AgentRecords`、`Wire Records`）。

这个设计决策直接体现在 BYF 的架构约束里：`Agent` 类必须可独立使用，构造函数不能强制创建 Session 实例。这意味着你可以拉一个 `Agent` 出来，给它 provider 和 system prompt，它就跑了——不依赖 Session 的生命周期。这种"薄 Agent"设计让 BYF 的 `/btw side query`（旁路查询）能够复用同一个 `Agent.generate()` 跑一次只读的 LLM 调用，而不干扰主循环的状态。

BYF 还对循环的**并发安全**做了精细处理：当主任务正在跑工具调用时，如果用户想插问一句话（`/btw`），系统会通过 `ContextMemory.getStableSnapshot()` 拿到一份剔除了"悬空 tool_call"的快照，在快照上跑查询。结果展示在浮层 overlay 里，不进历史、不进 wire records。主循环完全不受干扰。

这是同一个循环思想在不同项目中的差异化落地——核心骨架一样，但肌肉的纹路各不相同。

## 为什么这个循环如此重要

Think-Act-Observe 循环的重要性不在于它多复杂——它很简单。重要性在于它定义了一种**新的交互范式**：

在旧的范式里（Chatbot），AI 是**被动的应答者**。你说什么，它回答什么。能力边界由单次生成的文本长度决定。

在新的范式里（Agent），AI 是**主动的探索者**。它通过工具感知世界、通过行动改变世界、通过观察调整策略。能力边界由工具数量和循环深度决定。

这就是为什么同样的模型，放在 ChatGPT 里是聊天机器人，放在 Claude Code 里是编程助手。**不是模型变了，是模型运行的循环变了。**

## 下一篇

循环是骨架，但骨架本身不会干活。下一篇文章我们要拆解 Claude Code 的**工具系统**——看看 30 多个工具是怎么被组织、描述、调度的，以及为什么工具描述的质量直接决定了 AI 能干什么、不能干什么。

---

> 本系列基于 [Claude Code 官方源码](https://github.com/anthropics/claude-code)项目进行架构分析，聚焦设计思想而非代码实现。

