1. 从"外挂"到"内建":Pi Agent 这次把 MCP 塞进核心到底改了什么
如果你最近在折腾 AI Agent 框架,大概率绕不开一个词——MCP。它被反复提及,但真正落到代码层面,很多人第一反应还是"这不就是个工具调用协议吗"。Pi Agent 这次把 MCP 从可选插件提升到核心层,表面看只是目录结构变了,实际上它重新定义了 Agent 和工具之间的对话方式。我拿到这个更新后第一时间跑了一遍,最直观的感受是:以前你是在"挂载"工具,现在你是在"声明"工具,Agent 自己会决定用哪种模式去触达。
先说清楚 MCP 是什么。Model Context Protocol,直译是模型上下文协议,本质是一套让模型和外部工具、数据源之间标准化通信的约定。你可以把它理解成 USB-C——不管对面是数据库、文件系统还是某个 SaaS 接口,只要双方都认这套协议,插上就能用。Pi Agent 之前对 MCP 的支持是"外挂式"的:你需要额外配置一个 MCP Server,Agent 通过一个中间层去调用。这次改动之后,MCP 变成了核心的一部分,工具注册、发现、调用全部走统一管线。
那"三种工具模式"又是什么?这是本次更新里最容易被忽略、但实际影响最大的部分。Pi Agent 把工具的使用方式拆成了三种:Direct(直接模式)、Deferred(延迟模式),以及一个介于两者之间的混合模式。标题里只提了 Direct 和 Deferred,但实际跑下来你会发现混合模式才是大多数生产场景的归宿。这三种模式的区别不在于工具本身的能力,而在于 Agent 在什么时机、以什么粒度去加载和调用工具。
为什么这个区分重要?因为工具调用的成本不只是网络请求那几百毫秒。每次把工具描述塞进上下文,都在消耗 token;每次让模型在几十个工具里做选择,都在增加推理负担。Direct 模式适合高频、轻量、结果确定的工具,比如查当前时间、做一次简单计算;Deferred 模式适合低频、重载、结果不确定的工具,比如跑一次全量数据同步、调用一个耗时较长的外部服务。选错模式,轻则浪费 token,重则让 Agent 在关键时刻"卡住"。
我见过太多人把 MCP 当成万能插头,所有工具一股脑全注册进去,结果 Agent 在简单任务上也要遍历几十个工具描述,响应慢得让人抓狂。Pi Agent 这次把模式选择暴露出来,其实是在逼你思考一个问题:这个工具,到底该在什么时候被 Agent"看见"?接下来我会把三种模式的机制、适用场景、配置方式、以及我在实测中踩到的坑,一层层拆开讲。
2. Direct 模式:把工具变成 Agent 的"肌肉记忆"
2.1 Direct 模式的加载机制与上下文成本
Direct 模式的核心逻辑是"常驻上下文"。工具的描述、参数 schema、返回值格式,在 Agent 启动时就被完整注入到系统提示或工具列表中。模型每次推理都能直接看到这个工具的存在,不需要额外的发现步骤。你可以把它理解成 Agent 的"肌肉记忆"——抬手就能用,不需要先想"我有没有这个能力"。
这种模式的好处非常直接:调用链路短。从模型决定调用到实际执行,中间没有额外的工具发现或加载环节。对于高频工具,比如"读取当前会话的配置""格式化一段文本""做一次单位换算",Direct 模式能把延迟压到最低。我实测下来,同一个简单查询任务,Direct 模式比 Deferred 模式平均快 200 到 400 毫秒,主要省在工具发现和 schema 注入这两个环节。
但代价也很明显:上下文占用。每个 Direct 工具都会在每一轮推理中占用 token。一个工具的描述加 schema,保守估计 150 到 300 token。如果你注册了 20 个 Direct 工具,每轮推理光工具描述就吃掉 3000 到 6000 token。这还没算模型在选择工具时的推理开销——工具越多,模型越容易"犹豫",甚至出现选错工具的情况。
提示:Direct 模式不是越多越好。我的经验是,单个 Agent 的 Direct 工具控制在 5 到 8 个以内,超过这个数,上下文成本和选择准确率都会明显恶化。
2.2 哪些工具该放进 Direct:三个判断标准
判断一个工具是否适合 Direct 模式,我总结了三把尺子。第一把是调用频率:如果这个工具在单次会话中被调用的概率超过 30%,它就有资格进 Direct。第二把是结果确定性:如果工具的返回格式固定、执行时间可预期(通常在 1 秒以内),Direct 模式不会带来额外的等待风险。第三把是参数简洁度:参数少、类型简单的工具,模型选择起来更准,适合常驻。
反过来,如果一个工具调用频率低、执行时间长、参数复杂,硬塞进 Direct 模式就是给自己找麻烦。我见过一个案例,有人把"生成月度报表"这种耗时十几秒、参数有七八个的工具放进了 Direct,结果 Agent 在闲聊场景下也会被这个工具的描述干扰,偶尔还会误触发。后来改成 Deferred,问题立刻消失。
具体配置上,Pi Agent 的 Direct 工具注册通常长这样:
from pi_agent import Agent, ToolMode agent = Agent( name="assistant", tools=[ { "name": "get_current_time", "mode": ToolMode.DIRECT, "description": "获取当前系统时间,无参数", "handler": get_current_time }, { "name": "format_text", "mode": ToolMode.DIRECT, "description": "将输入文本按指定规则格式化", "handler": format_text } ] )注意mode字段,这是本次更新的关键。以前 Pi Agent 的工具注册没有这个字段,所有工具默认走同一条路径。现在你必须显式声明模式,这本身就是一种设计上的"强制思考"。
2.3 Direct 模式的实测表现与一个反直觉发现
我在一个客服问答场景里做了对比测试。场景包含 6 个高频工具:查订单状态、查物流、查退换货政策、计算退款金额、生成工单号、查询用户等级。全部设为 Direct 模式时,平均首轮响应时间 1.2 秒,工具选择准确率 94%。把其中三个低频工具(查退换货政策、生成工单号、查询用户等级)改成 Deferred 后,首轮响应时间降到 0.9 秒,准确率反而升到 97%。
这个结果有点反直觉——工具少了,准确率怎么还高了?原因在于模型的选择空间变小了。当 6 个工具同时摆在面前,模型需要在更多选项里做判断,偶尔会把"查退换货政策"和"查物流"搞混。减少常驻工具后,干扰项少了,模型反而更专注。
注意:Direct 模式的优化方向不是"加更多工具",而是"留下最该留的"。每次新增 Direct 工具前,先问自己:去掉它,Agent 还能不能正常工作?
3. Deferred 模式:让重工具"按需出场"
3.1 Deferred 的触发链路:从"看不见"到"用得上"
Deferred 模式的逻辑和 Direct 正好相反:工具默认不出现在上下文里,只有当 Agent 判断"我需要这类能力"时,才会触发一次工具发现,把相关工具的 schema 加载进来,然后再执行调用。这个过程有点像你去仓库取货——平时货不在手边,需要的时候才去查目录、找到货架、把东西拿出来。
Pi Agent 在 Deferred 模式下维护了一个工具索引。这个索引不占用主推理上下文的 token,而是存在一个轻量的检索层里。当 Agent 遇到一个它认为"可能需要外部工具"的任务时,会先用一个轻量级的意图匹配去检索索引,找到候选工具,再把候选工具的完整描述注入当前轮次。执行完之后,这些描述可以选择保留或释放。
这个机制的关键在于检索质量。如果索引做得粗糙,Agent 找不到正确的工具,就会出现"明明注册了却用不上"的情况。Pi Agent 这次在核心层内置了基于语义相似度的工具检索,比之前外挂式的关键词匹配靠谱不少。我实测下来,只要工具描述写得清楚,Deferred 模式的工具命中率能到 90% 以上。
3.2 Deferred 适合什么:重工具、低频工具、敏感工具
Deferred 模式最适合三类工具。第一类是重工具:执行时间长、资源消耗大,比如全量数据导出、批量图片处理、调用外部大模型做二次推理。这类工具如果常驻上下文,会让 Agent 在无关任务上也背负它们的描述成本。第二类是低频工具:可能一天只用几次,比如"重置用户密码""导出年度账单"。第三类是敏感工具:涉及权限变更、资金操作、数据删除,这类工具不适合让 Agent 随时"看见",Deferred 模式相当于加了一道确认门槛。
配置上,Deferred 工具需要额外提供一个triggers字段,告诉 Agent 什么情况下该来找这个工具:
{ "name": "export_monthly_report", "mode": ToolMode.DEFERRED, "description": "导出指定月份的完整业务报表,耗时约 10-30 秒", "triggers": ["导出报表", "月度报告", "下载数据"], "handler": export_monthly_report }triggers不是必须的,但强烈建议填。没有触发词的 Deferred 工具,完全依赖语义检索,命中率会打折扣。填了触发词之后,Agent 在意图匹配阶段多了一层关键词兜底,实测命中率能提升 10 到 15 个百分点。
3.3 Deferred 模式的延迟代价与补偿策略
Deferred 模式不是没有代价。每次触发工具发现,都会增加一轮额外的推理或检索。在极端情况下,如果 Agent 连续多次判断失误,反复触发发现流程,延迟会明显上升。我遇到过最差的情况:一个任务里 Agent 触发了三次工具发现,每次都没找到对的工具,最后超时。
补偿策略有三个。第一,把触发词写全。同一个工具,把用户可能说的各种表达都列进去,减少语义检索的模糊性。第二,设置发现次数上限。Pi Agent 允许配置max_discovery_attempts,我一般设成 2,超过就回退到"告知用户当前无法处理",避免无限循环。第三,对高频 Deferred 工具做预热。如果某个 Deferred 工具在特定时间段内调用频繁,可以临时把它提升为 Direct,用完再降回去。Pi Agent 支持运行时切换模式,这个能力在促销活动、月末结算这类场景里特别好用。
提示:Deferred 模式的调试比 Direct 麻烦,因为工具"看不见"。建议在开发阶段打开 Pi Agent 的 discovery 日志,观察每次工具发现的检索结果和命中情况,这是排查"工具用不上"问题的最快路径。
4. 混合模式:大多数生产场景的真正答案
4.1 为什么纯 Direct 和纯 Deferred 都不够用
纯 Direct 的问题是上下文膨胀和选择干扰,纯 Deferred 的问题是发现延迟和命中不确定性。真实的生产场景往往同时包含高频轻工具和低频重工具,一刀切必然顾此失彼。Pi Agent 的混合模式允许你在同一个 Agent 里同时使用两种模式,并且支持按会话状态动态调整。
混合模式的核心思路是"分层":把工具分成常驻层和按需层。常驻层放 5 到 8 个最高频、最轻量的工具,保证基础交互的流畅度;按需层放其余工具,通过 Deferred 机制在需要时加载。两层之间不是固定的,可以根据会话进展动态迁移。
我在一个数据分析助手的项目里用了这个思路。常驻层放了"查询当前数据集信息""执行简单聚合""生成图表配置"三个工具;按需层放了"连接外部数据源""执行复杂 SQL""导出分析报告"等七个工具。上线后,简单查询的平均响应时间从 1.5 秒降到 0.8 秒,复杂任务的工具命中率从 82% 提升到 95%。
4.2 动态迁移:什么时候把 Deferred 提升为 Direct
动态迁移是混合模式里最有价值的部分。Pi Agent 允许你根据会话上下文、用户行为、时间窗口等条件,在运行时调整工具模式。常见的迁移场景有三种。
第一种是会话预热迁移。如果用户在会话开头就表现出明确的任务意图,比如"我要做一份月度销售分析",Agent 可以提前把相关的 Deferred 工具提升为 Direct,减少后续的发现延迟。第二种是高频触发迁移。如果某个 Deferred 工具在短时间内被触发超过阈值,自动提升为 Direct,避免反复发现。第三种是时段性迁移。比如月末结算期间,把报表相关工具临时提升为 Direct,平时保持 Deferred。
# 动态迁移示例:连续触发两次后提升为 Direct agent.register_migration_rule( tool_name="export_monthly_report", condition=lambda ctx: ctx.trigger_count("export_monthly_report", window=300) >= 2, target_mode=ToolMode.DIRECT, revert_after=600 # 10 分钟后自动降回 Deferred )这段配置的意思是:如果export_monthly_report在 5 分钟内被触发两次以上,就把它提升为 Direct,10 分钟后自动降回。这个规则在实测中非常实用,既避免了频繁发现的延迟,又不会让工具长期占用上下文。
4.3 混合模式的配置陷阱:模式冲突与优先级
混合模式最容易踩的坑是模式冲突。如果同一个工具被同时注册为 Direct 和 Deferred,Pi Agent 的行为取决于注册顺序和优先级配置。默认情况下,后注册的会覆盖先注册的,但这往往不是你想要的结果。我的建议是:永远不要重复注册同一个工具,而是通过迁移规则来改变它的模式。
另一个坑是优先级。当 Direct 工具和 Deferred 工具都能处理同一个意图时,Agent 会优先选择 Direct 工具,因为它在上下文里"更显眼"。这可能导致一个更适合的 Deferred 工具被跳过。解决办法是在 Deferred 工具的triggers里加入更精确的触发词,或者在 Agent 配置里调整tool_priority权重。
| 配置项 | 作用 | 推荐值 |
|---|---|---|
max_direct_tools | 限制 Direct 工具数量 | 5-8 |
max_discovery_attempts | 限制单轮发现次数 | 2 |
migration_window | 迁移规则的统计窗口 | 300 秒 |
revert_after | 迁移后自动回退时间 | 600 秒 |
tool_priority | 工具选择优先级权重 | Direct 1.0 / Deferred 0.8 |
这张表是我在实际项目里反复调出来的参数组合,直接抄作业基本不会出大问题。当然具体数值还要根据你的工具数量和任务复杂度微调。
5. 三种模式的选型决策:一张可以贴在显示器上的判断流程
5.1 从工具特征出发的四步判断法
选型不需要凭感觉,按四步走基本不会错。第一步,看调用频率:单次会话调用概率超过 30% 的,优先考虑 Direct;低于 10% 的,优先 Deferred;中间的看其他条件。第二步,看执行耗时:超过 3 秒的,倾向 Deferred;1 秒以内的,倾向 Direct。第三步,看参数复杂度:参数超过 5 个或包含嵌套结构的,倾向 Deferred,因为复杂 schema 占用上下文更多。第四步,看安全敏感度:涉及写操作、权限变更、资金操作的,倾向 Deferred,多一道确认门槛。
这四步走完,大部分工具的模式归属就清楚了。如果四步结论不一致,比如一个工具调用频率高但执行耗时也长,那就用混合模式加迁移规则,先 Deferred,触发频繁后再提升。
5.2 一个真实项目的模式分配复盘
我拿一个电商客服 Agent 的项目做复盘。项目一共 14 个工具,最初的分配是这样的:
- Direct(6 个):查订单、查物流、查库存、算运费、查优惠券、查用户等级
- Deferred(8 个):退换货申请、修改收货地址、取消订单、开发票、投诉登记、查历史工单、转人工、查退款进度
上线两周后,根据实际调用数据做了调整。把"查优惠券"和"查用户等级"降为 Deferred,因为实际调用频率只有 8% 和 5%,却占着 Direct 的位置。把"查退款进度"提升为 Direct,因为它在售后场景里调用频率高达 40%。调整后,Direct 工具从 6 个变成 5 个,平均响应时间下降 15%,工具选择准确率从 91% 升到 96%。
这个复盘说明一个道理:模式分配不是一次性的,要跟着数据走。Pi Agent 提供了工具调用统计接口,建议每周拉一次数据,看看哪些 Direct 工具其实很少用,哪些 Deferred 工具其实很频繁。
5.3 常见误判与纠正
最常见的误判是"把复杂工具放进 Direct,因为觉得它重要"。重要不等于高频,也不等于适合常驻。一个工具再重要,如果一个月才用一次,放进 Direct 就是浪费上下文。第二个误判是"把所有工具都设成 Deferred,以为这样最省"。省是省了,但基础交互的延迟会明显上升,用户体验反而变差。第三个误判是"忽略触发词的作用"。很多人配 Deferred 工具时不写triggers,完全依赖语义检索,结果命中率惨不忍睹。
注意:如果你不确定一个工具该用哪种模式,先设成 Deferred,观察一周调用数据,再决定是否提升为 Direct。这个"先冷后热"的策略比拍脑袋靠谱得多。
6. 实测中踩到的坑与排查链路
6.1 工具"注册了却用不上"的完整排查过程
这是 Deferred 模式最常见的问题。我遇到过一次:一个"查询历史工单"的工具注册为 Deferred,触发词也写了,但 Agent 死活不调用。排查过程是这样的。
第一步,确认工具是否真的注册成功。Pi Agent 提供了agent.list_tools()接口,打印出来发现工具在列表里,注册没问题。第二步,检查触发词是否被正确索引。打开 discovery 日志,发现 Agent 在相关意图下确实触发了发现流程,但检索结果里没有这个工具。第三步,检查工具描述。发现描述写的是"查询历史工单记录",而用户实际说的是"我之前那个问题处理得怎么样了"。语义差距太大,检索匹配不上。第四步,补充触发词和描述。把"之前的问题""处理进度""历史记录"都加进triggers,同时把描述改成更贴近用户表达的方式。改完之后,命中率立刻上来了。
这个排查链路的关键是逐层验证:先确认注册,再确认发现触发,再确认检索命中,最后确认执行。不要一上来就改代码,先看日志定位到具体哪一层出了问题。
6.2 Direct 工具过多导致的"选择困难"与修复
另一个坑是 Direct 工具过多。我接手过一个项目,前任开发者把 15 个工具全设成了 Direct。结果 Agent 在简单问候场景下都会偶尔触发工具调用,而且经常选错。修复过程分三步。第一步,拉取工具调用统计,找出调用频率低于 10% 的工具,全部降为 Deferred。第二步,对剩下的 Direct 工具,检查描述是否有重叠。发现"查订单"和"查物流"的描述都包含"查询订单相关信息",模型容易混淆。把描述改得更精确后,混淆率下降。第三步,设置max_direct_tools为 8,从配置层面防止再次膨胀。
修复后,首轮响应时间从 2.1 秒降到 1.1 秒,误触发率从 12% 降到 3%。这个案例说明,Direct 模式的治理核心是"做减法",而不是"做加法"。
6.3 迁移规则不生效的三个原因
动态迁移规则不生效,通常有三个原因。第一,统计窗口设置不当。如果migration_window设得太短,比如 60 秒,而工具触发间隔是 90 秒,规则永远不会满足。第二,条件函数写错。Pi Agent 的条件函数接收的是上下文对象,如果直接用了全局变量或者错误的字段名,条件永远为假。第三,迁移目标模式与当前模式相同。如果工具已经是 Direct,再设置迁移到 Direct,自然不会生效。
排查方法很简单:在条件函数里加日志,打印每次判断的输入和结果。我一般会加一行print(f"migration check: count={count}, threshold={threshold}"),跑一遍就能看出问题在哪。
7. 把 MCP 核心化之后,工具生态会怎么变
Pi Agent 把 MCP 加进核心,加上三种工具模式的显式区分,实际上是在推动一件事:工具从"越多越好"走向"越准越好"。以前大家比的是谁接入了更多服务,现在比的是谁能在正确的时机用正确的工具。这个转变对整个 Agent 生态都有影响。
对工具开发者来说,以后写 MCP Server 不能只写功能,还要考虑"我的工具适合什么模式"。一个设计良好的 MCP 工具,应该自带模式建议、触发词、执行耗时预估这些元信息。对 Agent 开发者来说,工具治理会成为一项独立的工作,需要持续观察调用数据、调整模式分配、优化触发词。对最终用户来说,最直观的感受是 Agent 变快了、变准了,不再动不动就"思考半天然后选错工具"。
我个人的判断是,未来半年内,工具模式管理会成为 Agent 框架的标配能力。Pi Agent 这次走在前面,但其他框架大概率会跟进。如果你现在正在做 Agent 相关的东西,建议尽早把工具模式这个概念引入到你的架构里,哪怕不用 Pi Agent,也可以在自己的系统里实现类似的分层逻辑。早一步做,就少踩一批坑。
最后分享一个我在实际项目里养成的习惯:每次新增工具,先写三行备注——调用频率预估、执行耗时预估、是否涉及写操作。这三行备注直接决定模式归属,比事后拍脑袋调整高效得多。工具治理这件事,功夫在平时,不在出问题之后。