# Pensieve: instructions for coding agents You are wiring Pensieve into a project. Pensieve runs experiments on a client's data to find the post-training that gets the best outcome for one decision the project already makes, usually with a frontier model call or a set of rules. The winning recipe becomes an expert model that answers with one of a fixed set of values. Your job is to wrap the existing call so every call is recorded as data for those experiments, without changing what the project's users see. ## Rules 1. Never ask the person to paste a live key (`pk_live_...`) into the chat, and never write one into a file. Use a sandbox key: `pk_test_` and then letters, digits and underscores, up to 64 of them, such as `pk_test_`. A hyphen or any other character gets a 401. The person sets `PENSIEVE_API_KEY` for production themselves. 2. Change nothing the users see. Wrap the existing function with `capture`. Leave the model's routing as it is (`observe` for a new model). 3. Fail open. `capture` already falls back to the wrapped function if Pensieve is slow or down. Don't add code that throws when Pensieve fails. 4. Ask the person before starting an experiment, changing a model's budget or routing, or signing off, splitting, promoting or rolling back a release. Those are theirs. The one exception is the sandbox step in step 9. ## Steps 1. Find the function that makes the decision. It takes a case (an object or text) and returns a result your code acts on, such as a verdict, a label or allow/block. 2. Install the SDK: `npm install @pensievelab/sdk` (TypeScript) or `pip install pensievelab` (Python). Until the packages are published, call the HTTP API directly. 3. Find the model to record for. With the key set, list models: `GET https://api.pensievelabs.co/v1/models` (SDK: `models.list()`). Each model has `slug`, `answers` (the values it answers with) and `input_schema` (when it takes structured cases). In the sandbox, use one of the four sample models; for a decision Pensieve has no model for yet, the person assigns a task in the app or with `POST /v1/tasks`. 4. Map the function's result to one of the model's answer values, and back. Write two small functions: `answer(result) -> value` and `use(decision) -> result`. 5. Wrap the function. TypeScript: ```ts import Pensieve from "@pensievelab/sdk"; const pensieve = new Pensieve(); export const decide = pensieve.capture("", existingFunction, { answer: (result) => /* result → answer value */, use: (decision) => /* decision.answer → result */, key: (input) => /* a stable id for the case, if there is one */, }); ``` Python: ```python from pensievelab import Pensieve client = Pensieve() @client.capture("", answer=lambda r: ..., use=lambda d: ..., key=lambda case: ...) def existing_function(case): ... ``` 6. Replace calls to the old function with the wrapped one, or decorate it in place. 7. Where the project learns what really happened (a later status, a review, an incident), send an outcome: `outcomes.send(call_id, { outcome })`, or by your own key with `outcomes.sendMany`. Outcome values are listed on the model as `outcomes`. 8. For short-lived scripts and tests, call `flush()` before exit so recorded calls are sent. 9. Test against the sandbox with a `pk_test_` key: call the wrapped function, then confirm the call arrived with `GET /v1/calls?model=` (SDK: `calls.list({ model })`). In the sandbox, `grade-audit` and `finding-triage` already route calls to the model, so the wrapped function usually doesn't run: only the model's decision is recorded. To test the wrapper on one of them, first set it to observe in the sandbox workspace: `PATCH /v1/models//routing` with `{"mode": "observe"}` (SDK: `models.routing.update(slug, { mode: "observe" })`). `agent-actions` and `review-comments` already observe. In a live workspace, routing stays the person's call (rule 4). 10. Show the person the diff and what you tested. ## Reference - Docs: https://docs.pensievelabs.co (every page as one file: https://docs.pensievelabs.co/llms-full.txt) - API contract: https://docs.pensievelabs.co/openapi.json - Post-training methods are plain markdown files. If the person asks for a method of their own, write the file and add it with `methods.create({ slug, markdown })`: https://docs.pensievelabs.co/guides/write-a-method/ - Base URL: https://api.pensievelabs.co. Auth: `Authorization: Bearer `. Version header: `Pensieve-Version: 2026-11-08`. - Errors are `{"error": {"type", "message", "param", "request_id"}}`. `param` names the field that was wrong. `not_available` (501) means the operation exists but isn't served yet.