拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

统一网关tsm-hub:整合LLM、Tools、MCP与Skills的实践指南

统一网关tsm-hub:整合LLM、Tools、MCP与Skills的实践指南

1. 为什么我会动手做 tsm-hub 这个统一网关

先交代一下背景。我平时的工作流里,LLM、Tools、MCP、Skills 这四样东西一直是分开打理的:模型要接 OpenAI、Claude、本地 Ollama 好几家;工具脚本散落在不同项目里,有的走 HTTP 接口,有的只能命令行调用;MCP Server 又是另一套注册逻辑;Skills 则是跟着特定客户端走的,换个前端就废一半。每次搭一个新项目,光是环境接线就花掉小半天。

真正让我下定决心写 tsm-hub 的契机,是我同时跑三个智能体任务时翻车了。Agent A 要用网页抓取工具,Agent B 要调本地数据库 MCP,Agent C 要挂一套文档解析 Skills,三个任务各拉各的配置,互相之间完全不通气。结果,日志混乱、工具重复调用、模型上下文被各种冗长的工具定义挤爆。那一刻我意识到,缺的不是更多工具,而是一个能把它们统一收口的东西。

tsm-hub 这个名字,简短说就是 Tools, Skills, Models 的 Hub。它不是一个具体的模型,也不是又一个 MCP Server,而是一个位于应用层和各类资源之间的网关层,用一套统一接口把 LLM、Tools、MCP、Skills 全部暴露给上层调用方。上层不需要关心目标资源是跑在远程还是本地、走什么协议、用什么鉴权方式,只要按网关定的规范来就行。

这篇文章适合谁看?如果你也在做 Agent 开发、在维护多个 LLM 应用、或者正在被“工具定义写不进上下文”“Skills 换个客户端就没法用”“MCP 服务一多就乱”这些问题折磨,那这篇内容应该对你有用。我会把设计思路、核心模块、配置样例和踩坑记录都摊开讲,你说不定能直接拿去做参考实现。

2. 核心概念拆解:LLM、Tools、MCP、Skills 到底各司其职

2.1 四类资源的定位和边界

在动手设计统一网关之前,先把四类资源的分工理清楚。LLM 好理解,就是文本生成模型本体,负责推理和产出内容。但光有模型不行,模型不知道外部世界发生了什么,所以需要 Tools 让模型获得“行动能力”,比如查天气、发请求、执行代码。

MCP 是另一层抽象,它把工具能力协议化。原来你接一个工具要自己写 client,接十个工具要维护十套调用规范,MCP 的思路是统一成一套协议,服务方只要实现 MCP Server,调用方用标准 client 就能连接。这里面有个容易混淆的点:MCP 和普通 Tools 不是替代关系,而是组织关系。MCP Server 可以被包装成 Tool 暴露给模型,Tool 内部也可以选择用 MCP 协议和后端通信。

Skills 则是更上层的封装。一个 Skill 通常包含一组特定指令、提示词模板、工具引用和调用流程,本质上是一个“可复用的能力包”。比如“前端代码审查 Skill”可能包含审查规则清单、需要调用的静态分析工具、输出报告的格式模板。Skills 的价值在于把零散的模型能力和工具调用编排成固定套路。

打个比方:LLM 是员工,Tools 是员工能用的设备,MCP 是设备的标准化接口,Skills 是员工手里的标准作业流程手册。没有手册,员工有设备也不知道组合使用;没有标准化接口,换一台设备就要重新培训。

2.2 统一网关在这中间扮演什么角色

网关夹在中间,做的就是“统一收纳、统一转发、统一策略”的事情。统一收纳是指所有资源都在网关里注册,一个资源一个条目;统一转发是上层请求进来后,网关根据路由规则分发到对应资源;统一策略体现在模型选择、工具优先级、超时重试、权限校验这些横切逻辑上。

我见过不少项目不做网关,直接在代码里用 if-else 调各种客户端。短期内能用,一旦资源数量超过五个,或者需要给多个前端共享能力,代码就开始失控——每个前端都要重新实现一遍模型切换逻辑,每个工具都要单独做鉴权和错误处理。网关把这些共性逻辑下沉到一层,上层代码反而变得更薄。

3. tsm-hub 的整体架构设计与选型思路

3.1 设计目标:我不是在做另一个框架

动手前我给自己定了几条原则:第一,不重造协议,能站在 MCP 肩膀上就绝不自己发明一套;第二,可插拔,资源接入不能写死;第三,轻量,不想引入一个需要专门运维的重型中间件;第四,对上层暴露的接口尽量简单,哪怕团队里刚入职的新人也能快速上手。

基于这四条,我最终选择了“核心网关 + 适配器插槽”的架构。网关本体只负责路由、鉴权、装配、日志几件核心事,对每类资源定义一个统一抽象,再通过适配器把不同实现转换成抽象定义。好处是,接一个新型资源通常只需要写一个适配器,不动网关核心代码。

3.2 接入层:一套规范统一三类资源注册

统一网关首先要解决的就是“怎么描述一个资源”。我给 LLM、Tools、MCP、Skills 设计了一套统一的资源描述结构。资源描述里必须有:

  • 资源类型:model、tool、mcp、skill 四种之一。
  • 资源 ID:全局唯一,用于路由引用。
  • 连接信息:比如 base_url、model_name、command、url。
  • 认证信息:引用密钥库里的条目,不直接存明文。
  • 负载程度:这个资源适合处理什么类型任务、并发上限多少。

举个例子,一个 MCP Server 的描述是这样:

resources: - type: mcp id: local_db_mcp transport: stdio command: npx args: - mcp-server-db auth_ref: db_service_token load: medium

一个本地工具描述:

resources: - type: tool id: file_scanner exec: path/to/scan_script.py input_schema: file output_schema: json timeout_ms: 5000

一个模型描述:

resources: - type: model id: claude_main provider: anthropic model: claude-sonnet-4-5 max_tokens: 8192 fallback: local_ollama_8b

这层设计的核心价值是让上层调用只认识资源 ID,不关心底层细节。上层说“帮我调用local_db_mcp里的query工具”,网关去查注册表、建立连接、做鉴权、执行调用、返回格式化结果。

3.3 路由层:context 与能力匹配

资源都注册好之后,下一步是决定某次请求应该走哪条路径。tsm-hub 用的是“按需路由 + 上下文标签”的策略,而不用复杂的语义路由。上层在请求里带 context 标签,比如task_type=qa、domain=frontend、required_skills=code_review,网关根据标签过滤资源和能力项。

这个决策背后有一个重要原因:语义路由听上去智能,基于 embedding 给请求选资源,实际在工程里非常容易出偏差——模型能力边界频繁变化,工具是否可用随时变化,而标签路由是确定性的,完全可控。先保证确定性和可排查性,再考虑智能。

3.4 上下文装配:动态注入技能与工具定义

模型调用之前,需要把工具定义和技能说明装配进上下文。这一步做不好,多资源接入的收益会被上下文爆炸抵消掉。

网关的上下文装配策略如下:

  • 静态部分:系统提示词模板、常驻技能说明、全局指令。
  • 动态部分:按请求标签匹配到的工具定义和 MCP 工具集。
  • 会话部分:多轮对话历史中累积的中间结果。

动态装配时要注意 token 预算控制。工具定义谁写谁知道,动辄一两千 token 一个,同时塞十个工具就已经很占上下文了。网关里我设置了一个硬顶:默认最多装配 6 个工具定义,超出部分由模型先给出工具选择倾向,再由网关做第二轮装配。这算是工程向的取舍,很多方案都会加一层“候选工具排序”,但我觉得在网关层做粗过滤就够了,真正精细的筛选交给 Agent 逻辑。

技能也一样,不是全量注入。只有请求标签命中的 Skills 才会被装载到上下文里,没命中就不浪费 token。

4. 实操:从配置文件到跑通第一个请求

4.1 快速部署和启动配置

先交代一下运行环境。我用 Python 实现,依赖不多,核心就 FastAPI 加一个配置解析库。安装好依赖后,第一步是写配置文件。配置分三段:网关本身的监听地址、密钥库配置、资源注册表。

一个最小可用的配置文件如下:

gateway: host: 127.0.0.1 port: 8760 max_concurrent_tasks: 50 secrets: storage: env env_prefix: TSM_KV_ resources: - type: model id: local_ollama provider: openai_compatible base_url: http://localhost:11434/v1 model: llama3.1:8b api_key_env: OLLAMA_KEY - type: tool id: local_notes_parser exec: python path/notes_parser.py input_schema: text output_schema: json timeout_ms: 10000 - type: mcp id: web_search_mcp transport: sse url: http://127.0.0.1:8910/sse auth_ref: web_search_token

启动网关后,调用入口统一是/v1/exec。一个典型请求体长这样:

{ "resource_id": "local_ollama", "action": "generate", "messages": [ {"role": "user", "content": "用 web_search 搜一下最近的 AI Agent 框架动向"} ], "context_tags": ["research", "agent_framework"], "enable_skills": ["web_search_assist"], "max_tokens": 2048 }

网关收到之后,先解析 context_tags,从技能库装载web_search_assist,再把web_search_mcp下的搜索工具定义注入上下文,然后调用模型,等模型发出工具调用请求时,网关代理到 MCP Server 执行并返回结果。

这个流程里,对上层应用来说,它只感知到了“我提交了一个请求,得到一个回答”,并不知道中间发生了几次工具调用、走了哪个 MCP、技能是怎么装配的。这就是网关注入式的价值。

4.2 多模型多工具的负载分配逻辑

多资源接入之后不能每次都在第一条路径上吊死。tsm-hub 做了一套简单但实用的负载分配逻辑:

  • 按资源描述里的负载等级排序,优先选 load 低的资源。
  • 当同一资源被连续失败三次时自动熔断,降级到 fallback 资源。
  • 支持按请求来源分组限流,避免某个业务线把系统打爆。

这套逻辑不复杂,但是在实测中非常稳。有一次本地 Ollama 服务假死,网关连续三次请求失败后自动切到备用模型,上层完全无感。

4.3 实际接入一个自定义 Skill 的完整演示

Skill 是实践中最能体现网关上价值的部分。我演示一下我最近加的一个“前端代码审查 Skill”。

第一步,建一个 skill 描述文件:

skill: id: frontend_code_review description: 审查前端代码的潜在问题,输出结构化评审报告 trigger_tags: - frontend - review tools: - file_scanner - eslint_mcp prompt_template: | 你是资深前端评审专家。请使用 file_scanner 读取项目文件,使用 eslint_mcp 检查代码规范。 输出格式要求: 1. 问题列表(按严重程度排序) 2. 每条问题附修改建议 3. 最后给出整体质量评分,满分 100 token_budget: 4000 fallback_action: generate_issues_list_only

第二步,把 Skill 文件放到技能库目录,然后在网关配置里开启扫描路径,启动时自动加载。上层请求只要带review或frontend标签,这个 Skill 就会被自动装配。

第三步,实际请求体验。我拿了一个中等规模的前端仓库做测试,网关读文件 + 调用 eslint MCP + 模型分析,整个流程跑完只花了一次用户请求闭环。对比之前手动拼接这些步骤,操作成本下降得很明显。

Skill 机制带来的另一个收益是团队复用。一个负责写 Skill 的人把能力封装好,其他人不需要知道内部实现细节,只要在请求里打标签就能用上整套能力。这对团队协作的改善是肉眼可见的。

5. 遇到过的坑和处理方案

5.1 工具描述占满上下文问题

刚开始直接把所有注册工具全部注入上下文,结果一次请求打完发现 token 快爆了。排查后统计,光是所有工具的 JSON Schema 加起来就超过 12000 token,模型还没开始干活上下文先烧掉三分之一。

解决思路是上面说过的“双层装配”:第一层只装配请求标签命中的候选工具,第二层根据模型初步选择再精确装配。实测 token 消耗降低了约 70%,响应速度也有提升。这个问题的通用教训是:工具越多越要克制,上下文是稀缺资源,工具装配是典型的空间换时间矛盾。

5.2 不同 MCP Server 稳定性和协议差异

MCP 虽然规范统一,但实际各 Server 实现的质量参差不齐。有的走 stdio,有的走 SSE,有的走 WebSocket;有的连接一次能用很久,有的跑几轮就自动断。

我第一次接入一个 WebSocket 传输的 MCP Server 时,被“连接中断”折磨到怀疑人生。排查半天发现是 Server 端的空闲超时设置太短,只要 30 秒没有消息就自动断开。解决办法是网关层做连接保活和自动重连,同时对 MCP 调用统一包裹超时控制和错误重试。最终的配置是重试两次,超时时间按工具类型区分,查询类 10 秒,执行类 60 秒。

5.3 Skills 作用域和权限边界

早期我犯了一个错:Skill 一旦被装载,就能调用所有它能引用的工具,没有任何权限区分。结果一个只读类的审查 Skill 理论上也能触发写操作。这是很危险的设计。

现在我在 Skill 描述里增加了permissions字段,声明它允许执行的操作类型和禁止的操作类型。网关会在每次工具调用前做权限校验,不匹配直接拒绝。这套机制看起来简单,但对多租户或者共享网关的场景必不可少。

5.4 常见问题速查表

现象可能原因处理动作
请求超时MCP Server 空闲断开未重试检查保活配置,开启自动重连
模型报工具调用失败上下文未装配工具定义检查 context_tags 是否命中资源标签
Skill 未生效触发标签不匹配核对请求标签和 Skill 的 trigger_tags
工具返回数据格式错乱输出解析器不匹配按统一 JSON Schema 规范工具输出
配置改完不生效缓存未刷新调用网关 reload API 或重启进程

6. 一些现在用的顺手技巧

调试网关时,我推荐开一个“干跑模式”。这个模式下网关会把所有请求、装配结果、工具选择、token 消耗打印成跟踪日志,但不真正调用模型。用来排查“为什么某个工具没被选中”“为什么 Skill 没注入上下文”这类问题非常高效,成本还低。

另一个经验是,尽量把密钥管理和网关解耦。直接在配置文件里写明文密钥看着方便,但项目一旦共享给团队就要出事。我现在全部走环境变量注入,配置文件里只放变量名引用,这样即使配置库泄露也不会直接暴露密钥。

还有一点,日志里一定要记录每次请求的“资源实际调用链”。比如一次用户请求,实际走了 model A、调用了 tool B、mcp C 两次。这个信息对事后排查、成本归因以及优化路由策略都特别有用。我见过太多默默吞掉工具调用细节的系统,一出问题只能对着黑盒发愁。就冲这个调用链可视化的爱:

我在网关里专门存了一份结构化调用记录,字段包括资源 ID、调用时间、耗时、请求参数摘要、返回码。底层逻辑就是一次请求对应一棵调用树,上层监听接口随时能查。

后来我还给这套日志做了一页薄薄的仪表盘视感,从聚合视角能看到哪些工具调用频次高、哪些长期超时、哪些模型资源被闲置。调优路由策略的时候,有数据支撑和瞎猜感觉完全是两种效率。

7. 关于未来扩展的一点思考

网关搭好之后,最直接的扩展方向是把 Skill 做成可远程订阅的包。现在 Skills 放在本地目录,团队内部还能用,但跨团队分发还是要拷贝。下一步我打算加一个简单的仓库协议,让 Skill 能按版本从远端拉取,并且自动做兼容性检查,降低分发成本。

另一个方向是强化多模型调度。现在熔断和降级已经做了,但还没做到基于任务类型的智能调度——比如代码生成任务优先用 Code 能力强的模型,而通用问答走轻量模型。这需要预先对任务类型做分类,这个分类器本身又可以用 LLM 来实现,形成哪类任务走哪条链路的闭环。

还有个小方向我觉得很有实用价值:把工具调用的中间结果做缓存归一。很多 Agent 任务里,同一个查询可能在多轮对话中被重复调用多次,缓存下来能省不少时间和 token 消耗。这算是低成本高收益的优化点。

返回列表