1. 从源码阅读到能跑起来Policy Serving 到底卡在哪OpenClaw-RL 的 Policy Serving 模块是 Agentic RL 强化学习里最容易被低估的一环。它不是一个单纯的推理服务而是 OpenClaw 环境、SGLang 推理引擎、PRM 评分器和训练队列之间的协调层。源码里它叫OpenClawAPIServer但更准确的名字应该是 Proxy Server 或者 Training Pipeline Orchestrator。它负责转发用户请求、拦截 response 和 logprobs、触发 PRM 评分、构造训练 Sample最后把样本塞进output_queue给 Slime 消费。源码阅读笔记能帮你理解调用链但真正落地时会遇到一个很现实的问题Policy Serving 启动后它要调用外部模型服务做 hint judge、teacher forward、PRM 评分这些调用都需要 API Key。如果每个环节都单独配一套 Key配置文件会变得非常难维护。这篇就聚焦一件事把 OpenClaw-RL 的 Policy Serving 配置骨架搭起来用 TaoToken 统一 Key 接入然后验证连通性最后给一份报错排查清单。适合谁看正在读 OpenClaw-RL 源码、准备在本地复现 Policy Serving 的工程师做 Agentic RL 训练、需要把推理服务和训练管道串起来的人以及被多套 API Key 配置搞烦、想统一管理的人。下面所有配置和命令都可以直接复制改掉路径和 Key 就能跑。2. TaoToken 前置统一 Key 在 Policy Serving 里的位置在 OpenClaw-RL 的架构里Policy Serving 需要访问几类外部能力hint judge 用来从下一状态里提取后见之明提示teacher forward 用来计算 teacher logprobsPRM 评分用来给助手响应打 1/-1/0。这些调用在源码里分散在_fire_opd_task()、_opd_evaluate()、_compute_teacher_log_probs()等函数中。如果每个函数都硬编码不同的 base_url 和 api_key调试会非常痛苦。TaoToken 在这里的角色是统一入口。它提供 OpenAI 兼容的 API 格式base_url 是https://taotoken.net/api你只需要一个 Key就能在 Policy Serving 的各个调用点复用。这样配置文件里只需要维护一个TAOTOKEN_API_KEY而不是五六个不同的 Key。先做前置准备。你需要拿到一个可用的 Key入口在 API Keys 页面# 访问 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite # 登录后创建一个 Key复制保存 export TAOTOKEN_API_KEYsk-你的实际Key验证 Key 是否可用最直接的方式是调一次模型对话接口curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: reply with ok}], max_tokens: 16 }如果返回里有choices字段说明 Key 和网络都正常。这一步很重要因为后面 Policy Serving 的报错里有一大半其实是 Key 或 base_url 配错了而不是源码逻辑问题。注意TaoToken 的 API 地址是https://taotoken.net/api不要在后面多加/v1之外的路径。OpenAI 兼容接口的完整路径是/api/v1/chat/completions。3. 可复制配置settings.json 与 config.toml 骨架OpenClaw-RL 的配置分两层一层是 Policy Serving 自己的 settings一层是训练框架的 config。下面给一份最小可跑的骨架你可以按自己的目录结构调整。3.1 settings.jsonPolicy Serving 侧配置这份配置对应OpenClawAPIServer启动时读取的参数。重点是sglang_chat_url指向真正的推理引擎prm_router指向评分服务taotoken段统一管理外部调用。{ server: { host: 0.0.0.0, port: 30000, submission_enabled: true, session_timeout_sec: 600 }, sglang: { chat_url: http://127.0.0.1:30001/v1/chat/completions, api_key: EMPTY, timeout_sec: 120, stream: true }, prm: { router_url: http://127.0.0.1:30002/generate, num_votes: 3, timeout_sec: 60 }, taotoken: { base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, hint_model: claude-sonnet-4-20250514, teacher_model: claude-sonnet-4-20250514, max_concurrency: 4, timeout_sec: 90 }, opd: { eval_mode: opd_prm, top_k: 20, select_best_hint: true, drop_without_next_state: true }, logging: { level: INFO, prm_record_dir: ./records/prm, opd_record_dir: ./records/opd } }几个关键点解释一下。sglang.chat_url是 Policy Serving 转发用户请求的目标源码里对应forward_to_sglang()里的httpx.post(sglang_chat_url)。prm.router_url是 PRM 评分的入口对应_fire_prm_scoring()触发的异步任务。taotoken段是这篇的重点api_key_env指向环境变量避免把 Key 写进文件。3.2 config.toml训练框架侧配置训练框架侧需要知道 Policy Serving 的地址以及输出队列怎么消费。这份 config 对应 Slime 的启动参数。[rollout] mode openclaw api_server_url http://127.0.0.1:30000 output_queue_size 256 sample_timeout_sec 300 [policy] model_name qwen3-4b served_model_name qwen3-4b tp_size 2 gpu_ids [4, 5] [reward] prm_enabled true prm_gpu_ids [6, 7] prm_num_votes 3 [opd] enabled true teacher_base_url https://taotoken.net/api teacher_api_key_env TAOTOKEN_API_KEY teacher_model claude-sonnet-4-20250514 top_k 20 [training] actor_gpu_ids [0, 1, 2, 3] actor_tp_size 4 batch_size 8 learning_rate 1e-6这里teacher_base_url和teacher_api_key_env直接复用 TaoToken 的配置。OPD 模式下_compute_teacher_log_probs()会向这个地址发请求拿 teacher 模型对原始响应的对数概率。top_k对应_compute_teacher_topk_logprobs()如果你不需要 Top-K 蒸馏可以设成 0 关掉。3.3 环境变量注入配置文件里用api_key_env引用了环境变量启动前需要注入。推荐写一个env.sh#!/usr/bin/env bash export TAOTOKEN_API_KEYsk-你的实际Key export SGLANG_API_KEYEMPTY export OPENCLAW_PORT30000 export PRM_ROUTER_URLhttp://127.0.0.1:30002/generate export LOG_LEVELINFO启动 Policy Serving 前source env.sh即可。这样 Key 不会进 git也不会出现在配置文件里。4. 启动与验证连通性检查的完整动作配置搭好后按顺序启动三个服务SGLang 推理引擎、PRM 评分服务、Policy Serving。然后做连通性验证。4.1 启动顺序# 1. 启动 SGLang 推理引擎GPU 4-5 python -m sglang.launch_server \ --model-path /models/qwen3-4b \ --port 30001 \ --tp-size 2 \ --host 0.0.0.0 # 2. 启动 PRM 评分服务GPU 6-7 python -m prm_server.launch \ --model-path /models/prm-7b \ --port 30002 \ --gpu-ids 6,7 # 3. 启动 Policy Serving source env.sh python -m openclaw_rl.api_server \ --config ./settings.json \ --port 30000启动后Policy Serving 会监听 30000 端口。源码里OpenClawAPIServer用 FastAPI Uvicorn 起服务submission_enabled是一个threading.Event初始为 set 状态表示允许提交样本。4.2 连通性验证第一步验证 Policy Serving 本身活着curl -s http://127.0.0.1:30000/health # 期望返回 {status:ok,submission_enabled:true}第二步发一个带 session 头的对话请求模拟 OpenClaw App 的行为curl -s http://127.0.0.1:30000/v1/chat/completions \ -H Content-Type: application/json \ -H X-Session-Id: test-session-001 \ -H x-Turn-Type: main \ -d { model: qwen3-4b, messages: [{role: user, content: 写一个 python 函数计算斐波那契}], stream: false }如果返回里有choices[0].message.content说明 Policy Serving 成功转发到了 SGLang。同时源码里的_buffer_record()会把这一轮存进_pending_records等待下一轮触发 PRM 评分。第三步发第二轮触发 PRM 评分和样本提交curl -s http://127.0.0.1:30000/v1/chat/completions \ -H Content-Type: application/json \ -H X-Session-Id: test-session-001 \ -H x-Turn-Type: main \ -d { model: qwen3-4b, messages: [ {role: user, content: 写一个 python 函数计算斐波那契}, {role: assistant, content: def fib(n): ...}, {role: user, content: 改成迭代版本} ], stream: false }这一轮会触发_flush_pending_record(next_state)把上一轮的 response 和这一轮的 user 输入拼成 PRM 评估的 prompt然后_fire_prm_scoring()异步发起评分。评分完成后_maybe_submit_ready_samples()会把样本放进output_queue。第四步检查样本是否进了队列。可以在训练侧加一个简单的消费者import queue import json q queue.Queue() # 假设 output_queue 已经通过某种方式暴露 while True: sample q.get(timeout30) print(json.dumps({ loss_mask: sample.loss_mask[:5], reward: sample.reward, has_teacher_lp: hasattr(sample, teacher_log_probs) }, ensure_asciiFalse))如果看到loss_mask和reward字段说明 Policy Serving 的数据管道通了。4.3 OPD 模式的额外验证如果你跑的是 OPD 模式还需要验证 teacher forward 是否成功。在第二轮请求后检查records/opd/目录下是否有 JSONL 文件生成ls -la ./records/opd/ # 期望看到类似 opd_20250101_120000.jsonl 的文件 cat ./records/opd/*.jsonl | head -1 | python -m json.tool文件里应该有accepted、hint、teacher_lp字段。如果accepted是 false说明 hint judge 没有选出有效提示样本会被 DROP不会进队列。这是 OPD 和 RL 的核心差异RL 保留loss_mask0的样本OPD 直接丢弃。5. 本篇常见错排查清单这一节按报错现象分类给排查路径。大部分问题出在配置和网络层而不是源码逻辑。5.1 401 Unauthorized现象Policy Serving 日志里出现401或者 teacher forward 返回invalid api key。排查先确认TAOTOKEN_API_KEY环境变量是否注入成功。在启动 Policy Serving 的同一个 shell 里执行echo $TAOTOKEN_API_KEY如果为空说明source env.sh没生效。再确认 Key 本身有效用第 2 节的 curl 命令单独测一次。如果 curl 能通但 Policy Serving 报 401检查settings.json里api_key_env的拼写是否和实际环境变量名一致。5.2 Connection refused现象httpx.ConnectError: [Errno 111] Connection refused。排查这是 Policy Serving 连不上 SGLang 或 PRM。先确认 SGLang 是否真的在127.0.0.1:30001监听用curl http://127.0.0.1:30001/health测。如果 SGLang 在另一台机器settings.json里的sglang.chat_url要改成实际 IP。PRM 同理prm.router_url要指向 PRM 服务的/generate端点。5.3 样本一直不进队列现象请求返回正常但output_queue里一直没有样本。排查先看submission_enabled是否为 true。源码里这个 Event 控制是否允许提交如果被 clear 了样本会一直卡在_pending_records。再看 PRM 评分是否完成_maybe_submit_ready_samples()会检查 PRM task 是否 done。如果 PRM 服务超时评分任务会一直 pending。最后检查loss_mask逻辑RL 模式下如果score0且没有下一状态样本可能被标记为excludeTrue不会进队列。5.4 OPD 样本全部被 DROP现象records/opd/目录为空或者 JSONL 里accepted全是 false。排查这是 hint judge 没有选出有效提示。检查_select_best_hint()的逻辑它选的是最长的有效正面提示。如果 teacher 模型返回的 hint 都是空或者太短会被过滤掉。可以调低opd.select_best_hint的阈值或者换一个更强的 hint 模型。另外确认drop_without_next_state是否为 true如果是没有下一状态的 turn 会直接 DROP。5.5 teacher_log_probs 维度不匹配现象训练时报shape mismatchteacher logprobs 的长度和 response_ids 对不上。排查_compute_teacher_log_probs()返回的是[T]长度的张量T 是 response token 数。如果 teacher 模型的分词器和 policy 模型不一致token 数会对不上。确认settings.json里teacher_model和config.toml里policy.model_name用的是同一套分词器或者至少 tokenizer 兼容。OPD 模式下这个问题最常见因为 teacher 和 student 往往不是同一个模型。5.6 并发过高导致限流现象日志里出现429 Too Many Requests或者 teacher forward 大量超时。排查settings.json里taotoken.max_concurrency控制并发数默认 4。如果同时有多个 session 在跑并发会叠加。可以调低这个值或者给_fire_opd_task()加一个信号量。源码里 OPD 的 teacher 查询本身有并发限制但如果你改了代码要确认信号量还在生效。6. 继续往下走把 Policy Serving 接进训练循环Policy Serving 配通之后下一步是把它接进完整的训练循环。如果你还在验证模型对话和 teacher 信号可以先用模型对话页面单独测一下 hint 和 teacher 的输出质量确认模型选型没问题再跑训练。入口在这里# https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite如果你准备长期跑 Agentic RL 训练尤其是 OPD 这种需要频繁调 teacher forward 的场景建议看一下 Coding Plan它在并发和配额上更适合训练管道这种持续调用的模式# https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite接入文档里有 OpenAI 兼容接口的完整参数说明包括 stream、top_k、logprobs 这些 Policy Serving 会用到的字段# https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite最后提醒一个实操细节Policy Serving 的output_queue是内存队列训练进程重启后队列里的样本会丢。如果你在调试阶段频繁重启建议把_submit_turn_sample()里的样本同时落一份到磁盘方便回放。这个改动很小但在排查loss_mask和reward异常时非常有用。
阅读完成 · 觉得有帮助?