news 2026/9/30 18:28:49

Agent 基建实战:用 tsm-hub 网关统一 LLM、Tools、MCP 与 Skills

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent 基建实战:用 tsm-hub 网关统一 LLM、Tools、MCP 与 Skills

做 Agent 项目做到一半,大多数人都会遇到一个尴尬:模型切换要改代码,工具调用散落各处,MCP Server 一个项目一种连法,沉淀下来的 Skills 只能靠复制粘贴共享。我最近在梳理手头几个项目的时候,把 LLM、Tools、MCP、Skills 统一收敛到了一个叫 tsm-hub 的网关里。这个网关说白了就是一个中间层:上层给 Agent 应用暴露统一接口,下层接各种模型、函数工具、MCP Server 和技能包。这篇就把我的完整做法写下来,从为什么选网关、怎么设计结构、怎么部署、怎么把四类资产接进来,到踩过的坑和排查思路,一次性讲透。适合正在做 Agent 基建,或者已经被“模型、工具、技能”三方拉扯到头疼的团队参考。

1. 为什么要有 tsm-hub:模型、工具、技能各自为政的日子

1.1 三件事在割裂:模型调用、工具调用、技能沉淀

先说最直观的痛点。我接手过的几个 Agent 项目,几乎都有一个共性:代码里到处是 OpenAI SDK 或者各家模型厂商的 SDK,模型一换就要全局改 URL、改鉴权、改返回解析;工具函数散落在 service 层里,有些是 HTTP 调用,有些是直接 import 的 Python 函数,有些是命令行工具,Agent 要拿到正确工具列表,只能靠硬编码注册。

这种状态短期能跑,一旦工具数量超过 20 个、模型超过 3 个,项目就会开始变得僵硬。我见过最典型的一次:产品经理说要加一个“知识库检索”工具,开发同学花了两天,因为要同时改动对话逻辑、工具注册表、权限校验、日志链路四个地方。如果当时有一个统一网关,只需要在网关里注册一个新 Connector,业务侧不用动一行代码。

再来看 Skills 这一层。Skills 通常是一组提示词、脚本、参数模板、知识片段的组合,本质上是把模型调用的“行为模式”沉淀成可复用资产。但很多团队做 Skill 的方式是写 Markdown 丢进某个共享目录,或者塞进代码仓库的 prompts 文件夹,谁要用谁复制。这种做法的最大问题是:Skill 没有生命周期管理,没有版本,没有依赖声明,也没有统一的执行入口。Skill 更新以后,线上还在用旧版本,排查起来非常痛苦。

1.2 统一网关到底解决了什么

tsm-hub 的核心思路,是把模型的调用、工具的暴露、MCP 的连接、Skill 的执行都收进一个网关进程。上层应用不再关心“这个模型是哪个厂商的”“这个工具是 HTTP 还是本地函数”“这个 MCP Server 走 stdio 还是走 WebSocket”,只需要向网关发一个标准请求,网关负责路由、鉴权、编排、缓存和日志。

这个思路和 API Gateway 是一样的逻辑:把变化收敛到边界,让内部实现自由演化。对业务开发来说,Agent 应用只需要维护一套客户端,所有能力通过网关统一暴露。对平台团队来说,新增一个模型或者一个工具,不用再让业务侧发版,只要在网关配置中心加一段配置。

实际效果上,我打通 tsm-hub 之后,一个 40 多个工具、3 个模型、5 个 MCP Server 的项目,业务侧代码删掉了将近四成。那些被删掉的部分,就是之前散落各处的模型适配逻辑、工具注册逻辑和 Prompt 拼接逻辑。当然,网关本身会增加一层网络开销,但相比可维护性的提升,这点延迟完全可以接受。

1.3 为什么不是 LangChain、不是自研 SDK

有人会问,这些问题 LangChain 不是早就解决了吗?我的判断是:LangChain 解决的是“Agent 框架”层面的编排问题,而 tsm-hub 解决的是“企业接入层”的治理问题,两者不在一个层级。LangChain 里的 Tool 对象绑定死 Python 运行时,模型切换要改代码,MCP 支持也不是它的重点。而自研 SDK 更麻烦,每个项目都要引入依赖、都要维护版本、都要处理鉴权,本质上是在给团队增加长期负担。

我当时也明确过选型边界:如果要的是“开发体验”,可以用 LangChain 这类框架;如果要的是“接入治理”,一定需要一个独立网关层。tsm-hub 不属于某个语言框架,它更像一个独立的服务,用标准 HTTP/WebSocket 和外部通信。这样无论上游是 Python、Node.js 还是 Java,都可以统一对接。一句话总结:框架是给你写 Agent 用的,网关是给你管 Agent 的。

2. 整体架构拆解:一个网关,四种接入方式

2.1 分层设计:接入层、路由层、执行层、缓存层

tsm-hub 内部我分了四层,层与层之间只通过内部接口通信,这样每一层都能独立升级。

接入层是网关对外的门面,提供统一的 HTTP API 和 WebSocket 接口。HTTP 接口主要负责同步请求、健康检查、配置管理;WebSocket 主要负责流式输出,因为模型输出基本都是流式的,如果用 HTTP 轮询会非常浪费。接入层拿到请求以后,只做三件事:解析统一协议、提取身份信息、把请求丢给路由层。

路由层是网关的大脑。它根据请求里的 model、tool、skill 字段,结合当前的 Provider 配置、工具白名单、用户权限,决定这个请求应该走哪条链路。比如请求里带着“前端审查”这个 Skill,路由层会先匹配到对应的 Skill 定义,再把 Skill 引用的模型、工具、上下文模板全部拉出来,组装成一个可执行计划。

执行层真正干活。它负责并发调用多个模型、执行工具、启动 MCP Server 会话,然后把结果汇总。这里有一个关键设计:工具执行和 MCP 调用不一定是串行的,可以在同一个 Skill 里并行执行多个独立工具,执行层通过内部任务队列来控制并发度。

缓存层是我后来才加的,但效果非常明显。LLM 的结果缓存、工具返回的快照、MCP 连接池状态,都放在这一层。比如相同 question 的请求在短时间内重复出现,网关会直接返回缓存结果,不再浪费 Token。

2.2 核心概念:Provider、Connector、Route、Policy

tsm-hub 的配置模型只有四个核心概念:Provider、Connector、Route、Policy。

Provider 代表一个能力提供方,比如某个 LLM 厂商、某个本地模型服务、某个 MCP Server。一个 Provider 可以包含多个模型或工具项。Connector 是 gateway 和 Provider 之间的一段适配逻辑,它负责协议翻译。举例来说,OpenAI 的 Connector 负责把统一的调用协议转成 OpenAI 的 Chat Completions 格式,再解析返回;MCP 的 Connector 负责维护 MCP 会话,转换客户端工具调用为 MCP 工具调用。

Route 是一条调用路径,它声明了“什么请求走什么 Provider”。比如“所有 embedding 请求走 local-embedding 这个 Provider,所有对话请求走主模型 Provider”。Route 有优先级,支持通配符,也支持按来源应用分流。Policy 则是策略集,包括限流策略、权限校验、超时控制、成本配额。

这四个概念组合起来,就形成了 tsm-hub 的完整能力面。新手看配置可能觉得概念多,但一旦理解了 Route 和 Policy,后面做灰度发布和成本管控就非常简单。

2.3 配置长什么样

贴一段我在实际项目里用过的精简配置作为参考,核心声明了一个 LLM Provider、一个本地 MCP Server、一个 Tools HTTP 后端和一个 Skill:

gateway: host: 0.0.0.0 port: 8787 log_level: info providers: - name: openai-main type: llm connector: openai base_url: https://api.example.com/v1 api_key_env: OPENAI_API_KEY models: - id: chat-pro max_tokens: 8192 - id: embedding-3 max_tokens: 8192 - name: local-mcp-playwright type: mcp transport: stdio command: npx args: ["-y", "@playwright/mcp@latest"] - name: internal-tools type: tools base_url: http://internal-tool-service:8080 skills: - name: frontend-review model: chat-pro system_prompt: "你是资深前端工程师,关注可访问性与布局问题" tools: - playwright_snapshot max_iterations: 3 routes: - match: model/chat-pro provider: openai-main - match: tool/* provider: internal-tools - match: mcp/* provider: local-mcp-playwright - match: skill/frontend-review skill: frontend-review

配置通过 Git 仓库管理,网关启动时拉取,变更后可以热加载。我这里没有用数据库存配置,是因为配置属于低频率变更数据,Git 是最简单可追溯的方式,出问题还能快速回滚。

2.4 数据流一次走通

一个实际请求经过 tsm-hub 的流程大概是这样的:客户端向接入层发一个POST /v1/tsm/run请求,请求体里包含route=skill/frontend-review和用户的 query。接入层解析请求后,路由层根据配置找到 frontend-review 这个 Skill 定义,发现它依赖 chat-pro 模型和 playwright_snapshot 工具。

接着网关会先通过 MCP Connector 启动或复用 Playwright MCP 会话,获取当前页面截图和 DOM 结构,把这些上下文注入到系统提示词中,再把组装好的对话请求发送给 LLM Provider。模型返回审查意见后,执行层会把结果整理成统一响应格式,回传给客户端。整个过程里,客户端不需要知道 Playwright 是什么、MCP 是什么,只需要关心输入和输出,这就达到了网关的封装目标。

3. 从零部署与初始化:10 分钟跑起来

3.1 环境准备

tsm-hub 本身是一个无状态服务,依赖很少。部署前我建议准备一个能跑 Docker 的 Linux 机器或者直接用一台开发机,内存建议 2G 以上,因为网关会缓存配置和部分会话状态。如果后面要接 Playwright 这类浏览器 MCP,机器上还要准备相应的运行时和依赖,这属于 MCP Server 自己的要求,不算网关的硬性依赖。

安装方式有两种:一种是用 Docker 直接跑官方镜像,适合生产环境;另一种是从源码构建,适合要改内部逻辑的场景。我本地开发用的是 Docker Compose 整理的一套环境,里面包含 tsm-hub、一个 Mock LLM 服务、一个内网工具服务,这样可以在不消耗真实 Token 的情况下完整测试链路。

3.2 初始化配置

启动前需要先做三步:准备好配置文件、设置环境变量、建好日志目录。环境变量里最重要的是 API Key 类信息,我强烈建议不要直接写进 YAML,而是通过环境变量注入。YAML 里用api_key_env: OPENAI_API_KEY这种引用方式,网关读取配置时会自动从环境变量里取值,避免密钥进入 Git 历史。

第一次启动建议把log_level调成 debug,日志会打印每个路由的匹配结果和每一步的执行耗时。这一步对理解网关行为和排查问题非常有帮助。配置完成后,可以用tsm-hub config validate这类命令做一次语法校验,它会检查 Provider 引用是否存在、Skill 依赖的工具是否注册、Route 的 match 表达式是否合法。

3.3 启动服务与健康检查

启动命令很简单,指定配置文件路径就可以:

tsm-hub server --config ./config/tsm-hub.yaml

看到类似gateway started, listening on 0.0.0.0:8787的日志就说明启动成功了。接着做两个健康检查:先请求/healthz接口确认网关本身存活,再请求/v1/tsm/ping确认路由层能正常工作。我习惯写一个 30 秒的启动脚本,自动检查端口、读日志、请求健康接口,全部通过再打绿色标记,方便接入 CI/CD 流程。

如果启动时报端口占用、配置文件缺失或者 Provider 连接超时,先不要急着改代码,优先看日志输出。网关一般在 debug 模式下会把失败原因写得非常明确,比如“connector openai: connection timeout”,顺着这个提示去查网络通不通、Key 对不对,基本都能定位。

4. 四类资产接入实操

4.1 LLM 接入:多模型路由与 Key 管理

接入 LLM 是网关最基础的能力。配置里声明 Provider 以后,网关会自动生成几个标准路由,比如model/chat-pro、model/embedding-3。客户端调用时只要写模型名,网关负责把请求转发给真实的模型服务。这套设计最大的好处是:业务侧永远不会直接接触模型 API,换模型供应商只是配置变更。

多模型路由有一个关键经验:不要把所有模型都放在同一个 Provider 里。建议把“对话模型”“Embedding 模型”“视觉模型”拆成独立 Provider,因为它们的调用频率、Token 单价和限流策略完全不同。用同一个 Provider 管理会导致限流策略互相影响,比如 Embedding 调用量太大,把对话模型的配额也挤掉了。拆开之后,每个 Provider 可以单独配限流和超时参数。

Key 管理方面,网关支持一 Key 一模型,也支持一 Key 多模型。我采用的是每个 Provider 单独配置 API Key,配合环境变量注入。网关内部会做一次 Key 脱敏,日志中只显示 Key 的前四位和末四位,避免敏感信息在日志链路泄漏。平台上不同项目用的 Key 权限不同,这个字段在 Policy 里按租户隔离即可。

4.2 Tools 接入:函数注册与鉴权

Tools 接入分两种形态:一种是 HTTP 形态,网关直接转发到内部工具服务;另一种是本地函数形态,网关加载一个包含函数定义的动态模块。前者适合团队已经有的微服务,后者适合一些单机脚本、命令行工具、内部 Python 函数。

HTTP 形态的工具注册非常简单,配置里加一个base_url,然后在路由层声明tool/工具名指向这个服务。网关转发时会把原始请求参数透传过去,同时会注入调用方身份信息,方便工具服务做权限校验。这里有一个容易踩坑的地方:工具服务的接口协议必须统一。之前我们的内部工具服务有 REST、有 gRPC、还有几个直接读共享数据库的,网关对接时非常痛苦,最后统一约定所有工具暴露 REST 接口,问题才彻底解决。

鉴权方面,网关建议在 Policy 层配一套工具白名单。比如普通用户只能调用检索类工具,管理员才能调用写操作工具。白名单的优先级高于路由,请求到了网关会先查“人 + 工具”的权限组合,无权限直接返回 403。这套逻辑在业务侧本来要写很多 if else,现在全部下沉到网关,业务代码干净很多。

4.3 MCP 接入:本地与远程 MCP Server 统一代理

MCP 是 Model Context Protocol,一套用于让模型和外部工具/数据源通信的应用层软件协议,不是硬件协议。它解决的问题是把“模型怎么发现并使用工具”这件事标准化。MCP Server 可以是一个本地进程,也可以是一个远程服务。tsm-hub 把这两类统一收进来,上层请求不区分来源。

本地 MCP 接入走 stdio transport。典型例子是 Playwright MCP,它把浏览器控制能力暴露给模型。我在配置里用command: npx+args来启动,网关会维护这个子进程的输入输出流。需要注意的是,本地 MCP Server 的生命周期和网关必须绑定,网关重启时要把子进程一起清理,否则会残留僵尸进程占用端口。

远程 MCP 接入走 Streamable HTTP 或 WebSocket transport。生产环境我推荐用 WebSocket,因为长连接天然适合 MCP 这种多轮会话交互,还支持服务端主动推送。配置里声明了远程地址之后,网关会维护连接池,避免每次调用都重新握手。远程 MCP 需要注意网络策略,连接池的空闲超时建议不要设太长,否则长时间空闲的连接很容易被中间设备断开。

我实际接过的 MCP Server 大概分成三类:一类是浏览器自动化,比如 Playwright;一类是数据库操作,比如 MySQL、PostgreSQL 的 MCP 包装;还有一类是安全测试平台提供的 MCP 接口。它们协议一致,但能力边界和权限模型完全不同。所以网关里每个 MCP Provider 都要单独配 Policy,不能一把梭全放通。安全类的 MCP 只允许在特定测试环境使用,这个约束必须下沉到网关层强制生效。

4.4 Skills 接入:提示词、脚本与知识片的封装

Skills 是最有意思的一层。一个 Skill 可以理解为“为一个特定任务打包好的一组能力”,里面包含任务描述、System Prompt、依赖的工具列表、执行参数模板和迭代上限。

Skill 的引入让“给模型换个角色做专业任务”这件事从代码层面解耦了。比如我封装过一个“前端审查”Skill:模型角色是资深前端工程师,工具是 Playwright 截图和 DOM 提取,执行步骤是“先截全页图,再检查关键交互区域,最后输出问题清单”。业务侧只要调用skill/frontend-review这个路由,其他什么都不用管。Skill 的取名逻辑要清晰,我在命名时固定用“领域-动作”格式,比如frontend-review、>import requests resp = requests.post( "http://127.0.0.1:8787/v1/tsm/run", json={ "route": "skill/frontend-review", "query": "帮我审查一下当前首页在移动端布局的问题", "context": {"url": "https://example.com"}, }, headers={"Authorization": "Bearer your-token"}, timeout=60, ) result = resp.json() print(result["output"])

这个请求到了网关之后,会触发一条完整链路:路由层定位到frontend-reviewSkill,执行层启动 Playwright MCP 会话,对目标页面截图和提取 DOM,再交给 chat-pro 模型进行审查,最终把问题和建议以 JSON 格式返回。客户端从头到尾没有感知到任何底层细节。这个模式稳定跑了一段时间之后,我团队里新来的同学也能很快上手——接入一个新的工具或者模型,只需在配置里加一段声明,业务代码基本不动。

5. 常见问题与排查实录

5.1 高频问题速查表

现象可能原因解决方法
路由匹配不到,返回 404Route 的 match 表达式和请求 route 字段不一致检查routes配置里的通配符和请求字段
LLM 调用超时模型 Provider 网络不通或超时设置太短用curl测试 Provider 连通性,调大timeout
MCP 工具不可用MCP Server 启动失败或会话连接断开查看网关日志中 MCPexit code,手动执行启动命令复现
Skill 执行流程中断Skill 定义中依赖的工具未注册用tsm-hub skill list检查 Skill 依赖
日志太多刷屏log_level 设置了 debug生产环境把 log_level 调整为 info
模型返回格式错误Connector 解析逻辑与模型返回不匹配查看原始响应,检查 Connector 版本或扩展解析

这张表是我在实际运维中整理出来的,覆盖了 80% 的日常问题。每条排查路径都有一个共性:先看网关日志,再缩小范围。日志里如果能看到请求完整走完了路由和执行阶段,问题大概率出在 Provider 侧;如果日志在某个 Connector 处中断,问题大概率出在网关内部或网络。

5.2 性能与 Token 成本控制

网关的引入带来了额外的序列化开销和网络转发,但实测下来,如果只做转发不做重试和冗余处理,单请求增加延迟在 3-8 毫秒左右,几乎可以忽略。真正的性能瓶颈在模型响应时长和 MCP 工具执行时长,这两个才是大头。

Token 成本控制方面,我强烈建议开启网关的缓存层。配置里设置cache.enabled: true后,网关会对系统提示词 + 用户问题的拼接结果做哈希,命中缓存就直接返回。这在“同一类工具反复调用同一模型”的场景下特别省钱。我遇到过一个 RAG 问答项目,缓存命中率达到 35%,每月 Token 费用降低接近三成。当然缓存有风险,如果业务要求结果实时性高,比如股票价格、天气查询,一定要在路由里关闭缓存。

还有一个小技巧:给不同模型设置不同的max_tokens。比如草稿类任务可以限制 1024,正式报告类任务可以放大到 4096。网关在调用 Provider 前会强制覆盖默认值,这样避免模型无谓多输出,减少成本。注意有些模型对max_tokens有上限,设太大会直接报错,配置前先看一眼模型的文档。

5.3 权限与安全加固

网关作为统一入口,天然成为安全重点。我至少会做五件事:第一,所有外部接口强制走 HTTPS;第二,客户端调用必须带 Token,Token 在 Policy 层绑定角色;第三,工具级和 Skill 级细粒度权限,不以模型为唯一维度;第四,所有敏感字段在日志中脱敏,包括 API Key、Token、用户上传的文件内容;第五,网关自身的管理接口单独绑定内网网段,不对外暴露。

MCP 的权限要特别小心。因为 MCP 连接的是一个完整会话,一旦某个 MCP Server 被恶意利用,攻击面比单个工具大得多。我对远程 MCP 的策略是“默认拒绝,按需开放”,每个 MCP Provider 都配一份允许调用的方法清单。比如某个数据库 MCP,我只放行 SELECT 类方法,写操作在网关层直接拦截,从根上避免误操作或者越权。

Skill 的 Prompt 注入也是一类隐患。Skill 内容可能带着外部输入的 URL 或文件内容,模型被诱导后可能执行非预期工具。我的做法是在 Skill 定义里增加一个trust_level字段,外部内容只允许填充到参数区,不允许覆盖 System Prompt 的核心规则。这条规则在网关执行层用代码强制实现,而不是靠模型自觉。

6. 落地过程中的实操体会

最后分享几点个人经验。我最初把 tsm-hub 想复杂了,总希望把所有能力都做成插件、所有场景都支持,结果第一版根本跑不动。后来收敛思路:网关的核心价值就是把“接入”这件事集中化,接入能力稳定、路由清晰、日志完整,就已经完成了 90% 的使命。至于更花哨的编排能力,应该留给上层的 Agent 框架,让专业的人干专业的事。

在实际使用中,建议先从一个真实场景切入。比如你有一个“客服问答”项目,那就先把 LLM 接进来,再加一个检索工具,再把这个组合封装成一个 Skill,完整跑通后再逐步扩展其他工具和 MCP。一次加太多资产,出问题很难定位是配置问题还是代码问题。网关类系统的排错逻辑和普通应用不一样,它更像一层薄薄的壳,壳本身不容易出 bug,出问题的大多是壳外面接的那些 Provider。

日志和监控一定要从第一天做起。我之前偷懒没接监控,结果线上一个问题查了两个多小时,最后发现是某个 MCP Server 内存被打满,进程被系统杀掉,网关还一直尝试连接。后来我在网关侧加了进程健康检查和自动拉起机制,这类问题再没造成长时间故障。工具、模型、Skill 这三类资产既然已经统一进了网关,那它们的监控也应当在同一个面板上看,这一点越早做越省心。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 18:28:31

读者写者问题全解析:PV操作、信号量与读写公平

1. 读者写者问题到底在解决什么矛盾 操作系统、进程、PV操作、读者写者问题,这四个词放在一起,基本就是进程同步这一章的分水岭。前面生产者消费者还算好理解,一进到读者写者,很多人就开始迷糊:为什么读者之间不用互斥…

作者头像 李华
网站建设 2026/9/30 18:28:15

TensorFlow深度学习实战:环境配置、模型训练与部署避坑指南

在深度学习框架这块,TensorFlow 绝对是绕不开的名字。不管你是刚入门准备跑个图像分类,还是已经在搞大规模分布式训练,甚至是想把模型部署到手机端,你都会撞上它。这篇东西我打算换个角度来写——不给你念文档,而是结合…

作者头像 李华
网站建设 2026/9/30 18:27:13

OpenSpec:让配置文件成为可执行契约的规格驱动实践

1. OpenSpec 不是又一个 YAML 验证器,而是规格即契约的工程实践起点OpenSpec 这个名字最近在开发者社区里出现的频率明显高了——不是因为某家大厂突然开源,也不是某个明星项目背书,而是越来越多团队在重构 API 网关、设计微服务间通信协议、…

作者头像 李华
网站建设 2026/9/30 18:24:43

Java包与IDEA目录结构:从package声明到报错排查

刚接触Java那阵子,我最怕听到一句话:"你这类放错包了。"当时我脑子里的"包"就是一堆下载下来的jar文件,跟代码顶上那行package声明完全对不上号,可老师上课、同事沟通都用"包"这一个字,…

作者头像 李华
网站建设 2026/9/30 18:24:33

宝可梦精灵设计教科书:从剪影识别到进化叙事的完整方法论

1. 从“教科书级别”说起:宝可梦精灵设计到底强在哪 第一次看到“宝可梦教科书级别的设计,精灵学习的教科书”这个说法,我脑子里蹦出来的不是某一代作品,而是整整一套延续了二十多年的设计方法论。很多人聊宝可梦,聊的…

作者头像 李华