Skip to content

Describe an agent. Get a working agent in 5 minutes.

Agents from a paragraph.

looplet treats agents as data — a cartridge is a directory of files (config.yaml, prompts/system.md, tools/<name>/{tool.yaml, execute.py}) that the loader materialises into a runnable agent. The agent_factory cartridge builds new cartridges from English briefs; the loop engine runs them. Underneath, the same composable_loop iterator gives you full control if you'd rather hand-write the agent. Zero runtime dependencies.

PyPI Python License CI GitHub stars

289ms

Cold import

0

Runtime deps

4

Hook protocols

1,492

Tests, ~1s

from looplet import composable_loop

for step in composable_loop(llm=llm, tools=tools, task=task, config=cfg, state=state):
    print(step.pretty())          # "#1 search(query='…') → 12 items [340ms]"
    if step.usage.total_tokens > BUDGET:
        break                      # your loop, your control flow
pip install looplet                  # core — zero third-party packages
pip install "looplet[openai]"        # OpenAI, Ollama, Groq, Together, vLLM
pip install "looplet[anthropic]"     # Anthropic

looplet demo — three-step investigation loop


How it works

Every turn follows the same five-step story:

  1. The LLM proposes a tool call.
  2. The registry validates and dispatches it.
  3. Hooks observe or steer the turn.
  4. State records the step.
  5. The loop yields a Step to your code.

Bundles, presets, provenance, and evals all compile into this same mechanism.

flowchart LR
    subgraph Loop["composable_loop()"]
        direction LR
        P[pre_prompt] --> L[llm.generate]
        L --> D[pre_dispatch]
        D --> T[tool.execute]
        T --> R[post_dispatch]
        R --> C{check_done}
        C -- no --> P
        C -- yes --> Y([yield Step])
    end
    Y --> Caller[Your for-loop]
    Caller -. "stop / log / score" .-> Loop

    classDef hook fill:#EEF2FF,stroke:#4F46E5,stroke-width:1.5px,color:#1E1B4B
    class P,D,R,C hook

Four hook methods on any Python object. Implement only the ones you need. The loop uses hasattr — no base class, no registration.


The promise

  1. Own the loop. Your code iterates over Step objects and can stop, log, approve, score, or redirect at any point.
  2. Compose behavior. Hooks are plain Python objects. Add redaction, permission checks, compaction, metrics, or quality gates without subclassing a framework.
  3. Keep the trace. The same step stream powers live debugging, provenance, replay, and pytest-style evals.
  4. Package capabilities. Skills and bundles let you share a runnable agent folder while keeping the core loop inspectable Python.

Why looplet?

  • Fast to start, fast to run


    289 ms cold import. Zero runtime dependencies. pip install stays snappy on serverless and short-lived scripts.

    Benchmarks

  • Composable by Protocol


    Four @runtime_checkable hook methods. Any object implementing one or more is a hook. No base classes, no registration.

    Hooks

  • Observable by default


    step.pretty(), ProvenanceSink, and eval_* all read the same Step dataclass. One artifact, three uses.

    Provenance

  • Safe by design


    redact= scrubs PII before the provider sees it and before the trace is written. No wrapping-order footguns.

    Pitfalls

  • Compose agents as tools


    Any looplet agent is a function that returns a result. Wrap it in a ToolSpec and plug it into the next agent.

    Recipes

  • Debugging is evaluation


    What you do while debugging (print(step.pretty())) is a trajectory. Evals are pytest-style functions over the same data.

    Evals


See the difference

from langgraph.prebuilt import create_react_agent

agent = create_react_agent(
    model=llm,
    tools=[search, fetch],
    state_schema=State,
)
result = agent.invoke({"messages": [task]})
# Where does the loop stop?
# Where does the tool call happen?
# How do I intercept it?
# → read the framework source.
for step in composable_loop(
    llm=llm, tools=tools, task=task,
    hooks=[BudgetCap(10_000), Redactor()],
):
    if step.tool_call.tool == "delete":
        if not approve(step):           # (1)
            break
    log(step)                            # (2)
  1. Intercept any tool call with ordinary Python — no custom graph node needed.
  2. One Step object is the trace, the eval context, and the checkpoint unit.

Custom agent example

Start with Dependency Doctor if you want a demo people remember: point it at a repo and it audits dependency files for security, license, and maintenance risk. The agent is useful, concrete, and shows the looplet difference: every lookup, warning, and final claim is visible as a step you can log, gate, replay, or evaluate.

# Load the workspace; pass --workspace to point at the project to audit.
OPENAI_BASE_URL=http://127.0.0.1:11434/v1 \
OPENAI_API_KEY=ollama OPENAI_MODEL=llama3.1 \
python -c "from looplet import cartridge_to_preset; \
p = cartridge_to_preset('examples/dep_doctor.cartridge', runtime={'workspace': '/path/to/project'})"

Then explore examples/git_detective.cartridge/ for codebase-health reports, examples/threat_intel.cartridge/ for local-first security briefings, and examples/coder.cartridge/ for a coding-agent reference implementation — each is a self-contained Workspace under examples/ that round-trips losslessly with an AgentPreset via preset_to_cartridge / cartridge_to_preset.

# More dogfood — load each cartridge and run a scripted loop.
python -m looplet.examples.hello_world --scripted
python -m looplet.examples.ollama_hello --scripted
python -m looplet.examples.coding_agent "Implement add" --scripted --workspace /tmp/demo
python -m looplet.examples.data_agent --scripted --auto-approve --clean

Honest benchmarks

All numbers regenerate in one command on a fresh Python 3.11 venv. See Benchmarks for the full methodology.

looplet 289 ms · 0 deps

strands-agents 1,885 ms · 6 deps

LangGraph 2,294 ms · 31 deps

Claude Agent SDK 2,409 ms · 13 deps

Pydantic AI 3,975 ms · 12 deps

Cold-import time, median of 9 fresh subprocess runs. Python 3.11.13, Linux x86_64, PyPI wheels from 2026-04-21.


Start here

  • Quickstart


    Install. Run. Understand the loop in five minutes.

  • Tutorial


    Build an agent with hooks, context management, crash-resume, and approval — in five steps.

  • Hooks


    The four extension points. Recipes for every common pattern.

  • Evals


    pytest-style scoring over the same trajectory you debug with.

  • Provenance


    Capture every prompt and step to a diff-friendly directory.

  • Recipes


    Ollama, OTel, MCP, cost accounting, checkpoints.

  • Pitfalls


    Ten sharp edges worth knowing — with the "right way" for each.

  • Benchmarks


    Cold import and dependency footprint, with the harness.


Extension points at a glance

  • pre_prompt

    Inject context into the next prompt. Context managers, retrieval-augmented briefings, step-specific guidance.

  • pre_dispatch

    Intercept a tool call before it runs. Cache hits, permission gates, argument rewriting, approval flows.

  • post_dispatch

    React to a tool result. Duplicate-call warnings, error remediation messages, metric collection, streaming events.

  • check_done

    Reject premature completion. Quality gates, minimum-evidence thresholds, required-tool checks.


Talks and writing