A2A Python SDK v1.2.0 于 2026 年 9 月 29 日发布多副本集群模式:任意副本可接管别的副本的任务、推送通知补上 Authorization 头这条 MUST 级缺口,jsonrpc 与 gRPC 与 rest 三个绑定一致性测试首次全部零失败,前置部署 Forward Deployed Engineer(FDE)扩容 A2A 服务前先加 task_versions 与 task_events 两张表
A2A Python SDK 于 2026 年 9 月 29 日发布 v1.2.0,新增多副本集群模式,任意副本接管任务,并补上推送通知鉴权头 MUST 缺口。
这条版本更新直接落在「前置部署」Forward Deployed Engineer(FDE)的「多智能体编排」multi-agent orchestration 段:客户现场的智能体系统多数要部署在自有 Kubernetes 集群上,副本数由 HPA 动态伸缩,而在此之前 A2A 服务端把一个任务的状态只存在发起它的那个进程内存里,「前置部署」Forward Deployed Engineer(FDE)只能靠「会话粘性」session affinity 把同一个任务的请求钉回原副本,滚动更新一发生,正在跑的任务就跟着副本一起消失。
A2A 官方 Python SDK 仓库 a2aproject/a2a-python 于 2026-09-29T12:31:01Z 发布 v1.2.0,上一版 v1.1.5 的时间是 2026-09-21T09:51:54Z,中间隔了 8 天。
这次发布包含 12 个提交、改动 60 个文件,新增一整个 src/a2a/server/cluster/ 模块(含 cluster/__init__.py、task_store.py、event_stream.py、database_task_store.py、database_event_stream.py 五个文件),另加 samples/clustermode/ 下四个示例文件与一份 README,以及 tests/server/cluster/ 下九个测试文件。
官方 release notes 列出的新功能四项、缺陷修复五项、文档一项,但真正的分量在提交信息里:这批改动是围绕 A2A 官方「一致性测试」套件 ACTS 的一整摞补丁,目标是把 jsonrpc、gRPC、rest 三个绑定的 MUST 级失败清零。
第一条主线是新增多副本集群模式,这也是本版唯一一个需要改部署拓扑的改动。
改动前的实现:任务状态由「任务存储」保存,默认实现是内存版「任务存储」InMemoryTaskStore;事件流由运行该任务的进程独占,只有发起任务的那个副本能看到后续事件。结果是任何 follow-up 消息、取消请求、订阅请求都必须路由回原副本,客户端要在前面加一层「会话粘性」或者干脆只跑单副本。
改动后新增 {a2a.server.cluster} 模块,用两个关键接口把这个约束拆掉:一个是「版本化任务存储」VersionedTaskStore,一个是「共享事件流」TaskEventStream。官方描述是:不需要把任务的请求路由回启动它的副本,任意副本都能响应针对某个远程任务的请求。
「版本化任务存储」的工作方式是「乐观并发」:每次读取时记下这个任务当时的版本号 TaskVersion,写回时先检查任务在这一段时间内有没有被别的副本改过,如果改过就拒绝写入或重试,而不是安静地把另一个副本刚写的状态覆盖掉。抛出的异常类型是 ConcurrentTaskModificationError。
「共享事件流」的工作方式是把任务产生的事件集中记到一份共享记录里,这样即使本副本没有在跑这个任务的智能体,也能读到它的事件序列。直接收益是客户端可以重连到任意一个副本,继续收到正在别处运行的任务的更新。
取消也跨副本了:发起取消的那个副本把取消意图写进共享存储,真正运行智能体的那个副本在下一次写回时察觉到这个标记,然后停止。
重连订阅同样跨副本:接口先返回任务当前状态,再从这一刻起顺着「共享事件流」往下推,官方说法是客户端既看不到断点也看不到重复事件。
第二条主线是给这套机制配了数据库实现,这也是「前置部署」Forward Deployed Engineer(FDE)上手时最先要动的地方。
数据库版的两个实现叫 VersionedDatabaseTaskStore 与 DatabaseTaskEventStream,装法是 pip install a2a-sdk[sql],缺依赖时这两个类会在构造时报 ImportError 并提示这条命令,而不是静默降级。
表结构落在新增的 task_versions 与 task_events 两张表上,原来的 tasks 表完全没改。
task_versions 表四个字段:task_id 是 String(36) 主键、owner 可为空、version 是 BigInt 非空。
task_events 表五个字段:seq 是自增主键、task_id 是 String(36) 非空、owner 可为空、task_version 是 BigInt 非空、event_data 是 LargeBinary 非空;另有一个建在 task_id 上的索引 ix_task_events_task_id。seq 的类型在 SQLite 上会自动降为普通 INTEGER,因为 SQLite 的自增列只认 INTEGER,其他数据库用 BigInteger。
「迁移」脚本的 Alembic revision 是 b5e3d1c8a2f7,向上追溯的父版本是 38ce57e08137。这个 revision 只做建表,不改动任何已有表,所以向前升级不会破坏现存数据;表名可以在迁移配置里用 task_versions_table 与 task_events_table 两个选项改名。迁移依赖 a2a-sdk[db-cli] 这个额外依赖,没装时脚本会直接抛 ImportError。「回滚」路径是 downgrade:先删索引 ix_task_events_task_id 再删 task_events 表,然后删 task_versions 表。
这套机制是显式开启且向后兼容的,「前置部署」Forward Deployed Engineer(FDE)不改代码就不会被影响。
官方原话是:在没有事件流、并且用的是普通(非版本化)「任务存储」的情况下,既有行为保持不变。
构造参数上新增了一个可选的 event_stream 参数;task_store 这个参数现在既能接受普通存储也能接受版本化存储,并且对外仍然以 self.task_store 暴露原来的名字,所以老代码的引用不会断。
仓库还提供了一对可直接跑的示例命令。第一条启动三个副本,每个副本一个独立进程、共享同一个数据库:
python -m samples.clustermode.run_cluster --replicas 3 --base-port 41241
第二条是驱动器,它故意把同一个任务的消息打散到不同副本上:
python -m samples.clustermode.exercise --host 127.0.0.1 --ports 41241,41242,41243
示例默认用一份共享的 SQLite 文件,不依赖任何外部服务就能跑;把环境变量 A2A_CLUSTER_DSN 指向 PostgreSQL 或 MySQL,所有副本就会改用那个数据库;SQLite 文件路径可以用 A2A_CLUSTER_SQLITE 指定。每个副本的入口是 /a2a/jsonrpc。
测试证据方面,集成测试把两个 DefaultRequestHandlerV2 副本跑在同一个真实的 Postgres 或 MySQL 上、各自用独立的 engine;覆盖了「发送、重连订阅、取消这三件事都由另一个副本处理」的场景,以及一段多轮 input_required 会话在副本之间来回移动的场景。另有一条集成测试同时从两个副本给同一个任务发后续消息,断言两个轮次都不会丢。迁移测试覆盖了完整的 up 与 down 循环、「幂等」性和离线 SQL 生成。
第三条主线是一个 MUST 级安全缺口,它对已经在客户生产环境跑 A2A 的「前置部署」Forward Deployed Engineer(FDE)影响最直接:把 A2A 服务端接上企业自有 webhook 时,「推送通知」push notification 这一段就是安全验收项。
改动前:BasePushNotificationSender 从来没有读过 PushNotificationConfig.authentication 这个字段。它在整个生命周期里只发过一个鉴权相关头,就是来自相邻 token 字段的 X-A2A-Notification-Token。于是接收方拿不到任何可以比对的凭据,也就没有办法区分一条真正来自 A2A 服务端的推送和一条伪造的推送,而这恰恰是注册 authentication 字段的全部意义。
作者在 PR 描述里把根因说得很直白:构造函数文档字符串专门解释了为什么把「URL 白名单筛查」交给调用方,理由是规范把它定为 SHOULD;但对鉴权只字未提,而规范把它定为 MUST。代码已经在读紧挨着的那个 token 字段了,所以这更像一处遗漏而不是一个设计决定。
改动后:只要 PushNotificationConfig 里同时提供了 scheme 与 credentials,推送请求就会带上 Authorization: {scheme} {credentials} 头。
这里有一个刻意的边界:只有当两个字段都非空时才发。因为 AuthenticationInfo 是一个 message,配置可以只填 scheme 不填 credentials,那样下去会往网络上发一个写着 Bearer 后面什么都没有的头,看起来像凭据却不是凭据,比不发更糟。只填了 scheme 时服务端打一条日志说明没有发送 Authorization 头。
还有一点值得记住:这个实现里只有单数 scheme 一个字段。带复数 schemes 的只有 v0.3 兼容类型,conversions.py 在入口处就把它压成 schemes[0],所以发送端永远只面对一个值。
这条修复清掉 SEC-PUSH-001 与 SEC-PUSH-002 两条 MUST,以及 PUSH-DELIV-001 这条 MAY,而这三条此前在 nightly CI 里对所有 SDK 都是通过的。作者特意提醒这不是「修一个本来没坏的东西」:真正检查投递请求的那条断言是在 a2aproject/A2A 仓库的 82c277f 提交里新增的,语料库 CI 的时间早于它,老测试只验证了配置 API 能接受这份配置——用那句提交说明的原话,「一个把配置存下来然后匿名推送、或者干脆从不推送的服务端」也能通过。
第四条主线是「终端状态」的错误码变更,这条会打断现有客户端的分支判断。
改动前:向一个已经处于「终端状态」的任务发送消息,服务端抛的是 InvalidParamsError,对应 JSON-RPC 错误码 -32602。改动后:改为抛 UnsupportedOperationError,对应 -32004,语义等价于 gRPC 的 FAILED_PRECONDITION 与 HTTP 400。受影响的不只是发送消息,还有订阅任务,因为 v2 处理器的订阅路径没有自己的「终端状态」检查,它走的 get_or_create 最终会从 ActiveTask 里把错误抛出来。
取消操作保留了自己的错误类型:on_cancel_task 会捕获「终端状态」错误并重新抛 TaskNotCancelableError,这个重映射被保留下来了,只是改成按新的错误类型来匹配。客户端如果按 -32602 分支处理「任务已结束」,升级服务端后会走到默认分支,需要同步改。
第五条主线是 contextId 与 taskId 不匹配的校验被捞回来了。改动前:一条带着真实 taskId、但 contextId 属于别人的后续消息会被接受,返回 HTTP 200,客户端送来的 contextId 还会被写进这个任务的历史里,结果同一个任务内部出现两个互相矛盾的 context id。
根因是这条校验一直存在,位置在 RequestContext 的构建逻辑里,但它只在传进来的 task 不为 None 时才触发,而 v2 处理器为了规避并发读写问题刻意传了 task=None,于是这条检查在 v2 成为默认处理器之后变成死代码;老处理器照旧传 task,所以它仍然能拒绝。修复方式不是把 task=None 倒回去,而是把比对挪到 v2 本来就要取任务、用来证明任务存在的那一行之后,紧挨着它抛 TaskNotFoundError 的位置,因此不多一次读取,也不长期持有任务对象。
该校验只对「请求里点名了某个任务」的情况生效;只带 contextId 不带 taskId 属于在一个既有上下文里开新任务,仍然是合法的,这一点有测试锁住。
第六条主线是内存任务存储的时间戳排序缺陷,这条对做分页和多轮会话的「前置部署」Forward Deployed Engineer(FDE)直接可见。改动前:InMemoryTaskStore.list() 用 ToJsonString() 的结果做字典序比较,当小数秒精度不一致时,时间顺序会整个反过来。
作者给了一个可以复现的判断例子:2025-01-01T00:00:00.123Z 会错误地通过一个阈值为 2025-01-01T00:00:00.123000001Z 的过滤条件,尽管它比阈值早一个纳秒。同样的比较还会让分页顺序错乱。改动后改用 ToNanoseconds() 比较整数纳秒,过滤与排序都换掉,同时保留左闭右闭的过滤边界、按 ID 降序的并列打破规则,以及「没有时间戳的任务排在有时间戳的任务之后」这个位置约定。
这条修复新增了 15 个回归用例,覆盖秒、毫秒、微秒、纳秒、时间戳相等、跨秒边界、分页、ID 并列打破、时间戳缺失,以及 Unix 纪元之前和正好等于纪元的情况;修复前这 15 个用例里有 11 个失败、4 个通过,修复后内存任务存储的 35 个测试全部通过。作者同时给出了 Python 3.10 加 PostgreSQL 15 与 MySQL 8 的验证结果:2092 个测试通过、3 个 xfail、1 个 xpass,覆盖率 93.17%,脚手架构建后在六种依赖组合下跑通安装冒烟测试。
第七条主线是协议前向兼容。改动前两个分发器都按严格模式解析 protobuf,ignore_unknown_fields 保持默认的 False,于是客户端只要比服务端高一个小版本、多带一个服务端没听说过的字段,整个请求就被直接拒绝。改动后打开这个开关,四个调用点一处配置;v0.3 兼容处理器本来就是宽容的,这次是把 1.0 拉到同一水平。
这里有一个作者主动标出来请评审二次确认的加宽:ignore_unknown_fields 不只作用于字段名,protobuf 会在下一层套用同一条规则,接受一个未知的「枚举」值并把它归为 0。也就是说未来版本新增的 ROLE_SYSTEM 会以 ROLE_UNSPECIFIED 的身份到达服务端,而不是让请求失败。protobuf 没有提供只放宽字段、不放宽「枚举」的开关,所以这个副作用无法拆分,代码里在调用点写了注释、并用测试钉住了行为。
同一个 PR 还修了 error.data 的形状:规范要求它是一个对象数组、每个对象都必须带 @type 键,但解析失败分支走的是 pydantic 的 JSON-RPC 模型,它的 data 是 Any,于是原始的 ProtoJSON 诊断信息以裸字符串的形式发了出去。作者指出没有任何一致性测试能碰到这条路径——语料库构造不出一个 ProtoJSON 解析失败、还能回头检查 data 的请求,所以这是一条真实存在却不会被「一致性测试」发现的 MUST 违规。
第八条主线在 Agent Card 端点上,它为高频拉取元数据的部署省流量。
改动前:这个端点既不返回 Cache-Control 也不返回 ETag,客户端每次都把整张卡重新拉一遍,而且没有任何重新校验的手段,尽管规范自己就描述这张卡「相对客户端可能拉取的频率而言变化不频繁」。
改动后:ETag 总是会返回,值是对正在服务的那张卡做 json.dumps(sort_keys=True) 后取的哈希。官方解释为什么不用卡里的 version 字段:card_modifier 可以在不改 version 的情况下每次请求返回不同的卡,一个会悄悄失效的校验器比没有校验器更糟。
「条件请求」也被实现了:带上 If-None-Match 且命中时返回 304,比较按 RFC 9110 8.2.2 的「弱比较」规则来做,所以 W/"x" 能匹配 "x",星号能匹配,也接受列表。只发校验器却不理会用它的头,等于客户端照样重下整张卡,那样 8.6 这一节就白写了。
Cache-Control 则是显式开启的:create_agent_card_routes 新增了一个 cache_control 参数,而且没有默认值。理由是规范的 8.6.1 把 max-age 和「智能体预期的更新频率」绑定,这件事 SDK 无法知道——每次部署都重新生成的卡和一个几个月不变的卡需要的数字差很远,把猜错的数字烧进库里会得到一个没人解释得清为什么会过期的卡。ETag 无论开不开都会发,所以不配置也已经满足 8.6.1 的二选一要求,重新校验只花一个 304。
第九条主线是 rest 绑定的内容类型,这条的结果值得单独说一句。
改动前:所有 REST 响应都继承了 Starlette 的默认媒体类型 application/json。改动后成功响应改用 application/a2a+json;官方特别说明,在它之前四个有一致性测试指标公开的 SDK(python、go、java、rust)全部在 REST-CT-001 上失败,a2a-python 是第一个通过的。
这条改动有两个刻意留下的边界:只作用于成功响应,错误路径走的是 rest_error_handler,而这个处理器被兼容层的适配器共用,改动它会动到另一个协议版本的线上格式,所以 1.0 的错误响应仍然是 application/json;只作用于响应,不作用于请求,因为改变客户端发什么会撞上那些只接受 application/json 的服务端,而且没有测试覆盖这条路径。「前置部署」Forward Deployed Engineer(FDE)如果在客户侧自建了 REST 网关并做内容类型白名单,这条改动要同步把 application/a2a+json 放行,否则升级后成功响应会被网关挡掉。
第十条主线是一个新的可选校验开关,默认关闭。
改动前:ContentTypeNotSupportedError 这个错误类型早就已经接线完毕,错误码 -32005、gRPC 的 INVALID_ARGUMENT、HTTP 400 都齐了,但服务端从来不读 AgentCard.default_input_modes,所以一个没被声明的 mediaType 永远被接受。
改动后:DefaultRequestHandlerV2 新增 validate_input_modes 参数,默认值是 False,打开后消息里 mediaType 不在卡片声明范围内的部件会被拒绝。为什么默认关闭,作者给的理由是硬上会把今天能过的流量挡掉:仓库里有四张卡声明的「输入模式」和它的客户端互相矛盾,其中 samples/hello_world_agent.py 与 samples/agent_card_signing.py 声明的是 'text',而 'text' 根本不是媒体类型,一个发 text/plain 的客户端会被拒。同样的改动在 a2a-js 的 #745 里也落地了,结论一致。
两个豁免要记住:卡片完全没有声明「输入模式」时,等于没有声明而不是空白名单,此时接受任何类型;消息部件本身没有 media_type 时不检查,因为 protobuf 里缺失的字符串会以空串到达,只有客户端真的写出来的媒体类型才可能与卡片冲突。
「前置部署」Forward Deployed Engineer(FDE)打开这个开关之前,必须把自己所有卡片的 default_input_modes 盘一遍,否则会拒掉今天正常跑的流量。
第十一项是新增一份可运行的 Agent Card「签名」示例,它把两个容易踩的坑写在了注释里。
samples/agent_card_signing.py 的服务端生成一把 ES256 密钥、用 create_agent_card_signer 签出卡片,通过 create_agent_card_routes(card_modifier=...) 提供服务,并把公钥以 JWKS 文档的形式发布在 /.well-known/jwks.json,这个地址正是签名里 jku 指向的位置;客户端用 create_signature_verifier 校卡。
第一个坑:校验器是同步可调用对象,如果在它内部用同步 HTTP 客户端去取 JWKS,会阻塞事件循环,撞上进程内服务端时会死锁、最后报出「没有找到有效签名」,看起来像签名错了而不是网络错了。正确做法是异步预取 JWKS,校验器只做纯内存按 kid 查找。
第二个坑:create_agent_card_signer 会就地修改它签名的那张卡,所以把签名放进按请求执行的 card_modifier 会让签名不断累积;示例的做法是先深拷贝、再在启动时只签一次。
把结果汇成数字:v1.2.0 之后 jsonrpc 绑定 101/111 通过、MUST 59/59 全过,gRPC 绑定 88/111 通过、MUST 47/47 全过,rest 绑定 92/111 通过、MUST 49/49 全过,三个绑定的失败数都是 0,剩下的全部是测试夹具前置条件不满足而跳过的用例。
作为对照,第四条主线里的「终端状态」错误码修复一条就清掉了 21 个失败 MUST 测试实例中的 9 个:jsonrpc 从 52/59 提到 55/59,gRPC 从 40/47 提到 43/47,rest 从 42/49 提到 45/49。
作者自己承认的局限与没有覆盖的场景,「前置部署」Forward Deployed Engineer(FDE)做升级评审时要一并看。
第一,多副本模式是显式开启的,不用版本化「任务存储」和「共享事件流」就仍然是单副本语义;这次发布没有把默认存储换掉,也就是说升级本身不会自动获得多副本能力。
第二,数据库版的两个类分别需要 a2a-sdk[sql] 与 a2a-sdk[db-cli] 两个额外依赖,缺依赖时报错而不是降级,隔离环境里装包要先确认这两个 extras 能进得来。
第三,示例默认跑的是共享 SQLite 文件,这只是为了让示例不依赖外部服务,不是生产拓扑;真实多副本要把 A2A_CLUSTER_DSN 指向 PostgreSQL 或 MySQL,这一点官方示例本身没有替你兜底。
第四,这次发布没有给多副本模式任何量化指标:既没有故障转移耗时、也没有跨副本吞吐或延迟数字,也没有提到任何选主或协调组件的设计,所以「多副本到底能在多快接管」这件事无法从这次发布里得出结论。
第五,「推送通知」鉴权这条修复缺少跨 SDK 交叉验证——作者说明除本 SDK 之外没有别的 SDK 被这条真实断言测过,而且它清掉的三条测试此前在 nightly CI 里是绿的,所以判断依据来自代码本身,即改动前 push_info.authentication 在 src/a2a/server/ 下没有任何出现。
第六,ignore_unknown_fields 带来的「枚举」值静默归零是一个无法拆分的副作用,作者自己标了请评审二次确认,放宽字段带来的兼容性收益和放宽「枚举」带来的语义漂移是绑在一起的。
第七,error.data 那条 MUST 违规没有任何一致性测试覆盖,也就是说这类缺口不会被测试套件兜住,只能靠人读规范发现。
第八,application/a2a+json 只落在成功响应上,错误响应仍是 application/json;请求侧完全没有改,所以只认 application/json 的服务端不会被这次发布暴露出来,但对同一批服务端发出 a2a+json 的客户端行为变化也没有测试覆盖。
第九,「输入模式」校验默认关闭,仓库里就有四张卡声明与客户端矛盾,打开这个开关之前必须先把自己所有卡的 default_input_modes 盘一遍,否则会拒掉今天的正常流量。
谁该行动、具体怎么做,按下面六步走。
第一步,先判断该不该立刻升:只在单副本上跑 A2A、也不打算做多副本的团队,这次升级的主要收益是错误码语义修正与「推送通知」鉴权,属于安全相关,值得排进升级窗口;正在客户 Kubernetes 上跑多副本 A2A、靠会话粘性维生的「前置部署」Forward Deployed Engineer(FDE),这次升级是拿掉粘性配置的前提条件,应该优先做。
第二步,升级前先扫描自己的客户端代码里有没有按 -32602 判断「任务已结束」的分支,这类分支要改成按 -32004 / FAILED_PRECONDITION / HTTP 400 处理;同时检查有没有子类化 BasePushNotificationSender、在 _dispatch_notification 里自己注入 Authorization 头来绕过 issue #585 的 workaround,有的话要在升级时删掉这个覆写,否则可能出现两个 Authorization 头。
第三步,要用多副本就按这个顺序落地:装 pip install a2a-sdk[sql] 与 pip install a2a-sdk[db-cli],先跑 Alembic 的 b5e3d1c8a2f7 这个 revision 建出 task_versions 与 task_events 两张表,确认任务表没有被改动;再把 task_store 换成 VersionedDatabaseTaskStore、把新增的 event_stream 参数指向 DatabaseTaskEventStream,两者共用同一个数据库。
第四步,上生产之前用官方示例在本地把行为看一遍:先 python -m samples.clustermode.run_cluster --replicas 3 --base-port 41241 起三个副本,再用 python -m samples.clustermode.exercise --host 127.0.0.1 --ports 41241,41242,41243 把同一个任务的消息打散到三个副本上,观察「在副本 A 发起、在副本 B 订阅、在副本 C 取消」这条链路是否都成立。示例默认走共享 SQLite,生产验收必须换成 A2A_CLUSTER_DSN 指向 PostgreSQL 或 MySQL 之后再跑一遍。
第五步,顺手开两个低风险开关:给 create_agent_card_routes 传上 cache_control,数值取自己卡的预期更新频率;以及把服务端卡片里的 default_input_modes 改写成真正的媒体类型(text/plain 而不是 text)之后,再打开 validate_input_modes,把不合规的输入挡在智能体启动之前。
第六步,留好回滚路径:数据库侧的回滚就是执行那个 revision 的 downgrade,它会先删索引 ix_task_events_task_id、再删 task_events 表,然后删 task_versions 表,原来的 tasks 表不受影响,所以退回单副本等于把 task_store 换回普通存储、删掉 event_stream 参数,然后跑 downgrade;代码侧的回滚是回退到 v1.1.5,但要注意此时客户端已经改成按 -32004 判断、服务端又退回抛 -32602,两边的错误码约定必须成对回退。「前置部署」Forward Deployed Engineer(FDE)在客户生产环境做这类回退时,要同时改服务端与客户端,不能只回一边。
适用条件只有三条:服务端要跑在 A2A 1.0 绑定上(v0.3 兼容层不在这次改动的覆盖范围里);多副本落地的前提是能装 a2a-sdk[sql] 与 a2a-sdk[db-cli] 并能连到 PostgreSQL 或 MySQL;打开「输入模式」校验的前提是所有卡片都声明了真实媒体类型。三条里缺任何一条,都不要把这次升级写进交付计划。
最后一条判断规则:这次发布把三个绑定的 MUST 清零,但 MUST 清零只代表它满足规范,不代表它在你的拓扑里已经跑对。「前置部署」Forward Deployed Engineer(FDE)该做的是用示例的三个副本验收跨副本的发送、订阅与取消,把「任务不会因为滚动更新而丢」写成可观测的验收条件,而不是看到「一致性测试」绿灯就把多副本直接切到生产。
原始来源:
- https://github.com/a2aproject/a2a-python/releases/tag/v1.2.0
- https://github.com/a2aproject/a2a-python/pull/1281
- https://github.com/a2aproject/a2a-python/pull/1271
- https://github.com/a2aproject/a2a-python/issues/585
- https://github.com/a2aproject/a2a-python/pull/1268
- https://github.com/a2aproject/a2a-python/pull/1270
- https://github.com/a2aproject/a2a-python/pull/1272
- https://github.com/a2aproject/a2a-python/pull/1273
- https://github.com/a2aproject/a2a-python/pull/1274
- https://github.com/a2aproject/a2a-python/pull/1269
- https://github.com/a2aproject/a2a-python/pull/1233
- https://github.com/a2aproject/a2a-python/pull/1198
评论(0)
- 暂无评论。