你搭了一个路由器,里面有三个专职 Agent:code_agent、test_agent 和 docs_agent。你提交「Write a unit test for the authentication module」(给认证模块写个单元测试),结果路由器把它发给了 docs agent,对方写出了一篇关于认证的 Markdown README。又或者在 AutoGen 里,一个「数据库迁移」任务被路由给了通用助手而不是迁移专家,那个 Agent 直接对生产连接跑了 ALTER TABLE,而不是生成一个迁移文件。路由误判会浪费 token、产出错误结果,最糟糕时还会用错误的工具触发错误的副作用。
最快的修复: 把每一次路由决策连同它的置信度分数一起记录下来,然后加一个置信度阈值(从 0.75 起步),让低置信度的任务进入澄清环节,而不是硬猜。仅这一项改动就能拦下大部分误判,给你时间去收紧底层的 Agent 描述。下面会讲清楚怎么判断你到底踩中了六个根因里的哪一个。
你属于哪一类?
| 你观察到的症状 | 最可能的原因 | 跳到 |
|---|---|---|
| 同样的两个 Agent 总在相似任务上互相抢 | 类别描述重叠 | 原因 1 |
| 误判任务用的措辞你从没作为示例给过 | few-shot 示例太稀疏 | 原因 2 |
| 任务里改一个词,路由结果就翻盘 | 用的是关键词匹配,不是语义 | 原因 3 |
| 在真正模糊的任务上自信地选错 | 没有「不确定」/ 默认通路 | 原因 4 |
| 整整一类任务永远到不了它的 Agent | registry 里的 Agent 过期或不可见 | 原因 5 |
| 短任务(「修一下」)随机乱路由 | 任务文本里信号不足 | 原因 6 |
如果你用的是某个框架,故障通常带有该框架特有的形态:
- LangGraph(
add_conditional_edges):路由函数返回的字符串、或某个path_map键里有拼写错误,会悄无声息地把任务送到错误的节点或直接送到END。返回的字符串和 path-map 键必须完全一致。(Graph API 文档) - CrewAI(
Process.hierarchical):自动创建的manager_llm根据每个 Agent 的role/goal文本来决定路由。截至 2026 年 6 月,这个 manager 经常按顺序执行任务、或调用错误的 worker;常见修复是自定义一个带明确分步指令的manager_agent,或改用Process.sequential/ 在Task(agent=...)里钉死 Agent。(CrewAI 层级流程文档) - AutoGen(
SelectorGroupChat):你的selector_func一旦返回None就会回退到模型,因此一个覆盖面太窄的函数会悄悄把选择权交还给 LLM。此外还有一个已知的间歇性 bug:某些轮次之后selector_func不会被再次调用。(Selector Group Chat 文档)
常见原因
1. 路由提示太模糊——类别描述彼此重叠
最常见的原因。你用自然语言定义了 Agent 角色(「处理代码任务」「处理测试任务」),但对于模糊输入,路由模型必须在它们之间二选一。「写个测试」既涉及代码又涉及测试。「更新迁移」既涉及数据库又涉及代码。重叠的类别描述会产生不稳定的路由。
怎么判断:拿出最近 10 个误判任务,看哪两个 Agent 的描述最相似。任何一对(做 embedding 后)余弦相似度高于 0.85、或关键词重叠的组合,都会稳定地产生误判。
2. 路由提示里的 few-shot 示例不具代表性
路由器每个 Agent 只有 3-4 个示例任务。这些示例全用了很具体的术语(「write a Jest test」「create a Sequelize migration」)。而真实任务用的是别的措辞(「add coverage for the login flow」「bump the schema」)。模型无法从稀疏的示例泛化到全新的措辞。
怎么判断:收集最近 20 个误判任务,看其中有没有任何一个用了与现有示例相似的措辞。如果误判任务用的措辞全都不在示例里,说明示例集太窄了。
3. 路由器用关键词匹配而不是语义分类
路由器检查 if "sql" in task.lower() 就路由给数据库 Agent。而像「fix the SQL injection vulnerability in the auth layer」这样的任务命中了数据库 Agent 的关键词,但本该交给安全 Agent。关键词匹配处理不了上下文。
怎么判断:读路由代码。如果里面有 in task.lower()、startswith、对关键词做 re.match、或一串简单的 if/elif,那它就是基于关键词的,会在依赖上下文的任务上误判。
4. 缺少「默认」或「模糊」通路——路由器挑了最接近的错误项
当没有合适的匹配时,路由器会路由给第一个 Agent,或者 softmax 概率最高的那个,哪怕这个概率只是 0.52 对 0.48。它没有一条「我不确定」的通路去升级给人工、或反过来要求澄清。
怎么判断:给路由器加置信度日志。如果置信度低于 0.7 的路由决策与误判输出相关,那你就需要一个低置信度阈值。
5. Agent 能力清单过期——Agent 被弃用或改名了
编排器的路由表引用了 agent_v2_code,但活跃的 Agent 是 agent_v3_code_and_test。v2 那个 Agent 要么已不存在(路由静默失败、落到默认通路),要么还在但缺少新能力(写测试是 v3 才加的)。在 LangGraph 里这表现为一个 path_map 键不再匹配任何节点名;在 CrewAI 里则表现为一个在 crew 中却从不被选中的 Agent。
怎么判断:列出路由表里的所有 Agent ID,与当前活跃的 Agent 实例清单对比。路由表里任何匹配不上活跃 Agent 的 ID,都是过期项。
6. 任务描述太短——路由器缺少信号
「修一下」——两个字——什么信息也没给路由器。它只能靠猜,而且猜错。短任务常出现在编排器路由前把一个大任务做了摘要的时候。
怎么判断:对比误判任务描述与正确路由任务描述的字符长度中位数。如果误判任务明显更短(少于 30 个词),那简短就是病因。
最短修复路径
Step 1:把每一次路由决策连同任务文本和置信度一起记录
def route_task(task: str, router_model) -> tuple[str, float]:
response = router_model.classify(
task,
labels=list(AGENT_REGISTRY.keys()),
return_scores=True
)
top_agent = response.labels[0]
confidence = response.scores[0]
logger.info(
"ROUTE: agent=%s confidence=%.3f task=%r",
top_agent, confidence, task[:120]
)
return top_agent, confidence
复查最近 50 次路由决策,找出误判的规律。如果你用 LangGraph,把路由函数返回的确切字符串记下来,逐字符地与 path_map 的键比对——一个拼写错误就是最常见的静默误判。
Step 2:加一个带升级通路的置信度阈值
CONFIDENCE_THRESHOLD = 0.75
def route_with_fallback(task: str) -> str:
agent, confidence = route_task(task, router_model)
if confidence < CONFIDENCE_THRESHOLD:
logger.warning(
"Low-confidence route (%.2f) — escalating to clarification agent",
confidence
)
return "clarification_agent"
return agent
澄清 Agent 问一个问题来消歧,然后带着更多上下文重新路由。在 AutoGen 里就在 selector_func 内部这么做,并显式返回澄清 Agent 的名字;记住返回 None 会把选择权交还给模型,而不是走你的澄清通路。
Step 3:把 Agent 描述改写成互斥的
用明确的边界范围替换模糊描述:
AGENT_DESCRIPTIONS = {
"code_agent": (
"Writes, edits, or refactors production source code in .py, .ts, .go files. "
"Does NOT write tests, migration files, or documentation."
),
"test_agent": (
"Writes or edits test files (*.test.ts, test_*.py, *_spec.rb). "
"Does NOT edit production source files or migration files."
),
"migration_agent": (
"Generates database migration files using the project's migration framework. "
"Never runs migrations directly — only creates the migration file."
),
}
那些「Does NOT」(不做什么)的子句,和「Does」(做什么)的子句一样重要,能防止范围重叠。在 CrewAI 里这些内容写进每个 Agent 的 role 和 backstory,manager_llm 在委派时会读取它们。
Step 4:扩充 few-shot 示例,覆盖多样措辞
给每个 Agent 加至少 10 个示例,覆盖:
- 直接措辞(「write a test for X」)
- 间接措辞(「add coverage for X」)
- 行话变体(「spec for X」「unit test for X」「test case for X」)
- 不该路由到这里的跨领域任务(「fix the code that X tests」应交给 code_agent,而不是 test_agent)
TEST_AGENT_EXAMPLES = [
"Write a unit test for the authentication module",
"Add test coverage for the payment flow",
"Create a spec for the UserService class",
"The login tests are failing — update the test assertions",
# Counter-examples (what NOT to route here):
# "Fix the authentication module so the tests pass" => code_agent
# "Write docs for the test suite" => docs_agent
]
Step 5:上线前用带标注的评测集验证路由
ROUTING_EVAL = [
{"task": "Add a test for the JWT decoder", "expected": "test_agent"},
{"task": "Fix the JWT decoder implementation", "expected": "code_agent"},
{"task": "Document the JWT decoder API", "expected": "docs_agent"},
# ... 50+ examples
]
def evaluate_router(router):
correct = sum(
1 for ex in ROUTING_EVAL
if route_task(ex["task"], router)[0] == ex["expected"]
)
accuracy = correct / len(ROUTING_EVAL)
print(f"Router accuracy: {accuracy:.1%}")
assert accuracy >= 0.90, "Router accuracy below 90% threshold"
每当路由提示或 Agent 描述发生变化时,把这个评测当作一个 CI 检查来跑。
如何确认已经修好
- 在你的标注集上重跑
evaluate_router;准确率应达到或超过0.90,且之前失败的任务现在应当全部通过。 - 把最近 50 个生产任务用新路由器回放一遍,把选中的 Agent 与旧日志做 diff。每一个原来的误判都应当改变;任何原本正确的都不应回退。
- 观察一整天的低置信度计数器。如果置信度低于
0.75的任务占比很小、且它们全都落进澄清通路(而不是错误的专职 Agent),说明升级通路在正常发挥作用。
预防建议
- 用明确的边界范围(带「不处理哪类任务」子句)来定义 Agent 能力——描述里的歧义会直接导致误判。
- 在上线任何路由器之前,先建一个至少 50 条样例的带标注路由评测集,并在 CI 里强制 90% 准确率门槛。
- 记录每一次路由决策的置信度分数;对低于
0.75置信度的决策告警。 - 为低置信度路由加一个「澄清 Agent」或人工升级通路,而不是硬猜。
- 给你的 Agent registry 做版本管理;每次新增、删除或重命名 Agent,上线前都跑一遍路由评测套件。在 LangGraph 上要保持
path_map的键与节点名同步;在 CrewAI 上要重新核对 manager 对 crew 的认知。 - 让发给路由器的任务描述至少有 20 个词——如果编排器会生成短任务,就加一个任务扩写环节。
- 凡是超出简单路由的场景,都用语义分类(embedding 相似度或分类器模型)而不是关键词匹配。
- 在生产里每周复查误判任务;用它们来扩充评测集、改进示例。
常见问答 (FAQ)
Q: 我该用一个专职的路由模型,还是把路由直接做进编排器 LLM? A: 对于 3-5 个 Agent,把路由做进编排器提示里就很好用。对于 10 个以上的 Agent,请用一个专门的轻量分类器(微调的小模型或 embedding 相似度)——随着选项变多,编排器的通用模型在路由准确率上会下降。
Q: 我的 CrewAI 层级 crew 一直误判,或者把所有任务都串行执行了,是哪里变了?
A: 截至 2026 年 6 月,CrewAI 自动创建的层级 manager 经常没有按文档所说去协调——它可能按顺序执行任务、做不必要的工具调用、路由也很差。请自定义一个带明确分步委派指令的 manager_agent,或者用 Process.sequential 并为每个任务钉死 Task(agent=...),完全绕过动态路由。预期层级 manager 会比串行模式多用大约 30-50% 的 token。
Q: 我的 LangGraph 路由函数返回了正确的标签,但任务还是去了错误的节点。
A: 你的条件边函数返回的字符串,必须与某个 path_map 键(或节点名)完全一致。一个尾随空格、一处大小写差异、或一个被改名的节点,都会把任务送错地方或直接送到 END,而且没有报错。加上类型提示 / 显式 path_map,让 LangGraph 能校验目标节点,并把返回的字符串和可用的键一起打日志。
Q: 一个任务合理地同时属于两个 Agent,怎么办? A: 在路由前把任务拆开。加一个「任务分解」环节,把复合任务拆成原子子任务,每个子任务都干净地映射到一个 Agent。不要试图把一个复合任务路由给单个 Agent。
Q: 基于向量的路由能取代基于提示的路由吗? A: 通常可以,尤其是大型 Agent registry。给每个任务和每个 Agent 的能力描述都做 embedding,然后路由给余弦相似度最高的 Agent。它比让大模型逐个分类更快、更便宜、更稳定。要注意的是:语义相似不等于能力匹配(「写一份 SQL 注入报告」这个任务的 embedding 可能最接近文档 Agent),所以仍要拿你的标注评测集来验证,并设一个最低相似度门槛来兜底。