任务的生命周期
A2A 协议中的交互形态差异很大:从简单的无状态问答,到复杂的长时间运行流程。智能体收到客户端发来的消息后,有两种响应方式:
- 返回无状态的
Message:适用于即时、自包含的交互,完成后没有需要继续管理的状态。 - 发起有状态的
Task:智能体让Task经历明确的生命周期,按需汇报进度、请求输入,直到进入中断状态(如TASK_STATE_INPUT_REQUIRED、TASK_STATE_AUTH_REQUIRED)或终态(如TASK_STATE_COMPLETED、TASK_STATE_CANCELED、TASK_STATE_REJECTED、TASK_STATE_FAILED)。
任务状态
| 状态 | 类型 | 含义 |
|---|---|---|
TASK_STATE_SUBMITTED | 活跃 | 任务已提交 |
TASK_STATE_WORKING | 活跃 | 智能体正在处理 |
TASK_STATE_INPUT_REQUIRED | 中断 | 需要客户端补充输入 |
TASK_STATE_AUTH_REQUIRED | 中断 | 需要额外的认证 |
TASK_STATE_COMPLETED | 终态 | 成功完成 |
TASK_STATE_FAILED | 终态 | 执行失败 |
TASK_STATE_CANCELED | 终态 | 已取消 |
TASK_STATE_REJECTED | 终态 | 智能体拒绝执行 |
状态值的完整定义见协议规范 。
用 contextId 关联交互
contextId 是把多个 Task 和独立 Message 归为一组的关键标识,用来在一系列交互之间保持连续性。
- 客户端首次发送消息时,智能体返回新的
contextId;如果发起了任务,还会有taskId。 - 要延续之前的交互,客户端在后续消息中携带相同的
contextId。 - 客户端还可以在后续消息中附带
taskId,表明这条消息是对该特定任务的延续。
contextId 让多个任务围绕共同目标协作,或在若干可能并行的任务之间共享会话。在内部,A2A 智能体(尤其是基于 LLM 的)用 contextId 来管理对话状态或 LLM 上下文。
智能体响应:Message 还是 Task
选择返回 Message 还是 Task,取决于交互性质和智能体能力:
Message用于简单交互:事务性的交互无需长时间处理或复杂状态。智能体也可以先用消息就任务范围达成一致,再创建Task。Task用于有状态交互:智能体把收到的消息映射到某项能力;如果该能力需要在较长时间内完成可跟踪的实质性工作,就返回Task。
从复杂度看,智能体大致分为三类:
- 仅消息型智能体:总是返回
Message,通常不管理复杂状态或长时间执行,用contextId串联消息。这类智能体往往直接封装 LLM 调用和简单工具。 - 任务生成型智能体:总是返回
Task,即使是普通回答也建模为已完成的任务。任务一旦创建,智能体就只返回Task;任务完成后,不能再向其发送消息。这省去了"Task 还是 Message"的判断,但简单交互也会产生已完成的任务对象。 - 混合型智能体:同时生成
Message和Task。先用消息协商能力与工作范围,再创建Task跟踪执行并管理input-required、错误处理等状态。也就是说,Message承载确定"要做什么"的轻量往返,一旦有了需要执行和观察的确定工作,就创建Task。与任务生成型智能体一样,任务创建后,对后续消息只返回Task;任务完成后,不能再向其发送消息。
关于何时使用消息、何时使用任务的更多讨论,可参考官方社区文章 A2A protocol: Demystifying Tasks vs Messages 。
任务细化
客户端经常需要基于任务结果发出新请求,或细化之前任务的输出。做法是使用与原任务相同的 contextId 发起新的交互,并在 Message 中通过 referenceTaskIds 引用原任务,向智能体提供线索。智能体随后返回新的 Task 或 Message。
任务不可变性
任务一旦进入终态(completed、canceled、rejected 或 failed),就不能重启。与该任务相关的任何后续交互(如细化)都必须在同一个 contextId 内发起新任务。这样做有几个好处:
- 任务不可变:客户端可以可靠地引用一个任务及其状态、产出物和消息,输入与输出一一对应,便于编排与追溯。
- 清晰的工作单元:每个新请求、细化或跟进都是独立的任务,简化记账,支持细粒度跟踪,并能把每个产出物追溯到具体的工作单元。
- 实现更简单:智能体开发者无需再纠结"新建任务还是重启已有任务"。
并行跟进
A2A 支持并行工作:智能体可以为同一 contextId 内的每条跟进消息创建相互独立的并行任务。客户端可以逐个跟踪任务,并在前置任务完成后立刻创建依赖它的新任务。例如:
- 任务 1:预订飞往赫尔辛基的机票
- 任务 2:基于任务 1,预订酒店
- 任务 3:基于任务 1,预订雪地摩托活动
- 任务 4:基于任务 2,在酒店预订上追加水疗
引用之前的产出物
服务端智能体会根据被引用的任务或 contextId 推断相关的产出物。作为领域专家,它最适合消除歧义或发现信息缺失。遇到歧义时,智能体返回 input-required 状态向客户端澄清;客户端在回复中指明目标产出物,还可以在 Part 的 metadata 中添加产出物引用(artifactId、taskId)。
跟踪产出物的演变
跟进或细化任务通常会基于旧产出物生成新产出物。需要跟踪这些变化,让后续交互只使用最新版本,就像版本历史一样,每个新产出物都链接到前一个。
客户端最适合管理这种关联:由客户端决定什么算可接受的结果、是否接受新版本。因此服务端智能体不必跟踪产出物的演变,这种关联也不属于 A2A 协议规范。客户端应在本地维护版本历史,并向用户展示最新的可接受版本。
为便于客户端跟踪,服务端智能体在生成既有产出物的细化版本时,应复用一致的产出物名称(name)。
对于跟进或细化任务,客户端应明确指出要细化的产出物,理想情况下是它认为"最新"的版本。如果客户端没有提供产出物引用,服务端智能体可以:
- 尝试根据当前
contextId推断目标产出物; - 如果存在歧义或上下文不足,返回
input-required任务状态,向客户端请求澄清。
跟进示例
下面的示例展示一个典型的带跟进的任务流程(JSON-RPC 绑定)。
1. 客户端向智能体发送消息:
{
"jsonrpc": "2.0",
"id": "req-001",
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [
{
"text": "Generate an image of a sailboat on the ocean."
}
],
"messageId": "msg-user-001"
}
}
}2. 智能体返回一张帆船图片(已完成的任务):
{
"jsonrpc": "2.0",
"id": "req-001",
"result": {
"task": {
"id": "task-boat-gen-123",
"contextId": "ctx-conversation-abc",
"status": {
"state": "TASK_STATE_COMPLETED"
},
"artifacts": [
{
"artifactId": "artifact-boat-v1-xyz",
"name": "sailboat_image.png",
"description": "A generated image of a sailboat on the ocean.",
"parts": [
{
"filename": "sailboat_image.png",
"mediaType": "image/png",
"raw": "base64_encoded_png_data_of_a_sailboat"
}
]
}
]
}
}
}3. 客户端要求把船涂成红色。 这个细化请求引用之前的 taskId,并使用相同的 contextId:
{
"jsonrpc": "2.0",
"id": "req-002",
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"messageId": "msg-user-002",
"contextId": "ctx-conversation-abc",
"referenceTaskIds": [
"task-boat-gen-123"
],
"parts": [
{
"text": "Please modify the sailboat to be red."
}
]
}
}
}4. 智能体返回新的图片产出物(新任务、同一上下文、同名产出物): 智能体在同一 contextId 内创建新任务,新的帆船图片产出物沿用同一名称,但有新的 artifactId。
{
"jsonrpc": "2.0",
"id": "req-002",
"result": {
"task": {
"id": "task-boat-color-456",
"contextId": "ctx-conversation-abc",
"status": {
"state": "TASK_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": [
{
"filename": "sailboat_image.png",
"mediaType": "image/png",
"raw": "base64_encoded_png_data_of_a_RED_sailboat"
}
]
}
]
}
}
}