Skip to content

Interactive REPL demo v2: oc-repl × run-3 iter215 (4 tasks × 2 protocols) #13

Description

@HansBug

Interactive REPL demo (v2):用自研 oc-replrun-3 iter215 上跑 4 个 TB 任务 × 2 套协议

接续 #10 / #11 / #13 的工作。基于 #13 评论 里查明的 "camel 和 terminus 都没官方 REPL" 的结论,自己开了个独立工具 HansBug/oc-repl,用它重录了这次 demo——单一 CLI、Codex 风格 UI、一行启动、交互模式 + 非交互 exec 模式都支持。

TL;DRoc-repl 把 camel-ai 的 TerminalToolkit 协议(openai-tools)和 terminal-bench terminus-2 的 JSON 协议(terminus-json)统一在一个 30 行级别的 Codex-style REPL 里。<think> 默认折成 spinner,正式 assistant 输出(analysis + plan)和 tool block 视觉权重对等,命令输出做了篇幅控制(10 行 × 180 字符 + … N more lines clipped)。8 段动图 = 4 个真实 TB 任务 × terminus-json / openai-tools 两条协议。


1. 工具:HansBug/oc-repl

3 套协议在一个 CLI 里:

--protocol 来源 模型一次性输出
terminus-json(默认) terminal-bench/.../terminus-json-plain.txt {analysis, plan, commands[].keystrokes, task_complete}
terminus-xml terminal-bench/.../terminus-xml-plain.txt <response><analysis/><plan/><commands>…</commands><task_complete/></response>
openai-tools camel-ai TerminalToolkit OpenAI tool_callsshell + write_file 两个函数)

启动一行:

oc-repl --protocol terminus-json \
        --sandbox docker:openclaw-fixperm \
        --api-base http://127.0.0.1:30000/v1 \
        --model qwen3-8b-rl-iter215

非交互(Codex exec 等价品):

oc-repl exec --protocol terminus-json --sandbox docker:openclaw-fixperm \
    "Fix /app/process_data.sh so it can run, then run it."
# 退出码:0 = task_complete / 1 = parse 错误 / 2 = 没 task_complete / 3 = verify FAIL
# --json 输出机器可读的 {rounds, commands, task_complete, last_summary, verify}

带评分钩子(agent 自报 task_complete 后跑一段验证脚本,把"agent 觉得自己赢了"和"任务真做对了"分开):

oc-repl exec --sandbox docker:openclaw-recover \
    --verify-file /nfs/terminal-rl-workspace/openclaw-repl-demo/scripts/check_recover.sh \
    "Decode each *.b64_content in /app/sensitive_data/ — basename and content are both base64. Restore them into /app/recovered/."
# 结尾会多出一个 ✓ verified(绿) / ✗ verification failed(红)面板。

--sandbox 不是 required option,省略时默认 local(host shell,banner 会用黄字醒目提醒「⚠ commands will run on the host shell」)。docker:NAME 模式假设容器已经预先 docker run -d 起好,oc-repl 只 attach、不 spawn。

详见仓库 READMEAGENTS.md

2. UI 设计要点(响应 #13 评审)

元素 行为
<think>...</think> 默认隐藏,渲染成 thinking… 3.2s · 184 chars spinner。--show-thinking 或运行时 /think 切换。
analysis / plan 等正式输出 Codex 风格:assistant cyan 灯片 + 加粗 cyan 标签 + 缩进 normal-weight prose,与 tool block 等权重
Tool call 框圆角面板,标题栏 ▸ shell,命令一行加粗,captured stdout/stderr 收紧到 ≤10 行 × ≤180 字符,超出显示 … (N more lines clipped)exit 0 绿底、非零红底。
末尾 ✓ rounds=N commands=M task_complete=… verify=… 一行 footer,回到 提示。
sandbox 默认 local(host shell,banner 黄字警告);docker:NAME 自动检测当前 shell 是否有 docker group,没有就自动 wrap sg docker -c '...',并且只 attach 已经 running 的容器。
verify hook --verify "<bash cmd>"--verify-file PATH — agent 自报 done 后在 sandbox 跑一段评分脚本,结果用绿/红面板展示,覆盖 model 自报 task_complete 的语义。

3. Demo 视频:4 任务 × 2 协议

每条 demo:交互模式,banner → 输入 instruction → 模型 think(spinner 隐藏)→ analysis/plan + tool blocks → task_complete 标志 → 回到 prompt → /quit。末尾静止 8 秒。

任务选自 #10 long-comment 的 truly-learnable 桶 + 新加的 hello-world 起步例:

task difficulty 演示内容
hello-world easy echo Hello, world! > /app/hello.txt
fix-permissions easy 诊断 process_data.sh 权限 → chmod +x → 跑一次
recover-obfuscated-files hard base64 还原文件名 + 内容到 /app/recovered/
heterogeneous-dates medium paste + awk 算两个 CSV 的平均差

3.1 terminus-json(与 iter215 RL 训练分布对齐的协议)

任务 GIF MP4
hello-world mp4
fix-permissions mp4
recover-obfuscated-files mp4
heterogeneous-dates mp4

3.2 openai-tools(camel-ai TerminalToolkit 兼容协议)

任务 GIF MP4
hello-world mp4
fix-permissions mp4
recover-obfuscated-files mp4
heterogeneous-dates mp4

4. 复现 / 接管 runbook 摘要

完整版见持久路径 /nfs/terminal-rl-workspace/openclaw-repl-demo/README.md 以及 HansBug/oc-repl/README.md

# 1. 起 sglang(参数:--tool-call-parser qwen,不带 --reasoning-parser 以保留 <think> 流)
python -m sglang.launch_server --model-path /nfs/eval_models/qwen3-8b-rl-iter215 \
    --served-model-name qwen3-8b-rl-iter215 --port 30000 --tp 1 --mem-fraction-static 0.85 \
    --tool-call-parser qwen

# 2. 起 sandbox 容器(每个 TB 任务一个长寿 docker)
docker run -d --name openclaw-hello    -w /app tb__hello-world__client            sleep infinity
docker run -d --name openclaw-fixperm  -w /app tb__fix-permissions__client        sleep infinity
docker run -d --name openclaw-recover  -w /app tb__recover-obfuscated-files__client sleep infinity
docker run -d --name openclaw-hetdates -w /app tb__heterogeneous-dates__client    sleep infinity

# 3. 装 oc-repl
git clone https://github.com/HansBug/oc-repl && cd oc-repl && pip install -e .

# 4. 跑!
oc-repl --protocol terminus-json --sandbox docker:openclaw-fixperm
# 或者
oc-repl exec --protocol openai-tools --sandbox docker:openclaw-fixperm \
    "Fix /app/process_data.sh and run it"

录像通过 VHS 完成,scripts/make_oc_tapes.py 生成 tape,scripts/render_oc_all.sh 批量渲染 + 重置容器。

5. 训练协议 vs. 推理协议(FAQ — 已校正)

短答:ckpt215(以及 run-3 / run-4 etc.)的 RL 训练用的是 camel-ai ChatAgent + camel.toolkits.TerminalToolkit,sglang qwen25 tool-call parser;不是 terminus-2。terminus-2 只在 tb run 评测时用,不进 RL rollout 路径。oc-repl 0.2.0 新加了 --protocol camel-terminal-toolkit 来精确复刻这个分布,且已经作为默认

5.1 证据链

挨条对着源码看:

  1. wandb 启动命令里没有 --terminal-agent-type flagrun msp60ius(产出 iter215 那一次)的完整命令:

    python slime/train_async.py ... \
        --custom-generate-function-path generate.generate \
        --custom-config-path .../configs/rollout_qwen3.yaml
    

    没有 --terminal-agent-type,也没有任何环境变量改写。

  2. generate.py:142 默认值是 camel_agent

    rollout_agent = create_rollout_agent(
        agent_type=getattr(args, "terminal_agent_type", "camel_agent"),
        ...
    )
  3. rollout_agent.py:97-106 只处理 camel_agent 一条路径——其他值直接 raise ValueError

    if agent_type == "camel_agent":
        from agent.camel_agent import CamelAgent
        return CamelAgent(...)
    raise ValueError(f"Unsupported agent type: {agent_type!r}. Expected 'camel_agent'.")

    仓库里根本没有 terminus 这条路

  4. agent/camel_agent.py 是 camel.ChatAgent 的子类

    from camel.agents import ChatAgent
    ...
    class CamelAgent(ChatAgent):
        """ChatAgent extension used by AgentRunner's rollout loop."""
  5. remote/terminal_env.py:163-185 把 camel TerminalToolkit 的 4 个工具 schema 喂给 rollout

    self._terminal_toolkit = TerminalToolkit(use_docker_backend=True, ...)
    self._tools = {
        "shell_exec": ...shell_exec,
        "shell_view": ...shell_view,
        "shell_write_to_process": ...shell_write_to_process,
        "shell_write_content_to_file": ...shell_write_content_to_file,
    }
    tool_schemas = [FunctionTool(fn).get_openai_tool_schema() for fn in self._tools.values()]
  6. sglang 用 qwen25 parser 把模型的 <tool_call>...</tool_call> 转回结构化 tool_calls

    # configs/rollout_qwen3.yaml
    tool_call_parser: qwen25
  7. 训练 log 实证terminal-rl/logs/training_8b_tboverfit.log<tool_call> 出现 628 次,terminus-2 JSON 的 key "task_complete" 出现 0 次。模型实际吐的是:

    ToolCallRequest(tool_name='shell_exec',
        args={'id': '1', 'command': 'mkdir -p /app/ssl', 'block': True, 'timeout': 20},
        tool_call_id='call_9de3e5a5e3ce4d18bf20a0a8')
    

5.2 oc-repl 0.2.0:camel-terminal-toolkit 协议

新加了一个 protocol,对齐到训练分布字节级别

  • 4 个工具 schema:从 camel-ai==0.2.90FunctionTool.get_openai_tool_schema() 抓出来逐字 hardcode,包括 ["boolean", "null"] union 类型 + "strict": true 等训练时模型真的见过的 JSON-schema 细节。
  • System promptterminal-rl/agent/camel_agent.py::get_developer_agent_prompt(system='Linux (in Docker)', machine='x86_64', is_workforce=False, non_think_mode=True) 的整段输出原样复制,结尾带 /no_think
  • /no_think 这部分是 critical:training-time non_think_mode=True 是默认值(rollout_agent.py:62),模型 RL 训练时根本没生成过 <think> 块;如果把这个去掉、放任 <think> 跑,tool-call adherence 会塌掉(亲测:在 recover 任务上让 think 开着,模型生成 1500+ token 的 prose 然后 0 个 tool_call,verify 直接红)。
  • Sandbox 映射shell_exec 全保真;shell_write_content_to_file 用 here-doc 100% 保真;shell_view / shell_write_to_process 因为 oc-repl sandbox 是 stateless docker exec(不维护 per-id tmux session),降级为 stub 输出,schema 还在但 session 语义只是「best-effort」。

5.3 其他 protocol 在 iter215 上的语义

protocol 是否训练分布 实测表现
camel-terminal-toolkit(默认,新增 0.2.0) ✅ 字节级匹配 adherence 满分;recover 验证不通过是模型本身没解 filename 那层 base64 的能力问题,不是协议问题
openai-tools(简化版 1 个 shell(command) 工具) ❌ 不匹配 简单单步任务 OK(chmod +x 那种);复杂任务偶尔失配
terminus-json ❌ 模型 RL 阶段从没见过 靠 qwen3 底座的 JSON 模仿能力跑通;adherence 中等
terminus-xml ❌ 同上 比 JSON 还冷,8B 上 adherence 更差

→ 「这个 ckpt 应该用哪个」的答案现在是 camel-terminal-toolkit(默认就是它)。

本文档之前给过错误结论(声称训练用 terminus-2),原因是把 evaluation 时的 tb run --agent terminus-2 当成了训练 scaffolding。这一节是更正版,证据全部来自源码 + wandb 启动命令 + 训练 log。

6. 资产清单

📦 打包下载oc-repl-demo-v2.zip (8.7 MB) 包含全部 8 个 .tape、16 个 .gif + .mp4、make_oc_tapes.pyrender_oc_all.sh 以及一个简要的复现 README。

路径 作用
github.com/HansBug/oc-repl/ REPL 工具仓库(pip install -e .
/nfs/terminal-rl-workspace/oc-repl/ 工具本地路径
/nfs/terminal-rl-workspace/openclaw-repl-demo/scripts/make_oc_tapes.py 8 段 VHS scenario 生成器
/nfs/terminal-rl-workspace/openclaw-repl-demo/scripts/render_oc_all.sh 重置 sandbox + 批量 vhs 渲染
/nfs/terminal-rl-workspace/openclaw-repl-demo/scenarios-oc/*.tape 8 个 VHS scenario
/nfs/terminal-rl-workspace/openclaw-repl-demo/output-oc/oc-*.{gif,mp4} 8 段最终视频
/nfs/terminal-rl-workspace/openclaw-repl-demo/scripts/check_recover.sh recover 任务的示例 verify 脚本

之前版本(旧的 6 段 demo + camel_repl.py wrapper):见 #13 的提交历史;那一版的脚本和媒体保留在 scripts/{camel_repl.py,repl-camel,repl-terminus} + output/,仍可作为对照参考。本次的 v2 demo 把所有协议统一在 oc-repl 里,体验和 Codex 一致。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions