AgentLoop 工具调用的本质问题与核心原则
1927 字
10 分钟
AgentLoop 工具调用的本质问题与核心原则
AgentLoop 工具调用的本质问题与核心原则
一、从 10 个问题抽象本质
┌──────────────────────────────────────────────────────────────┐│ 10 个具体问题 → 5 个本质问题 │├──────────────────────────────────────────────────────────────┤│ ││ 1. 并发控制(工具依赖) → 不确定性 ││ 2. 参数验证(LLM参数) → 不确定性 ││ 9. 事件顺序(完成顺序) → 不确定性 ││ ───────────────────────────────────────────── ││ 外部输入不可控:参数、结果、顺序 ││ ││ 3. 中断取消(中途干预) → 长时性 ││ 5. 流式反馈(进度展示) → 长时性 ││ ───────────────────────────────────────────── ││ 执行时间长,需要中途介入 ││ ││ 4. 错误处理(失败影响) → 传播性 ││ ───────────────────────────────────────────── ││ 单点失败不应该扩散 ││ ││ 6. 执行拦截(权限确认) → 边界性 ││ 7. 结果修改(输出控制) → 边界性 ││ 2. 参数验证(输入校验) → 边界性 ││ ───────────────────────────────────────────── ││ 执行前/后都需要拦截/处理 ││ ││ 10. 上下文管理(状态分离) → 状态性 ││ 8. 终止信号(循环控制) → 状态性 ││ ───────────────────────────────────────────── ││ 运行时状态 vs 持久化状态 ││ │└──────────────────────────────────────────────────────────────┘二、本质问题详解
问题 1:不确定性
表现形式:
-
参数不确定:LLM 返回参数可能不符合 schema
- 类型错误:number → string
- 值错误:123 → “read”
- 缺少字段:required 未提供
-
结果不确定:工具返回结果可能不符合预期
- 太长:10000 行
- 格式不对:JSON → 纯文本
- 敏感信息:不该暴露
-
顺序不确定:并行执行完成顺序不可预测
- tc1 先完成,tc0 后完成,但对话需要顺序
-
依赖不确定:工具之间是否有依赖关系
- readFile + writeFile → 可能冲突
- Loop 不知道工具内部逻辑
-
LLM 行为不确定:LLM 不一定按预期调用工具
- 可能同时调用多个
- 可能不调用终止工具
问题 2:长时性
表现形式:
-
执行时间长:工具可能执行几十秒甚至几分钟
- 文件搜索、网络请求、大量数据处理
-
用户中途取消:用户可能在执行中途想停止
- 发现不需要了、发现错了、资源限制
-
信息延迟:用户等待时看不到状态
- 界面静止、不知道进度、不知道是否卡死
-
资源占用:长时间占用资源
- 网络连接、文件锁、内存
问题 3:传播性
表现形式:
- 工具失败、Hook 失败、参数校验失败
传统处理:
- 抛错 → Loop catch → 决定是否继续
- 抛错 → 整个流程中断
- 抛错 → 之前的工具结果丢失
问题:
- 一个工具失败 → 整个任务失败
- 其他工具执行白费
- LLM 看不到错误信息,无法调整策略
问题 4:边界性
表现形式:
-
执行前边界:
- 权限检查、资源限制、危险确认、参数修正、参数校验
-
执行后边界:
- 结果裁剪、结果转换、敏感过滤、错误恢复、错误建议
-
边界的作用:
- 防护:防止不该执行的操作
- 修正:修正不符合预期的结果
- 增强:增加额外信息或处理
问题 5:状态性
表现形式:
-
运行时状态:
- 当前对话、当前工具、当前结果、动态增长
-
持久化状态:
- 历史对话、本次产出、调用者关心的
-
终止信号状态:
- terminate:工具返回
- shouldStop:Loop判断
- 被稀释的风险
-
两者的冲突:
- 运行时需要完整,持久化只需要新增
- 中途停止如何处理
三、核心解决原则
┌──────────────────────────────────────────────────────────────┐│ 5 个本质问题 → 5 个核心原则 │├──────────────────────────────────────────────────────────────┤│ ││ 不确定性 → 声明式设计 ││ ───────────────────────────────────── ││ ├─ 工具自声明:executionMode ││ ├─ 兼容层声明:prepareArguments ││ ├─ Schema 声明:参数结构 ││ ├─ 替换语义:明确意图 ││ └─ 顺序分离:不同用途不同顺序 ││ ││ 长时性 → 传递式控制 ││ ───────────────────────────────────── ││ ├─ AbortSignal 传递 ││ ├─ onUpdate 回调 ││ ├─ 各层自主响应 ││ ├─ 流式非阻塞 ││ ││ 传播性 → 结果式错误 ││ ───────────────────────────────────── ││ ├─ 错误包装成 result ││ ├─ isError 标记 ││ ├─ 流程不中断 ││ ├─ LLM 看到错误 ││ ││ 边界性 → Hook 统一拦截 ││ ───────────────────────────────────── ││ ├─ beforeToolCall:执行前 ││ ├─ afterToolCall:执行后 ││ ├─ 双重验证:兼容 + 校验 ││ ├─ 统一入口 ││ ││ 状态性 → 双层分离 ││ ───────────────────────────────────── ││ ├─ currentContext:运行时 ││ ├─ newMessages:产出 ││ ├─ shouldStop:主动判断 ││ │└──────────────────────────────────────────────────────────────┘四、核心原则详解
原则 1:声明式设计
核心思想:让组件自己声明特性,而非外部猜测。
对比传统方式:
| 传统方式 | 声明式方式 |
|---|---|
| Loop 猜测工具是否有依赖 Loop 猜测参数是否符合 Loop 猜测结果是否需要修改 问题:Loop不知道工具内部逻辑,猜测可能错误 | 工具声明 executionMode 工具声明 prepareArguments 工具声明 schema Hook 声明 block/reason 优势:组件知道自己,声明准确,灵活配置 |
原则 2:传递式控制
核心思想:传递信号而非主动控制。
对比传统方式:
| 传统方式 | 传递式方式 |
|---|---|
| Loop 发现取消 → 强制中断工具 Loop 发现超时 → 强制停止执行 问题:工具类型多样,Loop不知道如何中断,可能资源泄漏 | Loop 传递 AbortSignal Tool 收到 signal → 自己决定如何响应 Hook 收到 signal → 自己决定如何响应 优势:各层自主,工具知道如何安全停止 |
原则 3:结果式错误
核心思想:错误也是结果,而非中断。
对比传统方式:
| 传统方式 | 结果式方式 |
|---|---|
| tool.execute() 抛错 → Loop catch → 流程中断 → 其他工具白费 问题:传播性,单点失败扩散,LLM看不到错误 | tool.execute() 抛错 → Loop 包装 → { content: “Error: xxx”, isError: true } → 加入对话 → LLM 看到错误 → 流程继续 优势:不传播,单点失败不扩散,LLM可以调整策略 |
原则 4:Hook 统一拦截
核心思想:边界统一处理,而非分散。
对比传统方式:
| 传统方式 | Hook统一方式 |
|---|---|
| 工具内部检查权限 工具内部裁剪结果 工具内部过滤敏感 问题:工具职责混乱,重复代码,难维护 | beforeToolCall:所有执行前处理 afterToolCall:所有执行后处理 工具只负责执行 优势:职责分离,统一入口,易维护 |
原则 5:双层分离
核心思想:运行时 vs 持久化,而非混淆。
对比传统方式:
| 传统方式 | 双层方式 |
|---|---|
| 一个 context ├─ 包含所有历史 ├─ 包含本次新增 ├─ 返回给调用者 问题:调用者拿到整个历史,可能重复保存,调用者不灵活 | currentContext:运行时完整 newMessages:本次产出 优势:调用者灵活,不重复,关注点分离 |
五、一句话总结
本质问题: 不确定性 + 长时性 + 传播性 + 边界性 + 状态性
核心原则: 声明式设计 + 传递式控制 + 结果式错误 + Hook统一拦截 + 双层分离
对应关系: 不确定性 → 声明式设计(组件自声明) 长时性 → 传递式控制(信号传递) 传播性 → 结果式错误(错误即结果) 边界性 → Hook统一拦截(边界集中) 状态性 → 双层分离(运行时/持久化)支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!
AgentLoop 工具调用的本质问题与核心原则
https://firefly.cuteleaf.cn/posts/learn-pi/agent-loop/13-工具调用本质问题/ 相关文章 智能推荐
1
AgentLoop 工具调用的设计问题与解决方案
pi 从开发者角度解析10个核心问题的具体解决方案,掌握并发控制、参数验证、中断取消、错误处理等关键技术
2
Loop与Agent 问题驱动的架构设计分析
pi 从表面问题出发,抽象本质问题,推导核心原则,展示技术实现的完整设计过程,理解agent-loop.ts与agent.ts的分层职责
3
Agent Loop 核心架构解析
pi 基于 agent-loop.ts 源码分析,理解双层循环架构、四大核心机制、五大问题抽象
4
Agent Loop 核心机制详解
pi 深入理解双入口设计、双层循环架构、三种消息注入机制、工具执行模式
5
Message 与 Tool 的时序关系
pi 理解工具调用的两阶段设计:LLM生成参数与Agent执行工具的时序边界与协作方式
随机文章 随机推荐