Agent 接入 MCP 后上下文爆炸、工具选串?一种“按需激活“的工具加载方案(含实现)

文章目录

      • [1. 问题:MCP 工具一股脑全注册,代价是什么](#1. 问题:MCP 工具一股脑全注册,代价是什么)
      • [2. 思路:把"拥有的工具"和"该暴露的工具"拆开](#2. 思路:把"拥有的工具"和"该暴露的工具"拆开)
      • [3. 实现:双池 + 三个元工具](#3. 实现:双池 + 三个元工具)
      • [4. 兜底:模型没激活就调用怎么办](#4. 兜底:模型没激活就调用怎么办)
      • [5. 完整流程演示](#5. 完整流程演示)
      • [6. 什么时候该用、什么时候不必](#6. 什么时候该用、什么时候不必)

1. 问题:MCP 工具一股脑全注册,代价是什么

给 Agent 接 MCP(Model Context Protocol)的标准动作是:连上 server,把它返回的所有工具一次性注册进可调用列表。接一两个 server 没感觉,接到三五个、每个再带十几个工具,问题就来了。

代价一:上下文被工具 schema 撑大。 每个工具暴露给大模型,是一段包含名字、描述、参数定义的 JSON。一个中等复杂度的工具,schema 摊开大约两三百个 token(经验量级,随参数多少浮动)。二十个工具就是好几千 token,而且每一轮请求都要带着,不管这轮用不用得上。

代价二:工具一多,模型就开始选串。 工具列表越长,大模型挑错工具、调错参数的概率越高。尤其当工具名相近时(get_row_by_id、get_rows_by_conditions、get_row_by_key_value),模型很容易混。工具不是越多越强,过了某个临界点反而掉准确率。

这两个问题在 MCP 生态爆发后特别突出------能接的 server 越来越多,全量注册的负担也越来越重。

2. 思路:把"拥有的工具"和"该暴露的工具"拆开

根因在于我们把两件事当成了一件:"Agent 能用的全部工具" 和 "这一刻该让模型看见的工具"。

把它俩拆开,方案就出来了:工具都注册进来,但默认不暴露给模型;模型需要时,自己去发现、激活。下面用我的开源框架 milu 的实现来讲具体怎么落地。

3. 实现:双池 + 三个元工具

第一步,工具分两个池。 注册表里维护两个字典:活跃池(注入模型,可直接调用)和休眠池(已注册但不注入):

python 复制代码
class ToolRegistry:
    def __init__(self):
        self._tools: dict[str, ToolWrapper] = {}     # 活跃池:schema 进上下文
        self._dormant: dict[str, ToolWrapper] = {}   # 休眠池:不进上下文

关键在生成给大模型的 schema 时,只遍历活跃池:

python 复制代码
def get_schemas(self) -> list[dict]:
    schemas = []
    for wrapper in self._tools.values():   # 只有活跃池,休眠工具一概不出现
        schemas.append({
            "type": "function",
            "function": {
                "name": wrapper.name,
                "description": wrapper.description,
                "parameters": wrapper.parameters_schema,
            },
        })
    return schemas

MCP 工具默认进哪个池?由一个开关决定,默认让它们休眠:

python 复制代码
mcp_tools_active_by_default: bool = False   # 默认 False:MCP 工具进休眠池

第二步,给模型三个"元工具"去自助。 既然工具休眠了,就得让模型能查、能激活。常驻三个轻量元工具在活跃池:

python 复制代码
# list_catalog ------ 列出所有休眠工具(按分类分组)
# search_tools ------ 关键词搜索休眠工具
# activate_tools ------ 激活指定工具,使其下一轮出现在工具列表里

休眠工具暴露给模型时只给摘要(名字 + 截断到 100 字的描述 + 分类),不给完整 schema,省得多:

python 复制代码
def list_dormant_tools(self) -> list[dict]:
    return [
        {"name": w.name, "description": w.description[:100], "category": w.category}
        for w in self._dormant.values()
    ]

分类是注册时从工具名前缀自动切的,fetch__html 归 fetch,mysql__query 归 mysql,列目录时自然分组。

第三步,激活就是搬家。 把工具从休眠池字典挪到活跃池字典,O(1):

python 复制代码
def activate(self, name: str) -> ToolWrapper | None:
    wrapper = self._dormant.pop(name, None)
    if wrapper:
        self._tools[name] = wrapper   # 进活跃池,下一轮 get_schemas 就带上它
    return wrapper

4. 兜底:模型没激活就调用怎么办

模型有时会"抢跑"------在目录里看到 mysql__query,没激活就直接调。执行器拦一道,回一句友好提示:

python 复制代码
if self._registry.is_dormant(func_name):
    return ToolExecutionResult(
        output=f"工具 {func_name} 尚未激活。请先调用 activate_tools 激活它。",
        is_error=True,
    )

模型读到这条,补一步 activate_tools 就接上了,不会卡死。

5. 完整流程演示

用户说"查下数据库里有多少用户",内部是这样跑的:

  1. 模型没看到 mysql 工具,但它知道有 search_tools;
  2. 调 search_tools("mysql user") → 返回 mysql__query 等候选的摘要;
  3. 调 activate_tools(["mysql__query"]) → 回"已激活: mysql__query";
  4. 下一轮 mysql__query 的完整 schema 进了工具列表,模型直接调它执行 SQL。

整个过程,上下文里始终只有 3 个元工具 + 真正被激活的那一两个,其余几十个工具全程休眠、不占上下文。

6. 什么时候该用、什么时候不必

这套方案不是无脑全上:

  • 工具就三五个:没必要,直接全激活(把开关设成 True)更省事。
  • 工具几十上百个、或上下文窗口吃紧:按需激活的收益明显,代价是首次用到某工具时多一两轮"搜索 → 激活"的往返(延迟换上下文)。
  • 它和"压缩历史消息"是两条互补的线:一个砍工具列表的常驻开销,一个砍对话历史的堆积,长对话两个一起上才稳。

完整实现在 milu 仓库的 src/milu/tools/registry.py 和 catalog.py,几百行,想抄思路或直接看代码都行。

项目地址:GitHub 搜 stephonGAO/milu(中文 LLM 一等公民的多用户 Agent 框架)

你在接 MCP 时遇到过工具太多导致模型选错、或上下文被 schema 撑爆的情况吗?评论区说说你的处理办法,我会一条条回。

相关推荐
song150265372982 小时前
小型零部件外观检测难?AI机器视觉检测设备,适配全行业小件质检
人工智能·计算机视觉·视觉检测
AI源码2 小时前
基于 Python 的短视频用户行为分析系统
开发语言·python
xsd202411182 小时前
AI 智能美甲推荐系统技术全解析
人工智能·计算机视觉
原子延迟2 小时前
批量压缩视频会变大?HandBrake等3款(2026)
图像处理·人工智能·计算机视觉·音视频
其实防守也摸鱼2 小时前
信创安全竞赛知识总结与题库
大数据·网络·人工智能·算法·安全·php
Zhao-Jichao2 小时前
AI Prompting for Everyone 课程笔记 - P19 数据分析 Data Analysis
人工智能·笔记·数据分析
阿里云大数据AI技术2 小时前
MaxAgent 实战课堂(一):从开通到第一次有效提问
人工智能·agent
第九序言T2 小时前
数据分析岗 Pandas 面试题 20 道(上):概念与读代码
python·数据分析·pandas·数据处理
Dmz丶2 小时前
前端转 AI 100 天 Day 37:Few-shot 与 CoT——让模型学会“推理”
python
孟健2 小时前
我用Grok Bot本地剪口播视频,3段提示词搞定气口切除、字幕与封面
人工智能·python·aigc