先说结论:用轻量模型做路由,不是简单地把大模型换成小模型,而是把“判断该谁干活”这个动作单独摘出来,交给最便宜且够用的模型去完成。我自己在跑的一套多智能体系统,把路由层换成 Claude Haiku 5.5 之后,成本降幅确实稳定在六成左右,不是标题党。
这套做法适合眼下正在被 Agent 账单折磨的团队,也适合那些刚准备上多智能体、想把架构设计得经济一点的开发者。你需要解决的核心问题只有一个:什么时候该用贵模型,什么时候用便宜模型就够了。这篇文章会把路由层的设计逻辑、成本核算方式、提示词写法、落地代码和常见坑位完整过一遍,尽量让你看完就能照着试。
1. 多智能体成本失控的真实原因
1.1 智能体不是“一次请求”,是一整轮循环
很多刚开始接触 Agent 的人会天然以为:一次用户请求,就是一次模型调用,模型生成一个回答,成本 = 单次调用的 token 费用。实际完全不是这样。
一个标准的多智能体流程是:用户进来 → 智能体理解意图 → 调用工具 → 拿到工具结果 → 把结果再次交给模型 → 模型继续决策 → 再调用下一个工具。这中间每一轮“模型看结果再决策”,都相当于一次完整的模型调用,而且上下文会越滚越大。第一次调用可能只需要几千 token,到第五次调用时,前面所有历史、工具返回、中间结论都堆在一起,可能已经到了上万 token。
所以真实的计费不是“请求次数 × 单次 token”,而是“循环轮次 × 每轮累积上下文”。轮次越多,越往后的每一轮越贵,这才是智能体账单爆炸的第一原因。把整条链路都交给一个贵模型,就是在给循环结构里的每一轮都按最高单价付费。
1.2 全链路大模型意味着钱在四个环节重复燃烧
我拆过很多“跑起来很爽但账单很难看”的多智能体项目,发现它们有一个共同毛病:入口意图识别用大模型、子任务拆解用大模型、工具执行层的推理用大模型、最后的结果汇总和润色还是大模型。
这四个环节的成本不是相加,是叠乘。因为每一环节的输出都会作为下一环节的输入,上一环节输出越啰嗦,下一环节的输入就越长。用贵模型做所有事情,本质上是在让一个全能选手反复做重复劳动。尤其是意图识别和任务分类这类工作,它们本质上是判断和抽取,不是创作,根本不需要顶级模型的生成能力。
这就好比餐厅里,你让主厨去洗菜、切菜、端盘子、擦桌子。菜确实洗得干净,盘子确实端得稳,但人力成本高到离谱。正确做法是:判断“这桌客人点什么菜”这种核心决策留给主厨,洗切配菜交给帮厨,传菜交给服务员。每个环节配一个够用的角色,成本自然降下来。
1.3 我自己项目里的成本“爆表”现场
我最初是在一个售后工单分发 Demo 上做这套优化的。项目本身不复杂:用户提交问题,系统判断问题类型、归属部门,再生成一段初步回复。刚开始图省事,三个子智能体全部调用大模型,代码是写得很顺,账单也很顺。
当时平均每个工单要跑 2.7 轮模型调用,输入 token 大约 4200,输出 token 大约 750。一个月 12 万次请求,月成本直接冲到差不多一万块钱量级。复盘之后发现,60% 以上的请求其实都是“订单进度怎么查”“退换货入口在哪”这类重复到不能再重复的简单问题,却在支付旗舰模型的单价。
那一次复盘让我意识到,问题不在“模型不够聪明”,而在“聪明被浪费了”。我需要的不是更强的模型,而是一个足够便宜、足够快的分流器,在请求进入真正的干活模型之前,先把大部分简单流量带走。
2. 路由层设计:Haiku 5.5 扮演什么角色
2.1 路由任务为什么是轻量模型最容易啃的骨头
路由任务本质上是分类,不是生成。模型不需要写长篇大论,不需要做复杂推理,它只需要在给定的一串路由目标里挑一个,最多再给出一个置信度和必要的参数。
比如用户说“我想改一下收货地址”,路由的目标不是去改地址,而是判断“这件事应该交给地址修改子智能体”,然后返回一个类似{"route_id": "R02", "confidence": 0.95}的结构。这就是一个封闭式选择题,不是一个开放式作文题。
封闭式选择题正是轻量模型和大模型差距最小的场景。只要路由表数量不大、每个路由的描述足够清楚、示例给够,轻量模型的分类准确率可以压到可接受的范围。而那些真正需要复杂推理、长文生成、多步骤规划的任务,才需要升级到大模型。
2.2 选型号时看什么:延迟、可靠性和结构化输出
我用 Haiku 5.5 做路由之前,主要考察这几个指标:
| 指标 | 为什么关键 | 我的可接受线 |
|---|---|---|
| 延迟 | 路由在用户请求的必经路径上,路由慢等于整体慢 | P50 控制在 700ms 以内 |
| 结构化输出成功率 | 输出不合法就意味着解析失败,兜底成本会吃掉节省 | 失败率低于 1% |
| 指令跟随能力 | 是否严格按角色设定和返回格式执行 | few-shot 下不跑偏 |
| 单价 | 每次路由调用本身也要花钱,太贵就没有意义 | 单价不超过大模型的十分之一 |
| 上下文窗口 | 有时需要带一点摘要或最近对话 | 能和路由提示词一起轻松装下 |
在这些指标里,我最看重的是“结构化输出成功率”。因为路由层的输出是要被程序直接消费的,不是给人看的。如果模型经常返回一段散文、夹杂 markdown、或者多一个逗号导致 JSON 解析失败,你省下的 token 钱会全部赔给重试和兜底逻辑。
2.3 不是所有轻量模型都适合,别只看价格
这一点我必须单独拎出来说。市面上便宜模型很多,但“便宜”和“适合当路由器”是两回事。我试过一个更便宜的模型,单价只有 Haiku 的几分之一,结果 10 次调用里有两三次输出不合规。有时候会突然多出一段解释性文字,有时候 JSON 外面包了代码块标记,搞得我只能写一堆容错代码去清洗。
兜底逻辑一开,成本就不受控制了。重试一次等于多付一次调用费用,清洗逻辑还占用你的开发时间。最后我算总账,反而比直接用现在的方案更贵。
所以我的建议是:路由模型不能只看算力单价,要看“稳定结构化输出能力”。Haiku 5.5 在这个任务上的表现比较稳,把温度调到 0 之后,基本能保持纯 JSON 输出,对中文口语化问题的理解也够用。它不是万能的,复杂语义下同样会误判,但只要路由表控制得好、示例给得足,完全能胜任分诊这个角色。
3. 成本模型与收益计算:六成怎么算出来
3.1 先把成本公式拆开
要搞清楚六成是怎么省出来的,得先把智能体的成本公式摆出来。
总成本 ≈ 调用次数 ×(输入 token × 输入单价 + 输出 token × 输出单价)× 上下文放大系数 + 兜底重试成本。
上下文放大系数是关键,因为多轮循环里每次模型调用都会带上之前的历史,导致实际输入 token 远大于“本轮回话”的 token。一般放大系数在 2 到 8 之间,看你的 Agent 设计得有多粗暴。
改造前,所有环节都按大模型单价计费。改造后,路由调用按 Haiku 单价计费,子任务里 70% 左右的简单任务也按 Haiku 单价计费,只有真正复杂的 30% 才升级到大模型。整体平均单价被拉低,总成本自然下降。
3.2 混合调用模式下的成本对比
我按自己当时的计费口径做了一版对比。先说假设:
- 月请求量:12 万次
- 改造前的每个请求,平均要消耗 5000 input token + 800 output token,并且走的是大模型链路
- 改造后,每个请求先过 Haiku 路由;路由平均消耗 2000 input token + 80 output token
- 其中 70% 的请求属于简单任务,后续子 agent 也走 Haiku,平均消耗 1500 input token + 300 output token
- 另外 30% 的复杂请求走大模型子 agent,但因为有路由层的拆解和指引,上下文更短,平均消耗 4000 input token + 1000 output token
下面这张表是简化后的估算:
| 项目 | 改造前成本 | 改造后成本 |
|---|---|---|
| 大模型输入部分 | 120000 × 5000 / 1000000 × 15 = 9000 美元 | 120000 × 4000 × 0.3 / 1000000 × 15 = 2160 美元 |
| 大模型输出部分 | 120000 × 800 / 1000000 × 75 = 7200 美元 | 120000 × 1000 × 0.3 / 1000000 × 75 = 2700 美元 |
| Haiku 路由输入部分 | 0 | 120000 × 2000 / 1000000 × 1.25 = 300 美元 |
| Haiku 路由输出部分 | 0 | 120000 × 80 / 1000000 × 6.25 = 60 美元 |
| Haiku 子 agent 输入部分 | 0 | 84000 × 1500 / 1000000 × 1.25 = 157.5 美元 |
| Haiku 子 agent 输出部分 | 0 | 84000 × 300 / 1000000 × 6.25 = 157.5 美元 |
| 合计 | 16200 美元 | 约 5535 美元 |
算下来降幅接近 66%,和“砍掉六成”对得上。我这里用的单价只是示例,你的实际账单要以控制台拿到的报价为准,但公式是通用的:把月请求量、每一步的 token 消耗、每档模型的单价填进去,十分钟就能算出自己的节省空间。
3.3 别忘了把隐藏成本算进去
上面那张表只算了模型调用本身的费用。真实系统里还有几笔容易忽略的账:
- 路由失败后的兜底成本。解析失败一次,等于浪费一次路由调用;升级到人工或大模型兜底,又是一笔额外支出。
- 离线评测的成本。你总要抽几百条历史数据来跑路由准确率,这部分测试调用的钱不多,但要预留。
- 开发维护成本。路由提示词要迭代,路由表要维护,这些是人力成本,不体现在云账单里,但确实存在。
所以我一般会建议:先按上面的模型算出一个“乐观值”,再留出 10% 到 15% 的缓冲给兜底和迭代成本。即便算上这些,降幅依然能达到五成以上,这个优化方向是站得住的。
4. 路由提示词与结构化返回实操
4.1 路由表先写死,不要让模型“自由发挥”
我第一次做路由的时候犯过一个错:让模型自己决定“这是不是复杂任务”,然后输出一段自然语言说明。看起来灵活,结果日志里什么妖魔鬼怪都有,“该走深度分析”“转人工”“需要更高级模型处理”这类泛泛而谈的描述全出现了,根本没法程序化消费。
后来我改成“硬编码路由表 + 封闭选项”。先定义好固定的路由 ID,提示词里给出每个 ID 的适用场景和示例,模型只能在 ID 列表里选。好处有三点:输出可控、日志可聚合、新增路由不需要改模型,只改提示词和路由表。
我当时的简化路由表长这样:
R01:售前咨询,比如价格、规格、库存 R02:售后问题,比如物流、退换、发票 R03:复杂投诉,涉及多部门、情绪激烈、需要升级 R04:闲聊或非业务内容 R05:无法确定或需要人工兜底注意 R05 是必须保留的“垃圾进料口”。模型拿不准的时候,给它一个明确的逃生出口,比逼它硬选一个错误路由要好得多。
4.2 JSON Schema + 示例,把分类结果变成可执行指令
路由提示词我只保留三个部分:角色设定、路由表、输出要求。输出要求一定要给 JSON 结构,并且明确告诉模型“只输出 JSON,不要解释”。
系统提示词大概是这样的:
你是一个智能体路由分类器。你的任务是根据用户输入选择唯一合适的路由 ID。 可选路由: R01:售前咨询。适用于价格、规格、库存、购买建议。 R02:售后问题。适用于物流、退换货、发票、地址修改。 R03:复杂投诉。适用于用户情绪激烈、涉及多部门、需要升级处理。 R04:闲聊或非业务内容。适用于纯闲聊、表情、问候。 R05:无法确定,需要人工兜底。 要求: 只输出 JSON,不要输出任何解释或附加文字。 JSON 格式为: {"route_id": "R01", "confidence": 0.9, "reason": "一句话原因"} 示例: 用户: 我想知道这个手机多少钱 输出: {"route_id": "R01", "confidence": 0.98, "reason": "用户询问价格"} 用户: 我的快递三天没动了,怎么回事 输出: {"route_id": "R02", "confidence": 0.96, "reason": "用户咨询物流进度"}我把 temperature 固定为 0,对路由这种任务来说,不要任何“创造性”,只要稳定性。输出里的confidence字段特别重要,它是后面兜底策略的判断依据。reason字段可以辅助日志排查,但程序不要依赖它做分支。
4.3 路由失败与低置信度的兜底策略
再稳的模型也会有翻车的时候,所以一定要设计兜底路径。我的策略很简单:
- 解析失败:不重试,直接走 R05 人工队列或默认升级。
- confidence 低于阈值(比如 0.6):走“询问澄清”流程,让用户再补充一句,而不是硬分类。
- 连续多次同一类型失败:触发告警,说明这个路由的描述可能有问题,需要人工看日志。
- 所有失败样本都落日志,按周复盘,持续补充 few-shot 示例。
为什么不重试?因为路由失败的样本往往是模型本来就不确定的样本,重试一次只是再付一次 token,成功率不会明显提高。把重试的钱省下来,用于积累失败样本、改进提示词,长期收益更大。
5. 最小可用的路由网关落地代码
5.1 网关目录结构与核心流程
代码层面的落地可以很轻,不需要引入重型框架。我搭的最小结构是这样的:
router_gateway/ ├── gateway.py # 路由网关主逻辑 ├── prompts/router_prompt.py # 路由提示词与 few-shot ├── routes.py # 路由表和兜底配置 ├── agents/light_agent.py # 轻量子智能体 ├── agents/heavy_agent.py # 复杂任务大模型智能体 └── cost_log.py # token 与成本日志核心流程只有四步:请求进来 → 调用 Haiku 路由 → 校验输出并选择目标 agent → 执行 agent 并记录日志。
5.2 核心代码:路由、降级与成本日志
下面是一段简化版网关伪代码,我刻意没绑死某个 SDK,你换成自己手头的客户端就行:
import json class RouterGateway: ROUTE_FALLBACK = "R05" CONFIDENCE_THRESHOLD = 0.6 def __init__(self, router_client, heavy_agent, light_agent): self.router = router_client self.heavy = heavy_agent self.light = light_agent self.cost_log = [] def _call_router(self, user_input): # 这里只传需要路由判断的最小上下文 response = self.router.complete( system=build_router_prompt(), user=user_input, temperature=0 ) self._log_usage(response.usage) return self._parse_route(response.text) def _parse_route(self, raw_text): try: # 只取第一个 JSON 对象,避免模型偶尔输出多余内容 start = raw_text.find("{") end = raw_text.rfind("}") return json.loads(raw_text[start:end + 1]) except Exception: return None def handle(self, user_input): route = self._call_router(user_input) if not route or route.get("confidence", 0) < self.CONFIDENCE_THRESHOLD: route = {"route_id": self.ROUTE_FALLBACK, "confidence": 0} if route["route_id"] in ("R03", "R05"): result = self.heavy.run(user_input, route["route_id"]) else: result = self.light.run(user_input, route["route_id"]) self._log_route(route, len(result)) return result有几个细节强调一下:
第一,_call_router里不要传完整会话历史,只传判断路由所需的信息,比如最近一条用户消息、结构化字段、一段不超过几百字的摘要。这是路由成本的控制核心。
第二,_parse_route用字符串截取第一个{到最后一个},是为了容忍模型偶尔在 JSON 前后多输出一些空白或说明。但这种容错只是辅助手段,不能成为常态,真正的常态应该是 prompt 约束到基本不产生额外文本。
第三,所有 token 用量都要记录。我的_log_usage会把每次路由调用的 input/output token 数追加到cost_log,这是月底算账的唯一依据。
5.3 上线前必做的三件事
代码能跑通只是第一步,直接全量上线大概率会出事。我建议上线前把这三件事做了:
第一,离线评测。从历史数据里抽 300 到 500 条已经标注好答案的请求,跑一遍路由,看分类准确率。目标定在 95% 以上,低于这个数先别上线。测试集不用一次性做很大,几轮迭代下来足够。
第二,小流量灰度。先切 10% 的流量,观察一段时间。重点看两个指标:路由到大模型的比例有没有异常高、路由解析失败率有没有异常上涨。稳定后再逐步放量。
第三,成本看板落地。不需要复杂的数据平台,一张表格就够。按日期、路由 ID、调用次数、平均 token、当日成本汇总,至少跑两周,拿到真实的“改造后单位成本”,才有底气对外说省了多少钱。
6. 常见问题排查与避坑实录
6.1 路由总是误判到“大模型”
这个坑我踩得很扎实。上线第一周,日志里 R03(复杂投诉)的命中率异常高,明显不是真实分布。查下来发现原因有三个:一是提示词里 R03 的描述写了太多分支细节,模型误以为提到“投诉”就必须进 R03;二是 few-shot 示例里 R03 占了一半,诱导模型往那边偏;三是某些用户消息被截断后只剩几个字,信息太少,模型只能选最“保险”的复杂路由。
解决思路是给每个路由加“负向条件”。比如 R03 的描述里明确写:仅当用户表达强烈不满或问题涉及多个部门时选中;仅是普通问价、查物流,一律不选 R03。然后把负向条件放到醒目位置。改完之后 R03 的命中率降了三分之一。
6.2 账单降幅不明显
如果你明明加了路由层,月底账单却没降,基本可以往三个方向查。
第一个是路由提示词太长。系统提示词如果塞了三千多 token,那么哪怕 Haiku 再便宜,每次路由都在烧钱。路由提示词最好压到 1000 token 左右,路由表长了就精简描述,不要把全部业务规则都塞进去。
第二个是子智能体把完整历史带上。路由已经把任务拆好分给子 agent 了,但子 agent 的 system prompt 里还带着此前所有对话记录,上下文还是那么大。改法是:子 agent 只接收“路由输出摘要 + 必要字段”,拒绝让它自己拉全量历史。
第三个是兜底重试太多。解析失败一次走 R05,相当于把所有简单流量又升级了一遍。先查日志里的失败率,如果超过 1%,优先修 prompt 而不是继续调代码。
6.3 长上下文把路由成本“吃”回去
有一种场景容易让路由层的成本失控:用户和历史对话都很长,而你为了让路由更准确,把整段历史都喂给 Haiku。这样做结果就是,路由层的单次调用可能不贵,但架不住天天如此,总量很惊人。
我的做法是给路由层输入瘦身。不是在代码里截断字符串,而是维护一份滚动摘要。每完成一轮任务,就用 Haiku 把关键信息压缩成 200 字以内的结构化摘要:用户诉求、已完成步骤、待解决问题。用户下一次请求进来时,路由只读“最新消息 + 这份摘要”,而不是完整历史。
这样做有两个好处:路由的输入 token 稳定可控;路由模型不会被几千字的无关上下文干扰判断。代价是摘要本身也要花一点 token,但它比每次路由都带全量历史便宜太多。
6.4 日志里的几个关键监控指标
我每次看路由健康度,只看下面几个数字:
| 指标 | 含义 | 健康线 |
|---|---|---|
| 路由解析失败率 | 模型输出不符合 JSON 结构的比例 | < 1% |
| 低置信度占比 | confidence 低于阈值导致兜底的比例 | < 10% |
| 大模型路由占比 | 命中复杂任务并升级到大模型的比例 | 20% - 40% 视场景 |
| 每请求平均成本 | 包含路由和子 agent 的完整成本 | 持续下降或保持平稳 |
| 路由平均延迟 | Haiku 判断耗时 | P50 < 700ms |
这五个指标建议每周拉一次。解析失败率往上走,说明提示词或输入格式出了问题;低置信度占比过高,说明路由表边界不清晰,需要补示例;大模型路由占比异常高,说明路由策略偏保守,简单任务被误升级了。
最后再分享一个小技巧:我处理路由问题时,养成了“日志驱动迭代”的习惯。每次改提示词之前,先去日志里捞最近几天的误判样本,把它们贴成一个错误集,改完 prompt 后再用这个错误集回归。这样改一次,效果肉眼可见,而不是凭感觉调参。如果你正在为多智能体成本头疼,我的建议很简单:先把账单拆到每个环节,再给路由层两周灰度时间,用你自己的数字验证一下这个方案。数字不会骗人。