# Asynchrone Aufgaben

> Wie LMSP lange Generierungen als persistierte Aufgaben ausführt, mit Aufgabenerstellung, Zustandsautomat, idempotentem Abbruch, Ereignisströmen und Neustartrichtlinien für unfertige Aufgaben.

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

## 7.1 Eine Aufgabe erstellen {#create}

Ein Client erstellt eine Aufgabe, indem er die Antwortanfrage, die er ausführen möchte, in einen Aufgaben-Umschlag verpackt per POST sendet. Ein `Idempotency-Key`-Header wird empfohlen ([Abschnitt 9.2](https://madeforagent.com/de/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"
}
```

| Feld | Erforderlich | Bedeutung |
| --- | --- | --- |
| `type` | Ja | `response`, der einzige Aufgabentyp in 1.0. |
| `input` | Ja | Eine Antwortanfrage, genau wie für `POST /lmsp/v1/responses`. Ihr Feld `stream` wird ignoriert. |
| `on_restart` | Nein | `fail` (der Standard), `retry` oder `resume`. Siehe [Abschnitt 7.5](https://madeforagent.com/de/lmsp/1.0/tasks.md#restart). |

Der Dienst **MUSS** die Aufgabe persistieren, bevor er antwortet. Danach gibt er zurück:

```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
}
```

| Aufgabenfeld | Bedeutung |
| --- | --- |
| `id` | Aufgabenkennung, etwa `task_...`. |
| `object` | Immer `"task"`. |
| `type` | `response`. |
| `status` | Der aktuelle Zustand ([Abschnitt 7.2](https://madeforagent.com/de/lmsp/1.0/tasks.md#states)). |
| `created_at`, `updated_at` | Zeiten in Sekunden seit der Unix-Epoche. |
| `response_id` | ID der Antwort, die die Aufgabe erzeugt. Sie wird bei der Erstellung der Aufgabe vergeben. |
| `events_url` | Pfad des Ereignisstroms. Vorhanden, wenn die Fähigkeit `event_resume` `true` ist. |
| `on_restart` | Die Neustartrichtlinie der Aufgabe. |
| `response` | Das Antwortobjekt, sobald `status` `succeeded` ist, sonst `null`. |
| `error` | Ein Problemobjekt ([Abschnitt 9.3](https://madeforagent.com/de/lmsp/1.0/common-rules.md#errors)), wenn `status` `failed` ist, sonst `null`. |

`GET /lmsp/v1/tasks/{task_id}` gibt das Aufgabenobjekt zurück. Clients, die keine Ereignisströme nutzen, fragen diesen Endpunkt regelmäßig ab. Eine unbekannte ID ergibt `404` mit `task_not_found`. Eine abgeschlossene Aufgabe sieht so aus:

```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 Zustandsautomat {#states}

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

| Zustand | Bedeutung | Folgezustände |
| --- | --- | --- |
| `queued` | Angenommen und persistiert, wartet auf den Start. | `loading`, `cancelled`, `failed` |
| `loading` | Das Modell wird geladen. | `running`, `cancelling`, `failed` |
| `running` | Das Modell generiert. | `succeeded`, `failed`, `cancelling` |
| `cancelling` | Der Abbruch wurde angefordert, und das Backend hat noch nicht angehalten. | `cancelled` |
| `succeeded` | Endzustand. Die Antwort ist vollständig. | keine |
| `failed` | Endzustand. | keine |
| `cancelled` | Endzustand. | keine |

Die Endzustände sind `succeeded`, `failed` und `cancelled`. Eine Aufgabe in einem Endzustand **DARF NICHT** mehr wechseln. Eine Aufgabe, deren Antwort als `incomplete` endet, zum Beispiel am Token-Limit, ist `succeeded`.

Jeder Übergang ist atomar, sodass eine Aufgabe nie zugleich abgebrochen und abgeschlossen ist. Es gewinnt der Übergang, der zuerst festgeschrieben wird.

## 7.3 Abbruch {#cancel}

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

Der Dienst gibt `200 OK` und das aktuelle Aufgabenobjekt zurück. Eine `queued`-Aufgabe wird sofort `cancelled`. Eine `loading`- oder `running`-Aufgabe wird `cancelling`, bis das Backend angehalten hat, und danach `cancelled`.

Der Abbruch **MUSS** idempotent sein. Der Abbruch einer Aufgabe, die bereits `cancelling` oder `cancelled` ist, gibt die aktuelle Aufgabe zurück und erzeugt keinen neuen Fehler.

Der Abbruch einer Aufgabe, die bereits `succeeded` oder `failed` ist, wird mit `409` und `task_not_cancelable` beantwortet. Eine unbekannte ID ergibt `404` mit `task_not_found`.

## 7.4 Ereignisströme {#events}

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

Dieser Endpunkt ist optional. Er ist verfügbar, wenn die Fähigkeit `event_resume` `true` ist.

- Die Ereignisse sind die Antwortereignisse aus [Abschnitt 6.3](https://madeforagent.com/de/lmsp/1.0/responses.md#streaming), für die Antwort, die die Aufgabe erzeugt. Nummerierung und Fortsetzung folgen [Abschnitt 6.4](https://madeforagent.com/de/lmsp/1.0/responses.md#reconnection).
- Der Stream beginnt mit `response.created`, das bei der Annahme der Aufgabe aufgezeichnet wird. Er endet mit genau einem letzten Ereignis, das zum Ergebnis passt: `response.completed`, `response.incomplete`, `response.failed` oder `response.cancelled`.
- Ein Client, der sich spät verbindet, erhält zuerst die verpassten Ereignisse und danach Live-Ereignisse. Der Dienst schließt den Stream nach dem letzten Ereignis.
- Ereignisse werden für eine Aufbewahrungsdauer vorgehalten ([Abschnitt 11.2](https://madeforagent.com/de/lmsp/1.0/persistence.md#rules)). Ältere Ereignisse ergeben `410 Gone` mit `event_history_expired`.

## 7.5 Neustartverhalten {#restart}

`on_restart` legt fest, was mit einer unfertigen Aufgabe geschieht, wenn der Dienst neu startet:

| Wert | Verhalten |
| --- | --- |
| `fail` | Der Standard. Der Dienst markiert die unfertige Aufgabe als `failed`, mit dem Fehlercode `server_restarted`. |
| `retry` | Der Dienst führt die Aufgabe von vorn erneut aus. |
| `resume` | Der Dienst setzt von einem Snapshot aus fort. Das ist nur erlaubt, wenn das Backend fortsetzbare Snapshots ausdrücklich unterstützt. Andernfalls wird die Erstellungsanfrage mit `400` und `unsupported_capability` abgelehnt. |

Eine Aufgabe, die beim Neustart des Dienstes `cancelling` ist, schließt ihren Abbruch ab und wird `cancelled`, unabhängig von ihrer Richtlinie.

`retry` wird als Standard nicht empfohlen, weil die Aufgabe möglicherweise schon Tools ausgeführt oder externe Nebeneffekte erzeugt hat. Ein Client, der `retry` wählt, **MUSS** es mit einem `Idempotency-Key` kombinieren und seine Tool-Aufrufe idempotent machen.
