Skip to main content

打造 AI 的 Token 永动机

· 16 min read
Kyle
CTO of the Ph.D. Creative Station

让 AI 持续稳定地工作,不在于囤积多少 API Key,而在于架构层面把「输入」和「输出」都兜住。本文记录我用 LiteLLM 搭建的一套 Token 流水线:多 provider 容灾保障输入不断,MCP 工具网关拓展输出质量,以及如何把任意本地能力转化为 MCP 服务喂给 Agent。


一、问题的起点:Token 为什么会断?

用 AI Agent 写代码、做研究的人,大概都经历过这种时刻——深夜灵感来了,敲下一段 prompt,然后看到一行红字:

litellm.exceptions.APIError: upstream service unavailable

单一 API 的脆弱性不用多说:速率限制、服务宕机、余额不足、区域封锁……任何一个都能让你的工作流戛然而止。

解决思路其实很简单:不要依赖任何单一入口。与其把命运交给一家服务商,不如用一个网关把多家聚合起来,一家挂了自动切到下一家——这就是 LiteLLM 的 fallback 机制。

但真正落地时,有几个细节值得认真对待。

二、第一层:多 provider 的 fallback 容灾

2.1 核心思路

以 DeepSeek 模型为例,同一个 deepseek-v4-pro,通过多条不同的路径获取:

对上游的 Agent 来说,它只需要请求 LiteLLM-Text-deepseek-v4-pro 这一个模型名。至于这个请求走哪条路到达真正的模型——那是 LiteLLM 的事。

2.2 model_list 配置

在 LiteLLM 的 config.yaml 中,每个 provider 通道都注册为一个独立的 model entry:

config.yaml(model_list 片段)
model_list:
- model_name: LiteLLM-Text-deepseek-v4-pro
litellm_params:
model: openai/deepseek-v4-pro
api_base: https://provider-a.example.com/v1
api_key: os.environ/PROVIDER_A_API_KEY

- model_name: LiteLLM-Text-deepseek-v4-pro-ProviderB
litellm_params:
model: openai/deepseek-v4-pro
api_base: https://provider-b.example.com/v1
api_key: os.environ/PROVIDER_B_API_KEY

- model_name: LiteLLM-Text-deepseek-v4-pro-ProviderC
litellm_params:
model: openrouter/deepseek/deepseek-v4-pro
api_key: os.environ/PROVIDER_C_API_KEY
note

命名规范采用 {网关}-{模态}-{模型}-{Provider} 的格式,既能一眼看出归属,也方便在 fallback 配置中引用。

2.3 fallback 链配置

有了多个 provider 通道后,在 router_settings.fallbacks 中定义优先级链:

config.yaml(fallback 配置)
router_settings:
routing_strategy: usage-based-routing
allowed_fails: 5
num_retries: 2
cooldown_time: 18000
fallbacks:
- LiteLLM-Text-deepseek-v4-pro:
- LiteLLM-Text-deepseek-v4-pro-ProviderB
- LiteLLM-Text-deepseek-v4-pro-ProviderC
- LiteLLM-Text-deepseek-v4-pro-ProviderD
- LiteLLM-Text-deepseek-v4-pro-ProviderE
- LiteLLM-Text-deepseek-v4-flash:
- LiteLLM-Text-deepseek-v4-flash-ProviderB
- LiteLLM-Text-deepseek-v4-flash-ProviderC
- LiteLLM-Text-deepseek-v4-flash-ProviderD

关键参数说明:

参数典型值含义
routing_strategyusage-based-routing按用量权重分配请求,而非简单轮询
allowed_fails5连续失败 N 次后才触发冷却
cooldown_time18000冷却 5 小时(秒)后重新尝试该 provider
num_retries2每个 provider 重试 N 次后才走 fallback
为什么不用轮询?

usage-based-routing 会优先把请求发往用量较低的 provider,避免某个通道因频率限制被过早触发冷却——这是一种更"省着用"的策略。

部署这套配置后,日常工作流最直观的变化是:几乎不再看到 API 报错。偶尔某个 provider 抽风,LiteLLM 在日志里默默地切到下一个通道,整个过程对上游 Agent 完全透明。

这就是第一层保障——Token 输入不断流。

三、第二层:MCP 工具网关——让 Token 烧得更有价值

Token 稳定供应只是第一步。更关键的问题是:Agent 拿着这些 Token 能干什么?

一个裸 LLM 只能聊天。但接入了工具的 Agent——能搜索论文、能操作文献库、能在 Jupyter 里跑代码、能操纵桌面——产出的价值完全不在一个量级。

这就是 MCP(Model Context Protocol)的意义。而 LiteLLM 从 v1.78.0 开始,不仅能代理 LLM 请求,还能同时承担 MCP Gateway 的角色:把所有 MCP Server 注册到 LiteLLM,然后本机所有 Agent 客户端共享同一套工具池。

3.1 架构一览

一个网关,同时承担 LLM 代理和 MCP 工具联邦两份职责,所有 Agent 客户端共享同一组工具——不需要每个 Agent 各自配置一遍。

3.2 mcp_servers 配置样例

来看两个典型的 MCP Server 接入方式,它们并列写在同一段 mcp_servers 配置中:

config.yaml(mcp_servers 片段)
mcp_servers:
arxiv:
transport: stdio
command: uvx
args:
- "--with"
- "mcp[cli]>=1.10.1,<2.0.0"
- "arxiv-mcp-server"
allow_elicitation: false
taibu:
transport: stdio
command: npx
args:
- "-y"
- "taibu-mcp"
allow_elicitation: false

其中,arxiv-mcp-server 让 Agent 具备了搜索、下载、阅读 arXiv 论文的能力——Agent 可直接调用 arxiv_search_papersarxiv_read_paper,而不必再手动打开浏览器检索。taibu-mcp 则是一个传统文化工具集(八字、紫微斗数、六爻、奇门遁甲等);接入后,查表推算类工作可完全自动化,Agent 能直接输出结构化的命盘分析。

3.3 工具名映射规则

MCP Server 注册到 LiteLLM 后,暴露给 Agent 的工具名遵循固定格式:

mcp__litellm__<server_name>_<tool_name>

例如 arxiv server 的 search_papers 工具,在 Agent 侧看到的名称是 mcp__litellm__arxiv_search_papers

命名注意

LiteLLM 的 MCP Server 名称不能包含连字符 -,因为它会和 MCP 工具名前缀分隔符冲突。用下划线替代:paper-searchpaper_search

3.4 从"写完"到"写好"

有了工具加持,Agent 的工作方式发生了质变。举个例子:

  • 没工具时:你问 Agent "最近有什么关于 multi-agent 协作的论文?"——它只能凭训练数据里的记忆编造或拒绝回答。
  • 有 arxiv 工具后:Agent 真的去搜 arXiv,下载 PDF,读摘要,然后告诉你真实的论文标题、作者、核心贡献。你甚至可以让它顺便把 BibTeX 导出来,直接入库 Zotero。

Token 还是那些 Token,但产出的信息密度完全不同。这就是"烧得更有价值"的含义。

四、第三层:把任意能力变成 MCP Server

前两层用的大多是社区现成的 MCP Server。但真正的灵活性在于:你可以把自己需要的任何工具包装成 MCP Server,然后 Agent 就能用上。

这一节以 PaddleOCR 为例,走通"本地部署 → MCP 封装 → LiteLLM 注册"的完整链路。

4.1 为什么选 OCR?

OCR(光学字符识别)是一个看似传统但 Agent 极度需要的能力。很多有价值的信息存在于截图、扫描件、PDF 图片中——Agent 如果不能"看懂"图片里的文字,就只能干瞪眼。

PaddleOCR 是百度开源的 OCR 引擎,中文识别效果一流。把它封装成 MCP Server 后,Agent 就获得了从任意图像中提取文字的能力。

4.2 部署 PaddleOCR MCP Server

社区已经有 paddleocr-mcp 封装好了 MCP 接口。关键是要处理好 GPU 依赖:

config.yaml(paddleocr mcp_servers)
mcp_servers:
paddleocr:
transport: stdio
command: uvx
args:
- --find-links
- https://www.paddlepaddle.org.cn/packages/stable/cu130/paddlepaddle-gpu/
- --with
- paddlepaddle-gpu>=3.3.0,<3.4.0
- --from
- paddleocr-mcp
- paddleocr_mcp
- --model
- PP-OCRv6
- --ppocr_source
- local
- --device
- gpu:0
env:
PADDLEOCR_MCP_DEVICE: "gpu:0"

几个值得注意的点:

  1. --find-links 而非 --index:PaddlePaddle 的 GPU 版本需要从特定源安装。用 --find-links 指定 wheel 地址比全局切换 PyPI 索引更精准,不会污染其他依赖的解析。

  2. --device gpu:0:OCR 模型推理在 GPU 上比 CPU 快一个数量级。如果是 CPU 环境,改为 cpu 即可。

  3. PP-OCRv6:这是目前最新的模型版本,中文识别准确率极高。

注册成功后,LiteLLM 暴露的工具名是 mcp__litellm__paddleocr_ocr——Agent 调用它就能提取图片中的文字。

4.3 拓展思路:什么都能变成 MCP

PaddleOCR 只是一个例子。这个模式的威力在于可复制性

无论是本地 GPU 推理、传感器数据采集、还是你写的某个 Python 脚本,只要包装成 MCP Server 的标准接口(stdio 或 HTTP),它就能进入 Agent 的工具池。

核心代码量通常不超过 100 行。以 Python 为例,目前有两个主流选择——官方 MCP Python SDK v2 和 Prefect 的 FastMCP,两者 API 非常接近:

官方 SDK v2(MCPServer

from mcp.server import MCPServer

mcp = MCPServer("my-tool")

@mcp.tool()
def my_function(param: str) -> str:
"""工具描述——Agent 会看到这段文字来判断何时调用"""
# 你的业务逻辑
return result

Prefect FastMCP

from fastmcp import FastMCP

mcp = FastMCP("my-tool")

@mcp.tool
def my_function(param: str) -> str:
"""工具描述——Agent 会看到这段文字来判断何时调用"""
# 你的业务逻辑
return result

两者的核心原语(tool / resource / prompt / completion)几乎一致。参数和返回值的类型注解会被自动转为 JSON Schema,在调用前后进行 Pydantic 验证——类型不对的参数直接被拦截,返回值不符合声明也会报错。v1.x 时代还需要手动写 asyncio.run(stdio_server(...))、函数必须 async;无论是 v2 的 MCPServer 还是 FastMCP,样板代码都已经消失。

4.4 选型:官方 SDK v2 vs Prefect FastMCP

社区里更流行的其实是 Prefect 维护的独立 fastmcp 包(pip install fastmcpfrom fastmcp import FastMCP),日均下载量过百万,v2 SDK 的设计受了它很大启发。两者的 API 现在非常接近,但定位不同:

MCP SDK v2.x(官方)FastMCP(Prefect)
定位协议的标准实现全栈 MCP 框架
能力Server + ClientServer + Client + 交互式 App(工具可返回表单/图表/表格)
TypeScript有官方 fastmcp-ts 对应包
治理Linux 基金会,紧跟协议 specPrefect 独立维护,发布节奏更快
适合需要与 spec 严格对齐、希望零额外依赖想用最少的代码获得最多的功能、需要交互式 UI
能力MCP SDK v2.xFastMCP(Prefect)
交互式 UI@mcp.tool(app=True) + Prefab 100+ 组件
认证TokenVerifier 协议 + OAuth 2.1 资源服务器5 种 provider + 开箱即用的 GitHub / Google / Auth0 / Keycloak 等
中间件OpenTelemetry 追踪(标记临时性)7 种命名中间件 + 类型化自定义钩子
服务器组合parent.mount(child)
OpenAPI 生成FastMCP.from_openapi(spec)
依赖注入Resolve(fn) 注解guard pattern

五、三层的协同效应

把三层放在一起看,它们不是独立的模块,而是一套递进增强的体系:

层级解决的问题效果
Provider 容灾Token 供应不稳定多通道 fallback,输入不断流
MCP 工具网关Agent 能力单一共享工具池,token 产出价值密度提升
自定义 MCP通用工具无法覆盖特殊需求任意本地能力转化为 Agent 工具,打造独特工作流

三层叠加后的工作流程:

整个过程:Token 输入稳定,工具能力丰富,输出质量立体。这就是"Token 永动机"的含义——不是无穷无尽的 Token,而是让每一枚 Token 都产出尽可能多的价值。

六、实际效果与一点体会

前面三层讲的是架构设计,这一节看看它们在生产环境中实际跑起来的效果。

6.1 Provider 容灾:故障自动切换

下图是 LiteLLM Playground 的请求日志截图。注意 01:10:57 这个时间点:

错误大模型连接的自动切换

同一秒内,7 条 LLM 请求全部返回 Failure——涉及 Logs、Guardrails Monitor、Teams、Internal Users、Organizations 等多个后台模块。几乎可以断定是上游服务商发生了故障:LiteLLM 自身正常运行(MCP 类型的请求仍然成功),但所有依赖该 LLM provider 的查询全部失败。

关键是从 01:13:35 开始,所有请求恢复正常。LiteLLM 在约两分半钟内完成了故障检测 → 冷却触发 → fallback 切换的全过程,后续请求全部路由到了备用 provider,对上游 Agent 完全透明。

注:这个断网是另一个程序偶然切断了本机到互联网的连接. 故这个试验只能说明本工具具有保持会话, 断网重连的能力; 不能完全说明具有自动切换大模型服务商的能力. 几天前的下图能更好的说明同一个大模型 deepseek-v4-pro 可以在多个供应商之间, 在额度耗尽之后 "瞬时" 切换的能力:

大模型瞬时切换 2026-07-31 02-05-10.png

你不需要手动换 API Key,不需要重启服务,甚至不需要注意到它发生过——这就是第一层容灾的实际表现。

6.2 自定义 MCP:从"看图"到"用图"

第二张截图展示了一条完整的自定义 MCP 工具链路:

利用自定义 MCP 工具提取图片文字

具体流程是:Agent 调用 mcp__litellm__paddleocr_ocr(就是第四节部署的 PaddleOCR MCP Server)提取了第一张截图中的全部文字——包括时间戳、请求类型、状态、Session ID、费用等完整表格数据,置信度 97.68%。随后 Agent 将提取的文字作为上下文,自动分析了故障时间线、恢复窗口和费用差异。

这个流程展现了第三层"自定义 MCP"的核心价值:Agent 不再是只能聊天的对话机器人,而是一个能"看图 → 识文 → 分析 → 输出结论"的完整工作管道。 每一步依赖的工具,都是你自己部署、自己控制的本地服务。

6.3 一点体会

回看这套架构的搭建过程,最大的感受是:Agent 时代的生产力不取决于你用了哪个模型,而取决于你为它搭建了怎样的基础设施。 模型会迭代、API 会涨价、provider 会调整策略——但只要网关和工具链在,换一个模型对你的工作流几乎没有影响。

token-perpetual-machine.png

如果你也在搭建自己的 Agent 工作环境,希望这三层思路能给你一些参考。