任务的生命周期¶
在 Agent2Agent (A2A) 协议中,交互可以从简单的无状态交换到复杂的长时间运行过程不等。当智能体从客户端接收到消息时,它可以以两种基本方式之一进行响应:
- 使用无状态
Message响应:这种类型的响应通常用于即时的、自包含的交互,这些交互在不需要进一步状态管理的情况下结束。 - 启动有状态
Task:如果响应是Task,智能体将通过定义的生命周期处理它,传达进度并根据需要请求输入,直到达到中断状态(例如,input-required、auth-required)或终止状态(例如,completed、canceled、rejected、failed)。
分组相关交互¶
contextId 是一个关键标识符,用于逻辑上分组多个 Task 对象和独立的 Message 对象,在一系列交互中提供连续性。
- 当客户端首次发送消息时,智能体响应一个新的
contextId。如果启动了任务,它还将具有taskId。 - 客户端可以发送后续消息并包含相同的
contextId,以表示它们在同一上下文中继续之前的交互。 - 客户端可选择将
taskId附加到后续消息中,以表示该消息继续特定任务。
contextId 使多个潜在并发任务之间朝着共同目标或共享上下文会话的协作成为可能。在内部,A2A 智能体(特别是使用 LLM 的智能体)使用 contextId 来管理其内部对话状态或 LLM 上下文。
智能体响应:消息或任务¶
选择使用 Message 还是 Task 进行响应取决于交互的性质和智能体的能力:
- 用于简单交互的消息:
Message对象适用于不需要长时间运行处理或复杂状态管理的事务性交互。智能体可能在提交到Task对象之前使用消息来协商任务的接受或范围。 - 用于有状态交互的任务:一旦智能体将传入消息的意图映射到需要大量、可跟踪工作的受支持能力上,智能体就会使用
Task对象进行响应。
从概念上讲,智能体在不同复杂性级别上运行:
- 仅消息智能体:总是使用
Message对象进行响应。它们通常不管理复杂状态或长时间运行的执行,而是使用contextId将消息连接在一起。这些智能体可能直接包装 LLM 调用和简单工具。 - 任务生成智能体:总是使用
Task对象进行响应,即使是响应,也会将其建模为已完成的任务。一旦创建任务,智能体将只在响应发送的消息时返回Task对象,一旦任务完成,就不再发送消息。这种方法避免了在Task与Message之间做出决定,但会为甚至简单的交互创建已完成的任务对象。 - 混合智能体:生成
Message和Task对象。这些智能体使用消息来协商智能体能力和任务的工作范围,然后发送Task对象来跟踪执行和管理input-required或错误处理等状态。一旦创建任务,智能体将只在响应发送的消息时返回Task对象,一旦任务完成,就不再发送消息。混合智能体使用消息来协商任务的范围,然后生成任务来跟踪其执行。 有关混合智能体的更多信息,请参见 A2A 协议:任务与消息解密。
任务优化¶
客户端通常需要基于任务结果发送新请求或优化先前任务的输出。这通过使用与原始任务相同的 contextId 开始另一次交互来建模。客户端通过在 Message 对象中使用 referenceTaskIds 提供对原始任务的引用来进一步提示智能体。然后智能体使用新 Task 或 Message 进行响应。
任务不可变性¶
一旦任务达到终止状态(completed、canceled、rejected 或 failed),它就无法重新启动。与该任务相关的任何后续交互(如优化)都必须在同一 contextId 内启动新任务。这一原则提供了几个好处:
- 任务不可变性。 客户端可以可靠地引用任务及其关联状态、工件和消息,提供输入到输出的清晰映射。这对编排和可追溯性很有价值。
- 明确的工作单元。 每个新请求、优化或后续操作都成为不同的任务。这简化了簿记,允许对智能体的工作进行细粒度跟踪,并能够将每个工件追溯到特定的工作单元。
- 更简单的实现。 这消除了智能体开发者关于是创建新任务还是重新启动现有任务的歧义。
并行后续操作¶
A2A 通过使智能体能够为在同一 contextId 内发送的每个后续消息创建不同的并行任务来支持并行工作。这允许客户端跟踪单个任务,并在先决任务完成后立即创建新的依赖任务。
例如:
- 任务 1:预订飞往赫尔辛基的航班。
- 任务 2:基于任务 1,预订酒店。
- 任务 3:基于任务 1,预订雪地摩托活动。
- 任务 4:基于任务 2,在酒店预订中添加水疗预约。
引用先前工件¶
服务智能体从引用的任务或 contextId 中推断相关工件。作为领域专家,服务智能体最适合解决歧义或识别缺失信息。如果存在歧义,智能体会通过返回 input-required 状态要求客户端澄清。然后客户端在其响应中指定工件,可选择在 Part 元数据中填充工件引用(artifactId、taskId)。
跟踪工件变更¶
后续或优化任务通常会导致基于旧工件创建新工件。跟踪这些变更是重要的,以确保在后续交互中只使用工件的最新版本。这可以概念化为版本历史,其中每个新工件都链接到其前身。
然而,客户端处于管理此工件链接的最佳位置。客户端确定什么构成可接受的结果,并有能力接受或拒绝新版本。因此,服务智能体不应负责跟踪工件变更,此链接也不是 A2A 协议规范的一部分。客户端应在本地维护此版本历史并向用户呈现最新的可接受版本。
为了便于客户端跟踪,服务智能体在生成现有工件的优化版本时应使用一致的 artifact-name。
在启动后续或优化任务时,客户端应明确引用他们打算优化的特定工件,理想情况下是从他们的角度来看的"最新"版本。如果未提供工件引用,服务智能体可以:
- 尝试基于当前
contextId推断预期工件。 - 如果存在歧义或上下文不足,智能体应使用
input-required任务状态进行响应,以请求客户端澄清。
后续操作示例场景¶
以下示例说明了带有后续操作的典型任务流程:
-
客户端向智能体发送消息:
-
智能体使用船图像进行响应(已完成的任务):
{ "jsonrpc": "2.0", "id": "req-001", "result": { "id": "task-boat-gen-123", "contextId": "ctx-conversation-abc", "status": { "state": "completed" }, "artifacts": [ { "artifactId": "artifact-boat-v1-xyz", "name": "sailboat_image.png", "description": "A generated image of a sailboat on the ocean.", "parts": [ { "kind": "file", "file": { "name": "sailboat_image.png", "mimeType": "image/png", "bytes": "base64_encoded_png_data_of_a_sailboat" } } ] } ], "kind": "task" } } -
客户端要求将船涂成红色。此优化请求引用了之前的
taskId并使用了相同的contextId。{ "jsonrpc": "2.0", "id": "req-002", "method": "message.send", "params": { "message": { "role": "user", "messageId": "msg-user-002", "contextId": "ctx-conversation-abc", "referenceTaskIds": [ "task-boat-gen-123" ], "parts": [ { "kind": "text", "text": "Please modify the sailboat to be red." } ] } } } -
智能体使用新的图像工件进行响应(新任务,相同上下文,更新的工件名称):智能体在同一
contextId内创建新任务。新的船图像工件保留相同名称但具有新的artifactId。{ "jsonrpc": "2.0", "id": "req-002", "result": { "id": "task-boat-color-456", "contextId": "ctx-conversation-abc", "status": { "state": "completed" }, "artifacts": [ { "artifactId": "artifact-boat-v2-red-pqr", "name": "sailboat_image.png", "description": "A generated image of a red sailboat on the ocean.", "parts": [ { "kind": "file", "file": { "name": "sailboat_image.png", "mimeType": "image/png", "bytes": "base64_encoded_png_data_of_a_RED_sailboat" } } ] } ], "kind": "task" } }