【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (10)--- PRM 与 TaoToken 配置实战

发布时间:2026/10/5 23:07:42
【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (10)--- PRM 与 TaoToken 配置实战 1. 从一次 PRM 打分失败说起OpenClaw-RL 源码阅读里最容易卡住的环节如果你正在读 OpenClaw-RL 的源码大概率会在 PRM 这一层停下来。原因不复杂这个项目里叫 PRM 的东西跟教科书上的 Process Reward Model 不是一回事。它本质是一个 zero-shot LLM Judge用同族模型Qwen3通过 prompt 对整条 response 打\boxed{1}/\boxed{0}/\boxed{-1}再做 majority vote 降噪。理解这一点之后源码里那些_build_prm_judge_prompt、_majority_vote、at-least-one guarantee的写法就顺了。但真正让人卡住的不是概念是环境。OpenClaw-RL 的 PRM Server 是一个独立的 SGLangRouter 进程Policy Server 是另一个 FastAPI 服务两者通过 HTTP 通信。你在本地想跑通一次 PRM 打分链路需要同时把 Judge 模型的推理端点、Policy 侧的调用地址、以及训练脚本里的模型 ID 对齐。任何一处不一致就会看到local proxy failed或者reading choices这类报错。这篇笔记的目标很具体在源码阅读的过程中用 TaoToken 统一 Key/API 通道把本地 PRM 打分链路跑通一次。你会拿到可复制的 settings 配置片段、Base URL 写法、Model ID 对照以及从_prm_evaluate到_majority_vote的完整验证步骤。适合已经在读 OpenClaw-RL 源码、想动手验证 PRM 模块行为的人。先说清楚 TaoToken 在这里扮演什么角色。它是一个统一的模型 API 通道把不同厂商的模型收敛到同一个 Base URL 和同一套 Key 体系下。对 OpenClaw-RL 这种需要同时调用 Judge 模型和 Policy 模型的框架来说好处是你不用为每个模型单独维护 endpoint 和鉴权。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 根地址是 https://taotoken.net/api 。我试过在本地把 PRM Judge 指向 TaoToken 的通道然后用一个最小的 Python 脚本模拟_prm_evaluate的调用确认返回的\boxed{}能被正确解析。下面把整个过程拆开写。2. TaoToken 前置准备Key、Base URL 与 PRM Judge 模型选型在动 OpenClaw-RL 的代码之前先把 TaoToken 侧的三个东西准备好API Key、Base URL、以及你要用作 PRM Judge 的 Model ID。这三样东西后面会同时出现在 settings 配置和源码里的self._prm_url调用中。2.1 获取 API Key 与确认 Base URL登录 TaoToken 控制台后在 API Keys 页面创建一个新的 Key。这个 Key 是后续所有请求的鉴权凭证。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API Keys 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。创建完成后你会拿到一串以sk-开头的字符串。把它存到环境变量里不要硬编码进源码export TAOTOKEN_API_KEYsk-你的实际Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api注意 Base URL 是https://taotoken.net/api不带任何路径后缀。OpenClaw-RL 里 SGLang 的调用习惯是往 Base URL 后面拼/v1/chat/completions所以你在配置里填的应该是根地址让框架自己去拼路径。这一点如果搞反了会直接导致 404。2.2 PRM Judge 的 Model ID 怎么选OpenClaw-RL 源码里 PRM 用的是同族 Qwen3 模型做 zero-shot 评分。你在 TaoToken 通道下选 Model ID 时要选一个指令跟随能力足够强、能稳定输出\boxed{}格式的模型。因为 PRM 的 prompt 里明确要求 give your final score inside \boxed{}模型如果格式跟随不好解析就会失败。在 TaoToken 的模型列表里确认你要用的 Model ID。常见的做法是选一个中等规模的指令模型作为 Judge因为 PRM 每个 turn 要跑 m3 次投票成本是 Policy 推理的三倍。Model ID 的准确字符串以控制台模型列表为准不要凭记忆写。你可以先用模型对话页面快速验证一下模型能不能按格式输出。对话入口在 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。把 PRM 的 system prompt 贴进去看它是否返回带\boxed{1}的回复。这一步花两分钟能省掉后面半小时的解析调试。2.3 三件套对照表把下面这张表填好后面配置时直接抄配置项值出现位置Base URLhttps://taotoken.net/apisettings、self._prm_urlAPI Keysk-...环境变量、请求头Model ID控制台确认的字符串PRM Judge 调用参数这三件套在 OpenClaw-RL 里会出现在两个地方一是 Policy Server 转发请求时的上游地址二是 PRM Server 自己作为 Judge 被调用时的模型参数。如果你用的是 CC Switch 或 Cline MCP 这类工具来管理多模型配置也要把这三个值填进对应的 provider 配置里。3. 可复制配置settings 片段与 PRM 调用参数对齐这一节给你可以直接复制的配置片段。OpenClaw-RL 的配置分散在几个地方训练脚本的环境变量、SGLangRouter 的启动参数、以及 Policy Server 的上游地址。我们逐个对齐。3.1 环境变量配置片段在训练脚本的启动环境里把 PRM 相关的变量指向 TaoToken 通道。下面是一个可复制的 shell 片段# TaoToken 统一通道 export TAOTOKEN_API_KEYsk-你的实际Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api # PRM Judge 配置对应源码里的 self._prm_url 与 PRM_MODEL_PATH export PRM_BASE_URL${TAOTOKEN_BASE_URL} export PRM_API_KEY${TAOTOKEN_API_KEY} export PRM_MODEL_ID你在控制台确认的Model ID export PRM_M3 # Policy Server 上游对应 openclaw_opd_api_server.py 的转发目标 export POLICY_UPSTREAM_BASE_URL${TAOTOKEN_BASE_URL} export POLICY_UPSTREAM_API_KEY${TAOTOKEN_API_KEY} export POLICY_MODEL_ID你的Policy模型ID # 服务端口 export HOST0.0.0.0 export PORT30000这里的关键是PRM_BASE_URL和POLICY_UPSTREAM_BASE_URL都指向同一个 TaoToken 根地址。OpenClaw-RL 的架构里PRM Server 和 Policy Server 是两个独立进程但它们可以共用同一个上游通道只是用的 Model ID 不同。3.2 JSON 格式的 provider 配置如果你用 CC Switch 或类似的配置管理工具下面是一个 JSON 片段把 TaoToken 作为一个 provider 注册进去{ providers: { taotoken: { base_url: https://taotoken.net/api, api_key: ${TAOTOKEN_API_KEY}, models: { prm_judge: 你在控制台确认的Judge模型ID, policy: 你的Policy模型ID } } }, prm: { provider: taotoken, model: prm_judge, majority_vote_m: 3, timeout_seconds: 60 } }这个片段里的base_url和api_key就是三件套里的前两件model字段对应第三件。majority_vote_m对应源码里的PRM_M3。3.3 TOML 格式如果你用 Codex 风格的配置有些工具链用 TOML 管理配置。下面是对应的写法[providers.taotoken] base_url https://taotoken.net/api api_key ${TAOTOKEN_API_KEY} [prm] provider taotoken model 你在控制台确认的Judge模型ID majority_vote_m 3 [policy] provider taotoken model 你的Policy模型ID3.4 源码里self._prm_url的对齐OpenClaw-RL 的openclaw_api_server.py里PRM 调用是通过self._prm_url发起的。你需要确保这个 URL 的构造方式跟你的配置一致。源码里通常是这样的模式# 源码中的调用模式示意 self._prm_url f{PRM_BASE_URL}/v1/chat/completions headers {Authorization: fBearer {PRM_API_KEY}} payload { model: PRM_MODEL_ID, messages: _build_prm_judge_prompt(response_text, ns_text, ns_role), temperature: 0.7, max_tokens: 512, }注意temperature这里不是 0。源码里 PRM 的随机性是有意保留的因为后面要靠 majority vote 降噪。如果你把 temperature 设成 0三次投票结果会完全一样majority vote 就失去意义了。_build_prm_judge_prompt返回的是一个 messages 列表里面包含 system prompt 和 user prompt。system prompt 里写明了评分规则user prompt 里放了response_text和next_state_text。这个结构跟 OpenAI 兼容的 chat completions 格式一致所以直接指向 TaoToken 的/v1/chat/completions就能用。3.5 一个容易忽略的点next_state_role源码里_build_prm_judge_prompt的签名是(response_text, next_state_text, next_state_role)。next_state_role有两个取值user和tool。这个参数会拼进 user prompt 里告诉 Judge 这条 next_state 是用户回复还是工具返回值。如果你在本地构造测试数据时忘了传这个参数Judge 的评分依据会不完整可能给出偏中性的分数。在配置层面这个参数不需要你设置它是运行时从对话历史里推断的。但你在写验证脚本时要手动指定否则测出来的分数不能反映真实行为。4. 验证请求跑通一次 PRM 打分链路配置对齐之后下一步是实际发一次请求确认 PRM 打分链路能跑通。我们分两步先用 curl 直接打 TaoToken 通道确认鉴权和模型可用再用 Python 模拟_prm_evaluate的完整流程包括 majority vote。4.1 用 curl 验证通道连通性先构造一个最小的 PRM 评分请求。下面这个 curl 命令模拟了 Judge 收到一条 response 和一条 next_state 后的评分过程curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer ${TAOTOKEN_API_KEY} \ -H Content-Type: application/json \ -d { model: ${PRM_MODEL_ID}, messages: [ { role: system, content: You are a process reward model (PRM) evaluating an AI assistant. Decide whether the assistant output successfully fulfilled the user intent, using the next state as evidence. Scoring rules: boxed{1} good, boxed{-1} bad, boxed{0} neutral. Think step-by-step, then give your final score inside boxed{}. }, { role: user, content: ## Assistant response (turn t)\nThe square root of 1764 is 42.\n\n## Next state (turn t1) [role: user]\nGreat, that is correct. Can you also compute the square root of 2025?\n\nNow output your decision. } ], temperature: 0.7, max_tokens: 512 }如果通道正常你会拿到一个 JSON 响应choices[0].message.content里应该包含\boxed{1}。这一步验证了三件事Key 有效、Base URL 正确、Model ID 存在。如果返回 401说明 Key 没传对或者过期了。如果返回 404大概率是 Base URL 多写了或漏写了路径。如果返回的 content 里没有\boxed{}说明模型格式跟随不好换一个 Model ID 试试。4.2 用 Python 模拟_prm_evaluate与 majority votecurl 通了之后写一个 Python 脚本完整模拟源码里的评分流程。这个脚本会调用三次 Judge然后做 majority voteimport os import re from collections import Counter import requests BASE_URL os.environ[TAOTOKEN_BASE_URL] API_KEY os.environ[TAOTOKEN_API_KEY] MODEL_ID os.environ[PRM_MODEL_ID] PRM_M int(os.environ.get(PRM_M, 3)) def build_prm_judge_prompt(response_text, next_state_text, next_state_roleuser): system ( You are a process reward model (PRM) evaluating an AI assistant. Decide whether the assistant output successfully fulfilled the user intent, using the next state as evidence. Scoring rules: boxed{1} good, boxed{-1} bad, boxed{0} neutral. Think step-by-step, then give your final score inside boxed{}. ) user ( f## Assistant response (turn t)\n{response_text}\n\n f## Next state (turn t1) [role: {next_state_role}]\n{next_state_text}\n\n Now output your decision. ) return [{role: system, content: system}, {role: user, content: user}] def query_judge_once(messages): resp requests.post( f{BASE_URL}/v1/chat/completions, headers{Authorization: fBearer {API_KEY}}, json{ model: MODEL_ID, messages: messages, temperature: 0.7, max_tokens: 512, }, timeout60, ) resp.raise_for_status() content resp.json()[choices][0][message][content] match re.search(r\\boxed\{(-?[01])\}, content) if not match: return None return int(match.group(1)) def majority_vote(scores): valid [s for s in scores if s is not None] if not valid: return 0.0 counter Counter(valid) top counter.most_common(1)[0] if list(counter.values()).count(top[1]) 1: return 0.0 return float(top[0]) def prm_evaluate(response_text, next_state_text, next_state_roleuser): messages build_prm_judge_prompt(response_text, next_state_text, next_state_role) scores [query_judge_once(messages) for _ in range(PRM_M)] return majority_vote(scores), scores if __name__ __main__: score, raw prm_evaluate( response_textThe square root of 1764 is 42., next_state_textGreat, that is correct. Can you also compute the square root of 2025?, next_state_roleuser, ) print(fraw scores: {raw}) print(ffinal score: {score})这个脚本里的majority_vote函数跟源码里的_majority_vote逻辑一致过滤 None取众数平票返回 0.0。prm_evaluate对应源码里的_prm_evaluate内部调用_query_judge_once三次。4.3 预期结果与解读跑通之后你会看到类似这样的输出raw scores: [1, 1, 1] final score: 1.0或者raw scores: [1, 1, -1] final score: 1.0如果三次投票结果不一致比如[1, -1, 0]最终分数会是 0.0对应源码里的平票保守策略。这个行为在训练时意味着这个 turn 的loss_mask会被设为 0不参与梯度更新。你可以改一下response_text故意给一个错误答案比如 The square root of 1764 is 43.看 Judge 是否给出 -1。这一步能验证 Judge 的判别能力是否符合预期。4.4 验证 at-least-one guarantee源码里有一个特殊逻辑当一个 session 的所有 turn 评分都是 0 时强制把第一条被评估的 turn 的loss_mask设为 1。你可以在脚本里模拟这个场景连续构造几条中性反馈看最终是否有至少一条样本被保留。这个逻辑在源码里的位置是_submit_turn_sample()核心判断是self._session_effective.get(session_id, 0) 0。你不需要在验证脚本里完全复现但要知道它的存在因为调试训练信号消失问题时这个保底机制是第一个要检查的点。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节对照真实报错给出排查路径。这些报错是 OpenClaw-RL 本地配置时最常遇到的。5.1 401 Unauthorized报错长这样{error: {message: Invalid API key, type: invalid_request_error}}原因通常是 Key 没传对。检查三处环境变量TAOTOKEN_API_KEY是否导出成功、请求头里的Authorization是否是Bearer sk-...格式、Key 是否在控制台被禁用或删除。一个容易忽略的点如果你在 shell 里export了 Key但在另一个终端窗口跑脚本那个窗口是拿不到这个环境变量的。用echo $TAOTOKEN_API_KEY确认一下。5.2 local proxy failed报错长这样local proxy failed: connection refused这个报错通常出现在 Policy Server 转发请求到上游时。OpenClaw-RL 的 Policy Server 监听 30000 端口它会把请求转发到POLICY_UPSTREAM_BASE_URL。如果这个地址填的是localhost或某个不存在的端口就会 connection refused。检查POLICY_UPSTREAM_BASE_URL是否指向https://taotoken.net/api。如果你之前把它填成了本地 SGLang 的地址比如http://localhost:8000改成 TaoToken 的根地址。另一个可能你的本地网络环境对taotoken.net的解析有问题。用curl -v https://taotoken.net/api/v1/chat/completions看一下连接过程确认 DNS 解析和 TLS 握手正常。5.3 reading choices 相关报错报错长这样KeyError: choices或者IndexError: list index out of range这个报错说明响应 JSON 里没有choices字段或者choices是空列表。原因通常是上游返回了一个错误响应但你的代码直接去取choices[0]了。排查方法在解析响应之前先把原始响应打出来。在query_judge_once里加一行print(resp.text)看上游到底返回了什么。常见的情况是返回了{error: ...}但代码没检查resp.status_code就直接解析。源码里的_query_judge_once有对响应的校验逻辑你在本地复现时要确保这部分没被跳过。5.4 OAuth 相关报错报错长这样OAuth token expired或者invalid_grant如果你用的是 Codex 风格的auth.json来管理凭证可能会遇到这个。auth.json里的 token 有过期时间过期后需要刷新。检查你的auth.json里expires_at字段是否已经过了当前时间。如果你同时用 TaoToken 的 API Key 和某个 OAuth 凭证确认请求走的是哪条路径。OpenClaw-RL 的 PRM 调用应该走 API Key 路径不走 OAuth。如果配置里混了把 OAuth 相关的字段清掉只保留base_url、api_key、model三件套。5.5 报错对照表报错关键词最可能原因检查点401 UnauthorizedKey 无效或未传环境变量、请求头格式local proxy failed上游地址错误POLICY_UPSTREAM_BASE_URLreading choices响应无 choices 字段先打印原始响应OAuth token expired凭证过期auth.json的expires_atboxed 解析失败模型格式跟随差换 Model ID 或调 prompt5.6 一个隐蔽的坑temperature 与投票如果你把 PRM 的temperature设成 0三次投票会返回完全一样的结果。这时候 majority vote 看起来总是通过但实际上没有起到降噪作用。源码里默认 temperature 是大于 0 的你在配置时不要为了稳定把它改成 0。反过来如果 temperature 太高比如 1.5三次投票可能给出三个不同结果最终平票返回 0.0导致大量样本被丢弃。建议保持在 0.7 左右跟源码默认值对齐。6. 继续深入从 PRM 打分到 OPD 与 Combine 的配置延伸跑通 PRM 打分链路之后你可以沿着源码继续往下读。OpenClaw-RL 的 PRM 在两个分支里扮演不同角色Binary RL 分支里它是评分员输出 ±1/0 直接作为 rewardOPD 分支里它是 hint 提取器输出[HINT_START]...[HINT_END]文本再喂给 teacher 做 forward pass。如果你要验证 OPD 分支需要额外配置 teacher 模型的调用。teacher 也可以走 TaoToken 通道只是 Model ID 换成 teacher 对应的模型。配置结构跟 PRM 一样三件套对齐即可。Combine 分支则是同时跑两条路径同一个 turn 并发发起 PRM eval 和 hint judge 两种 prompt分别决定是否发 RL 样本和 OPD 样本。这时候你的 TaoToken 通道会同时承载 Judge 调用和 teacher 调用注意并发量和超时设置。对于长期跑编码或 Agent 任务的场景可以考虑用 Coding Plan 来管理调用配额入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你的验证脚本需要频繁调用模型接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 有更详细的参数说明。最后留一个实用技巧在本地调试 PRM 时把每次 Judge 的原始响应写到日志文件里包括response_text、next_state_text、raw_scores、final_score。这样当你发现某个 turn 的评分不符合预期时可以回溯到具体的 Judge 输出看是 prompt 构造问题还是模型判断问题。这个日志在源码里没有现成的需要你自己在_query_judge_once外面包一层。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询