Rasa A2A运行合同:context亲和、任务幂等与push投递分账
Rasa A2A运行合同:context亲和、任务幂等与push投递分账
知识点轴:A2A / harness-runtime / sandbox-security / benchmark-evaluation,关联MCP Gateway出站治理。官方3.17文档有原生AgentCard、JSON-RPC、stream/cancel/auth/push细节;这是可操作的官方实现合同案例,不是已核实的用户部署、live Card或跨SDK互通实测。
为什么对用户重要
把业务对话助手包装成A2A sub-agent,比“agent之间互相发消息”多出状态、重试和出站权限。Hermes未来接外部业务agent时,最容易误判的是:共享数据库就能任意扩容、HTTP成功就是业务完成、拿到Card就拥有task权限。Rasa文档明确写出了这些条件,适合给A2A-Agent2Agent-Protocol和Harness-Engineering补真实运行合同,而不是只收一个产品名。
一阶机制:对话持久化与协议状态不是同一个账本
配置a2a_server后,rasa run在与REST/webhook相同的Sanic端口注册A2A JSON-RPC。Card发现是GET /.well-known/agent-card.json;message/send返回终态task,message/stream可发working状态及artifact delta。对话stack/tracker映射到working/input_required/completed/failed/canceled/rejected/auth_required,结构化DataPart可带state、active_flow和slots。
持久tracker不等于持久A2A调度状态。 文档说当前task、messageId去重、in-flight队列、push注册和context容量限制都在每个worker/pod内存里,因此:
SANIC_WORKERS=1是启动guardrail,不是任意调优建议。- 多副本必须按
contextId做亲和路由;tasks/cancel等body没contextId的操作,可用X-A2A-Context-Id或cookie传路由键。 - 普通round-robin会使后续消息丢协议上下文、重试重执行、cancel找不到工作、另一pod不知道push注册。
- 亲和路由只让同context常态落同pod,不提供pod崩溃后的状态接管/业务exactly-once。这与a2a-python-cluster-state-stream-execution-boundaries的“有共享存储≠已完成执行接管”是同一类边界。
文档还要求start_session_after_expiry: false,否则复用同contextId时,对话过期启动动作可能悄悄重置slot。input_required保留context,意味着等待用户的任务仍占资源,不能只按当前running计容量。
重试/超时合同:幂等有范围和寿命
去重键是(contextId, messageId):同ID在已完成且缓存有效时重放缓存terminal task;并发重复在in-flight时返回HTTP409,不能当业务失败后另起新messageId重做。默认task超时600秒;计时器按task建立,不被同context后续message延长;缓存TTL默认跟task timeout走,设0会立即过期。
因此要分别记:同ID重放、同ID并发冲突、TTL过期重做、pod重启后重做、业务副作用去重。协议缓存有用,但不证明转账/下单/写库只执行一次。文档的tasks/cancel可发取消信号,真实外部动作是否中止/回滚仍需要业务sink读回;不要把canceled状态当回滚回执。
认证、发现与push是三个边界
| 边界 | 官方合同 | 不能由此推出 |
| Card及JSON-RPC入口 | 配a2a_server.auth后两者均需Bearer JWT;不配置则开放 | 已有per-task owner/跨租户隔离 |
| 身份 | JWT验证orchestrator;终端用户身份在metadata/slot,不在此JWT | metadata中的用户声明自动可信 |
| claim约束 | issuer/audience可配置;对称secret须环境变量插值 | 所有实例都设置了issuer/audience |
| push注册 | 默认禁用;开启后可限制allowed hosts | 开启后自动安全,或已经可靠送达 |
Card描述了能力,并不代替task资源验权。对终端用户metadata和可写slot,应依据业务权限做二次过滤;“domain里定义过此slot”只是类型/字段准入,不是该orchestrator有权代用户设置任意值。这是从文档合同导出的工程要求,不是已证实的Rasa越权漏洞。
push文档提供重要的失败分账:callback只允许http/https;拒绝内网、loopback、link-local、内嵌凭据;注册和每次POST前做DNS检查;不跟随redirect。3xx按投递失败记日志且不重试,A2A task仍可正常完成。因此task completed ≠ push delivered,SSE/查询回读与通知可靠性应独立验收。
文档同时承认DNS检查未把解析IP绑定到实际httpx连接,仍有rebinding TOCTOU;host allowlist/HTTPS是缓解,不是连接时IP pinning。对应a2a-push-dial-ip-proxy-final-target-boundaries与contextforge-egress-path-parity-dns-rebinding,最终出口若经过代理还须检查代理的拨号策略。
对Hermes工作流的最小验收矩阵
以下是待实施设计,不是本机跑通声明:
- 先保存真实Card与服务版本;对无JWT、错issuer/audience、正确JWT分别测Card和task,不把发现401误诊成agent离线。
- 两pod执行同context后续turn与cancel:有/无亲和路由成对;强制pod退出后另验恢复,而不是从常态亲和成功外推高可用。
- 同messageId做并发、完成后重放、TTL过期和重启四类测试;从独立业务sink统计effect,别只看taskId。
- 合法push、3xx、超时和非法目标各测一次;分别保存task终态与投递状态,不让通知失败被“任务成功”掩盖。
- 在测试环境核slot预填来源与业务授权,确保用户可见文字或不可信metadata不能变成高权限业务事实。
深度判断与边界
值得正式入库的是内存状态所有权、去重寿命、路由亲和和push失败语义,能迁移到任何A2A adapter。没有创建独立Rasa实体页,也不把它当已完成enterprise deployment:本轮取得的是官方滚动文档快照,没有固定私有实现、真实endpoint、SDK运行轨迹或外部业务effect。文档升级可能改变条件,后续要以具体release和实际Card重新核验。
写入记录
- 2026-10-28 09:13 CST:深读Rasa官方架构/服务文档,新增A2A状态亲和、重试寿命、认证/slot边界与push投递合同;保留DNS连接时缺口及无本机部署/互通证据。