# 异步任务

> LMSP 如何把长时间的生成作为持久化任务来运行，包括创建任务、任务状态机、幂等取消、事件流，以及未完成任务的重启策略。

Source: https://madeforagent.com/zh-cn/lmsp/1.0/tasks/
Language: zh-CN
Specification: LMSP 1.0 (draft)
Updated: 2026-11-08

## 7.1 创建任务 {#create}

客户端通过发送它想要运行的响应请求来创建任务，请求要包在任务信封中。建议携带 `Idempotency-Key` 请求头（[第 9.2 节](https://madeforagent.com/zh-cn/lmsp/1.0/common-rules.md#idempotency)）。

```http
POST /lmsp/v1/tasks
Authorization: Bearer <token>
Idempotency-Key: 01J8M7C4Y3N5P8R2T6V9X1Z4BH
```

```json
{
  "type": "response",
  "input": {
    "model": "qwen3:14b-q4_k_m",
    "input": [
      {
        "type": "message",
        "role": "user",
        "content": [
          {
            "type": "input_text",
            "text": "Write a long report."
          }
        ]
      }
    ]
  },
  "on_restart": "fail"
}
```

| 字段 | 必需 | 含义 |
| --- | --- | --- |
| `type` | 是 | `response`，1.0 中唯一的任务类型。 |
| `input` | 是 | 一个响应请求，与 `POST /lmsp/v1/responses` 的请求相同。其中的 `stream` 字段会被忽略。 |
| `on_restart` | 否 | `fail`（默认）、`retry` 或 `resume`。参见[第 7.5 节](https://madeforagent.com/zh-cn/lmsp/1.0/tasks.md#restart)。 |

服务**必须**先持久化任务，再作出应答。随后返回：

```http
HTTP/1.1 202 Accepted
```

```json
{
  "id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC",
  "object": "task",
  "type": "response",
  "status": "queued",
  "created_at": 1794200100,
  "updated_at": 1794200100,
  "response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
  "events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events",
  "on_restart": "fail",
  "response": null,
  "error": null
}
```

| 任务字段 | 含义 |
| --- | --- |
| `id` | 任务标识符，例如 `task_...`。 |
| `object` | 始终为 `"task"`。 |
| `type` | `response`。 |
| `status` | 当前状态（[第 7.2 节](https://madeforagent.com/zh-cn/lmsp/1.0/tasks.md#states)）。 |
| `created_at`、`updated_at` | 时间，自 Unix 纪元起的秒数。 |
| `response_id` | 该任务所产生的响应的 id，在创建任务时分配。 |
| `events_url` | 事件流的路径。`event_resume` 能力为 `true` 时存在。 |
| `on_restart` | 该任务的重启策略。 |
| `response` | `status` 为 `succeeded` 后是响应对象，否则为 `null`。 |
| `error` | `status` 为 `failed` 时是一个 problem 对象（[第 9.3 节](https://madeforagent.com/zh-cn/lmsp/1.0/common-rules.md#errors)），否则为 `null`。 |

`GET /lmsp/v1/tasks/{task_id}` 返回任务对象。不使用事件流的客户端轮询该接口。未知的 id 返回 `404` 和 `task_not_found`。已结束的任务如下所示：

```json
{
  "id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC",
  "object": "task",
  "type": "response",
  "status": "succeeded",
  "created_at": 1794200100,
  "updated_at": 1794200162,
  "response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
  "events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events",
  "on_restart": "fail",
  "response": {
    "id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
    "object": "response",
    "created_at": 1794200100,
    "status": "completed",
    "model": "qwen3:14b-q4_k_m",
    "output": [
      {
        "id": "msg_01J8M8R6A2C5E8G1J4M7P0S3VY",
        "type": "message",
        "role": "assistant",
        "content": [
          {
            "type": "output_text",
            "text": "Local model services make on-device inference a shared, discoverable utility..."
          }
        ]
      }
    ],
    "usage": {
      "input_tokens": 12,
      "output_tokens": 1480,
      "total_tokens": 1492
    },
    "error": null,
    "incomplete_details": null
  },
  "error": null
}
```

## 7.2 状态机 {#states}

```text
queued --> loading --> running --> succeeded
  |           |           |
  |           |           +--> failed
  |           |           +--> cancelling --> cancelled
  |           +--------------> failed
  +--------------------------> cancelled
```

| 状态 | 含义 | 后续状态 |
| --- | --- | --- |
| `queued` | 已接受并持久化，等待开始。 | `loading`、`cancelled`、`failed` |
| `loading` | 正在加载模型。 | `running`、`cancelling`、`failed` |
| `running` | 模型正在生成。 | `succeeded`、`failed`、`cancelling` |
| `cancelling` | 已请求取消，后端尚未停止。 | `cancelled` |
| `succeeded` | 终态。响应已完成。 | 无 |
| `failed` | 终态。 | 无 |
| `cancelled` | 终态。 | 无 |

终态只有 `succeeded`、`failed` 和 `cancelled`。处于终态的任务**不得**再发生变化。响应以 `incomplete` 结束的任务（例如达到 token 上限）是 `succeeded`。

每次状态转换都是原子的，因此任务不会既被取消又已完成，先提交的转换生效。

## 7.3 取消 {#cancel}

```http
POST /lmsp/v1/tasks/{task_id}/cancel
```

服务返回 `200 OK` 和当前任务对象。`queued` 任务立即变为 `cancelled`；`loading` 或 `running` 任务先变为 `cancelling`，直到后端停止后再变为 `cancelled`。

取消操作**必须**幂等：重复取消已处于 `cancelling` 或 `cancelled` 的任务，仍返回当前任务，不产生新错误。

取消已经 `succeeded` 或 `failed` 的任务，返回 `409` 和 `task_not_cancelable`。未知的 id 返回 `404` 和 `task_not_found`。

## 7.4 事件流 {#events}

```http
GET /lmsp/v1/tasks/{task_id}/events
Accept: text/event-stream
```

该接口是可选的，在 `event_resume` 能力为 `true` 时可用。

- 事件是[第 6.3 节](https://madeforagent.com/zh-cn/lmsp/1.0/responses.md#streaming)中的响应事件，对应该任务所产生的响应。序号与恢复规则遵循[第 6.4 节](https://madeforagent.com/zh-cn/lmsp/1.0/responses.md#reconnection)。
- 事件流以 `response.created` 开始，它在任务被接受时记录。流以与结果相符的一个最终事件结束：`response.completed`、`response.incomplete`、`response.failed` 或 `response.cancelled`。
- 较晚连接的客户端先收到自己错过的事件，再收到实时事件。服务在最终事件之后关闭流。
- 事件会保留一段保留期（[第 11.2 节](https://madeforagent.com/zh-cn/lmsp/1.0/persistence.md#rules)）。更早的事件返回 `410 Gone` 和 `event_history_expired`。

## 7.5 重启语义 {#restart}

`on_restart` 规定服务重启时对未完成任务如何处理：

| 取值 | 行为 |
| --- | --- |
| `fail` | 默认值。服务把未完成的任务标记为 `failed`，错误码为 `server_restarted`。 |
| `retry` | 服务从头重新执行该任务。 |
| `resume` | 服务从快照继续执行。仅当后端明确支持可恢复快照时才允许，否则创建请求会被拒绝，返回 `400` 和 `unsupported_capability`。 |

服务重启时处于 `cancelling` 的任务，无论其策略如何，都会完成取消并变为 `cancelled`。

不建议把 `retry` 作为默认值，因为任务可能已经执行了工具或产生了外部副作用。选择 `retry` 的客户端**必须**同时使用 `Idempotency-Key`，并使其工具调用幂等。
