A2A Jakarta:跨节点状态复制不等于生产安全与故障恢复
A2A Jakarta:跨节点状态复制不等于生产安全与故障恢复
知识点轴:A2A / harness-runtime / sandbox-security。 价值不是又一个 SDK release,而是一个可逐文件核对的双节点案例:共享持久状态与复制实时事件各司其职;SDK 的安全默认值,却被示例配置主动关闭。
为什么值得深挖
Hermes 若把长任务委托给远端 agent,断流后的客户端可能连接到另一实例。单机 TaskHandle 能拿到任务 ID,不代表新节点知道历史状态、能收到后续事件或能安全继续工作。此案例把 A2A-Agent2Agent-Protocol 的任务协议落到 Harness-Engineering 的状态/事件基础设施,比仅可读 Agent Card 多了一层工程证据;不是用户系统的部署推荐。
来源与版本边界
- WildFly 公告 发布于 2026-09-10,声明 Jakarta 1.0.0.Final 支持 A2A 1.0、JSON-RPC/gRPC/REST 和 0.3 兼容层。
- 固定仓库 wildfly-extras/a2a-jakarta,commit
d611c7c336948c12dab20f4e46d324a22a492e41。根pom.xml的version.sdk=1.3.2.Final、version.wildfly=41.0.1.Final与公告一致。 - 文档争议:同 tag 根 README 仍写 SDK 1.0.0.Final。本页按 POM/公告记录依赖版本,保留
contested: true提醒文档未同步;未执行 dependency resolution,所以不是实际 WAR 依赖清单。 - 已读公告、上游 1.3.0 安全迁移说明、企业 README、Card/executor/client、配置及一个多租户 JSON-RPC 测试包装类。GitHub API 限流,未取得可靠当前 star 数;不影响固定 tag 代码取证。^[raw/articles/a2a-jakarta-cross-node-state-and-effective-security-2026-09-13.md]
2026-10-24 版本核对:1.1.0 并未消除示例边界
2026-10-21 官方公告 将 Jakarta 升到 1.1.0.Final、上游 SDK 升到 1.4.0.Final。固定 tag 对应 commit d712ac1ecbfa4c7bac4d3983f6e88b09d43c2587,根 POM 确认 version.sdk=1.4.0.Final、WildFly 仍 41.0.1.Final。上游 SDK release 发布于 09-28;不能把 10-21 集成公告当作新协议版本,也不重造一篇 release 薄页。旧 tag 的 README/POM 分歧仍属历史版本证据,本次未重新取根 README 判其已修。^[raw/articles/a2a-jakarta-110-java-140-pinned-enterprise-2026-10-24.md]
SDK 公告把 @Tenant qualifier 移到 server-common,以解决默认/tenant Card 的 CDI 选择与歧义;重复 extended Card 启动拒绝、SSE 每行默认限制增至 1 MB、application/a2a+json 兼容等也有说明。这是上游公告/完整 release notes 层证据,不是本轮逐项审计上游实现或运行通过;Card 路由修复并不自动建立 caller→task owner 授权。^[raw/articles/a2a-jakarta-110-java-140-pinned-enterprise-2026-10-24.md]
本轮固定企业 config/Card/client/README 仍见 a2a.authorization.required=false、每节点独立 Kafka group.id、B 续订后 Thread.sleep(1000),随后真实工作继续交给 A;只改集成版本不能推出安全默认已在该示例生效、订阅显式 ready 或 A 故障后可接管。归档 README 的 expected output 不是本机结果;未启动 Java、PostgreSQL/Kafka 或发送 Task。与 ssr-agent-discovery-candidate-context-and-retrieval-boundaries 并读:后者的 E2E 只到候选选择,本例才涉及任务/事件基础设施,但两者都不能替代实际部署的 owner、断流和业务 effect 验收。^[raw/articles/a2a-jakarta-110-java-140-pinned-enterprise-2026-10-24.md]
机制:状态存储、事件传播和执行恢复是三件事
- 状态面:两个 WildFly 节点共用 PostgreSQL;JPA
TaskStore和PushNotificationConfigStore替换内存实现,保存任务及推送配置。示例使用非破坏性 schema update,不应将启动 B 等同重建 A 的库。 - 事件面:Kafka-backed
ReplicatedQueueManager传播 live events。示例要求每节点独立group.id,让 A/B 都收到同一 topic 的事件;共享 consumer group 的分摊消费不能直接替代这一广播语义。 - 演示路径:客户端从 A/B 各自解析 Card;向 A 发
messageId="init"建任务,在 BsubscribeToTask,再向 A 发带同一 taskId 的 continuation。A 产生 working → artifact → completed,B 收集状态与产物。init是 executor 明写的 demo-only 约定,不是 A2A 标准握手。 - 恢复面未证明:A 仍负责实际执行,B 是观察者。本路径没有展示 A 宕机后的工作接管、业务动作幂等或跨节点取消。因此共享 task DB + 事件复制不推出执行 exactly-once 或 HA 完成保证。^[raw/articles/a2a-jakarta-cross-node-state-and-effective-security-2026-09-13.md]
这与 a2a-query-task-handle-approval-broker 互补:后者强调客户端状态句柄/重订阅,本文补服务端跨节点的共享状态与事件传播。MCP-Gateway-Runtime 的负载均衡或 session affinity 只能选路,不能替代任务状态协议。
对照2026-09-29发布的 a2a-python-cluster-state-stream-execution-boundaries:另一SDK以任务修订CAS、共享事件表和请求边界刷新支持跨副本读/续写/取消;它也未由此证明执行接管、跨租户身份或业务副作用只执行一次。两套实现需分别核各自SDK版本/有效配置,不可把一家的验收结论移接到另一家。
最重要反例:默认值与有效配置相反
上游 1.3.0 公告称未配置 TaskAuthorizationProvider 时默认拒绝任务操作,并将检查移入 DefaultRequestHandler 覆盖 CDI/builder 路径。这是 SDK 公告层证据,不是本机对实现的安全审计。固定 tag 的企业示例却明确配置 a2a.authorization.required=false。 把「SDK fail-closed」与「企业示例」两句话拼在一起,会制造不存在的授权保证。^[raw/articles/a2a-jakarta-cross-node-state-and-effective-security-2026-09-13.md]
多租户 MultiTenantJsonRpcTest 继承上游抽象测试并加载专用 fixture:同样关闭 authorization,而且 org.a2aproject.sdk.transport.skipValidation=true,注释说明共享 Card 宣告了该部署未装入的 HTTP_JSON transport。路由测试不等于租户授权或 Card 全能力验证;本次也未阅读继承父类的所有断言或运行测试。^[raw/articles/a2a-jakarta-cross-node-state-and-effective-security-2026-09-13.md]
失败模式与最小接入验收
| 观察到的具体边界 | 不能推出的结论 | 建议隔离验证 |
B 订阅后固定 Thread.sleep(1000) 再驱动 A | 订阅已经就绪、早期事件必达 | 用显式订阅就绪信号;断流/晚订阅后 getTask 对账 |
executor cancel() 抛 TaskNotCancelableError | task API 存在就可取消工作 | 验证不可取消响应,不自动重发有副作用任务 |
| 每节点独立 consumer group | 生产任意分区/分组布局都广播正确 | 对比共享组、独立组、重启/重复事件 |
| JPA push-config store 已装入 | 实际 push 已开启或 webhook 安全 | 单独核 Card、push enablement、目的地策略及回调回执 |
| demo 关闭授权、gRPC 使用 plaintext | 企业多租户隔离或 TLS 已满足 | 加真实 provider/TLS,测试 A 身份读取/续写 B 的任务 |
对 Hermes / llm-wiki 的可执行启发
- 在接入记录分开保存 SDK 默认值 / 示例 override / 测试 fixture / 实际部署配置,不能只抄 README 安全卖点。
- 任务结果区分「可查历史」「能订阅后续事件」「可恢复执行」「业务只执行一次」;用 Agent-Benchmarks 为每项单独设计断言。
- 沿用 a2a-tck-conformance-coverage-and-security-boundaries:记录跳过的校验及其原因,不将测试类名称、声明 transport 或示例 expected output 当运行通过。
本轮验证与未执行项
本机解析固定 tag 文件并完成 12 项静态断言,核对 POM、关闭授权、广播分组、demo init、取消拒绝、订阅 sleep、streaming Card、fixture 和 README/POM 差异。结果在 raw/notes/ai-knowledge-radar-2026-09-13.md。没有编译/启动 Java、Kafka/PostgreSQL,没有跑 TCK、task E2E、鉴权/崩溃恢复测试。 原文示例输出不作为本机结果。
写入记录
- 2026-09-13 09:14 CST:新增固定 tag 双节点机制分析,核对 12 项静态配置/源码事实;显式区分 SDK 默认、demo override、fixture 和真实部署,保留 README 版本分歧及未验证恢复边界。
- 2026-10-07 09:05 CST:新增A2A Python 1.2跨副本状态/事件与执行接管分验反链;不更改Jakarta原始证据范围。
- 2026-10-24 09:10 CST:固定 Jakarta 1.1.0.Final 企业配置/POM/Card/client 与 SDK 1.4 公告;确认关闭授权、广播与订阅 sleep 仍在,区分版本更新、发现选择和真实任务验收,不声称部署复现。