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

# SDK v0.2 → v0.3 迁移指南

> 将 LocalBackend 和 HarborBackend rollout 从 Osmosis SDK v0.2 迁移到 v0.3

Osmosis SDK v0.3 包含两项相互独立的 breaking change。所有 rollout 都改为一次 execution、一个 sample、一个 reward 的契约。与此同时，pre-v0.3 `HarborBackend` 被移除，`HarborBackendV2` 成为新的 `HarborBackend`，并采用不同的 constructor 和 execution model。

<Warning>
  请同时升级 package requirement、workflow、grader、integrations 和 backend entrypoint。混用 v0.2 rollout 代码与 v0.3 backend 通常会在 runtime 失败。
</Warning>

请选择与当前 backend 对应的迁移路径：

| 当前 backend               | 迁移路径                                                                  |
| ------------------------ | --------------------------------------------------------------------- |
| `LocalBackend`           | 保留 constructor，然后更新共享的 sample、reward、integration 和 routing API        |
| Pre-v0.3 `HarborBackend` | 先完成共享 API 变更，再替换 backend constructor 和 container execution model      |
| `HarborBackendV2`        | 完成共享 API 变更并改名为 `HarborBackend`；它的 v2 constructor 就是 v0.3 constructor |

修改代码前：

* 保留最近一次成功的 v0.2 evaluation run，以便对比。
* 使用 Python 3.12 或更高版本。
* 修改 SDK requirement 后，重新创建 rollout environment 和 lockfile。
* 迁移前后都搜索一次已移除的 API。

## LocalBackend 用户

`LocalBackend` 在 v0.3 中保留相同的 keyword-only constructor：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
from osmosis_ai.rollout import LocalBackend

backend = LocalBackend(
    workflow=MyWorkflow,
    workflow_config=my_workflow_config,
    grader=MyGrader,
    grader_config=my_grader_config,
)
```

迁移工作集中在 workflow、grader、integrations，以及 backend 周围的自定义 protocol 代码。

### 1. 升级 Package 并选择功能

基础 distribution 包含 CLI 和 framework-neutral rollout core。只添加 rollout 实际 import 的 extras：

<CodeGroup>
  ```bash Strands theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
  pip install --upgrade "osmosis-ai[strands]>=0.3,<0.4"
  ```

  ```bash OpenAI Agents theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
  pip install --upgrade "osmosis-ai[openai-agents]>=0.3,<0.4"
  ```

  ```bash Rollout server theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
  pip install --upgrade "osmosis-ai[server]>=0.3,<0.4"
  ```

  ```bash Everything theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
  pip install --upgrade "osmosis-ai[full]>=0.3,<0.4"
  ```
</CodeGroup>

需要时可以组合 extras。例如，使用 Strands 的 server entrypoint 应声明：

```toml theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
[project]
dependencies = [
    "osmosis-ai[server,strands]>=0.3,<0.4",
]
```

其他 extras 包括 `openai-agents`、`harbor`、`rubric` 和 `parquet`。

### 2. 从多个 Sample 改为一个 Sample

每次 workflow execution 现在最多生成一个 `RolloutSample`，grader 为它分配一个标量 reward。

| v0.2                                       | v0.3                        | 操作                                        |
| ------------------------------------------ | --------------------------- | ----------------------------------------- |
| `GraderContext.samples`                    | `GraderContext.sample`      | 读取并评分单个 sample                            |
| `ctx.set_sample_reward(sample_id, reward)` | `ctx.set_reward(reward)`    | 移除 sample ID 参数                           |
| `register_sample_source(name, source)`     | `set_sample_source(source)` | 只注册一个 source                              |
| `get_samples()`                            | `get_sample()`              | 返回一个 `RolloutSample` 或 `None`             |
| `RolloutSample.id`                         | 已移除                         | 使用 execution URL 提供的 rollout identity     |
| `MultiTurnMode`                            | 已移除                         | 在单个 sample 中保留 conversation history       |
| 多个已注册 agent 或 session                      | 一个已注册 agent 或 session       | 把独立 candidates 拆分为不同的 workflow executions |

将遍历 samples 的 grader 改为显式检查一个 sample：

<CodeGroup>
  ```python v0.2 theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
  class ExactMatchGrader(Grader):
      async def grade(self, ctx: GraderContext) -> None:
          for sample_id, sample in ctx.samples.items():
              answer = last_text(sample)
              reward = 1.0 if answer == ctx.label else 0.0
              ctx.set_sample_reward(sample_id, reward)
  ```

  ```python v0.3 theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
  class ExactMatchGrader(Grader):
      async def grade(self, ctx: GraderContext) -> None:
          if ctx.sample is None:
              raise ValueError("workflow produced no sample")
          answer = last_text(ctx.sample)
          reward = 1.0 if answer == ctx.label else 0.0
          ctx.set_reward(reward)
  ```
</CodeGroup>

Workflow 没有生成 sample 时，`set_reward()` 会抛出 `ValueError`。在评分前显式抛出错误，通常能提供更清晰的 evaluation failure。

### 3. 每次 Execution 只注册一个 Agent 或 Session

在 `AgentWorkflow.run()` 内只构造一个已注册的 `OsmosisStrandsAgent` 或 `OsmosisMemorySession`。第二次 registration 会抛出 `ValueError`。

对于 OpenAI Agents，v0.3 session 不再接收 name 或 sample-ID 参数：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
class OpenAIWorkflow(AgentWorkflow):
    async def run(self, ctx: AgentWorkflowContext) -> None:
        agent = OsmosisAgent(
            name="assistant",
            instructions="Answer the user's request clearly.",
            model=OsmosisRolloutModel(),
        )
        session = OsmosisMemorySession()
        await Runner.run(agent, ctx.prompt, session=session)
```

Handoff 和 tool call 可以继续保留在这一次 agent run 中。如果一个 prompt 需要多个 candidate answers，请配置 evaluation 或 training 多次执行 workflow。

### 4. 更新自定义 Sample Source

内置 integrations 已使用 v0.3 API。自定义 integration 必须实现单数形式的 source contract：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
class MySampleSource(SampleSource):
    async def get_sample(self) -> RolloutSample:
        return RolloutSample(messages=self.messages)


rollout_ctx.set_sample_source(MySampleSource(messages))
sample = await rollout_ctx.get_sample()
```

不要为 `RolloutSample` 设置 `id`。如需控制标准化的 ATIF transcript，请设置 `trajectory_messages`；将其设为 `None` 会关闭该 sample 的 trajectory 持久化。

### 5. 更新自定义 Routing 和 Backend Adapter

如果只使用内置 integrations 和 `LocalBackend`，请跳过此步骤。

将 execution 提供的 chat-completions 和 callback URL 视为不透明的 rollout-scoped endpoints。不要追加 rollout ID，也不要附加已移除的 `x-sample-id` 和 `x-rollout-id` routing headers。

自定义 backend 在 `ExecutionResult` 中返回一个 `sample`。如果直接读取 container exchange files，请将 reader 更新为单数 file names 和 payloads：

```json sample.json theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
{
  "messages": [],
  "reward": null,
  "remove_sample": false,
  "metrics": {},
  "extra_fields": {}
}
```

```json reward.json theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
{
  "reward": 1.0
}
```

### 6. 验证 LocalBackend 迁移

1. 确认 rollout 中不再出现已移除的 API：

   ```bash theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
   rg "ctx\.samples|set_sample_reward|register_sample_source|get_samples|MultiTurnMode|x-sample-id|x-rollout-id" rollouts
   ```

2. 重新创建 rollout environment，确保 lockfile 中包含 v0.3 和所选 extras。

3. 在本地运行 workflow，确认每次成功的 execution 都生成一个 sample。

4. 提交一次 evaluation run，确认每个已评分 sample 都有一个标量 reward。

5. 在提交 training 前，将 reward 和最终 messages 与最近一次成功的 v0.2 evaluation 进行对比。

### LocalBackend 常见问题

<AccordionGroup>
  <Accordion title="第二个 agent 或 session 触发 registration error">
    该 workflow execution 已经注册了 sample source。请复用一个 agent 或 session 完成 conversation，或者将独立 candidates 拆分到不同的 workflow executions。
  </Accordion>

  <Accordion title="Grader 没有 sample">
    请在 `AgentWorkflow.run()` 内构造受支持的 agent 或 session。在模块 import 时创建的 object 无法向 active rollout context 注册。
  </Accordion>

  <Accordion title="无法导入可选 integration">
    在 rollout-local environment 中安装对应的 extra，然后重新生成 lockfile。
  </Accordion>

  <Accordion title="Model call 到达了错误的 rollout">
    将提供的 chat-completions URL 直接传给 integration。移除重新构造 URL、追加 rollout path segment 或附加旧 routing headers 的代码。
  </Accordion>
</AccordionGroup>

## HarborBackend 用户

Harbor 用户必须先完成 [LocalBackend 用户](#localbackend-用户)中的共享 workflow、grader、sample-source 和 routing 变更，然后再迁移 Harbor class 和 constructor。

<Warning>
  `HarborBackend` 这个名称在 v0.3 中仍可 import，但现在指向之前名为 `HarborBackendV2` 的实现。使用 `task_dir`、`user_code_dir` 或 `workflow` 的 pre-v0.3 调用会抛出 `TypeError`；不存在 legacy compatibility mode。
</Warning>

### 1. 升级 Package 和 Import

安装 v0.3 Harbor 和 server 功能：

```bash theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
pip install --upgrade "osmosis-ai[server,harbor]>=0.3,<0.4"
```

<Warning>
  请安装 `osmosis-ai[harbor]`，不要安装 Harbor 的 `skypilot` extra。Managed rollout runtime 会提供兼容的 SkyPilot SDK。
</Warning>

使用 Harbor submodule import：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
from osmosis_ai.rollout.backend.harbor import HarborBackend
```

如果已经使用 `HarborBackendV2`，只需修改 class name，并保留其 v2 constructor arguments：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
# v0.2 preview API
from osmosis_ai.rollout.backend.harbor import HarborBackendV2

# v0.3
from osmosis_ai.rollout.backend.harbor import HarborBackend
```

`HarborBackendV2` 不会作为 alias 保留。

### 2. 替换 Pre-v0.3 Constructor

旧 backend 会把 source tree 和 SDK mount 到 task environment。v0.3 backend 会把 workflow project 打包为 wheel，并安装到 task container 内。

<CodeGroup>
  ```python Pre-v0.3 theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
  from pathlib import Path

  from osmosis_ai.rollout.backend.harbor import HarborBackend

  backend = HarborBackend(
      orchestrator=trial_queue,
      task_dir=Path("tasks/my-task"),
      user_code_dir=Path("."),
      workflow=MyWorkflow,
      workflow_config=my_workflow_config,
      grader=MyGrader,
      grader_config=my_grader_config,
      cleanup_successful_trials=True,
  )
  ```

  ```python v0.3 theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
  from pathlib import Path

  from osmosis_ai.rollout.backend.harbor import HarborBackend

  backend = HarborBackend(
      orchestrator=trial_queue,
      tasks_dir=Path("tasks/my-task"),
      task_mode="template",
      agent=MyWorkflow,
      workflow_config=my_workflow_config,
      grader=MyGrader,
      grader_config=my_grader_config,
      code_dir=Path("."),
      cleanup_successful_trials=True,
  )
  ```
</CodeGroup>

按照下表迁移所有旧 constructor parameters：

| Pre-v0.3 parameter           | v0.3 replacement             | 迁移说明                                                                   |
| ---------------------------- | ---------------------------- | ---------------------------------------------------------------------- |
| `orchestrator=`              | `orchestrator=`              | 保持不变                                                                   |
| `task_dir=`                  | `tasks_dir=` 和 `task_mode=`  | 一个可复用 task 使用 `"template"`，task directory 集合使用 `"dataset"`             |
| `user_code_dir=`             | `code_dir=` 或 `bundle=`      | `code_dir` 指向包含 `pyproject.toml` 的 package project，或提供预构建 bundle wheel |
| `workflow=`                  | `agent=`                     | 传入 `AgentWorkflow` class/import path 或已注册的原生 Harbor agent name         |
| `workflow_config=`           | `workflow_config=`           | 保持不变；它会与 workflow agent 一起打包                                           |
| `grader=`                    | `grader=`                    | 保持不变；grader 会被打包并作为 Harbor verifier 运行                                 |
| `grader_config=`             | `grader_config=`             | 保持不变                                                                   |
| `trials_dir=`                | `trials_dir=`                | 继续支持，但省略时现在使用 backend-specific temporary root，而不是 `Path("trials")`     |
| `custom_tests_dir=`          | 已移除                          | 把 tests 放在每个 task 的 `tests/` 下，或传入 Osmosis `grader=`                   |
| `environment_config=`        | `environment_config=`        | 保持不变                                                                   |
| `prebuild_local_image=`      | 已移除                          | Harbor 负责 image caching；开始服务前调用 `prewarm()` 或 `prewarm_lifespan()`     |
| `symlink_environment=`       | 已移除                          | 每个 rollout 都会 materialize 一个 task 副本，并依赖 Harbor image caching          |
| `cleanup_successful_trials=` | `cleanup_successful_trials=` | 保持不变                                                                   |

如果 harness 使用了 private `_sdk_source_dir` argument，请将其移除。

v0.3 constructor 还新增了以下控制项：

| New parameter               | 用途                                             |
| --------------------------- | ---------------------------------------------- |
| `native_agent_kwargs`       | 配置已注册的原生 Harbor agent                          |
| `model_name`                | 选择原生 agent model；默认为 `openai/osmosis-rollout`  |
| `bundle`                    | 复用预构建 Osmosis bundle wheel，而不是从 `code_dir` 构建  |
| `patch_dockerfile_with_sdk` | 控制是否在 task image 中预装 bundle dependencies       |
| `agent_setup_timeout_sec`   | 限制 Harbor agent setup 时间                       |
| `max_queue_depth`           | 限制 queued rollouts，并启用 `429` admission control |

### 3. 选择 Task Mode

要最接近地替代旧的单个 `task_dir`，请使用 template mode：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
backend = HarborBackend(
    orchestrator=trial_queue,
    tasks_dir=Path("tasks/my-task"),
    task_mode="template",
    agent=MyWorkflow,
)
```

Request prompt 会在每个 rollout 的副本中替换 `instruction.md`。

当 `tasks_dir` 中每个 task 都有独立目录时，请使用 dataset mode：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
backend = HarborBackend(
    orchestrator=trial_queue,
    tasks_dir=Path("tasks"),
    task_mode="dataset",
    agent=MyWorkflow,
)
```

每个 request 都必须设置 `metadata["harbor_task_id"]`；选中的 task 会保留自己的 `instruction.md`。

无论使用哪种 mode，`metadata["harbor_task"]` 都可以选择 per-rollout 本地路径、`"org/name@ref"` 形式的 Harbor registry package，或 Git task。Git task 还需要设置 `metadata["git_url"]`，并应固定 `metadata["git_commit_id"]`。

如果旧 backend 使用 `custom_tests_dir`，请把这些 tests 移入每个 task：

```text theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
tasks/my-task/
├── instruction.md
├── environment/
│   └── Dockerfile
└── tests/
    └── test.sh
```

然后传入 `grader=None`，使用 task-native verifier。

### 4. 打包 Workflow 代码，而不是 Mount

对于 `AgentWorkflow`，backend 会从 `code_dir` 构建 wheel，并在 trial 启动时安装到 container。该目录必须包含 `pyproject.toml` 和一个可导入的 top-level Python package。省略 `code_dir` 时，backend 会尝试定位包含 workflow class 的 project。

如果 build system 会提前创建 Osmosis bundle wheel，请使用 `bundle=`。不要同时传入两个路径并期望它们合并；提供 bundle 后会直接使用它。

旧的 `HarborAgentWorkflowContext.environment` adapter 已移除。Workflow 现在会在 task container 内运行，接收标准 `AgentWorkflowContext`，并通过普通 Python API 访问文件或 processes：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
import asyncio

from osmosis_ai.rollout import AgentWorkflow, AgentWorkflowContext


class HarborWorkflow(AgentWorkflow):
    async def run(self, ctx: AgentWorkflowContext) -> None:
        result = await asyncio.create_subprocess_exec(
            "python",
            "/workspace/run_task.py",
        )
        await result.wait()
```

`OsmosisInstalledAgent` 也已移除。不要实例化或继承它；请通过 `agent=` 传入 `AgentWorkflow`，或选择已注册的原生 Harbor agent name。

### 5. 选择 Agent 和 Reward Source

`agent=` 接受 `AgentWorkflow` class/import path，或以下已注册的原生名称之一：`"terminus-2"`、`"mini-swe-agent"` 和 `"oracle"`。

<Note>
  `"oracle"` 会运行 reference solution 来验证 dataset 或 verifier。它不会生成 model trajectory，不能用于 training。
</Note>

请选择一种 reward 路径：

| Configuration     | Reward source                             |
| ----------------- | ----------------------------------------- |
| `grader=MyGrader` | 打包后的 Osmosis grader 作为 Harbor verifier 运行 |
| `grader=None`     | Task 自己的 `tests/` 生成 reward               |

### 6. 添加 Prewarming 和 Lifecycle Controls

在 rollout server 接收流量前，prewarm 所选 task images 和 agent setup：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
from osmosis_ai.rollout.server import create_rollout_server

app = create_rollout_server(
    backend=backend,
    lifespan=backend.prewarm_lifespan(),
)
```

Dataset mode 需要显式 task IDs：

```python theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
app = create_rollout_server(
    backend=backend,
    lifespan=backend.prewarm_lifespan(["task-a", "task-b"]),
)
```

设置 `max_queue_depth` 后，queue 已满时 `POST /rollout` 会返回带 `Retry-After: 5` 的 `429`。v0.3 还提供：

* `GET /rollout/{rollout_id}/status`
* `POST /rollout/cancel`
* `HarborBackend.rollout_status()`
* `HarborBackend.cancel_rollouts()`

### 7. 验证 HarborBackend 迁移

1. 确认旧 class 和 constructor keywords 不再出现：

   ```bash theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/cli.json"]}}
   rg "HarborBackendV2|HarborAgentWorkflowContext|OsmosisInstalledAgent|task_dir=|user_code_dir=|workflow=|custom_tests_dir=|prebuild_local_image=|symlink_environment=" rollouts
   ```

2. 确认 `tasks_dir` 指向有效的 template task 或 dataset root。

3. 构建 workflow bundle，并修复 package layout 或缺少 dependency 的错误。

4. Template mode 运行 `await backend.prewarm()`；dataset mode 则传入 task IDs。

5. 提交一个 rollout，并检查 workflow sample、reward source、Harbor logs 和已归档 artifacts。

6. 如果 calling controller 依赖 status 和 cancellation，请实际验证这些功能。

7. 在 training 前运行 evaluation，并将 reward 和最终 messages 与最近一次成功的 v0.2 run 对比。

### HarborBackend 常见问题

<AccordionGroup>
  <Accordion title="HarborBackend 拒绝 task_dir、user_code_dir 或 workflow">
    Import 已解析到 v0.3 backend，但调用仍使用已移除的 constructor。请按照上表替换所有旧 parameters。
  </Accordion>

  <Accordion title="无法打包 workflow project">
    将 `code_dir` 指向包含 `pyproject.toml` 和一个可导入 package 的目录。确保 workflow 和 grader 可以通过该 package 的 import path 寻址。
  </Accordion>

  <Accordion title="Dataset-mode request 找不到 task">
    将 `metadata["harbor_task_id"]` 设为 `tasks_dir` 下的目录，或将 `metadata["harbor_task"]` 设为受支持的本地、package 或 Git reference。
  </Accordion>

  <Accordion title="Trial 完成后没有 reward">
    传入 Osmosis `grader`，或设置 `grader=None` 并确认所选 task 包含可正常运行的 `tests/` verifier。
  </Accordion>

  <Accordion title="Rollout 代码仍依赖 ctx.environment">
    移除 legacy adapter 调用。Workflow 现在运行在 container 内，因此请使用普通 filesystem、subprocess 和 network API。
  </Accordion>
</AccordionGroup>

## 相关资源

* [执行后端](/zh/cli/rollout/execution-backends)
* [构建 AgentWorkflow](/zh/cli/rollout/agent-workflows)
* [构建 Graders](/zh/cli/rollout/graders)
* [更新日志](/zh/changelog)
