-
Notifications
You must be signed in to change notification settings - Fork 64
[examples]feat: add Claude Code blackbox training recipe #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
zhaizhiqiangA
wants to merge
11
commits into
verl-project:main
Choose a base branch
from
zhaizhiqiangA:blackbox_cc_recipe
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
0164e4f
claude code recipe
zhaizhiqiangA 70daef7
feat(gateway): add provider adapters and Anthropic messages
zackcxb 7af71e4
feat(gateway): add standalone debug launcher
zackcxb a6de3ed
Merge branch 'blackbox_cc_recipe' into cxb/blackbox-cc-anthropic-adapter
zhaizhiqiangA d521e18
claude code blackbox recipe
zhaizhiqiangA d1d252c
Merge pull request #31 from zackcxb/cxb/blackbox-cc-anthropic-adapter
zhaizhiqiangA d5ec374
remove redundant files
zhaizhiqiangA 38800c0
fix(framework): tag trajectory materialization reason
zackcxb 1db7cca
Merge pull request #36 from zackcxb/cxb/blackbox-cc-anthropic-adapter
zhaizhiqiangA e954a3b
add infer script
zhaizhiqiangA 55483d4
fix pre-commit check error
zhaizhiqiangA File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
21 changes: 21 additions & 0 deletions
21
examples/blackbox_recipes/claude_code/Dockerfile.claude-code-tool
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Claude Code sidecar tool image. | ||
| # | ||
| # Mounted at /opt/claude-code inside the SWE-bench sandbox. | ||
|
|
||
| FROM node:20-bookworm-slim AS builder | ||
|
|
||
| ARG TOOL_VERSION="latest" | ||
| ARG NPM_REGISTRY="" | ||
|
|
||
| ENV DISABLE_AUTOUPDATER=1 \ | ||
| IS_SANDBOX=1 \ | ||
| npm_config_audit=false \ | ||
| npm_config_fund=false \ | ||
| npm_config_update_notifier=false | ||
|
|
||
| RUN if [ -n "${NPM_REGISTRY}" ]; then npm config set registry "${NPM_REGISTRY}"; fi \ | ||
| && npm install -g --prefix /opt/claude-code "@anthropic-ai/claude-code@${TOOL_VERSION}" \ | ||
| && /opt/claude-code/bin/claude --version | ||
|
|
||
| FROM scratch | ||
| COPY --from=builder /opt/claude-code / |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| # Claude Code In-Sandbox Execution | ||
|
|
||
| ## Overview | ||
|
|
||
| Claude Code runs inside the SWE-bench sandbox through a sidecar tool image. The | ||
| external runner creates the sandbox, mounts the tool image at `/opt/claude-code`, | ||
| invokes the `claude` binary against the gateway URL, and evaluates the reward in | ||
| the same sandbox. | ||
|
|
||
| Unlike the mini-swe-agent recipe, there is no in-sandbox Python entrypoint | ||
| (`run_agent.py`): the runner builds a single `claude -p ...` command and executes | ||
| it directly. The agent reaches the LLM gateway through the sandbox-internal | ||
| tunnel (`ANTHROPIC_BASE_URL` rewritten to `http://127.0.0.1:<proxy_port>`). | ||
|
|
||
| The Claude Code tool image uses a Node builder to install the | ||
| `@anthropic-ai/claude-code` npm package, then copies the result into a minimal | ||
| `FROM scratch` final stage. The sandbox base image therefore does not need Node | ||
| or npm for the sidecar tool runtime. | ||
|
|
||
| **This recipe is self-contained.** It shares only | ||
| [`../sandbox_client.py`](../sandbox_client.py) with the mini-swe-agent recipe; | ||
| everything else (`dataset.py`, `reward.py`, `build_tool.sh`, `run_train.sh`, | ||
| config) lives in this directory and does not depend on `mini_swe_agent/`. | ||
|
|
||
| **Supported runners:** | ||
|
|
||
| | runner | Description | | ||
| |--------|-------------| | ||
| | `claude_code` | Claude Code sidecar runner | | ||
|
|
||
| **Supported sandbox types:** | ||
|
|
||
| | Type | Description | | ||
| |------|-------------| | ||
| | openyuanrong | Uses `akernel_sdk.Mount` and `sandbox.commands.run()` | | ||
|
|
||
| ## Architecture | ||
|
|
||
| ```text | ||
| [Rollouter Host: claude_code_runner] | ||
| | | ||
| |-- SandboxClient.create(image, sidecar_image, sidecar_target="/opt/claude-code") | ||
| | `-- akernel: Sandbox(mounts=[Mount(target="/opt/claude-code", ...)]) | ||
| | | ||
| |-- sandbox.run("<env> /opt/claude-code/bin/claude -p <task> ...") | ||
| | `-- [Inside Sandbox] | ||
| | claude binary, ANTHROPIC_BASE_URL -> 127.0.0.1:<proxy_port> | ||
| | commands run inside the SWE-bench sandbox /testbed | ||
| | | ||
| |-- SandboxEnvForReward(sandbox) -> evaluate_in_env() | ||
| `-- POST session.reward_info_url | ||
| ``` | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| 1. **AKernel** — set `AKERNEL_SERVER_ADDRESS` and `AKERNEL_TOKEN`. | ||
| 2. **Tool image** — build the claude-code tool image and push it to a remote | ||
| registry if the sandbox service cannot access local Docker images. | ||
|
|
||
| ## 1. Build Tool Image | ||
|
|
||
| `claude_code` is injected into the SWE-bench sandbox as a sidecar tool image. | ||
| Use `build_tool.sh` to build it. | ||
|
|
||
| | Default tool image | Dockerfile | Sandbox mount path | Image contents | | ||
| |--------------------|------------|--------------------|----------------| | ||
| | `claude-code-tool:latest` | `Dockerfile.claude-code-tool` | `/opt/claude-code` | Node-built `@anthropic-ai/claude-code` npm package | | ||
|
|
||
| ```bash | ||
| # Use the default npm registry. | ||
| bash examples/blackbox_recipes/claude_code/build_tool.sh | ||
|
|
||
| # Use a custom npm mirror. | ||
| bash examples/blackbox_recipes/claude_code/build_tool.sh --npm-registry https://registry.npmmirror.com | ||
|
|
||
| # Pin a specific claude-code version. | ||
| bash examples/blackbox_recipes/claude_code/build_tool.sh --tool-version latest | ||
|
|
||
| # Build and push to a remote registry. | ||
| bash examples/blackbox_recipes/claude_code/build_tool.sh --registry swr.cn-east-3.myhuaweicloud.com/openyuanrong | ||
| ``` | ||
|
|
||
| ### Build Environment Variables | ||
|
|
||
| | Variable | Default | Description | | ||
| |----------|---------|-------------| | ||
| | `TOOL_IMAGE` | `claude-code-tool` | Image name | | ||
| | `TOOL_TAG` | `latest` | Image tag | | ||
| | `TOOL_VERSION` | `latest` | `@anthropic-ai/claude-code` package version (`--tool-version`) | | ||
| | `NPM_REGISTRY` | unset, use npm default | npm registry URL (`--npm-registry`) | | ||
|
|
||
| After pushing, point training at it with `CLAUDE_CODE_TOOL_IMAGE`. | ||
|
|
||
| ## 2. Training (Fully Async) | ||
|
|
||
| ```bash | ||
| AKERNEL_SERVER_ADDRESS="6.2.179.37:8888" \ | ||
| AKERNEL_TOKEN="<token>" \ | ||
| CLAUDE_CODE_TOOL_IMAGE=swr.cn-east-3.myhuaweicloud.com/openyuanrong/claude-code-tool:latest \ | ||
| MODEL_PATH=~/models/Qwen3.5-9B \ | ||
| bash examples/blackbox_recipes/claude_code/run_train.sh | ||
| ``` | ||
|
|
||
| The training YAML keeps `claude_code` as the only runner: | ||
|
|
||
| ```yaml | ||
| agent_runner_fqn: examples.blackbox_recipes.claude_code.claude_code_runner.claude_code_runner | ||
| ``` | ||
| ## 3. Configuration | ||
| | Variable | Default | Description | | ||
| |----------|---------|-------------| | ||
| | `AGENT_MAX_TURNS` | `100` | `claude --max-turns` (the agent's turn budget); read by the runner from the `AGENT_MAX_TURNS` env var | | ||
| | `SWE_AGENT_EVAL_TIMEOUT` | `600` | Reward evaluation timeout (seconds) | | ||
| | `SWE_AGENT_RUN_TIMEOUT` | `7200` | Max wall time for the claude process in the sandbox | | ||
| | `CLAUDE_CODE_TOOL_IMAGE` | `swr.cn-east-3.myhuaweicloud.com/openyuanrong/claude-code-tool:latest` | Sidecar tool image | | ||
| | `CONDA_ENV` | `testbed` | Conda env activated inside the sandbox before running claude | | ||
|
|
||
| `AGENT_MAX_TURNS` is the only knob that bounds the agent. The trainer's | ||
| `multi_turn.max_assistant_turns` is not enforced on the blackbox rollout path | ||
| (`AgentFrameworkRolloutAdapter`) — claude runs to its own `--max-turns` inside | ||
| the sandbox and the gateway counts the turns afterward — so it is not exposed as | ||
| a separate knob. A value of `1` would cripple the agent, hence the default `100`. |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| #!/usr/bin/env bash | ||
| # Build the Claude Code sidecar tool image. | ||
| # | ||
| # The image installs the @anthropic-ai/claude-code npm package into a minimal | ||
| # `FROM scratch` layer rooted at /opt/claude-code. It is mounted into the | ||
| # SWE-bench sandbox at /opt/claude-code, so the sandbox base image does not need | ||
| # Node or npm to run the agent. | ||
| # | ||
| # Usage: | ||
| # bash examples/blackbox_recipes/claude_code/build_tool.sh | ||
| # bash examples/blackbox_recipes/claude_code/build_tool.sh --npm-registry https://registry.npmmirror.com | ||
| # bash examples/blackbox_recipes/claude_code/build_tool.sh --tool-version latest | ||
| # bash examples/blackbox_recipes/claude_code/build_tool.sh --registry swr.cn-east-3.myhuaweicloud.com/openyuanrong | ||
| # | ||
| set -euo pipefail | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| IMAGE_NAME="${TOOL_IMAGE:-claude-code-tool}" | ||
| IMAGE_TAG="${TOOL_TAG:-latest}" | ||
| TOOL_VERSION="${TOOL_VERSION:-latest}" | ||
|
|
||
| # Parse args | ||
| REGISTRY="" | ||
| NPM_REGISTRY="${NPM_REGISTRY:-}" | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| --registry) REGISTRY="$2"; shift 2 ;; | ||
| --npm-registry) NPM_REGISTRY="$2"; shift 2 ;; | ||
| --tool-version) TOOL_VERSION="$2"; shift 2 ;; | ||
| *) echo "Unknown arg: $1"; exit 1 ;; | ||
| esac | ||
| done | ||
|
|
||
| BUILD_ARGS=(--build-arg "TOOL_VERSION=${TOOL_VERSION}") | ||
| if [[ -n "${NPM_REGISTRY}" ]]; then | ||
| BUILD_ARGS+=(--build-arg "NPM_REGISTRY=${NPM_REGISTRY}") | ||
| fi | ||
|
|
||
| echo "==> Building claude_code tool image: ${IMAGE_NAME}:${IMAGE_TAG}" | ||
| docker build \ | ||
| -f "${SCRIPT_DIR}/Dockerfile.claude-code-tool" \ | ||
| -t "${IMAGE_NAME}:${IMAGE_TAG}" \ | ||
| "${BUILD_ARGS[@]}" \ | ||
| "${SCRIPT_DIR}/" | ||
|
|
||
| if [[ -n "${REGISTRY}" ]]; then | ||
| FULL_TAG="${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" | ||
| echo "==> Tagging and pushing: ${FULL_TAG}" | ||
| docker tag "${IMAGE_NAME}:${IMAGE_TAG}" "${FULL_TAG}" | ||
| docker push "${FULL_TAG}" | ||
| echo " Pushed." | ||
| fi | ||
|
|
||
| echo "" | ||
| echo "Tool image ready: ${IMAGE_NAME}:${IMAGE_TAG}" | ||
| if [[ -n "${REGISTRY}" ]]; then | ||
| echo " Remote sandbox: ${FULL_TAG}" | ||
| fi | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since
set -uis active, referencing$2when parsing the last argument without a value (e.g.,--registrywith no value) will raise an unbound variable error and crash the script. We should use${2:-}to safely default to an empty string.