Pi Agent 学习笔记:多个工具怎样并行执行

hello 我是逆境不可逃

模型一次可能提出多个工具请求。例如分析项目时,同时读取日志和配置;修改代码时,先编辑文件,再运行检查。执行这些请求,需要考虑它们之间有没有依赖,以及失败后还剩下什么状态。

Pi 的工具调度支持串行和并行。这篇从两个文件读取请求出发,梳理调度、结果排序和部分失败的处理思路。

哪些操作适合同时执行

假设读取日志需要 3 秒,读取配置需要 1 秒,两者都只读、互不依赖。这些时间只是用于说明流程。

text 复制代码
串行:读取日志 3 秒 → 读取配置 1 秒 → 约 4 秒完成
并行:两个读取同时进行 → 约 3 秒完成

并行减少了等待,但需要先确认操作可以独立进行。

如果任务变成"修改配置,再读取修改后的内容",读取就依赖修改结果。同时启动时,读取可能先完成,拿到旧配置。两个工具同时改同一份文件,也可能相互覆盖。

Pi 当前根据执行配置和工具声明选择模式:整体配置要求串行,或者同一批调用中有任意工具声明必须串行,整批就按顺序执行;否则走并行路径。

这段调度逻辑不会自动分析两个文件路径是否相同,也不会推断业务依赖。它依赖工具声明和任务安排表达约束。串行执行能保证先后顺序,但如果后一步的参数必须根据前一步结果才能确定,通常还需要把前一步结果交回模型,再生成下一次请求。

并行之前,还有依次准备

Pi 的并行路径并不是一收到调用列表,就立即启动所有工具。它会先按顺序为每次调用做准备,包括查找工具、准备并校验参数,以及执行前置钩子。前置钩子是允许其他逻辑检查或阻止调用的位置。

普通情况下,流程可以写成下面的简化伪代码:

text 复制代码
依次检查每个工具请求:
    如果检查失败或调用被阻断:
        保存错误结果
    否则:
        保存待执行操作

并行启动通过检查的操作
等待各操作结束
按原请求顺序整理结果

因此,如果 A 的执行前检查耗时 5 秒,即使 B 的检查很快,B 也要等准备阶段结束后才开始实际执行。这是当前流程的安排,并行带来的时间收益主要发生在后面的执行阶段。

依次准备让前置检查按固定顺序进行;代价是慢检查会延迟后续工具启动。不过,检查通过也不等于操作之间自动没有冲突,工具设计仍然需要考虑共享文件或外部状态。

先完成的工具,结果不一定先进入历史

继续看两个读取请求:

text 复制代码
模型请求顺序:A 读取日志 → B 读取配置
实际完成顺序:B → A
结果消息顺序:A → B

Pi 把执行结束通知与正式结果消息分开处理。工具完成后,可以发出结束通知;等这一批操作全部结束,再按原始请求顺序生成工具结果消息。

这样,界面订阅者可以先知道 B 已完成,下一轮模型则在整批结果收齐之后继续。结果排列不会随着某次网络或磁盘操作快慢而改变。

调用编号仍然不可缺少。顺序让记录稳定,编号明确每条结果属于哪次请求,尤其是多个请求使用同一个工具名称时。

整批等待也有成本。如果 A 要 10 秒,B 只要 1 秒,当前路径不会在第 1 秒就拿 B 的结果启动下一轮模型请求。A 会拖住这一批。要实现部分结果提前驱动下一轮,就需要另外设计未完成调用的管理和后续消息合并。

一个调用被阻断,其他调用怎么办

假设 A 在前置检查时被阻断,B 检查通过,并且没有取消整个任务。

Pi 会为 A 生成错误结果,让 B 继续执行。结果整理后,模型能看到两件事:A 没有实际执行,B 得到了什么结果。

text 复制代码
A:操作被阻断,错误结果
B:操作执行成功,成功结果

如果只返回 B,模型就缺少 A 的实际状态。明确反馈"执行前被阻断",也比笼统写成"失败"更有用,因为它与"执行了一部分后失败"有不同的恢复方式。

这里还要区分阻断与终止:阻断控制某次调用能否执行,终止控制是否继续模型循环。Pi 的工具批次只有在非空且所有结果都要求终止时,才通过这个条件结束后续循环;单个结果要求终止,不会自动取消同批其他调用。用户取消等其他停止条件则另行处理。

部分失败后,不能直接整批重跑

工具批次通常没有数据库事务那样的整体回滚能力。Pi 的这段调度不会因为 B 失败,就自动撤销 A 已经产生的修改。

例如:

text 复制代码
A:追加一条配置记录,成功
B:读取另一个文件,返回"文件不存在"

模型找到正确路径后,可以重新发起 B。把 A、B 全部再执行一次,可能会重复追加配置。

因此,恢复前要查看各次调用的实际结果。对失败调用也不能只凭"报错了"就认定它毫无影响:读取失败与写入一半后失败,需要不同的处理方式。

如果业务要求两个修改一起成功,就需要额外设计,例如暂存修改并统一提交,或者为已经完成的操作提供补偿。是否能这样做,取决于工具背后的文件系统或业务服务,不能由并行调度自动保证。

取消不等于撤销

假设 A 已经修改文件,B 仍在执行,此时用户取消任务。A 的修改已经发生,发送取消请求不会让文件自动恢复。

Pi 会把取消信号传给工具,并在相关调度位置检查取消状态。正在执行的工具能否及时停止,还取决于具体实现是否处理这个信号,以及操作进行到了哪一步。

这里应分别看待"请求取消""确认停止"和"撤销修改"。例如,一个外部请求可能已经成功,只是响应还没回来;此时仅凭本地取消,无法确认远端操作也被撤销。

从结果反馈的设计上,应尽量说明哪些操作已完成、哪些已停止、哪些状态还不确定。只显示"任务已取消,文件没有变化",会掩盖已经发生的修改。

用两个小测试观察行为

本地运行了仓库已有的两个定向测试,使用模拟模型和简单的回显工具,不访问真实模型服务。两个测试均通过。

第一个测试让第一个调用等待,第二个调用先完成,再检查事件与结果消息。通过的断言对应以下顺序:

text 复制代码
执行结束通知:第二个 → 第一个
工具结果消息:第一个 → 第二个
本轮结束携带的结果:第一个 → 第二个

这验证了完成顺序和结果顺序可以不同。测试检查的是实际事件和消息,而不仅是最终文本有没有出现。

第二个测试在前置钩子中阻断第一个调用,并设置终止标记;第二个调用正常执行。通过的断言表明:

text 复制代码
实际执行的调用:只有第二个
模型请求次数:2 次

由于并非所有工具结果都要求终止,循环继续请求模型。这也说明某个调用的终止标记不能当作整批任务的取消开关。

这两个测试验证了排序与局部阻断。实际文件写入后的回滚、外部服务的取消与补偿,则需要针对具体工具另外验证。

理解 Pi 的工具并行,可以沿着一条顺序看:先判断操作是否独立,再看准备和执行如何安排,然后检查结果如何配对,最后确认部分失败或取消后留下了什么状态。这样才能判断并行究竟节省了哪些等待,又需要承担哪些恢复成本。


相关推荐
Flynt2 小时前
Java 27 升级实测:默认值动得比新特性多,有个老参数会让 JVM 直接起不来
java·jvm·后端
Bs_MoneyMagnet2 小时前
基于springboot+vue的个人健康管理系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·vue3·springboot3·计算机毕业设计
星云API技术支持2 小时前
企业微信二次开发:群权限设置、成员管理与群资料维护的接口组合实践
java·前端·企业微信
niucloud-admin2 小时前
JAVA V6 多商户商城 开发文档——job 计划任务开发
java·python·github
海宇AI3 小时前
零信任架构实战:基于海宇柠檬查出险-登记证构建自动化车抵贷核保网关
java·人工智能·架构·自动化
cfm_29145 小时前
单例模式详解
java
我叫张土豆5 小时前
本体论、DDD、SDD、TDD:AI 编程时代的四层认知栈
java·人工智能
白远山5 小时前
家政服务家政社区派单实战:调度系统架构设计与实现指南
java·开发语言·小程序·架构·需求分析
海南java第二人5 小时前
从诞生到回收:JVM对象生命周期与垃圾回收全链路解析
java