Skip to content

First-class agentic RL: example, sandbox substrate, and Kubernetes services#5083

Draft
htahir1 wants to merge 6 commits into
feature/add-harbor-integrationfrom
feature/first-class-agentic-rl
Draft

First-class agentic RL: example, sandbox substrate, and Kubernetes services#5083
htahir1 wants to merge 6 commits into
feature/add-harbor-integrationfrom
feature/first-class-agentic-rl

Conversation

@htahir1

@htahir1 htahir1 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Draft — stacked on #5029. This branch includes the Harbor integration merge; the effective diff shrinks to ~+11.9k lines (of which ~7k are 327 generated Harbor task files) once #5029 lands in develop. Live validation (Docker smoke, GPU tier, cluster service leg) is still pending — see Verification.

What this does

Makes the agentic RL stack (verifiers + Harbor + vLLM + prime-rl) first-class in ZenML, with ZenML owning what none of those record: the sandbox rollouts run in, the gate in front of the GPU, and the lineage behind it. prime-rl keeps its loop; ZenML never pretends to orchestrate RL training.

Core (src/zenml)

  • Docker sandbox flavor — container-isolated local sessions (exec with demuxed streams, archive file transfer, docker commit snapshots, attach). The laptop counterpart to the kubernetes/modal flavors.
  • Flavor-agnostic sandbox image overrideBaseSandbox.image_settings() capability (docker/kubernetes/modal), replacing the Modal-only branch in the Harbor bridge.
  • LocalSandbox file transfer with workdir containment (absolute paths outside the session workdir are refused rather than writing to host /).
  • Harbor integration on harbor 0.18 — pin bump from the dead >=0.8,<0.9; the 0.8-era allow_internet check (which refuses every current task) is replaced by harbor's own capability validation.
  • Multi-node command steps on the Kubernetes step operatornode_count gated on command steps (the Baseten contract): indexed Job, rank from JOB_COMPLETION_INDEX, job-owned headless service, and a rank-dispatch so exactly one pod runs ZenML's step machinery (N concurrent StepRunner.run calls for one step_run_id would race on status/logs/success).
  • KubernetesPodService — long-running services on the existing BaseService framework: deterministic naming (named services have URLs known before provisioning), server-side registration via Client.create_service, and deadline-based GC teardown (max_lifetime_seconds), so a GPU server leaked by a crashed run self-terminates.
  • datasets cap raised to <5.0.0 (the <4 cap silently disabled HFDatasetMaterializer in datasets-4.x environments).

Example (examples/agentic_rl)

Gate → train → receipts, in tiers: hermetic smoke (oracle vs nop, no API keys), eval campaigns, prime-rl GRPO as one CommandStep, per-rollout traces.jsonl ingestion into a single table artifact (reward ↔ sandbox session id join), checkpoint discovery, HTML lineage report, and an opt-in serve/probe/stop leg using KubernetesPodService. 64 committed Harbor task dirs scored by the RL spike's verifier byte-identical (asserted by test); a verifiers taskset package carrying ZenMLSandboxRuntime written to upstream-contribution shape.

Key honesty rules encoded: the gate checks errors before rewards (an errored shard logs no mean reward — a reward-only gate passes broken campaigns); a crashed scorer is an errored trial, never a fake 0.0; the file-tier rollout table carries rewards + sandbox ids but not advantages/tokens (excluded upstream); the harbor-agents-vs-served-policy eval is a named cut.

Docs

component-guide/sandboxes/docker.md, a multi-node section in the step-operator guide, user-guide/agent-evals-guide/prime-rl-on-kubernetes.md (incl. cluster prerequisites: what ZenML deploys per-run vs. what you provide once), and docs/design/multi-node-command-steps-on-kubernetes.md.

Reviewer attention

  • The rank-dispatch + GC-teardown design in kubernetes_step_operator.py (correctness of one-bookkeeper semantics).
  • KubernetesPodService teardown philosophy (deprovision = happy path, GC deadline = guarantee) and the never-fail-the-step service registration in the example.
  • The harbor 0.18 migration (network_mode handling now delegated to harbor's capability validation).

Verification

  • 247 core unit tests (kubernetes/sandboxes/harbor) + 21 example tests green; mypy clean on all touched modules; ruff clean (examples/tests under CI's --extend-ignore D).
  • Oracle solution scored exactly 1.0 through the byte-identical verifier locally; verifiers 0.2 runtime dispatch patch verified against the real package.
  • Tier 0 smoke: GREEN, run live 2026-07-17 on the docker sandbox flavor (WSL, Docker 28.5.1): oracle 1.0 / nop 0.0 through real sandbox containers and the byte-identical verifier, gate passed at mean 0.500 over 6 rewards with 0 blocking errors, 1m27s wall. The first live run also proved the honest-failure design: two shim bugs (harbor's reward.json numeric-map precedence; nop's missing submission crashing the scorer) surfaced as 6/6 errored trials and a loud gate failure — never a fake 0.0 (fixed in ed795a9dfa).
  • Found in live validation, fixed separately: local sqlite stores fail with database is locked under concurrent mapped steps (sqlite3's 5s default lock wait; branch fix/sqlite-busy-timeout).
  • Still pending live runs: --train needs 2 GPUs + a prime-rl checkout; the service/multi-node legs need a cluster and are experimental until then. These are the remaining gates before undrafting.

Depends on #5029. Suggest full CI (run-slow-ci) once undrafted.

🤖 Generated with Claude Code

@htahir1 htahir1 added enhancement New feature or request release-notes Release notes will be attached and used publicly for this PR. labels Jul 15, 2026
@github-actions github-actions Bot added the internal To filter out internal PRs and issues label Jul 15, 2026
@htahir1
htahir1 changed the base branch from develop to feature/add-harbor-integration July 15, 2026 14:42
@htahir1
htahir1 force-pushed the feature/first-class-agentic-rl branch from 27563ac to 6fd1beb Compare July 15, 2026 14:44
@socket-security

socket-security Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​harbor@​0.18.1.dev20260716012574100100100100
Addedpypi/​click@​8.4.296100100100100
Addedpypi/​hatchling@​1.31.096100100100100

View full report

@htahir1
htahir1 force-pushed the feature/first-class-agentic-rl branch 4 times, most recently from 5653432 to 44a7f9c Compare July 15, 2026 15:15
The RL spike proved the loop shape works but the ecosystem stack
(verifiers + harbor + vLLM + prime-rl) was never wired first-class.
This adds the missing substrate and the example that proves it:

- Docker sandbox flavor: container-isolated local sessions (exec,
  archive transfer, commit-based snapshots), the laptop counterpart to
  the kubernetes/modal flavors
- Flavor-agnostic sandbox image override (image_settings capability),
  replacing the Modal-only branch in the Harbor bridge
- LocalSandbox file transfer with workdir containment
- Harbor integration bumped to 0.18 (allow_internet -> network_mode;
  the 0.8-era check refused every current task)
- huggingface datasets cap raised to <5.0.0 (the <4 cap silently
  disabled HFDatasetMaterializer in datasets-4.x environments)
- Kubernetes step operator: multi-node command steps (indexed Job,
  rank-0-only ZenML entrypoint, job-owned headless service) and
  heterogeneous auxiliary pod sets with injected discovery env -
  distributed launchers own the launch, Kubernetes GC owns teardown
- KubernetesPodService: run-scoped long-running services on the
  existing BaseService framework, with deterministic naming,
  server-side registration, and deadline-based GC teardown
- examples/agentic_rl: gated eval campaigns (errors before rewards),
  prime-rl trainer as a CommandStep, per-rollout trace ingestion with
  the reward<->sandbox lineage join, serve/probe/stop after-eval leg,
  64 committed Harbor task dirs scored by the spike's verifier
  byte-identical, and a verifiers taskset package carrying the
  upstream-shaped ZenMLSandboxRuntime
- Docs: prime-rl-on-Kubernetes guide, docker sandbox page, multi-node
  design doc

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@htahir1
htahir1 force-pushed the feature/first-class-agentic-rl branch from 44a7f9c to 99f40f1 Compare July 15, 2026 15:21
htahir1 and others added 5 commits July 17, 2026 10:49
First live smoke run caught two adaptation bugs in the generated
test.sh shim:

- Harbor gives verifier/reward.json precedence over reward.txt and
  validates it as a flat numeric map; the shim wrote the scorer's rich
  diagnostic JSON there, turning every scored trial into an errored
  trial. The rich JSON now lands in details.json (still downloaded with
  the verifier dir as the forensics channel) and only the bare float is
  exposed via reward.txt.
- A missing /app/pipeline.py (the nop agent) crashed the scorer and
  produced an errored trial. An agent that submits nothing has failed
  the task: the shim now records a genuine reward of 0.0 before the
  scorer is ever invoked.

With both fixes the hermetic smoke tier runs green end to end on the
docker sandbox flavor: oracle 1.0, nop 0.0, gate passes at mean 0.500
over 6 rewards with 0 blocking errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Concurrent steps publishing artifacts against a local sqlite store (for
example mapped steps of a dynamic pipeline) routinely exceed sqlite3's
default 5-second lock wait and fail with "database is locked". Wait up
to 60 seconds for the write lock instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request internal To filter out internal PRs and issues release-notes Release notes will be attached and used publicly for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants