任务生命周期
任务的生命周期(Life of a Task)
在 Agent2Agent (A2A) 协议中,交互既可以是简单的无状态请求,也可以是复杂、长时间运行的流程。当服务端智能体收到客户端消息时,通常会以两种基本方式响应:
- 无状态
Message:立即完成、无需跟踪状态的交互。 - 有状态
Task:需要跟踪进度与状态的工作,可能分多步执行、需要补充输入,并在过程中产生工件(Artifacts)。
使用 contextId 关联一组交互
contextId 是服务端生成的标识,用于把一组相关的 Task 和 Message 组织在一起:
- 首次交互时,服务端会返回一个新的
contextId(如果创建任务,也会同时有taskId)。 - 后续请求可携带相同的
contextId来延续同一会话/目标。 - 也可以携带
taskId来继续 某一个具体任务。
这为多轮协作提供了基础,也方便客户端与服务端围绕同一目标串联多次调用。
何时用 Message,何时用 Task?
取决于工作性质:
- Message 适合轻量交互:澄清、协商、一次性问答、无需追踪的简单操作。
- Task 适合有状态工作:需要状态机、可重连/订阅、需要持续更新、或需要产出工件的场景。
有的智能体总是返回 Message;有的总是返回 Task(即便是简单回答也会建一个已完成任务);也有 混合型:先用 Message 协商范围,再创建 Task 执行。
任务的跟进与改写(Refinement)
客户端经常会在已有结果基础上进行改写/迭代。通常做法是在同一个 contextId 下发起新的交互,并通过 referenceTaskIds 引用之前的任务作为上下文提示。服务端可能返回新的 Task(更常见),也可能在可立即完成时返回 Message。
任务不可变性(Immutability)
任务一旦进入终态(例如 completed、canceled、rejected、failed)就不会“重启”。任何与之相关的后续交互都会在同一 contextId 下创建 新任务。这样可以获得:
- 清晰的工作单元划分
- 可追溯性更好
- 客户端编排更可预测
跟进场景示例
1)客户端请求生成一个工件:
{
"jsonrpc": "2.0",
"id": "req-001",
"method": "message.send",
"params": {
"message": {
"role": "user",
"messageId": "msg-user-001",
"parts": [
{ "kind": "text", "text": "Generate an image of a sailboat on the ocean." }
]
}
}
}2)服务端以“已完成任务 + 图片工件”的形式返回(示意):
{
"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",
"parts": [
{
"kind": "file",
"file": {
"name": "sailboat_image.png",
"mimeType": "image/png",
"bytes": "base64_encoded_png_data"
}
}
]
}
],
"kind": "task"
}
}3)客户端在同一 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." }
]
}
}
}服务端通常会创建一个新的任务(同一 context,新 taskId),并产生新版本工件。