Skip to main content

agentprobe

pytest-compatible test harness for AI agents — deterministic record & replay for Anthropic Claude.

Test your Claude agents in CI without hitting the real API on every run. Record once, replay forever — zero cost, zero flakiness.

def test_agent_uses_bash(agentprobe):
    with agentprobe.replay("tests/fixtures/list_files.jsonl") as probe:
        result = my_agent.run("list files in /tmp")
        probe.assert_tool_called("bash")
        probe.assert_max_iterations(3)
        probe.assert_output_contains("/tmp")

Install

pip install pytest-agentprobe

Requires Python 3.9+ and anthropic>=0.40.0.


How it works

agentprobe intercepts calls to anthropic.Anthropic.messages.create (and the async equivalent) at the class level — no changes to your agent code needed.

  • Record mode — runs your agent against the real API and saves every request/response pair to a JSONL fixture file.
  • Replay mode — feeds the saved responses back to your agent instead of making real API calls. Deterministic, instant, free.
  • Auto mode — records on first run, replays on every subsequent run.

Quick start

1. Record a session

from agentprobe import Session
import anthropic

session = Session()
client = anthropic.Anthropic()  # uses ANTHROPIC_API_KEY

with session.record("tests/fixtures/my_agent.jsonl") as probe:
    result = my_agent.run(client, "what files are in /tmp?")
    # assertions are optional during recording
    probe.assert_tool_called("bash")

# fixture is written to disk — commit it to your repo

2. Replay in CI

def test_my_agent(agentprobe):
    client = anthropic.Anthropic(api_key="dummy")  # not used during replay

    with agentprobe.replay("tests/fixtures/my_agent.jsonl") as probe:
        result = my_agent.run(client, "what files are in /tmp?")

        probe.assert_tool_called("bash")
        probe.assert_not_tool_called("web_search")
        probe.assert_tool_called_with("bash", command="ls /tmp")
        probe.assert_max_iterations(4)
        probe.assert_output_contains("/tmp")
        probe.assert_stop_reason("end_turn")
        probe.assert_max_tokens(500)

3. Auto mode (record-on-first-run)

def test_my_agent(agentprobe):
    with agentprobe.auto("tests/fixtures/my_agent.jsonl") as probe:
        result = my_agent.run(client, "what files are in /tmp?")
        probe.assert_tool_called("bash")

Async agents

Full async support via AsyncAnthropic:

import pytest
import anthropic
from agentprobe import Session

@pytest.mark.asyncio
async def test_async_agent():
    session = Session()
    client = anthropic.AsyncAnthropic(api_key="dummy")

    async with session.async_replay("tests/fixtures/my_agent.jsonl") as probe:
        result = await my_async_agent.run(client, "list files in /tmp")
        probe.assert_tool_called("bash")
        probe.assert_max_iterations(3)

Async equivalents: async_record, async_replay, async_auto.


Assertion API

All assertions return self for chaining.

Iteration count

Assertion Description
assert_max_iterations(n) At most n LLM calls
assert_min_iterations(n) At least n LLM calls
assert_iteration_count(n) Exactly n LLM calls

Tool calls

Assertion Description
assert_tool_called(name) Tool was called at least once
assert_not_tool_called(name) Tool was never called
assert_tool_called_with(name, **kwargs) Tool was called with these input fields
assert_tool_called_before(first, second) First tool was called before second

Output

Assertion Description
assert_output_contains(text) Final text response contains text
assert_output_not_contains(text) Final text response does not contain text
assert_stop_reason(reason) Final call stop reason equals reason (e.g. "end_turn")

Token budget

Assertion Description
assert_max_tokens(n) Total tokens across all calls ≤ n

Introspection

probe.iteration_count       # int — number of LLM calls
probe.tools_called          # list[str] — sorted tool names used
probe.final_output          # str | None — last text block in session
probe.total_tokens          # int — input + output tokens across all calls
probe.total_input_tokens    # int
probe.total_output_tokens   # int

CLI

Inspect and compare fixtures without writing Python:

# Pretty-print a fixture
agentprobe show tests/fixtures/my_agent.jsonl

# Compare two fixtures (exits 1 if differences found)
agentprobe diff tests/fixtures/v1.jsonl tests/fixtures/v2.jsonl

Example show output:

fixture: tests/fixtures/my_agent.jsonl  (2 call(s))

── Call 1/2  model=claude-opus-4-8  stop=tool_use  in=50 out=30  312ms
  [tool_use] bash({"command": "ls /tmp"})

── Call 2/2  model=claude-opus-4-8  stop=end_turn  in=80 out=25  280ms
  [text] The /tmp directory contains: file1.txt, file2.txt, temp.log

total tokens: 185  (130 in + 55 out)

pytest fixture

agentprobe is auto-registered as a pytest plugin. The agentprobe fixture is available in all tests without any conftest.py setup:

def test_something(agentprobe):
    with agentprobe.replay("tests/fixtures/session.jsonl") as probe:
        ...

To use Session directly (e.g. in scripts or non-pytest contexts):

from agentprobe import Session

session = Session()
with session.replay("tests/fixtures/session.jsonl") as probe:
    ...

Fixture format

Fixtures are newline-delimited JSON (.jsonl). Each line is one messages.create call:

{"request": {"model": "...", "messages": [...], "tools": [...]}, "response": {"id": "...", "content": [...], "stop_reason": "tool_use", "usage": {"input_tokens": 50, "output_tokens": 30}}, "timestamp": 1748700000.0, "duration_ms": 312.5}

Fixtures are plain text — safe to commit to git, diff in PRs, and edit by hand.


License

MIT

Metadata

Release files for pytest-agentprobe 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-agentprobe 0.1.0
File Size Uploaded Provenance
pytest_agentprobe-0.1.0.tar.gz 10.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-agentprobe 0.1.0
File Interpreter ABI Platform
pytest_agentprobe-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 20.0 kB

Release files / pytest_agentprobe-0.1.0.tar.gz

Download URL pytest_agentprobe-0.1.0.tar.gz
Size 10.2 kB
Tags Source
SHA-256 checksum
How to use checksums
dab9f2463ac91924bb131f62348e7f9d6ae6e5f8d5a8a778a0fdca41ae8a3acb
BLAKE2b-256 checksum
How to use checksums
0333bf781408119691b0f9c1232bbe2d1300257f9e50e31f4ee70de14d19f42d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / pytest_agentprobe-0.1.0-py3-none-any.whl

Download URL pytest_agentprobe-0.1.0-py3-none-any.whl
Size 9.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
11b3e9970075efe9f57f6df21f51f5bba6dde47a39c1079ae1c44ba3ecc9cb38
BLAKE2b-256 checksum
How to use checksums
dd1c4fe3f5965874dac62365d7ae80a6556520421648982d6cdd7258fc58e269
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for pytest-agentprobe 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page