Quickstart
Reef adds four things to an ordinary inference endpoint: a scenario, a receipt, a report, and a release chain.
Two more terms matter once you want it to learn: the recipe and the artifact, shown below.
Run the loop #
This runs the serving half of the loop against a hosted provider, on a laptop, with no GPU.
-
Install. Follow the laptop path in Installation.
-
Serve. external-provider.yaml is one process that proxies an OpenAI-compatible provider and records what it serves.
export REEF_TOKEN=reef-local export REEF_UPSTREAM_API_KEY=sk-... reef serve -c recipes/basic/external-provider.yamlThe config supplies the provider, the model, and a .reef/ state directory beside the checkout. Only the two secrets stay in the environment: the upstream key and the Reef token. The file is a template to copy, so it does not include a token.
reef serve runs in the foreground and holds the terminal until Ctrl-C. Leave it running and open a second terminal for everything below.
curl -f http://127.0.0.1:8900/healthz # {"ok": true} -
Send a request and report on it. The body is the provider's; x-reef-scenario is the only thing Reef adds. The response header x-reef-agent-record-id carries the receipt, which names the stored exchange. Tests, a verifier, a rubric, a thumbs-down, or any other grader you already have can report feedback against it.
import httpx reef = httpx.Client( base_url="http://127.0.0.1:8900", headers={"Authorization": "Bearer reef-local", "x-reef-scenario": "hello-reef"}, ) response = reef.post( "/v1/chat/completions", json={"model": "gpt-4o", "messages": [{"role": "user", "content": "Return exactly: reef is ready"}]}, ) response.raise_for_status() receipt = response.headers["x-reef-agent-record-id"] # e.g. ee5aa401634b4567bf9dae21816abde4 matched = response.json()["choices"][0]["message"]["content"].strip() == "reef is ready" report = reef.post( "/reef/report", json={"score": float(matched), "feedback": "matched" if matched else "wrong answer", "references": [receipt]}, ) print(report.json()) # {"agent_record_id": "cce17dd7...", "scenario": "hello-reef", "request_type": "report"}Reports are records too, so the response carries the report's own record id. Only inference-record ids are receipts, and they appear only in references.
-
Or use the wire client. Install the stdlib-only reef-client with pip install reef-client. It keeps the receipt for you.
from reef_client import ReefClient client = ReefClient("http://127.0.0.1:8900", token="reef-local") body, receipt = client.inference_with_record( "hello-reef", # the scenario "/v1/chat/completions", {"model": "gpt-4o", "messages": [{"role": "user", "content": "hi"}]}, ) answer = body["choices"][0]["message"]["content"] # ... whatever already judges your agent decides this score ... client.report("hello-reef", {"score": 1.0}, references=[receipt])With an OpenAI SDK instead, point base_url at http://127.0.0.1:8900/v1, send x-reef-scenario as a default header, and read x-reef-agent-record-id off the raw response.
On the wire it is an ordinary provider request with one added header, and the receipt comes back in a response header:
curl -sS -D - -o /dev/null \ http://127.0.0.1:8900/v1/chat/completions \ -H "Authorization: Bearer reef-local" \ -H "x-reef-scenario: hello-reef" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "hi"}]}' # x-reef-agent-record-id: ee5aa401634b4567bf9dae21816abde4 -
Read the release chain.
curl -sS -H "Authorization: Bearer reef-local" \ http://127.0.0.1:8900/reef/scenarios/hello-reef/releases # {"scenario": "hello-reef", "releases": [{"release_id": "...", "operation": "creation", "current": true, ...}]}One release, and it will stay at one: this deployment's recipe is the core recipe, which records and trains nothing.
To make the chain advance, bind a recipe that learns. To use a weight recipe, copy recipes/basic/external-provider.yaml, set reef.recipe: recipes.sao.recipe:SAORecipe, and serve the new config. Weight recipes need GPUs (Evolve your model).
The one that runs on a laptop is harness_evolve, and it takes a second file: a preset naming your propose and evaluate callables and the tasks to evaluate on. tutorials/harness_evolve/run.sh wires the whole loop together. Run it, then read Evolve your harness for an explanation of each piece.
Scenario #
A scenario is one workload: its records, its training state, its release chain. Scenarios never share data or updates.
The first request carrying a new x-reef-scenario creates it and binds it to the deployment's recipe, permanently. Later requests just name it.
curl -sS -H "Authorization: Bearer reef-local" http://127.0.0.1:8900/reef/scenariosOne deployment serves one recipe, so a request never names a method.
Receipt #
Every recorded exchange gets an id, and that id is the receipt. It identifies the request, the response, and the release that produced it.
The receipt arrives in the x-reef-agent-record-id response header or in the terminal SSE metadata for a stream. HTTP API lists the exact field for each dialect. A stream carries it only after the record is stored.
Report #
A report is feedback that quotes receipts. It carries score, feedback, references, and metadata; HTTP API gives the types and the rules.
Whatever already decides whether your agent did well stays in your harness. That may be a test suite, a verifier, or a human. Reports are consumed at most once, so a retry or late arrival is never counted twice.
Release chain #
Every accepted update creates a release with a parent, so a scenario's history is a chain rather than a mutable pointer. A receipt names the release that served it.
Durable releases are Git-backed and can be pinned or rolled back. Between checkpoints, runtime load IDs live in engine memory; their bytes are not restorable after a restart. Architecture has the details.
Recipe and artifact #
The artifact is the thing that gets versioned: model weights, or a harness tree of rules, prompts, skills, and config.
The recipe is the method that produces the next one. It decides which records are eligible, how they become a batch, what signal that batch carries, and whether a candidate is good enough to publish. Write a recipe covers each of those decisions.
Pick a cookbook recipe from Choosing a recipe, or write one in Write a recipe.