Skip to main content

How to Configure Self-Improvement

๐Ÿ”ง Tools ยท ๐Ÿ“‹ Prompt ยท ๐Ÿ’ฌ Conversation

Two independent loops let the agent improve on a task without a person in the loop:

LoopWhat it doesPieces
AttemptsOn a known task, the agent commits candidate answers; a scorer grades each against a rubric, and only a candidate that beats the best so far is acceptedrubric context source, rubric sources, an attempt store, a scorer, the attempts_tools tools source, optionally the attempt_plateau and attempt_transcript_recorder interjection sources
RefinementThe agent's memories, skills, prompt notes and sub-agents are edited from its own trajectory, on its request or at harness checkpointsthe refiner, the refine_tools tools source, the refine interjection source, writable stores

Both are off by default. The only extra model calls are short-lived helpers (a judge, a refiner, a gate) that never act on the task, and each runs only when configured. They are agent runs: name an agent from agent_setups.json for each, ideally a lean one with tools and memory off, or leave the identifier unset to run a fresh copy of the current agent.


Attemptsโ€‹

On a turn whose user message matches a rubric, the agent gets two tools and a ## Attempts prompt section telling it not to answer directly but to work in attempts: state a hypothesis, produce the full candidate, commit it. Turns that match no rubric are answered normally, with neither tools nor prompt.

{
"agent_configuration": {
"tools_provider_configuration": { "direct_only_tools": ["commit_attempt", "read_attempt"] },
"rubric_context_source_configuration": { "enabled": true },
"disk_rubric_source_configuration": { "enabled": true, "rubrics_directories": ["rubrics"] },
"file_attempt_store_configuration": { "enabled": true, "directory": "attempts" },
"llm_judge_scorer_configuration": { "enabled": true, "judge_agent_identifier": "judge", "repeats": 3 },
"attempts_tools_source_configuration": { "enabled": true, "min_improvement": 0.5, "max_attempts": 5 },
"interjections_provider_configuration": { "enabled": true },
"attempt_plateau_interjection_source_configuration": { "enabled": true },
"attempt_transcript_recorder_interjection_source_configuration": { "enabled": true }
}
}

The attempts gate needs all of: the rubric context source, at least one rubric source, one attempt store, one scorer and the attempts_tools source. Without an enabled store or a matching rubric, every piece stays inert.

Rubricsโ€‹

A rubric names a task, says which user messages are that task, and holds the reference answer. The reference never enters the agent's transcript; only the scorer sees it.

FieldTypeRequiredDescription
namestryesTask name; keys the lineage of attempts
matchstryesCase-insensitive substring of the latest user message, or a regular expression as re:<pattern>
referencestryesThe reference answer. For exact_match, one accepted answer per line
taskstrnoWhat the question asks, given to the judge
criteriastrnoExtra grading criteria, given to the judge
pass_thresholdfloatnoOverrides the scorer's pass_threshold for this task
metadatadictnoStructured data a scorer or harness needs beyond the reference, such as the options of a multiple-choice question

The rubric context source matches the latest user message against the rubrics of every enabled rubric source; the first match wins. It adds nothing to the prompt.

za agents capabilities context-sources configure rubric
Config keySettingTypeDefaultDescription
rubric_context_source_configurationenabledboolfalseMatch the user message against the rubrics and select the task for the turn
disk_rubric_source_configurationenabledboolfalseLoad rubrics from Markdown files
rubrics_directorieslist[str]["rubrics"]Directories of <name>.md rubric files
in_memory_rubric_source_configurationenabledboolfalseUse the rubrics given inline
rubricslist[Rubric][]Rubric objects with the fields above

A rubric file is named after the task. Its YAML frontmatter holds match and the optional fields; its body is the reference answer:

rubrics/water-boiling-point.md
---
match: "re:boiling point of (pure )?water"
task: The boiling point of water at sea level.
criteria: The value must be in degrees Celsius.
pass_threshold: 8
---
100 ยฐC

in_memory_rubric_source_configuration takes the same rubrics as JSON, which is how a harness that controls the request passes the reference for one turn.

Toolsโ€‹

attempts_tools (CLI name attempts-tools):

ToolDescription
commit_attemptScore a complete candidate (hypothesis, artifact, optional notes and parent_id) and record it. Returns the attempt id, verdict, score, the best score before it, the scorer's details and the attempts remaining
read_attemptRead an earlier attempt by attempt_id: hypothesis, score, verdict, notes, scorer details and full artifact

A candidate is accepted when the scorer passes it and its score is at least the best accepted score plus min_improvement; otherwise it is rejected and kept with its score. Its parent defaults to the best accepted attempt. The prompt section lists the lineage so far. Nothing forces the model to commit or to answer with the best accepted artifact: the instructions are the only lever.

Committing is the model's decision, so keep both tools out of the Python REPL with direct_only_tools.

za agents capabilities tool-sets configure attempts-tools

attempts_tools_source_configuration:

SettingTypeDefaultDescription
enabledboolfalseOffer the tools and the ## Attempts prompt on turns that matched a rubric
scorerstr or nullnullScorer to grade with: exact_match or llm_judge. null uses the first enabled
min_improvementfloat0.0How much a candidate must beat the best accepted score by. 0 accepts ties, right for deterministic scorers; for llm_judge, raise it to the judge's observed spread
max_attemptsint or nullnullCommits allowed per task, rejected ones included. null is unlimited. With a pass/fail scorer and few possible answers, a budget stops the agent from enumerating answers instead of reasoning
digest_limitint20Most recent attempts listed in the prompt
instructionsstr or nullnullReplace the built-in ## Attempts instructions
defer_toolsbool or nullnullLoad the tools on demand, see Tool search

Scorersโ€‹

The scorer runs only inside commit_attempt, against the turn's rubric.

ScorerConfig keyPasses when
exact_matchexact_match_scorer_configurationThe artifact equals one line of the reference (or, with contains, includes one). Score 1 or 0
llm_judgellm_judge_scorer_configurationThe mean of repeats judgements, each 0 to 10, is at least the pass threshold

exact_match_scorer_configuration:

SettingTypeDefaultDescription
enabledboolfalseSwitch the scorer on
containsboolfalsePass when a reference line appears anywhere in the artifact
normalizebooltrueCasefold and collapse whitespace before comparing

llm_judge_scorer_configuration:

SettingTypeDefaultDescription
enabledboolfalseSwitch the scorer on
judge_agent_identifierstr or nullnullAgent that grades, once per judgement in a fresh context; its llm_configuration_name picks the grading model. null runs a fresh copy of the current agent
repeatsint3Independent judgements to average, run in parallel
pass_thresholdfloat7.0Mean score at or above which a candidate passes, unless the rubric sets its own

The agent gets the mean, the spread over the repeats and the threshold. The judge's reasons are stored with the attempt and never returned to the agent, because a grader explaining a rejection tends to paraphrase the reference.

Attempt Storesโ€‹

The first enabled store holds the lineage of every task.

StoreConfig keySettingsKeeps attempts
filefile_attempt_store_configurationenabled (false), directory ("attempts")On disk: <directory>/<rubric name>/index.jsonl, and per attempt a folder with attempt.json, artifact.txt and, with the transcript recorder on, transcript.json
in_memoryin_memory_attempt_store_configurationenabled (false)In process memory, lost on restart

Artifacts are plain files, so a read_only_mounts entry can expose the attempts directory to the REPL.

The attempt_plateau interjection source tells the agent when its attempts stop improving, and attempt_transcript_recorder saves how each attempt came about.


Refinementโ€‹

The refiner edits the agent's harness: a lean agent reads the current memories, skills, instruction texts and sub-agents plus the evidence, and proposes at most max_edits typed edits, which are written to the stores. What it writes is in the system prompt from the next turn on. It is told never to store task answers, scores or references, and never sees a rubric.

It runs two ways:

TriggerConfigured withEvidence
The agent asksrefine_tools_source_configurationThe agent's one-sentence observation
The harness checks inrefine_interjection_source_configurationThe transcript so far and the gate agent's instructions
{
"agent_configuration": {
"tools_provider_configuration": { "direct_only_tools": ["request_refinement"] },
"refiner_configuration": { "refiner_agent_identifier": "refiner", "max_edits": 3 },
"refine_tools_source_configuration": { "enabled": true },
"memory_provider_configuration": { "enabled": true },
"file_memory_store_configuration": { "enabled": true, "file_path": "memories/agent_memory.json" },
"disk_skills_source_configuration": { "enabled": true, "skills_directories": ["skills/"] },
"disk_instruction_source_configuration": { "enabled": true, "instructions_directories": ["instructions"] },
"instructions_provider_configuration": { "writable_source": "disk_instructions" }
}
}

Writable Targetsโ€‹

Each edit kind goes to one provider's writable source. A kind whose provider is off or has nowhere to write is not offered; with no kind available, refine_tools and the refine interjection source turn themselves off.

Edit kindWritten toSettingDefault
memory: a durable factMemory storememory_provider_configuration.writable_sourceFirst active store
skill: a repeatable procedureSkills sourceskills_provider_configuration.writable_sourceFirst active source. Set a source that can write skills, such as disk
prompt_note: a narrow behavioural ruleInstruction sourceinstructions_provider_configuration.writable_sourceFirst active source that can write: disk_instructions, which writes <name>.md into its first instructions_directories entry
sub_agent: a specialist the agent can delegate toDelegable agents sourceagent_delegation_provider_configuration.writable_sourceFirst active source that can write. The SDK ships none

To let the refiner add sub-agents, subclass WritableDelegableAgentsSource (zav.agents_sdk.adapters.agent_delegation): available_tools names the tools sources a new sub-agent may be given, create_agent(name, description, instructions, tools) stores it, and get_agents returns it as a DelegableAgent on the next build.

The request_refinement Toolโ€‹

refine_tools (CLI name refine-tools) adds a ## Refinement prompt section and one tool:

ToolDescription
request_refinementRun the refiner on the agent's observation (a repeated failure or a reusable tactic). Returns what was kept

Keep it in direct_only_tools so a script in the REPL cannot call it.

za agents capabilities tool-sets configure refine-tools

refine_tools_source_configuration:

SettingTypeDefaultDescription
enabledboolfalseOffer request_refinement and the ## Refinement prompt
defer_toolsbool or nullnullLoad the tool on demand, see Tool search

The Refinerโ€‹

refiner_configuration is shared by both triggers. It has no enabled flag: the triggers switch it on.

SettingTypeDefaultDescription
refiner_agent_identifierstr or nullnullAgent that proposes the edits. null runs a fresh copy of the current agent
max_editsint3Edits kept from one refinement; the rest are dropped
max_transcript_charsint24000Characters from the end of the rendered transcript given to the refiner
ledger_pathstr or null"refinements.jsonl"File recording every edit. null turns the ledger off
instructionsstr or nullnullReplace the built-in refiner instructions; {kinds} is replaced by the allowed edit kinds

The refiner must reply with a JSON array of edits, each with kind, name (skills, prompt notes, sub-agents), description (skills, sub-agents), content, tools (sub-agents), trigger and intended_effect.

The Refinements Ledgerโ€‹

Every proposed edit is appended to ledger_path as one JSON line, applied or not. A failed edit is recorded, never raised.

FieldDescription
id, atRecord id and UTC timestamp
moderequested (the tool) or auto (the interjection source)
focusThe agent's observation or the gate's instructions
kind, name, description, content, toolsThe edit
trigger, intended_effectThe evidence and what the edit should change
resultWhat was written (memory <id>, skill <name>, prompt note <name>, sub-agent <name>) or error: <reason>

Seeing What the Model Sawโ€‹

The system prompt is rebuilt every turn from the stores and is not part of the transcript, so saved attempt transcripts do not contain it. A tracing_configuration on the agent setup records every model call, system prompt included, with the judge, reviewer, gate and refiner runs nested in the same trace. The file vendor writes one JSON file per trace:

{ "tracing_configuration": { "vendor": "file", "vendor_configuration": { "file": { "directory": "traces" } } } }