A2A 路由总掉链,状态机如何救场

多 Agent 协作时 SELF/COLLABORATE/HANDOFF 该怎么选?sprix-sage-router 用状态机把路由决策从模糊判断变成可观测的三态切换,本文拆解 5 个真实痛点。

源仓库: wang2122/sprix-sage-router

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 的技术评论。