> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentscope.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> For AgentScope Python, use https://docs.agentscope.io/stable/en/index for new projects. For existing projects, check the installed agentscope version and use matching versioned documentation.
> The /latest/ alias points to development documentation. Use it only with the matching development source. Do not mix AgentScope 1.x and 2.x APIs.
> State the AgentScope version when providing installation commands or code examples. ReMe uses its own continuously updated /reme/latest/ documentation.

# SOP Service

> Store standard operating procedures in the agent service and let their runs advance on their own through ordinary sessions.

When developers want to keep a [standard operating procedure](/en/versions/2.0.10dev/building-blocks/sop) in the [agent service](/en/versions/2.0.10dev/deploy/agent-service) and start runs of it again and again, they can use the SOP support of the service layer. Procedures are stored as data, every run advances step by step in ordinary sessions, and a frontend can watch it live through the session event streams.

The service-layer SOP provides the following capabilities:

| Capability        | Description                                                                                                                           |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Procedure storage | Procedures are stored as `SOPData` with full CRUD over HTTP, and a frontend can render the editor form from `GET /sop/schema`         |
| Background runs   | Starting a run returns immediately while the run advances in the background, each step handed to its session as an ordinary chat turn |
| Human sign-off    | A verifier can be an agent or a person; with a person, the run parks until someone files a verdict                                    |
| Tool confirmation | A tool confirmation inside a step is answered through `POST /chat` like in any session, and the run carries on once answered          |
| Cascading deletes | Deleting a run also deletes the sessions and workspaces it created, and deleting a procedure deletes all of its runs                  |

<Note>
  The SOP service is experimental, and its interfaces and storage layout may change in later releases. A custom storage backend has to implement the SOP methods of `StorageBase`; until it does, these endpoints raise `NotImplementedError` while the rest of the service keeps working.
</Note>

## Define a Procedure

A procedure in the service is described by `SOPData`. Unlike the SDK, which holds agent objects directly, each step here refers to its executor and verifier by agent ID and session key. `SOPData` has the following fields:

| Field              | Type                                                | Description                                       |
| ------------------ | --------------------------------------------------- | ------------------------------------------------- |
| `name`             | `str`                                               | The procedure name                                |
| `description`      | `str`, defaults to `""`                             | What the procedure is for                         |
| `steps`            | `list[SOPStepDataV1]`                               | The ordered steps, at least one                   |
| `workspace_grain`  | `"run"` \| `"per_session_key"`, defaults to `"run"` | How many workspaces a run gets                    |
| `session_settings` | `dict[str, SessionSettings]`                        | How the session behind each session key is opened |

Each step (`SOPStepDataV1`) has the following fields:

| Field          | Type                                         | Description                                                                   |
| -------------- | -------------------------------------------- | ----------------------------------------------------------------------------- |
| `subject`      | `str`                                        | A short name for the step                                                     |
| `description`  | `str`                                        | What the step must achieve                                                    |
| `executor`     | `SOPAgentRef`                                | The executor, made of `agent_id` and `session_key`                            |
| `verifier`     | `AgentVerifier` \| `HumanVerifier` \| `None` | The verifier. With `None`, the step passes as soon as the executor hands over |
| `max_attempts` | `int`, defaults to `3`                       | How many refusals before the whole run fails                                  |

### Session Keys

`session_key` decides which session an agent works in. Two references with the same key share one session, and therefore one context; different keys put even the same agent into two sessions that cannot see each other. For example, to let a writer agent carry its context from one step to the next, give both steps' executors the same key; to have a reviewer judge each time from a blank context, give it a key of its own.

A session key belongs to exactly one agent. The session's model and permission mode are also configured per key in `session_settings`, and every key in use must be configured there, with these fields:

| Field                        | Type                           | Description                                                                                     |
| ---------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
| `chat_model_config`          | `dict`                         | The session's model configuration, including `type`, `credential_id`, `model`, and `parameters` |
| `fallback_chat_model_config` | `dict \| None`                 | The fallback model configuration, used when the primary model fails                             |
| `permission_mode`            | `str`, defaults to `"default"` | The session's permission mode                                                                   |

### Verifiers

There are two kinds of verifier, told apart by the `type` field:

| Type                        | Fields                                                                                | Behavior                                                                                       |
| --------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `AgentVerifier` (`"agent"`) | `agent`: `SOPAgentRef`; `criteria`: acceptance criteria beyond the step's description | An agent judges in its own session, so one reviewer can hold different steps to different bars |
| `HumanVerifier` (`"human"`) | `question`: what the person is asked                                                  | The run parks after the executor hands over, until someone files a verdict through the API     |

### Workspaces

A run's sessions get their workspaces when they are created, and those workspaces belong to that run alone, never shared with another run. `workspace_grain` takes the following values:

| Value             | Effect                                                                                                          |
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
| `run`             | The whole run shares one workspace, so steps can pass work along as files by naming their paths in the handover |
| `per_session_key` | One workspace per session, so no step sees another's files                                                      |

The following request creates a two-step procedure: a writer agent produces a draft that a reviewer agent accepts, and the final version is then signed off by a person:

```bash Create a procedure theme={null}
curl -X POST http://localhost:8000/sop/ \
  -H "X-User-ID: alice" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "name": "write-article",
      "steps": [
        {
          "subject": "Write the draft",
          "description": "Write a first draft of an article introducing vector databases.",
          "executor": {"agent_id": "agent-writer", "session_key": "writer"},
          "verifier": {
            "type": "agent",
            "agent": {"agent_id": "agent-reviewer", "session_key": "reviewer"},
            "criteria": "Covers at least index structures and similarity metrics."
          }
        },
        {
          "subject": "Finalize",
          "description": "Turn the draft into a version ready to publish.",
          "executor": {"agent_id": "agent-writer", "session_key": "writer"},
          "verifier": {"type": "human", "question": "Is the final version ready to publish?"}
        }
      ],
      "session_settings": {
        "writer": {
          "chat_model_config": {
            "type": "dashscope",
            "credential_id": "cred-xxx",
            "model": "qwen3.8-max",
            "parameters": {}
          }
        },
        "reviewer": {
          "chat_model_config": {
            "type": "dashscope",
            "credential_id": "cred-xxx",
            "model": "qwen3.8-max",
            "parameters": {}
          }
        }
      }
    }
  }'
```

The following procedures are refused with 422 when created or updated, rather than failing once a run starts:

| Case                                                      | Reason                                 |
| --------------------------------------------------------- | -------------------------------------- |
| A step uses a session key missing from `session_settings` | A run could not open a session for it  |
| One session key is used by two different agents           | A session belongs to exactly one agent |
| An invalid model configuration                            | A run could not open a session with it |
| Empty `steps`                                             | A run of it could never end            |

## List the Endpoints

All SOP endpoints live under `/sop`, split into procedures and runs:

| Method and path                       | Description                                                                      |
| ------------------------------------- | -------------------------------------------------------------------------------- |
| `GET /sop/schema`                     | Return the JSON Schema of `SOPData`, for rendering the editor form               |
| `GET /sop/`                           | List the current user's procedures                                               |
| `POST /sop/`                          | Create a procedure, returning `sop_id`                                           |
| `GET /sop/{sop_id}`                   | Read one procedure                                                               |
| `PATCH /sop/{sop_id}`                 | Replace the procedure's contents with a new `data`; existing runs are unaffected |
| `DELETE /sop/{sop_id}`                | Delete the procedure and all of its runs                                         |
| `POST /sop/{sop_id}/runs`             | Start a run, with the body `{"inputs": [Msg, ...]}`                              |
| `GET /sop/runs`                       | List runs, newest first, filterable by `sop_id` and `phase`                      |
| `GET /sop/runs/{sop_run_id}`          | Read one run and its current state                                               |
| `DELETE /sop/runs/{sop_run_id}`       | Delete one run and the sessions it created                                       |
| `POST /sop/runs/{sop_run_id}/verdict` | File a verdict on a step waiting for human sign-off                              |

<Tip>
  `GET /sop/runs?phase=awaiting` lists every run waiting on someone, whether for human sign-off or for a tool confirmation.
</Tip>

## Run a Procedure

Starting a run first copies the procedure's current contents into the run record, so editing the procedure later never affects a run already started. It then opens one session per session key, named "procedure name / session key", and returns the run record immediately while the run advances in the background:

```bash Start a run theme={null}
curl -X POST http://localhost:8000/sop/sop-xxx/runs \
  -H "X-User-ID: alice" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [
      {"name": "alice", "role": "user", "content": [{"type": "text", "text": "Aim it at beginners."}]}
    ]
  }'
```

In the returned `SOPRunRecord`, `sessions` maps each session key to its session ID, and `state` is the run state, whose `phase` tells where the run stands as a whole. Developers can subscribe to those sessions' event streams at `GET /sessions/{session_id}/stream` to watch live, and poll `GET /sop/runs/{sop_run_id}` for progress.

### Submit Results

Each step is handed to the agent in its session as an ordinary chat turn. That turn is equipped with one extra submit tool, through which the agent writes its result straight into the run state:

| Tool             | Given to     | Purpose                                                                                                                         |
| ---------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `SubmitHandover` | The executor | Submits the account handed to the following steps                                                                               |
| `SubmitVerdict`  | The verifier | Submits whether the work passes, with the reason when it does not; the reason goes to the executor verbatim on the next attempt |

If the agent's reply ends without calling its submit tool, it is reminded to call it. After three reminders with nothing submitted, the reply ends in an error and counts as a refusal. An executor with no handover and a verifier with no verdict both spend the step's `max_attempts`.

<Note>
  Only turns dispatched by the run get a submit tool. A developer or user typing directly into a step's session is just having a conversation: they are neither asked to submit nor able to move the run.
</Note>

### Sign Off as a Person

With a `HumanVerifier`, the step enters `awaiting` once the executor hands over, and the run stops. A verdict filed through the following endpoint at any time lets the run carry on in the background:

```bash File a human verdict theme={null}
curl -X POST http://localhost:8000/sop/runs/run-xxx/verdict \
  -H "X-User-ID: alice" \
  -H "Content-Type: application/json" \
  -d '{
    "step_index": 1,
    "passed": false,
    "message": "The ending lacks references; add them and resubmit."
  }'
```

The request body has the following fields:

| Field        | Type                    | Description                                         |
| ------------ | ----------------------- | --------------------------------------------------- |
| `step_index` | `int`                   | The index of the step being judged, starting at 0   |
| `passed`     | `bool`                  | Whether the work passes                             |
| `message`    | `str`, defaults to `""` | Why it was refused, handed to the executor verbatim |

The endpoint returns the updated run record as soon as the verdict is recorded. It answers 404 when the run or step does not exist, and 409 when the step is not judged by a person or is not currently waiting to be.

### Handle Tool Confirmations

When an agent in a step asks for tool confirmation, the step also enters `awaiting`. Answering works exactly as in any session: send `POST /chat` to that session with a `UserConfirmResultEvent` or `ExternalExecutionResultEvent` as the body. The resumed turn still carries the submit tool, and once it finishes the run carries on in the background with no further call. If the resumed reply is interrupted, the run stays where it is.

## Delete Procedures and Runs

Deletes cascade to the resources a run created:

| Endpoint                        | What is deleted                                                                                                                           |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `DELETE /sop/runs/{sop_run_id}` | The run record and every session it created (including any turn in progress and the session event streams), and its workspaces are closed |
| `DELETE /sop/{sop_id}`          | The procedure record and all of its runs, each cleaned up as in the row above                                                             |

The run created these sessions, so they serve no purpose once it is gone, and a background tool finishing later could otherwise wake them up again. That is why they are deleted along with it.

## Current Limitations

The SOP service currently has the following limitations:

| Limitation            | Description                                                                                                                   |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| No way to stop a run  | There is no endpoint yet to stop a run that is advancing; it can only be deleted                                              |
| No automatic recovery | If the service process crashes or is redeployed, a run that was advancing stays at `running` and does not continue on its own |
| Cross-node deletes    | In a multi-node deployment, deleting a run that is advancing on another node waits until that node's advance stops            |
