运行机制:机器循环运行,直到任务完成

Agent Loop 每转一圈经历 5 个步骤,下面逐步拆解。

第一步:用户说话

你输入一条消息,系统把它和完整的历史对话打包在一起,准备发给 Claude API。

注意"完整的历史对话"——每一轮都会把所有历史带上,对话越长,打包的内容越大。这会带来一个核心问题:Claude 的上下文窗口是有限的,长时间运行后历史会把空间撑满。这个问题的解法详见第四节:上下文压缩:Loop 如何应对"记忆爆炸"。

这个"打包"动作由 query() 函数负责(src/query.ts)。Loop 每转一圈,都带着一个随身记事本,记录当前状态:

// 单轮循环的可变状态(在迭代间传递)
type State = {
  messages: Message[]                    // 对话历史
  toolUseContext: ToolUseContext          // 工具执行上下文
  autoCompactTracking: AutoCompactTrackingState  // 压缩状态追踪
  maxOutputTokensRecoveryCount: number   // 输出截断恢复计数
  hasAttemptedReactiveCompact: boolean   // 是否已尝试响应式压缩
  turnCount: number                      // 当前轮次
  transition: Continue | undefined       // 上一次迭代为何继续
}

逐项解释:

字段通俗含义
messages到目前为止所有的消息,包括你说的、Claude 说的、工具返回的结果。每转一圈都会往里追加新内容。
toolUseContext当前可以用哪些工具、权限是什么、在哪个目录下操作。相当于"工具箱的钥匙和使用说明"。
autoCompactTracking记录是否已经压缩过历史对话,以及连续压缩失败了几次。用来防止反复压缩失败浪费资源。
maxOutputTokensRecoveryCount如果 Claude 回复太长被截断,系统会尝试续写。这个数字记录已经续写了几次,最多允许 3 次。
hasAttemptedReactiveCompact防止同一轮里重复触发 reactiveCompact(错误恢复压缩)。触发一次后打上标记,不再重复。
turnCount这一轮任务里 Claude 已经转了多少圈。可以用来限制最大循环次数,防止无限跑下去。
transition上一圈结束后为什么没停下来、继续循环的原因(比如"还有工具结果没处理"、"输出被截断需要续写")。主要用于调试和追踪异常路径。

第二步:Claude 思考

消息发给 Claude API 后,Claude 会先进行内部推理(Thinking),再决定说什么或做什么。

这个"思考"过程在代码里有严格规则。开发者用魔法咒语风格写了一段著名注释:

"The rules of thinking are lengthy and fortuitous... If ye does not heed these rules, ye will be punished with an entire day of debugging"(不遵守这些规则,你将被处以整整一天的调试惩罚)

具体规则:

  1. 含有思考内容的消息,必须在允许思考的请求里发送(max_thinking_length > 0)
  2. 思考内容不能是消息的最后一个 block,后面必须跟着实际输出
  3. 思考内容必须在整个对话轨迹中保持完整,不能中途丢失

这些规则之所以这么严格,是因为思维链一旦在对话历史里断裂,Claude 后续的推理就会出错,且错误极难排查。

第三步:决定用哪个工具

思考之后,Claude 给出响应,分两种情况:

  • 直接回复文字 → 流式输出给用户,Loop 可能在此结束
  • 决定调用工具 → 进入下一步

Claude 能用的工具由 src/tools.ts 统一注册和管理,分三类:

常规工具(所有用户可用)

工具作用
BashTool执行终端命令
FileReadTool读文件
FileEditTool改文件(精确替换)
FileWriteTool写新文件
GlobTool按模式搜文件
GrepTool在文件中搜索内容
WebFetchTool抓取网页
WebSearchTool搜索互联网
AgentTool创建子 Agent
TodoWriteTool维护任务清单
EnterPlanModeTool / ExitPlanModeTool进入/退出计划模式
AskUserQuestionTool主动向用户提问
LSPTool代码语言服务(代码补全/跳转等)
NotebookEditTool编辑 Jupyter Notebook
TaskOutputTool查看后台任务输出

内部员工专属工具(USER_TYPE === 'ant')

工具作用
REPLTool改变 Claude 的操作模式:隐藏所有基础工具,强制通过 REPL 界面操作(用于研究 Claude 在不同约束下的行为)
SuggestBackgroundPRToolClaude 主动建议创建后台 PR(测试"主动式助手"交互模式)

REPL 模式下被隐藏的工具:FileRead / FileWrite / FileEdit / Glob / Grep / Bash / NotebookEdit / Agent

Feature Flag 控制的实验性工具

功能方向工具Feature Flag
定时任务CronCreate / CronDelete / CronListAGENT_TRIGGERS
远程触发RemoteTriggerToolAGENT_TRIGGERS_REMOTE
监控MonitorToolMONITOR_TOOL
主动推送PushNotificationTool、SendUserFileTool、SubscribePRToolKAIROS
等待/定时SleepToolPROACTIVE / KAIROS
浏览器操作WebBrowserToolWEB_BROWSER_TOOL
终端截图TerminalCaptureToolTERMINAL_PANEL
计划验证VerifyPlanExecutionTool环境变量 CLAUDE_CODE_VERIFY_PLAN=true

KAIROS 是一个反复出现的代号,关联推送通知、GitHub PR 监听、文件发送——指向一个正在研发的"主动式 Agent"形态:Claude 不等用户叫,而是主动监听事件并通知。

第四步:执行工具

系统调用 Claude 指定的工具,执行实际操作(读文件、跑命令、搜索……)。

其中 AgentTool 是最特殊的一个——Claude 可以用它创建子 Agent,把子任务委托出去。关于 Agent 如何启动、路由到哪种任务类型,详见第五节:AgentTool:子任务如何启动与路由。

第五步:看结果 → 再思考 → 直到完成

工具执行完毕后,结果以 tool_result 的形式追加到对话历史里,然后重新回到第一步,再次调用 Claude API。

Claude 看到工具结果后重新思考:任务完成了吗?还需要继续操作吗?如此循环,直到 Claude 不再调用任何工具,输出最终回复,Loop 结束。

第一步:用户说话
    ↓
第二步:Claude 思考
    ↓
第三步:决定用哪个工具
    ↓
第四步:执行工具
    ↓
第五步:结果塞回对话 → 回到第二步
    ↓
(直到 Claude 不再调用工具)
    ↓
输出最终回复,本轮结束