Agent 任务

任务表示一次可持续运行、可观察的 Agent 执行。发送消息后,调用方应保存 Task idcontextId,并根据 status.state 驱动界面与后续操作,而不是把 HTTP 请求结束视为任务结束。

Task 结构

Task 的字段会随 Agent 和 A2A 协议版本扩展。客户端通常需要关注:

{
  "kind": "task",
  "id": "task_123",
  "contextId": "ctx_123",
  "status": {
    "state": "working"
  },
  "artifacts": []
}

字段

客户端用途

id

查询、取消和重新订阅时使用的稳定 Task 标识

contextId

后续用户消息继续同一上下文时使用

status.state

当前生命周期状态

status.message

与本次状态相关的可选消息;按 Part 解析

artifacts

Agent 已产生的文本或结构化产物

history

Agent 提供时的消息或状态历史

常见状态包括 workinginput-requiredcompletedfailedcanceled。客户端必须容忍未知状态,不能把未识别的值当作成功。completedfailedcanceled 可作为终态;input-required 表示当前执行暂停等待用户,而不是失败。

查询 Task

使用 tasks/get 读取最新状态。params.id 是 Task ID,不是 JSON-RPC 请求 ID:

{
  "agent_code": "explore",
  "jsonrpc": "2.0",
  "id": "req_task_get_01",
  "method": "tasks/get",
  "params": {
    "id": "task_123"
  }
}

查询时必须使用创建该 Task 时相同的 Agent 选择器。Task ID 本身不负责把请求路由到正确的 Agent。

如果应用没有使用流式调用,可以在 Task 仍为非终态时轮询:

import time

while True:
    task = call_a2a("tasks/get", {"id": task_id})["result"]
    state = task["status"]["state"]
    if state in {"completed", "failed", "canceled"}:
        break
    if state == "input-required":
        show_requested_input(task)
        break
    time.sleep(2)

call_a2a 在此表示应用对 POST /newmoi/agents/a2a 的封装,并非当前官方 Python SDK 的方法。生产环境应增加整体截止时间、指数退避和随机抖动,避免大量 Task 以固定频率同时轮询。

取消 Task

tasks/cancel 请求服务端停止 Task:

{
  "agent_code": "explore",
  "jsonrpc": "2.0",
  "id": "req_task_cancel_01",
  "method": "tasks/cancel",
  "params": {
    "id": "task_123"
  }
}

取消是一次状态转换请求,不保证能撤销已经完成的外部副作用。收到取消响应后,应读取返回的 Task 状态;如果调用结果不明确,再用 tasks/get 核实。不要因为本地用户关闭页面就假定服务端 Task 已取消。

重新订阅事件

流式连接在 Task 到达终态前中断时,使用 tasks/resubscribe 恢复事件。它与查询、取消的参数名不同:

{
  "agent_code": "explore",
  "jsonrpc": "2.0",
  "id": "req_task_resubscribe_01",
  "method": "tasks/resubscribe",
  "params": {
    "taskId": "task_123",
    "afterSeq": 12
  }
}
  • taskId 指定要恢复的 Task。

  • afterSeq 是客户端已完整处理并持久化的最后一个 SSE id

  • 响应仍是 text/event-stream,解析方式与 message/stream相同。

只有在事件对应的本地状态已经持久化后,才能推进保存的序号。这样进程若在写入中途崩溃,可以安全地从较早序号重放。消费者还应按事件序号或业务事件标识去重,因为网络恢复可能带来重复投递。

如果从未收到有效事件,可以省略 afterSeq;具体保留时长由服务端配置决定,客户端不能假定 Task 事件会永久保留。重新订阅失败后,先用 tasks/get 确认最新 Task 状态,再决定提示用户、重新执行或人工排查。

处理 input-required

当状态变为 input-required 时,从状态消息或相关 Artifact 中读取 Agent 提出的问题,并在 UI 中明确展示可选项和上下文。用户作答后,发送一条新的普通 A2A 用户消息,并复用该 Task 的 contextId

{
  "kind": "message",
  "role": "user",
  "messageId": "msg_answer_01",
  "contextId": "ctx_123",
  "parts": [
    {
      "kind": "text",
      "text": "将结果保存到知识库"
    }
  ]
}

当前通用入口没有在本文档中承诺独立的“恢复 Task”SDK 方法。继续执行仍走 message/sendmessage/stream;不要自行构造未确认的输入提交方法。

持久化与运维建议

  • 将 Agent 选择器、Task idcontextId、状态、最后事件序号和业务记录关联保存。

  • 状态更新应满足幂等性;迟到或重复事件不能让终态回退到 working

  • 取消、失败和超时分别记录。客户端超时不代表服务端 Task 已失败。

  • Artifact 可能包含较大的结构化数据。按类型投影,限制日志大小,并遵守工作区的数据访问规则。

  • 排查时先核对 JSON-RPC id 与 Task id 是否混用,再检查 Agent 选择器、鉴权、最后事件序号和终态事件。

返回消息查看建流和 SSE 解析示例。