<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>MCP on Zewang's Blog</title><link>https://zewang0217.github.io/tags/mcp/</link><description>Recent content in MCP on Zewang's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Mon, 13 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://zewang0217.github.io/tags/mcp/index.xml" rel="self" type="application/rss+xml"/><item><title>Agent Tool 动态注册、发现、注入：一个工业级方案</title><link>https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/</link><pubDate>Mon, 13 Jul 2026 00:00:00 +0000</pubDate><guid>https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/</guid><description>&lt;img src="https://zewang0217.github.io/" alt="Featured image of post Agent Tool 动态注册、发现、注入：一个工业级方案" /&gt;&lt;h2 id="背景为什么需要动态工具管理"&gt;背景：为什么需要动态工具管理
&lt;/h2&gt;&lt;p&gt;假设你正在设计一个大型 Agent 系统。一开始只有 5 个工具，硬编码是自然的——写一个数组，模型调哪个你都知道。但当工具增长到 50 个、200 个时，两个问题会迫使你重新设计。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一个问题：运维成本。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;硬编码意味着每次新增工具都要：改代码 → 走 CI → 重启服务。在微服务架构里没有人能接受这种模式——为什么 Agent 的工具就要这么原始？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二个问题，更根本：LLM 的注意力不是无限的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当一个 Agent 有 200 个工具时，你不能把 200 个 Schema 都塞进上下文。Token 开销是一方面，更重要的是&lt;strong&gt;选择困难&lt;/strong&gt;：工具越多，模型选错工具的概率越大。这是信息过载，不是模型能力问题。&lt;/p&gt;
&lt;p&gt;所以动态管理的核心驱动力不是&amp;quot;做不做得到&amp;quot;，而是&lt;strong&gt;大规模下必须解决这两个问题，否则系统不可扩展&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="顶层设计三个正交维度"&gt;顶层设计：三个正交维度
&lt;/h2&gt;&lt;p&gt;一个好的动态工具系统，本质上要在三个维度上做解耦：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;维度&lt;/th&gt;
 &lt;th&gt;解决什么问题&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;注册&lt;/strong&gt;（工具怎么进来）&lt;/td&gt;
 &lt;td&gt;新增工具不重启服务&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;发现&lt;/strong&gt;（工具状态怎么感知）&lt;/td&gt;
 &lt;td&gt;运行时感知工具的增删和变更&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;注入&lt;/strong&gt;（工具怎么进 LLM 上下文）&lt;/td&gt;
 &lt;td&gt;不把全部工具塞进一个 prompt&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;三者正交——你可以只做注册不做注入（那注册了也没用），也可以只做注入不做注册（那就还得硬编码）。三个都做，才叫工业级。&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="309px" data-flex-grow="128" height="1326" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/architecture.png" srcset="https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/architecture_hu_5bd48640ad3da4ed.png 800w, https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/architecture_hu_5c53d3425723a0eb.png 1600w, https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/architecture.png 1710w" width="1710"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;五个模块各司其职：Registry 管数据、Loader 管本地生命周期、MCP Pool 管远程生命周期、Orchestrator 管调用、Injector 管上下文筛选&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="第一链路本地-local-工具"&gt;第一链路：本地 Local 工具
&lt;/h2&gt;&lt;h3 id="注册选择注解--目录扫描而非配置文件"&gt;注册：选择&amp;quot;注解 + 目录扫描&amp;quot;而非&amp;quot;配置文件&amp;quot;
&lt;/h3&gt;&lt;p&gt;为什么选择注解+目录扫描？两个原因：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;理由一：解耦开发流程。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果用配置文件注册（YAML 里写 tools: [&amp;hellip;]），新增工具需要改两个文件——工具本身 + 配置。这看起来只是多一步，但在多人协作的工程里，意味着代码审查、合并冲突、配置同步问题。如果用注解，开发者只新建一个文件，框架自动扫描注册，这是真正的&amp;quot;零侵入&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;理由二：文件系统本身就是最好的&amp;quot;注册表&amp;quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文件存在 = 工具存在，文件删除 = 工具下线。不需要额外维护一份状态配置，用文件系统的事实状态作为真相来源。&lt;/p&gt;
&lt;p&gt;伪代码的核心逻辑：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 启动时：扫描目录，加载所有 .py 文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;py_file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;plugin_root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rglob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;*.py&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exec_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 触发 @register_tool 装饰器 → 自动写入注册中心&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 运行时：watchdog 监听文件增删改&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;on_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;exec_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;on_modify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_meta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="发现区分启动发现和运行时发现"&gt;发现：区分&amp;quot;启动发现&amp;quot;和&amp;quot;运行时发现&amp;quot;
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;启动发现&lt;/strong&gt;是静态的——扫描一遍目录，注册所有工具。简单，快。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时发现&lt;/strong&gt;是动态的——通过 watchdog 监听文件变更，实时同步注册表。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为什么需要区分？因为&lt;strong&gt;启动发现&lt;/strong&gt;处理的是存量，&lt;strong&gt;运行时发现&lt;/strong&gt;处理的是增量。存量用扫描（一次性成本），增量用监听（持续成本）。如果运行时也全量扫描，日志里会塞满噪音。&lt;/p&gt;
&lt;h3 id="注入四层策略的设计意图"&gt;注入：四层策略的设计意图
&lt;/h3&gt;&lt;p&gt;注入是整个系统里最关键也最容易设计过度的部分。四层模式不是一次性想出来的，是从最简单的&amp;quot;全部注入&amp;quot;逐步演化的结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;模式 1：全局常驻&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;意图：有一些工具确实是每次对话都要用的——获取时间、检查网络、简单的文本处理。这些工具放不放都很别扭：不放的话每个对话都要多一次调用去找它们，放的话它们占了上下文但大部分时候用不到。&lt;/p&gt;
&lt;p&gt;权衡：只对调用频率极高、Schema 极轻的工具使用。数量不超过 3-5 个。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 初始化时，从注册中心筛选 core 分组的工具，永久挂载&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;global_fixed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;core&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;模式 2：意图驱动（核心模式）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是最主流的方案，也是设计工作量最大的。每一轮用户 query 进来后，先做意图分类，再匹配工具。&lt;/p&gt;
&lt;p&gt;为什么不用关键词匹配就完了？因为工具的语义和用户 query 的语义不一定重叠。用户说&amp;quot;帮我查一下这个订单&amp;quot;，但工具名可能是 &lt;code&gt;tracking_info_lookup&lt;/code&gt;。关键词匹配不到&amp;quot;订单&amp;quot;，但意图分类器应该能映射到。&lt;/p&gt;
&lt;p&gt;意图分类器为什么不做成全量 LLM？——成本。每一次对话都调一次大模型做分类，Token 开销大、延迟高。&lt;/p&gt;
&lt;p&gt;所以工业级的做法是&lt;strong&gt;双阶段分类&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 阶段 1：正则（高速，毫秒级）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;RULES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 阶段 2：小模型（兜底，几十毫秒）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bert_model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;正则能匹配的直接返回，匹配不到的 fallback 到小模型。这样 95% 的情况都在 5ms 内完成。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inject_by_intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;classifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;global_fixed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# 硬上限，防止分类器失误导致工具过多&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;模式 3：权限过滤&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这不是功能，是安全基线。任何注入策略都必须附带权限校验——不仅仅是因为安全合规，更实际的原因是：没有权限过滤，你就无法在同一个 Agent 实例上服务不同角色的用户。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 注入前，校验用户角色是否在工具 permission 白名单内&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="c1"&gt;# 跳过该工具&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;模式 4：会话手动指定&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;多 Agent 场景下，父 Agent 知道子 Agent 需要什么工具，不需要意图识别。这种场景下&amp;quot;精确指定&amp;quot;比&amp;quot;猜测&amp;quot;更高效。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 上层编排层精确指定工具名，注入控制器精准取出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;specified_names&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="为什么四层而不是一层"&gt;为什么四层而不是一层？
&lt;/h3&gt;&lt;p&gt;因为&lt;strong&gt;没有一种注入策略能覆盖所有场景&lt;/strong&gt;。全局常驻覆盖高频，意图驱动覆盖主流场景，权限过滤是安全兜底，手动指定覆盖编排场景。四层共存意味着每个场景都能找到最合适的方案，而不是用一把锤子砸所有钉子。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="第二链路mcp-远程工具"&gt;第二链路：MCP 远程工具
&lt;/h2&gt;&lt;h3 id="为什么需要单独一个-mcp-链路"&gt;为什么需要单独一个 MCP 链路？
&lt;/h3&gt;&lt;p&gt;如果本地工具已经覆盖了所有场景，为什么还要 MCP？&lt;/p&gt;
&lt;p&gt;答案在于&lt;strong&gt;所有权边界&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;本地工具是 Agent 进程内的——你写代码、你部署、你控制。但真实业务中，大部分工具不属于 Agent 团队。订单查询工具属于订单团队，物流工具属于物流团队，支付工具属于支付团队。&lt;/p&gt;
&lt;p&gt;你不能让每个业务团队都把工具写成 Python 文件放到 Agent 的插件目录里——这是耦合，也是安全灾难。&lt;/p&gt;
&lt;p&gt;MCP 的核心价值不是&amp;quot;远程调用&amp;quot;，而是&lt;strong&gt;组织边界隔离&lt;/strong&gt;。每个团队独立部署、独立运维自己的 MCP Server，Agent 只通过标准协议消费工具。&lt;/p&gt;
&lt;p&gt;&lt;img class="gallery-image" data-flex-basis="644px" data-flex-grow="268" height="876" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/dual-path.png" srcset="https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/dual-path_hu_dbf86d612538bbf1.png 800w, https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/dual-path_hu_72277b312129a584.png 1600w, https://zewang0217.github.io/p/agent-tool-dynamic-register-inject/dual-path.png 2352w" width="2352"&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;图：本地工具链路与 MCP 远程链路双架构&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="动态注册通过协议而非配置"&gt;动态注册：通过协议而非配置
&lt;/h3&gt;&lt;p&gt;MCP 的注册机制不同于本地工具的&amp;quot;文件扫描&amp;quot;。它基于协议：Client 在握手阶段调用 &lt;code&gt;list_tools&lt;/code&gt;，Server 返回全部工具 Schema。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Client 启动时：连接每个配置的 Server，拉取工具列表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;configs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;MCPClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;list_tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ToolMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 用 server_id 做命名空间，避免跨服务重名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;inputSchema&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mcp:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;group&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;permission&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;注意 &lt;code&gt;name&lt;/code&gt; 的命名方式：&lt;code&gt;server_id + &amp;quot;_&amp;quot; + tool_name&lt;/code&gt;。这不是随意设计的。不同 MCP Server 可能暴露同名工具（比如订单 Server 和退款 Server 都有 &lt;code&gt;query&lt;/code&gt;），必须用命名空间隔离。&lt;/p&gt;
&lt;h3 id="发现三种机制的取舍"&gt;发现：三种机制的取舍
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;机制&lt;/th&gt;
 &lt;th&gt;成本&lt;/th&gt;
 &lt;th&gt;实时性&lt;/th&gt;
 &lt;th&gt;适用场景&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;心跳+重连&lt;/td&gt;
 &lt;td&gt;低&lt;/td&gt;
 &lt;td&gt;中等&lt;/td&gt;
 &lt;td&gt;检测服务崩溃和恢复&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;定时轮询&lt;/td&gt;
 &lt;td&gt;中&lt;/td&gt;
 &lt;td&gt;低（秒级）&lt;/td&gt;
 &lt;td&gt;感知工具内部变更&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;配置热更新&lt;/td&gt;
 &lt;td&gt;低&lt;/td&gt;
 &lt;td&gt;高&lt;/td&gt;
 &lt;td&gt;新增/删除 Server 节点&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;为什么不只用一种？因为&lt;strong&gt;每种机制覆盖一种故障模式&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;心跳保活解决&amp;quot;Server 挂了我不知道&amp;quot;&lt;/li&gt;
&lt;li&gt;轮询解决&amp;quot;Server 没挂但工具列表变了&amp;quot;&lt;/li&gt;
&lt;li&gt;配置热更解决&amp;quot;新增了一台 Server&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;只做心跳不做轮询，Server 添加了新工具你得等重启才知道。只做轮询不做心跳，Server 挂了要等下一轮轮询（最多 60s）才能发现。&lt;/p&gt;
&lt;h3 id="注入统一"&gt;注入统一
&lt;/h3&gt;&lt;p&gt;MCP 工具注册后，和本地工具共享同一个 &lt;code&gt;AgentInjector&lt;/code&gt;。注入层不需要区分工具来源——四种模式全部复用。唯一的区别在调用阶段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ToolOrchestrator 路由&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mcp:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mcp_pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpc_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;call_tool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_cls&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这一行 &lt;code&gt;if&lt;/code&gt; 是整个双链路设计的精髓——注册中心归一化后，上层业务无感知。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="幻觉抑制的设计取舍"&gt;幻觉抑制的设计取舍
&lt;/h2&gt;&lt;p&gt;工具注入中的幻觉抑制，本质是&lt;strong&gt;预防而非纠错&lt;/strong&gt;。一旦工具进了上下文，LLM 选了它就是选了它，你没法&amp;quot;事后撤回&amp;quot;。&lt;/p&gt;
&lt;p&gt;所以所有抑制策略都在注入阶段完成：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;策略&lt;/th&gt;
 &lt;th&gt;设计意图&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;数量硬上限（≤15）&lt;/td&gt;
 &lt;td&gt;不是限制工具，是限制选择的复杂度。15 是经验值——超过这个数，模型准确率开始下降&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;禁用过滤&lt;/td&gt;
 &lt;td&gt;紧急熔断通道。线上出问题直接标记 disabled，不需要重新部署&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;权限过滤&lt;/td&gt;
 &lt;td&gt;不是为了&amp;quot;安全&amp;quot;的抽象目标，而是为了让同一个 Agent 能服务不同角色的用户&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;描述约束&lt;/td&gt;
 &lt;td&gt;在 &lt;code&gt;description&lt;/code&gt; 里写明&amp;quot;不可用于 XXX 场景&amp;quot;——从 prompt 层约束调用意愿&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;注入日志&lt;/td&gt;
 &lt;td&gt;不是为了调试，是为了&lt;strong&gt;归因&lt;/strong&gt;。当模型调错工具时，你能回答&amp;quot;为什么这个工具当时在上下文中&amp;quot;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="思考与延伸从-tools-到-skills"&gt;思考与延伸：从 Tools 到 Skills
&lt;/h2&gt;&lt;p&gt;（本部分为用户补充思考）&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;这套架构是为了解决 tools 的使用问题。能不能迁移到 skills 层面？如何提高 skills 命中率？&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;从架构角度看，Tools 和 Skills 面对的困境是&lt;strong&gt;同构的&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Tools&lt;/th&gt;
 &lt;th&gt;Skills&lt;/th&gt;
 &lt;th&gt;共同本质&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;全量 Schema 注入 → Token 爆炸&lt;/td&gt;
 &lt;td&gt;全量 Skill 列表注入 prompt&lt;/td&gt;
 &lt;td&gt;候选集超过模型注意力范围&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;工具太多 → LLM 选错&lt;/td&gt;
 &lt;td&gt;Skills 太多 → Agent 忽略&lt;/td&gt;
 &lt;td&gt;信息过载导致选择退化&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;同构问题应当用同构方案解决。&amp;ldquo;按需注入&amp;quot;不是 Tools 层的专属模式，而是 Agent 系统处理&lt;strong&gt;大量可选项&lt;/strong&gt;时的通用范式。无论可选项是工具、技能、知识库文档还是 API 端点，当候选集超过有效注意力范围时，就需要一个前置分类层来缩小范围。&lt;/p&gt;
&lt;p&gt;如果把这个思路投射到 Skills 管理上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户 query → 轻量分类器 → 只注入 Top-3/5 匹配 skills → 节省 80%+ prompt 空间
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ (低置信度)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 退化为全量 skill 列表（兜底）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这和 Tools 的&amp;quot;意图驱动注入&amp;quot;是同一种架构模式。区别在于：Tools 的注入是 LLM 函数调用层面的，Skills 的注入是 Agent 行为引导层面的。&lt;/p&gt;
&lt;h3 id="延伸二skill-命中率的工程化实践"&gt;延伸二：Skill 命中率的工程化实践
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;参考：&lt;a class="link" href="https://x.com/yyyole/article/2061725917215605236" target="_blank" rel="noopener"
 &gt;沐阳 — Skill 装太多，如何提升命中率？4 个实用方法讲透&lt;/a&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;上文从架构视角讨论了 Tools → Skills 的迁移可行性。而实际工程中，提升 Skills 命中率有四个经过验证的具体方法，它们和本文的工具注入架构形成了有趣的对照：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法 1：优化 Skill 描述&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不要只写&amp;quot;这个 Skill 能做什么&amp;rdquo;，要写清&amp;quot;什么时候该用、什么时候不该用&amp;quot;。一个合格的描述包含 5 个要素：解决的问题、适用场景、输入输出、不适用场景、与相似 Skill 的区别。&lt;/p&gt;
&lt;p&gt;这与本文中工具 Schema 的 &lt;code&gt;description&lt;/code&gt; 约束策略一脉相承——从 prompt 层约束调用意愿。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法 2：建立 Skill Tree（分层路由）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当 Skill 超过 100 个时，平铺列表必然导致选择困难。正确做法是分层：先判断意图大类，再判断子类，最后选择具体 Skill。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户任务 → 意图分类（信息查询 / 内容处理 / 执行动作）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 来源分类（公网 / 邮箱 / 文件）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 具体 Skill 选择
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这和本文的&amp;quot;意图驱动注入&amp;quot;是同一种模式——先用分类缩小候选集，再做精确选择。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法 3：负样本描述&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;写清楚&amp;quot;什么时候不用&amp;quot;比写&amp;quot;什么时候用&amp;quot;更重要。每个 Skill 至少写三类负样本：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;相似但不该用的任务&lt;/strong&gt;（写邮件 vs 发邮件）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容易混淆的相邻 Skill&lt;/strong&gt;（公网搜索 vs 知识库搜索）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高风险操作的确认边界&lt;/strong&gt;（支付类必须先确认）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这实际上把&amp;quot;权限过滤&amp;quot;和&amp;quot;描述约束&amp;quot;结合为了一个可执行的规则。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法 4：召回 + 重排机制&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当 Skill 上百个时，不能全塞进 prompt 让模型选。应设计为检索系统：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用户 query → 召回候选（向量/关键词）→ 规则过滤 → 模型重排 → 执行
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这与本文的 ToolOrchestrator + AgentInjector 架构完全同构。区别在于工具注入是系统层的 Schema 筛选，而 Skill 路由是 Agent 行为层的知识筛选。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;四个方法共同揭示了一个规律：当候选项超过模型的注意力边界时，不要指望模型自己解决，而应该用工程手段缩小候选范围。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;从架构师的视角来看，这套系统的设计没有&amp;quot;创新&amp;quot;，只有&lt;strong&gt;合理的权衡&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;注册&lt;/strong&gt;选注解+目录扫描而非配置 → 为了开发者体验和零侵入&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;发现&lt;/strong&gt;分启动扫描和运行时监听 → 为了区分存量和增量两种工作负载&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注入&lt;/strong&gt;做四层而非一层 → 因为没有一种策略能覆盖所有场景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Local + MCP 双链路&lt;/strong&gt; → 为了组织边界隔离，而非技术层面的&amp;quot;分布式调用&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;幻觉抑制在注入阶段做&lt;/strong&gt; → 因为预防成本远低于纠错&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个设计决策背后都有一个具体的工程约束在驱动。理解这些约束，比记住架构图更重要。&lt;/p&gt;</description></item></channel></rss>