news 2026/8/26 13:15:08

Agent技能路由:分层架构让大模型只做精排,工程落地详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent技能路由:分层架构让大模型只做精排,工程落地详解

最近在整理 Agent 相关的面试问题时,看到这样一个提问:一个 Agent 系统里挂了上百个技能,用户的请求进来之后,到底应该让大模型自己决定调用哪个技能,还是先用检索的方式把候选技能筛出来?如果你的第一反应是“让模型自己选不就行了”,那这道题大概率已经是一个减分项了。

这个问题的专业叫法是技能路由(Skill Routing),它是 Agent 架构里最不起眼但最影响体验的决策层。从表面看,它只是“选择哪个技能”这么简单,实际上它决定了系统的延迟、成本、稳定性、可解释性,甚至安全边界。很多人把 ReAct 这类框架里“模型每步自己决定工具调用”当成默认答案,忽略了在技能规模变大之后,这条路会越走越窄。

这篇文章我会从面试官想听什么讲起,再拆解检索式路由和模型式路由的本质区别,最后给出一套工程上可落地的分层路由实现。你不需要背八股,照着这套思路去理解 Agent 技能路由,就能回答得比大多数候选题更扎实。

1. 面试官真正想听的不是“二选一”

1.1 一个容易被低估的面试题

很多 Agent 相关岗位的面试题看起来是在问工具选型,其实是在考察系统设计能力。技能路由这个问题最有意思的地方在于:它没有一个标准答案,但有一个标准的思考框架。

一个候选人如果只说“用大模型选更智能”,面试官立刻会追问:

  • 你的 Agent 挂了 500 个技能,上下文放不下怎么办?
  • 模型选错了技能,你怎么发现、怎么回滚?
  • 一次路由调用要花多少 Token、多少延迟?
  • 高风险技能被模型误调用了,谁负责?

所以面试官真正想听到的,是你对“决策成本”“可控性”“可观测性”这三个维度的理解,而不是单纯的技术名词。

1.2 无脑让模型选,错在哪里

“让模型自己选”这个回答之所以显得业余,是因为它把路由问题简化成了一个提示词问题。在只有三五个技能的原型项目里,这种方案确实可行。但一旦进入生产环境,技能数量增长到几十甚至几百,模型面对一长串工具描述时,选择准确率会明显下降。

更关键的是,模型决策是一个概率过程。同一个请求,今天可能会选技能 A,明天模型服务升级后可能就选技能 B。这种不确定性对 Agent 产品来说是不可接受的。用户不会因为“模型今天心情不好”而原谅你调错了工具。

1.3 技能路由的本质是决策问题

剥开所有技术术语,技能路由的本质是一道决策题:给定一个用户请求,如何从技能集合中选出最合适的那个执行单元。

这道决策题有三个约束:

  1. 要快。路由层不应该成为 Agent 响应链路的主要瓶颈。
  2. 要准。选错技能的代价不只是浪费一次调用,还可能导致后续流程全部走偏。
  3. 要能解释。线上出问题时,必须能回答“为什么调用了这个技能”。

带着这三个约束再去看检索式路由和模型式路由,思路就会清晰很多。

2. 先搞清楚:技术路由到底在路由什么

2.1 Agent 技能到底是什么

在讨论路由之前,先统一概念。所谓技能(Skill),在 Agent 系统里通常指一个可以被模型或程序调用的原子能力,比如:

  • 查询订单接口
  • 天气查询 API
  • 发送即时消息
  • 调用内部工单系统
  • 执行一段 SQL
  • 调用第三方 SaaS 服务

工程实现上,技能往往被描述为一个函数、一个工具 Schema、一个 MCP(Model Context Protocol)服务,或者一个子 Agent。技能注册表(Skill Registry)就是维护这些技能元数据的中心化组件。

2.2 路由层的输入和输出

路由层的输入是用户请求,可能还包含当前对话上下文、用户身份、环境信息。路由层的输出通常是一个技能名或技能 ID。

需要特别注意,路由层不一定只做一次决策。复杂 Agent 中,路由可能发生在多个层级:

  • 主 Agent 决定先调用哪个业务域
  • 业务域内部再决定调用哪个具体技能
  • 技能执行过程中可能还会涌现新的子任务

所以一个健壮的技能路由设计,必须支持多级路由,并且每一级都能独立观测、独立降级。

2.3 技能数量膨胀是路由问题的导火索

很多人对技能路由重视不足,是因为做 Demo 时只有两三个技能,模型闭着眼睛都能选对。一旦技能数量膨胀,问题立刻暴露:

  • 所有技能描述加起来可能超过模型上下文窗口
  • 相似技能变多,模型难以区分
  • 新技能上线后,模型没有稳定机制去学会使用它
  • 路由日志不规范,选错后难以复盘

因此,技能路由不是一个“有就行”的模块,而是决定 Agent 能否从原型走向生产的核心组件。

3. 检索式路由:把选择问题变成匹配问题

3.1 检索式路由的三种形态

检索式路由的核心思想是:不直接让大模型做选择题,而是通过事先构建好的索引或规则,快速缩小技能候选范围。常见形态有三种。

第一种是规则匹配。通过关键词、正则、用户意图映射表来匹配技能。比如请求中包含“订单”,直接命中订单查询技能。这种方式最简单,准确率高,但覆盖不全,无法处理语义变化。

第二种是分类器路由。训练一个轻量级文本分类模型,将用户请求分类到不同技能。这种方式比关键词灵活,但需要训练数据,而且分类器本身需要持续维护。

第三种是向量检索路由。把技能描述和用户请求都转换成向量,通过余弦相似度或点积计算相关性,返回 Top-K 个技能。工程实践上通常会结合 BM25 做混合检索,实现“向量召回 + 关键词召回 + 多路融合”,这样召回效果会更稳定。

3.2 检索式路由的优点

检索式路由最大的优势是快。规则匹配和向量检索的耗时通常是毫秒级,远低于大模型推理的耗时。成本上,检索式路由不需要消耗 Token,尤其是向量检索,在离线建好索引后,在线只需要做一次向量计算。

另一个优势是可解释性强。规则命中就是命中,向量检索可以输出得分,这些信息很容易写入日志,方便排障。相比之下,模型的解释文本往往只是“事后补理由”,并不能真正反映决策过程。

3.3 检索式路由的短板

检索式路由也有明显边界。它本质上是在做相关性匹配,无法完成复杂的语义推理。比如用户问“订单被取消了,钱什么时候退回来”,这句话里没有“订单”和“退款”关键词的直接组合,规则匹配会很吃力,向量检索也可能召回多个相似技能,无法精确判断。

所以检索式路由往往不能单独作为最终决策器,它是“召回层”,而不是“精排层”。

4. 大模型式路由:靠推理做最终决策

4.1 什么是模型式路由

所谓大模型路由,就是把技能列表和用户请求一起交给大模型,让模型输出要调用的技能名。实现方式有两种主流做法。

一种是纯提示词路由。在系统提示词中列出所有技能的名称和描述,让模型从列表中选择一个。另一种是基于 Function Calling 的路由,把每个技能定义成函数,模型在推理过程中返回函数名和参数。

基于 Function Calling 的做法更可靠,因为模型输出的格式被约束住了,不需要再从文本中解析技能名。现在主流的 Agent 框架,比如 LangChain、LlamaIndex,底层都是这么做的。

4.2 大模型路由的优点

模型式路由最突出的优势是语义理解能力强。它能处理自然语言表达中的省略、指代、意图转换,不需要人工为每个技能设计关键词和规则。在技能数量较少、描述清晰、用户表达比较多样化的场景下,模型路由的效果很好。

另外,模型路由天然具备泛化能力。新技能上线时,只要把技能描述补充到提示词里,模型大概率能正确使用,不需要重新训练分类器。

4.3 大模型路由的短板

模型路由的短板也很致命,主要体现在三个方面。

第一,延迟和成本高。一次路由调用就是一次大模型推理,按照目前主流模型的响应速度,通常会比检索多出几百毫秒到数秒。如果需要反复调用,Token 成本会持续累积。

第二,稳定性不可控。模型服务升级、提示词微调、上下文长度变化,都会影响路由结果。你可能上线前一天测试全通过,第二天模型服务商调整了参数,路由结果就变了。

第三,技能数量有限制。所有技能描述都塞进提示词,会快速消耗上下文窗口。即使上下文足够,模型也会在大量候选技能之间出现“注意力稀释”,选错概率上升。

所以模型式路由更适合做“最终决策器”,而不是“全量选择器”。

5. 为什么不能无脑让大模型自己选

5.1 技能列表膨胀后,选择准确率会下降

这是最关键的一点。假设你只有 5 个技能,每个技能描述 50 个字,模型很容易选对。当技能数量增加到 200 个,技能描述总字数可能超过 1 万甚至更多,模型需要在长文本中找到最合适的那个。人类的注意力在长列表场景下会下降,模型同样如此。

更麻烦的是相似技能。比如“查询订单详情”和“查询订单物流”,描述非常接近,模型很容易混淆。此时如果只靠模型自由选择,错误率会随着技能数量的增加而快速上升。

5.2 延迟和成本不可控

每做一次路由,就相当于多一次模型调用。在普通对话场景中,一次 Agent 完整执行可能需要打多个电话;如果每次都要模型路由,叠加起来延迟就会非常明显。对于实时性要求高的场景,比如客服机器人、语音助手,几百毫秒的差距就是完全不同的体验。

成本上也是如此。大模型的 API 按 Token 计费,技能描述、用户请求、模型输出都会产生 Token 消耗。一次两次不多,一旦 Agent 请求量上来,这笔费用会非常可观。

5.3 上下文会被技能清单占满

如果把所有技能描述都放在系统提示词里,那么这个提示词可能比真正的用户问题长几十倍。这会导致两个问题:一是用户上下文被压缩,模型难以记住关键信息;二是每轮请求都要重复发送这些技能描述,浪费大量算力。

这也是为什么很多生产级 Agent 不会把所有技能一股脑塞给模型,而是先做一轮召回,再把候选技能传给模型。

5.4 安全边界和权限管控缺失

让模型自由选技能还有一个隐蔽问题——安全。模型可能因为提示词注入而被诱导调用高风险技能,比如删除数据、发送消息、修改配置。如果路由层没有权限控制和规则拦截,模型的一次错误决策就可能造成生产事故。

比较稳妥的做法是:高风险技能必须走规则或人工审批,不能完全交给模型自动决策。安全这条线,永远不能依赖模型的“自觉”。

5.5 可观测性和复盘困难

当线上 Agent 行为异常时,你需要回答“为什么调用了这个技能”。检索式路由的答案很明确:因为关键词命中,或者向量相似度最高。模型路由的答案却很模糊:模型说它认为应该调用这个技能。

这种模糊一旦变成常态,Agent 系统的调试就会非常痛苦。你很难判断是技能描述不清晰、上下文被截断,还是模型服务本身发生了变化。

6. 工程上的正确答案:分层路由

6.1 一条核心原则

技术路由设计的核心原则可以浓缩成一句话:能用规则解决的不交给检索,能用检索解决的不交给大模型。

这不是否定大模型的价值,而是把大模型用在不可替代的地方。规则适合处理高确定性场景,检索适合处理模糊匹配场景,大模型适合处理复杂语义决策和精排。三层各司其职,整个路由系统才能在准确率、延迟、成本之间取得平衡。

6.2 三层路由架构

生产环境中比较稳妥的架构是“规则层 + 召回层 + 精排层”。

规则层:精确命中关键词、正则可以一步到位的请求,直接返回技能,不走后续流程。例如包含“订单号”的请求,直接进入订单查询技能。

召回层:规则没有命中时,通过向量检索、BM25 等多路召回方式,把候选技能缩小到 3 到 5 个。这一步要配合阈值设置,召回数量不能太大,否则交给模型的候选太长;也不能太小,否则容易漏掉正确技能。

精排层:如果召回结果中 top1 的得分显著高于 top2,可以直接采用 top1。如果 top1 和 top2 得分接近,说明存在歧义,这时候才把候选技能列表交给大模型做最终决策。

最后还要有兜底策略。当所有层级都无法确定时,应该返回“未匹配到技能”或转人工,而不是硬选一个大概率错误的技能。

6.3 什么情况下让大模型介入

大模型介入的时机非常关键。按照我的实践经验,以下三种情况适合让模型做精排:

  • 召回结果中 top1 和 top2 分数差距很小
  • 用户请求包含多层意图,例如“下雨导致订单取消”
  • 技能描述相似度高,检索方式无法有效区分

只要满足其中一种,就可以让模型做最终裁决。但在模型决策前,候选技能列表必须已经缩小到个位数,这样才能保证决策的准确率和 Token 成本都可控。

7. 完整示例:从零实现一个分层技能路由

下面我用一个可运行的 Python 示例,把分层路由的完整流程跑通。这个示例只依赖 numpy,不需要真实的大模型 API,本地就能运行。

7.1 定义技能注册表

# skill_registry.py from dataclasses import dataclass, field from typing import List @dataclass class Skill: name: str description: str tags: List[str] = field(default_factory=list) examples: List[str] = field(default_factory=list) enabled: bool = True class SkillRegistry: def __init__(self): self._skills = {} def register(self, skill: Skill): self._skills[skill.name] = skill def get(self, name: str): return self._skills.get(name) def all(self): return list(self._skills.values())

这里把技能拆成了 name、description、tags、examples 四个字段。description 给向量检索和模型精排使用,tags 给规则路由使用,examples 可以用于构建评测集和路由索引。这四个字段在工程中非常重要,它们决定了后续每层路由的信息基础。

7.2 实现规则路由和向量检索路由

# routers.py import numpy as np class RuleRouter: def __init__(self, registry: SkillRegistry): self.registry = registry def match(self, query: str) -> list: hits = [] for skill in self.registry.all(): if not skill.enabled: continue for tag in skill.tags: if tag in query: hits.append(skill.name) break return hits class VectorRouter: def __init__(self, registry: SkillRegistry, embed_func): self.registry = registry self.embed_func = embed_func self.index = {} for skill in self.registry.all(): self.index[skill.name] = embed_func( f"{skill.name} {skill.description} {' '.join(skill.examples)}" ) def _cos_sim(self, vec1, vec2): vec1 = np.asarray(vec1, dtype=np.float64) vec2 = np.asarray(vec2, dtype=np.float64) norm1 = np.linalg.norm(vec1) norm2 = np.linalg.norm(vec2) if norm1 == 0 or norm2 == 0: return 0.0 return float(np.dot(vec1, vec2) / (norm1 * norm2)) def match_with_scores(self, query: str, top_k: int = 3, threshold: float = 0.5): q_vec = self.embed_func(query) scores = [] for name, vec in self.index.items(): score = self._cos_sim(q_vec, vec) if score >= threshold: scores.append((name, score)) scores.sort(key=lambda x: x[1], reverse=True) return scores[:top_k]

RuleRouter 的逻辑很简单,遍历技能所有标签,只要有一个标签出现在 query 中,就命中。VectorRouter 则把所有技能描述和示例 embedding 成向量,再计算 query 和技能向量的余弦相似度。

这里用字符 n-gram 向量模拟 embedding,目的是让你先把流程跑通。生产环境应该换成真实的 embedding 模型,比如接入本地部署的 Ollama 或 vLLM 提供的向量接口。

7.3 实现大模型精排路由

# llm_router.py import json class LLMRouter: def __init__(self, client, model="gpt-4o-mini"): self.client = client self.model = model def decide(self, query: str, candidates: list) -> dict: prompt = ( "你是一个 Agent 技能路由助手。请根据用户请求从候选中选择最合适的一个技能。\n" f"用户请求:{query}\n" f"候选技能:\n{json.dumps(candidates, ensure_ascii=False, indent=2)}\n" "只输出 JSON,格式为:{\"skill\": \"技能名\", \"reason\": \"简短理由\"}\n" ) resp = self.client.chat.completions.create( model=self.model, messages=[{"role": "user", "content": prompt}], temperature=0, ) content = resp.choices[0].message.content return json.loads(content) class MockLLMRouter: """本地验证用的模拟路由,生产环境请替换为 LLMRouter。""" def decide(self, query: str, candidates: list) -> dict: for c in candidates: if c["name"] == "get_weather" and ( "天气" in query or "下雨" in query or "带伞" in query ): return {"skill": "get_weather", "reason": "命中天气关键词"} return {"skill": candidates[0]["name"], "reason": "默认选择第一个"}

LLMRouter 的核心是把“让模型选技能”变成“让模型做候选精排”。注意这里传给模型的只有候选技能列表,而不是全部技能。这解决了上下文过长和注意力稀释的问题。

temperature 设为 0,并要求只输出 JSON,是为了尽可能让路由结果稳定可解析。

7.4 组装成分层路由主流程

# hybrid_router.py class HybridRouter: def __init__(self, rule_router, vector_router, llm_router, registry, min_margin=0.05): self.rule_router = rule_router self.vector_router = vector_router self.llm_router = llm_router self.registry = registry self.min_margin = min_margin def route(self, query: str): trace = [] # 第一层:规则路由 rule_hits = self.rule_router.match(query) trace.append({"step": "rule", "candidates": rule_hits}) if len(rule_hits) == 1: return self.registry.get(rule_hits[0]), trace # 第二层:向量召回 vector_hits = self.vector_router.match_with_scores(query, top_k=3, threshold=0.5) trace.append({"step": "vector", "candidates": [h[0] for h in vector_hits]}) if len(vector_hits) == 1: return self.registry.get(vector_hits[0][0]), trace if len(vector_hits) >= 2: top1, score1 = vector_hits[0] _, score2 = vector_hits[1] if score1 - score2 >= self.min_margin: return self.registry.get(top1), trace # 第三层:大模型精排 if len(vector_hits) == 0: candidates = [s.name for s in self.registry.all() if s.enabled] else: candidates = [h[0] for h in vector_hits] trace.append({"step": "llm", "candidates": candidates}) llm_candidates = [] for name in candidates: skill = self.registry.get(name) llm_candidates.append({"name": skill.name, "description": skill.description}) decision = self.llm_router.decide(query, llm_candidates) return self.registry.get(decision["skill"]), trace

这个 HybridRouter 就是分层路由的骨架。规则层命中一个技能就直接返回;向量召回只有一个高置信度命中也可以直接返回;如果 top1 和 top2 得分接近,才让大模型介入。

route 方法返回了 skill 和 trace。trace 记录了每个层级命中的候选列表,这是路由可观测性的关键。线上排查时,只要看 trace 就能知道决策路径。

需要提醒的是,如果技能数量特别大,例如超过 500 个,vector_hits 为空时把所有技能传给模型仍然不合理。这种情况下应该再加一层粗排,或者设置默认拒绝策略。

7.5 完整的本地演示代码

# demo.py import hashlib import numpy as np from skill_registry import Skill, SkillRegistry from routers import RuleRouter, VectorRouter from llm_router import MockLLMRouter from hybrid_router import HybridRouter def char_ngram_embed(text: str, dim: int = 64) -> np.ndarray: vec = np.zeros(dim) for i in range(len(text) - 1): gram = text[i:i + 2] idx = int(hashlib.md5(gram.encode("utf-8")).hexdigest(), 16) % dim vec[idx] += 1 return vec def build_registry(): registry = SkillRegistry() registry.register(Skill( name="query_order", description="查询订单的状态、金额、数量、物流进度", tags=["订单"], examples=["我的订单到哪了", "查询订单金额"], )) registry.register(Skill( name="get_weather", description="查询城市实时天气、气温、降水概率", tags=["天气"], examples=["北京今天天气", "上海会下雨吗"], )) registry.register(Skill( name="send_message", description="向指定联系人发送消息", tags=["发送", "消息"], examples=["给张三发消息", "把文件发给李磊"], )) return registry def main(): registry = build_registry() rule_router = RuleRouter(registry) vector_router = VectorRouter(registry, embed_func=char_ngram_embed) llm_router = MockLLMRouter() hybrid_router = HybridRouter(rule_router, vector_router, llm_router, registry) queries = [ "帮我查一下昨天订单金额", "北京明天天气怎么样", "把项目周报发给李磊", "明天出门要不要带伞", ] for query in queries: skill, trace = hybrid_router.route(query) print(f"请求: {query}") print(f"路由结果: {skill.name}") print(f"路由轨迹: {trace}") print("-" * 50) if __name__ == "__main__": main()

这里特意做了一个 MockLLMRouter,这样你不需要申请任何模型 API 就能在本地看到完整的分层路由效果。生产环境只需要把 MockLLMRouter 换成真实的 LLMRouter,并传入对应的模型客户端。

8. 运行结果与效果验证

8.1 运行方式

环境要求是 Python 3.9 及以上,安装 numpy 即可:

pip install numpy python demo.py

8.2 预期输出结构

运行后,你看到的输出应该是一个结构化的路由轨迹。以“帮我查一下昨天订单金额”为例:

请求: 帮我查一下昨天订单金额 路由结果: query_order 路由轨迹: [{"step": "rule", "candidates": ["query
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 13:14:51

光谱检测入门:从原理到实践,掌握物质分析的“光指纹”技术

1. 项目概述:从“看颜色”到“读光谱”的认知跃迁 “光谱检测的知识积累第一天”,这个标题听起来像是一个技术人的学习笔记开篇,但它背后指向的是一个庞大而精密的物理化学分析世界。很多人对光谱的第一印象,可能还停留在中学物理…

作者头像 李华
网站建设 2026/8/26 13:14:40

光谱检测入门:从原理到实践的第一天系统指南

1. 项目概述:从“第一天”开始的系统性知识构建光谱检测,这四个字听起来既熟悉又陌生。熟悉是因为它在工业质检、环境监测、食品安全甚至医疗诊断等领域无处不在;陌生则是因为其背后涉及的光学、物理、化学、电子和数据分析知识体系庞大而复杂…

作者头像 李华
网站建设 2026/8/26 13:12:46

数据标准化与正则化:提升模型收敛速度与泛化能力的关键技术

1. 从“量纲”到“收敛加速”:数据预处理与正则化的核心逻辑 做机器学习或者数据分析的朋友,肯定都遇到过这样的场景:模型训练时,损失函数曲线像坐过山车一样忽上忽下,半天不收敛;或者好不容易收敛了&#…

作者头像 李华
网站建设 2026/8/26 13:10:28

数字滤波器实现指南:从FIR/IIR选型到嵌入式定点优化

Filter这个词,在不同软件里代表完全不同的东西。你在同花顺里写filter,可能是在做条件选股;你在Fiddler里找filter,大概率是在配抓包过滤规则;但如果你在信号处理团队里说Filter Realizations,那我们聊的就…

作者头像 李华
网站建设 2026/8/26 13:09:09

从零构建支持中断恢复的AI Agent:状态机与记忆系统设计实践

1. 项目概述:为什么我们需要一个能“被打断”的AI助手?最近在折腾一个挺有意思的项目,核心目标就写在标题里了:从零开始搭建一个能理解“人机协作”与“中断恢复”的AI Agent。这听起来可能有点抽象,但如果你用过市面上…

作者头像 李华
网站建设 2026/8/26 13:08:45

拆解IDM与鼓打贝斯:从Breakbeat切片到Reese低音的制作全流程

如果你在播放器里看到《Sadesper Record - Externalization 1 (1999)》这个标题,大概率会直接划过去。它没有流行旋律,没有大厂封面,也没有一首能让人立刻哼出来的副歌。但把视角从“乐评”切到“技术分析”之后,它反而变成了一份…

作者头像 李华