ARTICLE DETAIL

资讯详情

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

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

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、Policytsm-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 后端和一个 Skillgateway: 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/mcplatest] - 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请求请求体里包含routeskill/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: npxargs来启动网关会维护这个子进程的输入输出流。需要注意的是本地 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}, timeout60, ) 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 连通性调大timeoutMCP 工具不可用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第二客户端调用必须带 TokenToken 在 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 这三类资产既然已经统一进了网关那它们的监控也应当在同一个面板上看这一点越早做越省心。
返回列表