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]
机制:状态存储、事件传播和执行恢复是三件事
- 状态面:两个 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 只能选路,不能替代任务状态协议。
最重要反例:默认值与有效配置相反
上游 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 版本分歧及未验证恢复边界。