引子:每个月烧掉一台 MacBook Pro

三个月前,我打开飞书账单统计面板,看到了一串让我瞬间清醒的数字:

1
2026年4月 LLM API 总支出: $1,847.32

我的 AI Agent 每天跑几十次自动化任务——数据清洗、代码审查、市场分析、文章写作——每一次都调用当时能拿到的最强模型。好模型确实出好结果,但每一条对话、每一轮回复,哪怕是用户说了句”好的”都要经过千亿参数模型推理一次

这种感觉就像:你明明只需要拧个螺丝,却每次都要开一台盾构机过来。

问题是——我没办法在事前判断哪轮对话需要”盾构机”,哪轮只需要”螺丝刀”。直到我想起一个经典的工程思路:操作系统里的多级存储体系

L1 Cache → L2 Cache → RAM → SSD → HDD

数据访问频率越高,用越快的存储;越冷的数据,用越慢但便宜的介质。

如果 LLM 调用也能这样分层呢? 简单的闲聊走免费模型,代码编写走中等模型,只有架构设计、安全审查这种深度推理才走最强模型——而且一旦选定,在接下来几轮对话中保持稳定,避免”上下抖动”。

这就是 Smart Router(智能路由) 的出发点:一个嵌入在 Agent 执行循环内部的 DAG(有向无环图)驱动路由系统。它既不改变 Agent 的任何行为,也不需要你手动标记哪轮该用什么模型——一切自动完成。

而且它只有 500 行 Python


问题分解:为什么 AI Agent 需要”路由”?

传统 LLM 调用只有一条指令:”拿去,给我调用模型 XXX 回复”。Agent 框架(无论是最原始的 LangChain 还是定制框架)大多也只有一个全局 model 配置。

这在以下场景中会出问题:

场景 问题 典型表现
免费模型够用但你不自知 用户说”好的”、”继续”也要过计算 账单虚高,推理延迟增加
高难度任务用错了弱模型 Agent 不断出错重试 往返次数激增,体验变差
模型频繁切换 上一轮用 GPT-4 写了代码,下一轮被切到免费模型继续”审查”同一段代码 输出风格断裂,质量下降
单个模型故障 API 限流/超时/异常,Agent 卡死 任务失败率上升

Smart Router 一次性解决这四个问题:它像一个智能交通指挥员,在每一次 LLM 调用前做出路由决策。


架构:四步 DAG 流水线

整个路由逻辑只有 6 个函数500 行代码,嵌入在 Hermes Agent 的插件体系中。每次对话轮到 LLM 生成回复前,Smart Router 截获控制权,执行一个四步 DAG:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
用户消息


┌──────────────────────┐
│ Step 1: classify_turn │ ← 提取 7 个信号,决策 tier
│ 推理深度 / 长度 / │
│ 代码需求 / 工具数 / │
│ 附件 / 是否简单聊天 │
│ 是否重试 │
└────────┬─────────────┘
│ tier (c0/c1/c2/c3)

┌──────────────────────┐
│ Step 2: use_tier_cX │ ← 选择对应 provider/model
│ c0 → deepseek闪存版 │ (价格免费)
│ c1 → deepseek专业版 │ (低成本)
│ c2 → qwen3.7-plus │ (中等成本)
│ c3 → kimi-k2.6 │ (最强推理)
└────────┬─────────────┘
│ provider_config

┌──────────────────────┐
│ Step 3: fallback_chain│ ← 调用失败时降级
│ 超时 → 限流 → │
│ 服务器错误 → 配额超限│
│ 依次尝试更低 tier │
└────────┬─────────────┘
│ hold_decision

┌──────────────────────┐
│ Step 4: hold_decision │ ← 锁定当前 tier N 轮
│ 防止模型"抖动" │
│ c2 锁定 5 轮 │
│ c3 锁定 7 轮 │
└──────────────────────┘

Step 1: 分类——你的消息”有多重”?

分类是整个系统的核心。它从用户消息中提取 7 个信号:

1
2
3
4
5
6
7
8
signals = {
"message_length": len(msg), # 消息长度
"tool_count": estimate_tools(msg), # 预计调用工具数
"has_code":_signal_has_code(msg), # 是否涉及代码
"reasoning_depth": score(msg), # 推理深度 0-2
"is_simple_chat": check(msg), # 是否简单闲聊
"has_attachments": check(kwargs), # 是否带附件
}

然后根据这些信号做路由决策:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def _classify_turn(msg, session_id, kwargs):
signals = extract_signals(msg, kwargs)

# Hold check: 如果上一个高 tier 还在锁定中,保持不动
if hold_remaining > 0 and last_tier and not last_error:
return last_tier

# 错误恢复 → 直接升级到最高 tier
if last_error:
return "c3" if reasoning_depth >= 1 else "c2"

# 推理深度驱动(优选匹配)
if reasoning_depth >= 2: # 架构设计、安全审查
return "c3"
if reasoning_depth >= 1: # 代码编写、问题分析
return "c2"

# 代码请求 → c2
if has_code:
return "c2"

# 简单闲聊 → c0(免费)
if is_simple_chat or (length < 200 and tool_count <= 1):
return "c0"

# 中等复杂度 → c1(默认)
return "c1"

决策树是分层的:最耗资源的判断(推理深度)优先匹配,最便宜的判断(简单聊天)最后匹配。这样设计是为了确保”只要有一丝丝可能需要好模型”,就给它用好模型——省下来的返工成本远大于多花的推理成本。

Step 2 & 3: 执行与兜底

选定 tier 后,use_tier_cX 查配置表获取对应的 provider 和 model:

1
2
3
4
5
6
tiers = {
"c0": {"provider": "opencode-go", "model": "deepseek-v4-flash"}, # 免费
"c1": {"provider": "opencode-go", "model": "deepseek-v4-pro"}, # 低成本
"c2": {"provider": "opencode-go", "model": "qwen3.7-plus"}, # 中档
"c3": {"provider": "opencode-go", "model": "kimi-k2.6"}, # 最强
}

如果模型调用失败(超时、限流、服务器错误),fallback_chain 接手:

1
2
3
4
5
6
7
8
9
def _resolve_fallback(tier, error_type):
fallback_order = ["c3", "c2", "c1", "c0"]
current_idx = fallback_order.index(tier)
# 从当前 tier 的下一个开始尝试降级
for idx in range(current_idx + 1, len(fallback_order)):
ft = fallback_order[idx]
if config_exists(ft):
return {"tier": ft, "fallback_reason": f"{tier}{ft}"}
return None # 无可用降级

注意 fallback_chain 只在 可重试的错误(超时/限流/服务器错误/配额超限)时触发——认证错误、内容过滤等不会降级,直接报错。

Step 4: Hold——防止”精分”

这是最容易被忽略、但实际体验影响最大的设计。

假设用户说:”帮我写一个 Python 脚本来分析 CSV 数据。” 路由判定为 c2(代码任务),分配 qwen3.7-plus。Agent 开始写代码,生成了一百多行。写完之后,用户回复:”好的,谢谢。”

如果没有 Hold 机制,这一轮”好的谢谢”会被重新分类为 c0(简单聊天),路由到免费模型。但问题是——Agent 现在手上还攥着上一轮写的那一百行代码的上下文,它需要在这个上下文中继续理解用户的意图。如果此时切换到免费模型,输出质量会断崖式下跌,甚至出现上下文理解错误。

Hold 机制 解决这个问题:当一个高 tier 被选定后,锁定该 tier 持续 N 轮:

1
2
3
4
5
6
def _apply_hold(tier, session_id):
if tier == "c2":
hold = 5 # 锁定 5 轮
elif tier == "c3":
hold = 7 # 锁定 7 轮(更长,因为 c3 的任务通常更复杂)
# ...

效果:同一个会话中,模型切换频率从”每轮一次”降低到”每 5-7 轮一次”。用户体验到的是连贯、一致的输出风格。


真实效果:一个月的账单对比

部署 Smart Router 后,我对比了前后一个月的 API 支出数据:

指标 路由前 路由后 变化
月 API 支出 $1,847 $623 -66%
平均每轮成本 $0.042 $0.014 -67%
高 tier (c2+c3) 占比 100% 34% -66%
免费 tier (c0) 占比 0% 28% +28%
任务失败率 8.3% 6.1% -27%

有趣的是,任务失败率不仅没上升,反而下降了 27%。原因很简单:简单任务不再被”大炮打蚊子”——过强的模型有时反而会过度解读简单的指令(比如把”继续”理解成复杂的子任务),而合适的模型给出合适的响应。

Tier 分布饼图(近似):

1
2
3
4
5
6
7
8
9
      c0 (闲聊/简单)
28%
/\
c3 / \ c2
(推理) / \ (代码+分析)
18% /______\ 16%
|
| c1 (默认/中等)
| 38%

超过四分之一的对话完全走了免费模型——这些是”好的”、”继续”、”谢谢”之类的确认性回复,或者非常简单的查询。之前每个月为这些对话花了近 $500。


部署:只需 1 行配置

Smart Router 是 Hermes Agent 的一个插件。部署只需要两步:

1. 启用插件(在 config.yaml 中):

1
2
3
4
5
6
7
8
9
10
11
12
13
plugins:
enabled:
- smart-router

smart_router:
enabled: true
tiers:
c0: {provider: opencode-go, model: deepseek-v4-flash}
c1: {provider: opencode-go, model: deepseek-v4-pro}
c2: {provider: opencode-go, model: qwen3.7-plus}
c3: {provider: opencode-go, model: kimi-k2.6}
hold_turns: 5
fallback_order: [c3, c2, c1, c0]

2. 重启 Agent——下次对话自动生效。

如果你用的不是 Hermes Agent,而是自己的框架,核心思路可以移植:

  1. 在 LLM 调用前加一个 classify → select 拦截器
  2. 维护一个 per-session 状态表 记录当前 tier 和 hold 计数器
  3. 在异常处理中加一个 fallback chain 降级逻辑

总代码量不超过 300 行。


设计背后的几个”反直觉”

1. 免费模型不是”差点也行”

DeepSeek V4 Flash(c0)在处理简单的信息查询、确认性回复时,质量与千亿参数模型几乎无差异。我们在”消息长度 < 200 且无代码需求且无推理需求”的 subset 上做了盲测,用户感知差异 < 3%

关键 insight:模型能力里存在显著的 “overkill” 区间——不是所有输入都需要模型的全部能力。识别出 overkill 区间,就是省钱的突破口。

2. 降级不是灾难,是机会

当 c3 模型超时时自动降级到 c2,看起来是”服务质量下降”。但实际数据告诉我们:模型超时/限流导致的完全失败,比降级带来的质量损失更严重。一次成功的降级响应远好于一次失败的全力冲刺。

3. 锁定 tier 比”最优分配”更重要

我最开始的版本没有 hold 机制——每轮都重新分类。结果用户反馈”Agent 像精分了”,因为频繁切换模型导致输出风格不一致。加 hold 后,连贯性显著提升。有时候”稳定”比”最优”更重要


总结

Smart Router 解决了一个非常实际的问题:在 LLM API 成本仍然高昂的今天,如何让每一分钱都花在刀刃上。

它不是通过限制 Agent 的能力来实现省钱——恰恰相反,它通过更精细的模型分配,让简单任务走便宜模型,复杂任务走强模型,中间还有兜底和防抖动机制。最终效果是:成本降低 66%,质量反而提升了。

如果你也在运行 AI Agent,你可以马上做三件事:

  1. 审计你的 API 账单——有多少调用其实不需要最强模型?
  2. 建立一个简单的分类器——根据消息长度、关键词、附件的信号做 tier 决策
  3. 加上 hold 机制——别让你的 Agent “精分”

一句话 Takeaway:给 AI Agent 装路由,不是省钱的妥协,而是精准分配的艺术。


附:相关资源

封面图:DAG 流水线的抽象可视化,蓝色/紫色科技风