Agent Loop 核心机制详解

1672 字
8 分钟
Agent Loop 核心机制详解

Agent Loop 核心机制详解#

一、核心功能点拆解#

1.1 双入口设计#

agentLoop() → 新对话开始,添加新消息到上下文
agentLoopContinue() → 从现有上下文继续,用于重试场景

记忆口诀:一个入口两种模式 —— 新建或继续

1.2 双层循环架构#

外层循环 (while true)
├── 处理 follow-up messages(循环后的额外消息)
└── 内层循环 (while hasMoreToolCalls || pendingMessages)
├── 处理 steering messages(转向消息)
├── 流式获取 assistant 响应
├── 执行工具调用
└── 检查终止条件

记忆口诀:外层管消息队列,内层管执行流程

1.3 三种消息注入机制#

机制触发时机用途
prompts启动时初始用户输入
steeringMessages每个 turn 开始前中途干预方向
followUpMessages循环结束时延续对话

1.4 工具执行两种模式#

// 并行执行(默认)
executeToolCallsParallel() → Promise.all() 同时执行
// 串行执行
executeToolCallsSequential() → for...of 顺序执行

触发条件:

  • config.toolExecution === "sequential"
  • 任一工具的 executionMode === "sequential"

1.5 工具执行三阶段#

prepareToolCall()
├── 参数验证
├── beforeToolCall 钩子
└── 返回 prepared/immediate
executePreparedToolCall()
├── tool.execute()
└── 发送 partial update 事件
finalizeExecutedToolCall()
├── afterToolCall 钩子
└── 修改/增强结果

二、遇到的问题 → 抽象提炼#

问题 1:重试时如何保持上下文?#

场景:LLM 返回错误,需要重试但不能丢失已执行的工具结果。

抽象:状态连续性问题 —— 如何在不重新开始的情况下继续执行?

解决原则

  • 分离”新建”和”继续”两种入口
  • 继续时验证最后一消息不是 assistant(必须是 user 或 toolResult)
  • 保持 context.messages 不变,只追加新消息

问题 2:流式响应如何实时更新?#

场景:LLM 流式返回 token,需要实时显示进度。

抽象:部分状态管理 —— 如何在数据不完整时管理状态?

解决原则

  • 使用 partial message 表示不完整状态
  • 每次事件到来时替换 context.messages[-1]
  • 区分 message_start(初始)、message_update(增量)、message_end(完整)
// 关键模式:就地更新
context.messages[context.messages.length - 1] = partialMessage;

问题 3:工具调用何时并行、何时串行?#

场景:读取文件的工具可以并行,但创建 git commit 的工具需要串行。

抽象:依赖关系建模 —— 如何表达工具间的依赖?

解决原则

  • 工具级别配置 executionMode
  • 混合模式检测:任一 sequential → 全部串行
  • 保持结果顺序与调用顺序一致

问题 4:如何在循环中注入外部指令?#

场景:用户在 agent 执行过程中输入新指令,需要改变方向。

抽象:控制流中断 —— 如何在不可中断的操作中注入控制?

解决原则

  • Steering messages:在每个 turn 之前检查,用于改变方向
  • Follow-up messages:在循环结束时检查,用于延续任务
  • 分离”注入点”和”执行点”
// 注入点设计
pendingMessages = (await config.getSteeringMessages?.()) || [];
// 执行点设计
for (const message of pendingMessages) {
currentContext.messages.push(message);
}

问题 5:API Key 过期怎么办?#

场景:长时间运行的 agent,API key 可能中途过期。

抽象:凭证生命周期管理 —— 动态资源获取。

解决原则

  • 每次调用前通过 getApiKey() 动态获取
  • 回退机制:resolvedApiKey = await getApiKey() || config.apiKey

三、面试/分享核心讲述点#

开场:一句话概括#

“Agent Loop 是一个事件驱动的对话循环,它管理 LLM 和工具之间的交互,通过双层循环处理用户干预,通过三阶段执行保证工具调用的可控性。“

核心架构图(手绘风格)#

用户输入
┌─────────────────────────────────┐
│ agentLoop / agentLoopContinue │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 外层循环:follow-up 消息检查 │◄────────┐
└─────────────────────────────────┘ │
│ │
▼ │
┌─────────────────────────────────┐ │
│ 内层循环:工具调用执行 │ │
│ ├─ steering messages 注入 │ │
│ ├─ 流式 assistant 响应 │ │
│ ├─ 工具执行(并行/串行) │ │
│ └─ 终止条件检查 │ │
└─────────────────────────────────┘ │
│ │
▼ │
有 follow-up? ───── 是 ────────────────────┘
结束

面试高频问题#

Q1: 为什么需要双层循环?#

回答要点

  1. 内层循环:处理 LLM → 工具 → LLM 的自动循环(如:调用工具后又产生新工具调用)
  2. 外层循环:处理 agent “想停下来”但外部有新任务的情况(如:用户在等待时输入了新指令)
  3. 设计思想:分离”自主循环”和”外部驱动”,让 agent 既有自主性又能被干预

Q2: 工具执行的 prepare/execute/finalize 三阶段解决了什么问题?#

回答要点

prepare → 验证 + 钩子干预(可以阻止执行)
execute → 真正执行 + 实时更新
finalize → 结果增强(可以修改返回内容)
  1. 解耦:把验证、执行、后处理分离
  2. 可扩展:通过钩子实现权限控制、日志、监控
  3. 可测试:每个阶段可单独测试
  4. 错误隔离:一个阶段的错误不影响其他阶段的清理

Q3: 流式响应如何保证消息完整性?#

回答要点

  1. partial message 模式:始终在 context 中维护一个”当前正在构建”的消息
  2. 事件驱动更新:每次收到 delta 就替换 partial
  3. 最终一致性response.result() 返回完整消息,确保最后状态正确
  4. 边界处理:处理 startupdate*end 和直接 done 两种路径
// 两种路径
case "start":
context.messages.push(partial); // 添加占位
case "done":
context.messages[-1] = final; // 替换为完整

四、设计模式提炼#

4.1 事件流模式#

定义:将异步操作建模为事件序列,消费者通过事件类型响应。

好处

  • UI 可以细粒度响应
  • 易于测试和调试
  • 支持中间状态持久化

4.2 钩子链模式#

定义:在操作前后插入自定义逻辑。

钩子点

  • beforeToolCall:权限检查、参数修改、阻止执行
  • afterToolCall:结果修改、日志、清理
  • prepareNextTurn:上下文压缩、模型切换
  • transformContext:消息过滤、重排序

4.3 延迟执行模式#

定义:将异步操作表示为函数,按需执行。

好处

  • 统一处理立即结果和异步操作
  • 支持并行优化
  • 保持顺序一致性

五、记忆卡片#

卡片 1:双入口#

  • agentLoop:新建对话
  • agentLoopContinue:继续对话(最后一消息必须是 user/toolResult)

卡片 2:双层循环#

  • 外层:follow-up 消息(延续任务)
  • 内层:toolCalls + steering 消息(自主执行)

卡片 3:三阶段工具执行#

  • prepare:验证 + 钩子(可阻止)
  • execute:执行 + 实时更新
  • finalize:结果增强

卡片 4:三种消息注入#

  • prompts:启动时
  • steering:每 turn 前
  • follow-up:循环结束时

卡片 5:两种工具执行#

  • parallel:Promise.all(默认)
  • sequential:for…of(工具配置或全局配置)

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Agent Loop 核心机制详解
https://firefly.cuteleaf.cn/posts/learn-pi/agent-loop/12-AgentLoop核心机制/
作者
AltumSisy
发布于
2026-06-13
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
AltumSisy
Hello, I'm AltumSisy.
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
32
分类
3
标签
25
总字数
68,347
运行时长
0
最后活动
0 天前

文章目录