Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ PlugRL 文档站点源码,使用 MkDocs Material 构建,支持中英文双
本仓库使用 uv 管理 Python 环境与依赖。

```bash
cd plugrl-docs
git clone https://github.com/PlugRL/plugrl.github.io.git
cd plugrl.github.io
uv sync --frozen
uv run mkdocs serve -a 127.0.0.1:8000
```

`plugrl-docs` 是 pyproject 里的发行包名,不是目录名;`git clone` 得到的目录叫
`plugrl.github.io`。已经在仓库根目录时,跳过前两行即可。

在浏览器打开 http://127.0.0.1:8000/ 。

中文站点路径为 /zh/ 。
Expand Down
32 changes: 24 additions & 8 deletions docs/algorithm/custom_algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,26 @@ Put the code in one of these layouts.

The WebSocket server loop calls these methods.

- `infer(obs) -> (action, internal_state)`
- `infer(obs) -> (action, runtime_state)`
- `feedback(...) -> (prev_node, global_step, log_dict)`
- `learn() -> (global_step, log_dict)`
- Scheduling and checkpoint hooks: `should_learn`, `should_save`, `should_stop`, `create_checkpoint`, `load_checkpoint`

See `plugrl_server/algorithm/base_algorithm.py` for exact signatures.
See `plugrl_server/algorithm/base_algorithm.py` for exact signatures. The
server does call `learn()`, but `learn()` is concrete on `BaseAlgorithm`: it
calls `learn_impl()` and then wraps the result with `build_train_info`.
`learn_impl` is the abstract method, so that is the one you override.
Overriding `learn` instead leaves `learn_impl` unimplemented and the class
abstract, and `make_algo` fails with `TypeError`. An earlier version of the
template below did exactly that.

`infer` and `feedback` take and return `PolicyRuntimeState` from
`plugrl_server.policy.state`; `feedback` also takes `train_state:
PolicyTrainState = None`. There is no `InternalState` type and no
`get_action_and_internal_state` method anywhere in `plugrl-server` - the
template used both names and neither imports. Every `feedback` parameter is
keyword-only, so a mismatched name is a `TypeError` on the server's first
call, not a silent rename.

## Minimal template

Expand All @@ -46,7 +60,8 @@ import numpy as np
from plugrl_server.algorithm.base_algorithm import BaseAlgoConfig, BaseAlgorithm
from plugrl_server.algorithm.registration import register_algo, register_algo_config
from plugrl_server.common.checkpoint_manager import Checkpoint
from plugrl_server.policy.base_policy import BasePolicy, InternalState
from plugrl_server.policy.base_policy import BasePolicy
from plugrl_server.policy.state import PolicyRuntimeState, PolicyTrainState

UID = "your-algo"

Expand All @@ -63,15 +78,16 @@ class YourAlgorithm(BaseAlgorithm):
super().__init__(config=config, policy=policy)
self.global_step = 0

def infer(self, obs: dict) -> tuple[np.ndarray, InternalState]:
action, internal_state = self.policy.get_action_and_internal_state(obs)
return action, internal_state
def infer(self, obs: dict) -> tuple[np.ndarray, PolicyRuntimeState]:
action, runtime_state = self.policy.get_action_and_runtime_state(obs)
return action, runtime_state

def feedback(
self,
*,
obs: dict,
internal_state: InternalState | None,
runtime_state: PolicyRuntimeState,
train_state: PolicyTrainState = None,
terminated: bool,
truncated: bool,
next_obs: dict,
Expand All @@ -84,7 +100,7 @@ class YourAlgorithm(BaseAlgorithm):
self.global_step += 1
return prev_node, self.global_step, {}

def learn(self) -> tuple[int, dict]:
def learn_impl(self) -> tuple[int, dict]:
return self.global_step, {}

def should_learn(self) -> bool:
Expand Down
29 changes: 21 additions & 8 deletions docs/algorithm/custom_algorithm.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,23 @@

WebSocket server loop 会调用这些方法。

- `infer(obs) -> (action, internal_state)`
- `infer(obs) -> (action, runtime_state)`
- `feedback(...) -> (prev_node, global_step, log_dict)`
- `learn() -> (global_step, log_dict)`
- 调度与保存:`should_learn`、`should_save`、`should_stop`、`create_checkpoint`、`load_checkpoint`

准确签名见 `plugrl_server/algorithm/base_algorithm.py`。
准确签名见 `plugrl_server/algorithm/base_algorithm.py`。server 确实调用的是
`learn()`,但 `learn()` 在 `BaseAlgorithm` 上已经实现了:它调用 `learn_impl()`
再用 `build_train_info` 包一层结果。抽象方法是 `learn_impl`,要覆写的是它。
覆写 `learn` 会让 `learn_impl` 悬空、类仍然是抽象的,`make_algo` 会直接抛
`TypeError`。下面这份模板此前正是这么写的。

`infer` 与 `feedback` 收发的是 `plugrl_server.policy.state` 里的
`PolicyRuntimeState`,`feedback` 还要接 `train_state: PolicyTrainState = None`。
`plugrl-server` 里没有 `InternalState` 这个类型,也没有
`get_action_and_internal_state` 这个方法 - 模板里这两个名字都用了,而且都 import
不进来。`feedback` 的每个参数都是 keyword-only,所以名字对不上会在 server 第一次
调用时直接 `TypeError`,不会被悄悄当成改名放过。

## 最小模板

Expand All @@ -46,7 +57,8 @@ import numpy as np
from plugrl_server.algorithm.base_algorithm import BaseAlgoConfig, BaseAlgorithm
from plugrl_server.algorithm.registration import register_algo, register_algo_config
from plugrl_server.common.checkpoint_manager import Checkpoint
from plugrl_server.policy.base_policy import BasePolicy, InternalState
from plugrl_server.policy.base_policy import BasePolicy
from plugrl_server.policy.state import PolicyRuntimeState, PolicyTrainState

UID = "your-algo"

Expand All @@ -63,15 +75,16 @@ class YourAlgorithm(BaseAlgorithm):
super().__init__(config=config, policy=policy)
self.global_step = 0

def infer(self, obs: dict) -> tuple[np.ndarray, InternalState]:
action, internal_state = self.policy.get_action_and_internal_state(obs)
return action, internal_state
def infer(self, obs: dict) -> tuple[np.ndarray, PolicyRuntimeState]:
action, runtime_state = self.policy.get_action_and_runtime_state(obs)
return action, runtime_state

def feedback(
self,
*,
obs: dict,
internal_state: InternalState | None,
runtime_state: PolicyRuntimeState,
train_state: PolicyTrainState = None,
terminated: bool,
truncated: bool,
next_obs: dict,
Expand All @@ -84,7 +97,7 @@ class YourAlgorithm(BaseAlgorithm):
self.global_step += 1
return prev_node, self.global_step, {}

def learn(self) -> tuple[int, dict]:
def learn_impl(self) -> tuple[int, dict]:
return self.global_step, {}

def should_learn(self) -> bool:
Expand Down
13 changes: 11 additions & 2 deletions docs/algorithm/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,18 @@ Discovery.

## Built-in algorithms

Five UIDs are registered in `plugrl-server`.

- `fpo`: FPO training loop - the algorithm the quickstarts run
- `dummy`: protocol and connectivity smoke tests
- `dppo`: DPPO training loop
- `dppo-dist`: distributed DPPO via Ray launcher
- `eval`: run a policy without training it, optionally from
`--algo.policy-checkpoint-path`
- `dppo`: DPPO training loop, needs the `dppo` extra
- `dppo-dist`: distributed DPPO via the Ray launcher, needs the `dppo` extra

Without the `dppo` extra installed, `plugrl-run-server` logs
`Could not import DPPO algorithm module` at startup and the `dppo-dist`
subcommand is absent.

## Troubleshooting

Expand Down
11 changes: 9 additions & 2 deletions docs/algorithm/index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,16 @@ plugrl-run-env-client dummy-v1 --num-episodes 1

## 内置算法

`plugrl-server` 里一共注册了五个 UID。

- `fpo`:FPO 训练循环 - 快速开始跑的就是它
- `dummy`:协议与联通性验证
- `dppo`:DPPO 训练循环
- `dppo-dist`:Ray 分布式 DPPO
- `eval`:只跑策略不训练,可用 `--algo.policy-checkpoint-path` 指定权重
- `dppo`:DPPO 训练循环,需要 `dppo` 可选依赖
- `dppo-dist`:经 Ray 启动的分布式 DPPO,需要 `dppo` 可选依赖

没装 `dppo` 可选依赖时,`plugrl-run-server` 启动会打印
`Could not import DPPO algorithm module`,并且没有 `dppo-dist` 子命令。

## 常见问题

Expand Down
25 changes: 21 additions & 4 deletions docs/algorithm/ppo.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,32 @@ Key properties.

## Common options

- Tracking: `--track.enabled true`, `--track.tracker swanlab|wandb`
- Checkpoints: `--checkpoint-base-dir ./checkpoints`, `--resume true`
- Tracking: `--track.enabled`, `--track.tracker swanlab|wandb`
- Checkpoints: `--checkpoint-base-dir ./checkpoints`, `--resume`

Multi GPU runs via Ray launcher.
`--track.enabled` and `--resume` are bare boolean flags. Writing
`--track.enabled true` or `--resume true` is a parse error - tyro reports
`Unrecognized arguments: true` and exits. The off switches are
`--track.no-enabled` and `--no-resume`. Both were written with a `true`
argument on this page.

Multi GPU runs via the Ray launcher.

```bash
plugrl-run-server-ray dppo-policy default dppo hopper --num-ddp-gpus 4
plugrl-run-server-ray dppo-policy default dppo-dist hopper --num-ddp-gpus 4
```

!!! note "The Ray launcher is not a supported path today"

The algorithm has to be `dppo-dist`, not `dppo`: `cli_ray.py` asserts
`isinstance(algo, DDPAlgorithm)`, and only `DPPOAlgoDistributed` under the
UID `dppo-dist` mixes `DDPAlgorithm` in. This page previously showed
`dppo`, which trips that assertion. The launcher also requires the `dppo`
extra, builds its worker list from the *local* GPU count - so a multi-node
cluster still only sees the head node - and its server speaks an older
dialect of the protocol than the WebSocket one. Use `plugrl-run-server`
unless you are working on the Ray path itself.

## Troubleshooting

- `--resume` does nothing: confirm the experiment directory already contains checkpoints.
Expand Down
21 changes: 18 additions & 3 deletions docs/algorithm/ppo.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,30 @@ server 以调度循环驱动训练。

## 常用参数

- 指标追踪:`--track.enabled true`、`--track.tracker swanlab|wandb`
- checkpoint:`--checkpoint-base-dir ./checkpoints`、`--resume true`
- 指标追踪:`--track.enabled`、`--track.tracker swanlab|wandb`
- checkpoint:`--checkpoint-base-dir ./checkpoints`、`--resume`

`--track.enabled` 与 `--resume` 是不带值的布尔开关。写成
`--track.enabled true` 或 `--resume true` 会直接解析失败 - tyro 报
`Unrecognized arguments: true` 并退出。关掉它们用 `--track.no-enabled`
与 `--no-resume`。本页此前这两处都多写了一个 `true`。

多 GPU 训练使用 Ray 启动。

```bash
plugrl-run-server-ray dppo-policy default dppo hopper --num-ddp-gpus 4
plugrl-run-server-ray dppo-policy default dppo-dist hopper --num-ddp-gpus 4
```

!!! note "Ray 启动器目前不是受支持的路径"

算法必须写 `dppo-dist` 而不是 `dppo`:`cli_ray.py` 里有
`isinstance(algo, DDPAlgorithm)` 断言,而只有 UID 为 `dppo-dist` 的
`DPPOAlgoDistributed` 混入了 `DDPAlgorithm`。本页此前写的是 `dppo`,
会卡在这条断言上。这个启动器还需要 `dppo` 可选依赖,它按*本机* GPU 数量
构造 worker 列表 - 所以多节点集群也只看得见头节点 - 而且它的 server 说的
是比 WebSocket 那套更旧的协议方言。除非你就是在改 Ray 这条路径,否则请用
`plugrl-run-server`。

## 常见问题

- `--resume` 没生效:确认实验目录里已经有 checkpoint。
Expand Down
25 changes: 20 additions & 5 deletions docs/env/custom_env.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@ class CustomConfig(BaseEnvConfig):

@register_env(UID)
class CustomEnv(BaseEnv):
def __init__(self, config: CustomConfig, worker_id: int | None = None, total_workers: int | None = None):
super().__init__(config=config)
self.worker_id = worker_id
self.total_workers = total_workers
def __init__(
self,
config: CustomConfig,
num_envs: int = 1,
process_id: int | None = None,
total_processes: int | None = None,
):
super().__init__(
config=config,
num_envs=num_envs,
process_id=process_id,
total_processes=total_processes,
)

def prepare_obs(self, obs: np.ndarray) -> Observation:
return Observation(images={}, states={}, text="")
Expand Down Expand Up @@ -60,6 +69,12 @@ plugrl-run-env-client custom-v1 --num-episodes 1
- Config inherits `BaseEnvConfig`.
- Implement `reset` and `step`.
- Convert raw env outputs into `Observation` in `prepare_obs`.
- `__init__` takes `config, num_envs, process_id, total_processes`, the same
four as `BaseEnv.__init__` and as the shipped `MuJoCoEnv`. `EnvSpec.make`
always passes `num_envs`, and `gym.make_vec` forwards `process_id` and
`total_processes`. An earlier version of this page used `worker_id` and
`total_workers`; those names appear nowhere in `plugrl-env-client`, and a
class with that signature raises `TypeError` on the unexpected `num_envs`.

## Registration

Expand All @@ -70,7 +85,7 @@ plugrl-run-env-client custom-v1 --num-episodes 1
## Troubleshooting

- Env ID not listed: module import did not run.
- Multi process init conflicts: try `--use-env-lock`.
- Multi process init conflicts: try `--runner.use-env-lock`.

## Next steps

Expand Down
24 changes: 19 additions & 5 deletions docs/env/custom_env.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,19 @@ class CustomConfig(BaseEnvConfig):

@register_env(UID)
class CustomEnv(BaseEnv):
def __init__(self, config: CustomConfig, worker_id: int | None = None, total_workers: int | None = None):
super().__init__(config=config)
self.worker_id = worker_id
self.total_workers = total_workers
def __init__(
self,
config: CustomConfig,
num_envs: int = 1,
process_id: int | None = None,
total_processes: int | None = None,
):
super().__init__(
config=config,
num_envs=num_envs,
process_id=process_id,
total_processes=total_processes,
)

def prepare_obs(self, obs: np.ndarray) -> Observation:
return Observation(images={}, states={}, text="")
Expand Down Expand Up @@ -61,6 +70,11 @@ plugrl-run-env-client custom-v1 --num-episodes 1
- config 继承 `BaseEnvConfig`
- 实现 `reset` 与 `step`
- 在 `prepare_obs` 中把原始输出转成 `Observation`
- `__init__` 接收 `config, num_envs, process_id, total_processes` 四个参数,
与 `BaseEnv.__init__` 以及内置的 `MuJoCoEnv` 一致。`EnvSpec.make` 总会传
`num_envs`,`gym.make_vec` 会转发 `process_id` 与 `total_processes`。本页
此前用的是 `worker_id` 与 `total_workers`,这两个名字在 `plugrl-env-client`
里根本不存在,按那个签名写的类会因为多出来的 `num_envs` 直接抛 `TypeError`。

## 注册

Expand All @@ -71,7 +85,7 @@ plugrl-run-env-client custom-v1 --num-episodes 1
## 常见问题

- CLI 找不到 env id:模块没有被 import。
- 多进程初始化冲突:可尝试 `--use-env-lock`。
- 多进程初始化冲突:可尝试 `--runner.use-env-lock`。

## 下一步

Expand Down
Loading
Loading