A2A 路由总掉链,状态机如何救场
多 Agent 协作时 SELF/COLLABORATE/HANDOFF 该怎么选?sprix-sage-router 用状态机把路由决策从模糊判断变成可观测的三态切换,本文拆解 5 个真实痛点。
sprix-sage-router 是 wang2122 在 GitHub 开源的 A2A 智能体路由库,核心是把 Agent 间协作拆成 SELF/COLLABORATE/HANDOFF 三态切换。当前 A2A 协议下的多 Agent 框架普遍面临「谁来接、接不住怎么办」的路由塌方,sprix-sage-router 通过显式状态声明把决策路径固化下来,让分布式 Agent 协作可观测、可回放、可调试。⭐1412 星的背后,是开发者对「路由也是代码」的共识——既然 HTTP 路由都有状态机,Agent 协作也该有。
痛点 1:SELF/COLLABORATE 边界模糊,Agent 重复抢活
现象:两个 Agent 同时判断「我能处理」,结果重复执行同一段任务,token 翻倍、结论互相打架。
根因:缺乏显式状态机,全靠 LLM 自描述能力。Prompt 里写一句「如果我能做就做吧」看似优雅,实际是一次掷骰子。
解法:用 sprix-sage-router 的状态声明,把「擅长做什么」从自由文本升级成枚举:
from sprix_sage import SageRouter, SELF, COLLABORATE
router = SageRouter()
router.register("researcher", scope=SELF, skills=["search", "summarize"])
router.register("analyst", scope=COLLABORATE, skills=["search", "analyze"])
result = router.dispatch(query="查找 Q3 财报数据", current="researcher")
SELF 表示只接自己的强 skill,COLLABORATE 表示愿意接同伴甩过来的活。注册表是 O(1) 查表,没有 LLM 介入,从根上消除重复派发。
痛点 2:HANDOFF 时上下文丢失
现象:Agent A 算到一半把任务扔给 Agent B,B 一脸懵——「我在干嘛?我为什么被叫过来?」
根因:HANDOFF 时只传了 query 字符串,丢失了中间计算结果、用户偏好、对话历史。
解法:把状态打包成结构化 SageState,新 Agent 拿到即可解析:
from sprix_sage import HANDOFF, SageState
state = SageState(
context={"user_pref": "tech-only", "partial": partial_result},
reason="analysis skill required",
)
router.handoff(from_="researcher", to="analyst", state=state)
SageState 是带 schema 的 dict(pydantic v2),字段未填全会抛 ValidationError,而不是悄悄传空字符串。
痛点 3:循环自调用把 token 烧光
现象:日志里看到 Agent A 调用自己 17 次,最后一次 prompt 长达 3 万 token,账单先哭。
根因:路由决策没有最大深度限制,LLM 一拍脑袋又跳回起点。
解法:开启 MAX_DEPTH 与 LRU 缓存:
router = SageRouter(max_depth=5, cycle_cache_size=128)
cycle_cache_size 会记住最近 128 个 (agent, state_hash) 对,遇到重复就强制 HANDOFF 或终止。state_hash 用 blake2b 算 64 位,碰撞概率可忽略。
痛点 4:路由决策不可观测
现象:线上出问题,PM 问「为什么是这个 Agent 接的?」开发答「我……我也不太清楚,看日志吧」。
根因:路由逻辑藏在 prompt 里,跑完只剩一行字符串。
解法:开启 trace 导出,把每一次状态切换都变成 OpenTelemetry span:
router = SageRouter(trace_sink="otel://collector:4317")
所有 SELF/COLLABORATE/HANDOFF 决策都会带上 span,字段包括触发的 skill、当时的 context 哈希、决策耗时。在 Grafana 里能直接看到「3 次 SELF 后 2 次 HANDOFF」这种火焰图模式,定位慢路径。
痛点 5:多 Agent 并发竞争,状态机进入非法态
现象:同一秒收到两个 HANDOFF,状态机闪了一下进入 undefined,runtime 抛 InvalidTransition。
根因:状态切换不是原子的,并发读改写之间没有栅栏。
解法:用 SAGE_LOCK 给状态切换加分布式锁:
from sprix_sage import SAGE_LOCK
with SAGE_LOCK("task-123"):
router.handoff(from_="researcher", to="analyst", state=state)
底层是 Redis 分布式锁(SET NX PX),TTL 默认 30s,超时自动释放避免死锁。多实例部署也不用担心。
Sources:本文参考 GitHub Trending 2026-08-24 榜单 wang2122/sprix-sage-router 项目 README、r/ClaudeAI 关于 A2A 路由塌方的讨论帖,以及 Hacker News 上 state-aware routing 的技术评论。