Test-driven harness engineering for Python agents
Looplet¶
Own the loop. Test every change.
Keep prompts, tools, hooks, cases, and graders in code and files your team can review. Capture a failure, inspect the resulting world, and turn the behavior into a required pytest or CI contract.
Reviewable
Harness changes are ordinary Python, YAML, Markdown, and JSON.
Observable
Model calls and tool dispatches can become durable evidence.
Re-executable
Recorded responses can exercise fresh tool and hook code.
Gateable
Host-observed outcomes become required release checks.
No API key and no network. The response sequence stays fixed while changed tool code executes again and an independent collector checks the output. Read the proof and its limits.
Start with the job in front of you¶
I have a private tool loop¶
Adapt one tool, replace only the control loop, and establish parity before adding hooks or cartridges.
I have a failure worth preserving¶
Capture the run, collect the real outcome, and decide whether replay, a mock, or fresh model samples answer the question.
I need the exact interface¶
Find commands, Python entry points, artifact files, and operational controls without reading the package source.
One workflow from prototype to release¶
01
Build
Own the model, tools, state, and dispatch loop in Python or a cartridge.
02
Capture
Persist prompts, responses, steps, stop reasons, and metadata as readable files.
03
Test
Collect resulting world state and compare it with grader-only expectations.
04
Ship
Make required graders and thresholds fail closed in pytest or CI.
The execution boundary stays visible¶
from looplet import OpenAIBackend, composable_loop, tool, tools_from
@tool(description="Look up one service owner by name.")
def lookup_owner(service: str) -> dict:
owners = {"payments": "fintech-platform", "search": "discovery"}
return {"service": service, "owner": owners.get(service)}
for step in composable_loop(
llm=OpenAIBackend.from_env(),
tools=tools_from([lookup_owner], include_done=True),
task={"goal": "Find the owner of payments, then finish."},
max_steps=5,
):
print(step.pretty())
Every dispatch returns to the caller as a typed Step. Hooks can observe or
steer prompt construction, permissions, dispatch, completion, compaction, and
lifecycle events without requiring a graph runtime. Cartridges are optional;
they package the same harness as reviewable files when that helps distribution
or code review.
Follow the quickstart | Read the hook protocol | Inspect cartridge boundaries
The harness can cross runtime boundaries¶
The shipped coder_portable cartridge is the complete coding-harness
reference architecture with zero in-process portability blockers:
| Boundary | What crosses it |
|---|---|
| MCP | All 16 coding tools |
| LEP | Permission, test, cache, stale-file, and linter hooks |
| SSP | Shared mutable file-cache state |
| MGP | Host model access for web_fetch and subagents |
from looplet import bundled_cartridge_path
from looplet.cartridge import analyse_cartridge
coder = bundled_cartridge_path("coder_portable")
assert analyse_cartridge(coder).profile == "portable"
Portable means the loader does not import author-owned tool, hook, or state code. The bundled protocol servers are Python programs launched with the active Looplet interpreter, SSP and MGP use Unix sockets, and the full coder has not yet run on a production Rust, Go, or TypeScript loader. The Python-host coder remains the agent factory default and keeps host-owned eval and dynamic-memory behavior that the portable reference deliberately omits.
Study the portable coder | Inspect the cartridge format
Evidence has different jobs¶
| Evidence | Use it for | Do not claim |
|---|---|---|
Yielded Step stream |
Live routing, approval, display, and instrumentation | Independent product correctness |
| Provenance trace | What the model saw, returned, and dispatched | That recorded prompts are safe to publish |
| Captured-response replay | Tool, hook, permission, state, and dispatch changes under fixed model responses | Better future model decisions |
| Outcome collector and grader | Whether the resulting file, command, record, or service state is correct | Isolation when the candidate owns the runner |
| Fresh sampled cases | Prompt, model, schema, and context changes that affect decisions | Universal performance from one sample |
Looplet calls replay captured-response replay because tools, clocks, networks, randomness, and side effects execute again. Protected promotion oracles belong in a host-owned runner; arbitrary untrusted code also requires OS or process isolation.
Capture and replay | Behavioral evals | Saved artifact reference
Designed for a specific team and stage¶
Looplet is a good fit when¶
- one model calls tools until it is done;
- your team already reviews Python, files, pytest, and CI;
- prompt, tool, model, or hook changes need regression evidence;
- exact interception points and local artifacts matter;
- you want to own execution rather than adopt a hosted control plane.
Choose another layer when¶
- the workflow is naturally a durable branching graph;
- a managed control plane should be the source of truth;
- you need a finished assistant, sandbox, or annotation product;
- your main need is fleet analytics or a hosted experiment dashboard;
- a small disposable loop is still enough.
Looplet can run inside a workflow engine and export to observability systems. It does not try to replace either one. Core uses only the Python standard library; provider SDKs are optional extras.
Read the selection guide | Check the FAQ | Operate a production loop