运行机制:机器循环运行,直到任务完成
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"(不遵守这些规则,你将被处以整整一天的调试惩罚)
具体规则:
- 含有思考内容的消息,必须在允许思考的请求里发送(
max_thinking_length > 0) - 思考内容不能是消息的最后一个 block,后面必须跟着实际输出
- 思考内容必须在整个对话轨迹中保持完整,不能中途丢失
这些规则之所以这么严格,是因为思维链一旦在对话历史里断裂,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 在不同约束下的行为) |
SuggestBackgroundPRTool | Claude 主动建议创建后台 PR(测试"主动式助手"交互模式) |
REPL 模式下被隐藏的工具:FileRead / FileWrite / FileEdit / Glob / Grep / Bash / NotebookEdit / Agent
Feature Flag 控制的实验性工具
| 功能方向 | 工具 | Feature Flag |
|---|---|---|
| 定时任务 | CronCreate / CronDelete / CronList | AGENT_TRIGGERS |
| 远程触发 | RemoteTriggerTool | AGENT_TRIGGERS_REMOTE |
| 监控 | MonitorTool | MONITOR_TOOL |
| 主动推送 | PushNotificationTool、SendUserFileTool、SubscribePRTool | KAIROS |
| 等待/定时 | SleepTool | PROACTIVE / KAIROS |
| 浏览器操作 | WebBrowserTool | WEB_BROWSER_TOOL |
| 终端截图 | TerminalCaptureTool | TERMINAL_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 不再调用工具)
↓
输出最终回复,本轮结束