任务的生命周期

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"
            }
          ]
        }
      ]
    }
  }
}

进一步阅读

来源:A2A 官方文档 · Life of a Task