Task、Message、Part 与状态机

A2A 有三个核心数据结构——Message(消息)、Part(内容块)、Task(任务)

Task(任务:有状态、有生命周期)
 ├── status: 当前状态(submitted/working/completed/...)
 ├── history: Message 列表(对话历史)
 │     └── Message(一轮发言)
 │           ├── role: ROLE_USER(客户端)或 ROLE_AGENT(服务端)
 │           ├── messageId / taskId / contextId
 │           └── parts: Part 列表(具体内容)
 │                 └── Part(最小内容单元)
 │                       ├── text  —— 纯文本        (TextPart)
 │                       ├── raw / url + mediaType  (FilePart:
 │                       │    文件字节或文件链接)
 │                       └── data  —— 结构化 JSON   (DataPart)
 └── artifacts: 任务产物(见 05 篇)
  • Part 是最小单元,三种形态:
    • TextPart:{"text": "帮我查天气"} —— 最常用;
    • FilePart:内联字节(raw,base64)或文件链接(url), 配 mediaType(如 image/png)和 filename;
    • DataPart:{"data": {...}} —— 结构化 JSON,适合表单、参数、机器可读结果。
  • Message 是一轮通信,带角色;一条消息可混合多种 Part (比如一段文字 + 一张图 + 一个 JSON 表单)。
  • Task 是有状态的工作单元,由 Server 创建并分配唯一 id; Client 之后可凭 id 查询、取消、追加消息。
  • contextId 把多个相关 Task 串成一个"会话”(类似群聊的 thread)。

2. 发起一个任务:SendMessage

Client 向 Server 发送第一条消息,Server 二选一地回应:

  • 直接回一条 Message:适合一问一答的简单场景,无任务管理开销;
  • 回一个 Task 对象:适合需要跟踪进度/长时间运行的工作。

示例 JSON-RPC 请求/响应:

// POST /  (HTTP 头需带 A2A-Version: 1.0)
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "msg-001",
      "role": "ROLE_USER",
      "parts": [{ "text": "roll 2d6" }]
    }
  }
}
// 响应:Server 创建并完成了任务
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "task": {
      "id": "4b22460b-8ea6-4989-adf4-0383f48a6faf",
      "contextId": "496b80c4-d8ae-4364-adfb-858f19211370",
      "status": { "state": "TASK_STATE_COMPLETED",
                  "timestamp": "2026-11-12T10:49:58.838213Z" },
      "artifacts": [{
        "artifactId": "2f3a398c-...",
        "name": "dice-result",
        "parts": [{ "text": "掷 2 个 6 面骰子: [4, 4],总和 = 8" }]
      }],
      "history": [{ "messageId": "msg-001", "role": "ROLE_USER",
                    "parts": [{ "text": "roll 2d6" }] }]
    }
  }
}

3. 任务状态机

Task 的 status.state 按以下状态机流转(v1.0 共 8 个有效状态):

                    ┌─────────────┐
     Client 发消息  │  submitted  │ 已受理,排队中
        ──────────> └──────┬──────┘
                           ▼
                    ┌─────────────┐
            ┌──────►│   working   │ Agent 处理中
            │       └──┬───┬───┬──┘
  Client 补 │          │   │   │
  充了输入   │          │   │   └───────────────┐
            │          │   │                   │
     ┌──────┴───────┐  │   │            ┌──────▼──────┐
     │input-required│◄─┘   │            │auth-required│ 需要额外认证
     │ 等用户补充    │       │            └─────────────┘
     └──────────────┘      │
                           ▼
        ┌───────────┬──────────────┬───────────┬──────────┐
        ▼           ▼              ▼           ▼          ▼
   ┌─────────┐ ┌────────┐    ┌─────────┐ ┌────────┐ ┌────────┐
   │completed│ │ failed │    │canceled │ │rejected│ │(error) │
   └─────────┘ └────────┘    └─────────┘ └────────┘ └────────┘
        └──────────────── 终态(不再接受新消息)────────────┘
状态 含义 终态?
submitted 已受理,尚未开始 否
working 正在处理 否
input-required 暂停,等 Client 补充信息(见 06 篇) 否(可恢复)
auth-required 暂停,需要额外的认证/授权 否(可恢复)
completed 成功完成,产物在 artifacts ✅
failed 处理出错 ✅
canceled 被 Client 主动取消(CancelTask) ✅
rejected Server 拒绝接单 ✅

两条重要规则:

  1. 终态任务不可复活:向 completed/failed/canceled/rejected 的任务再发消息 会收到错误;要继续聊,带同一个 contextId 开新任务。
  2. 状态只由 Server 变更:Client 只能发消息/取消,不能直接改状态。

示例:用 SDK 构造消息(Python)

import uuid
from a2a.types import Message, Part, Role, SendMessageRequest

request = SendMessageRequest(
    message=Message(
        message_id=str(uuid.uuid4()),
        role=Role.ROLE_USER,
        parts=[
            Part(text="帮我分析这份数据"),                 # TextPart
            Part(data={"quarter": "Q3", "metric": "营收"}),  # DataPart
        ],
    )
)