AgentVal
Testing framework for multi-step, multi-agent AI workflows.
Test agent behavior, not just outputs. Like pytest for your AI agent's decision-making process.
The Problem
Existing eval tools test inputs → outputs (black box). But when your 10-step agent workflow fails at step 8, they can't tell you why.
- Was it a bad LLM decision at step 3?
- Did a tool return empty results at step 2 that cascaded?
- Is the agent calling tools in a dangerous order?
AgentVal tests the journey, not just the destination.
Quick Start
pip install agentval
Test a pre-recorded trace
import agentval as ap
def test_agent_searches_before_summarizing():
trace = ap.Trace.from_file("traces/my_agent.json")
# Assert tool was called
ap.tool_called(trace, "web_search")
# Assert step order
ap.step_order(trace, ["web_search", "summarize"])
# Assert safety: agent should never delete anything
ap.tool_not_called(trace, "delete_file")
# Assert output quality
ap.output_contains(trace, "python", step_name="summarize")
# Assert no errors
ap.no_errors(trace)
pytest test_my_agent.py -v
Capture traces from live code
import agentval as ap
@ap.trace_tool()
def web_search(query: str) -> list[dict]:
return call_search_api(query)
@ap.trace_tool()
def read_url(url: str) -> str:
return fetch_page(url)
@ap.trace_llm()
def summarize(context: str) -> str:
return call_llm(context)
def test_live_agent():
with ap.capture("search_agent") as trace:
results = web_search("python async patterns")
content = read_url(results[0]["url"])
summary = summarize(content)
ap.tool_called(trace, "web_search")
ap.step_order(trace, ["web_search", "read_url", "summarize"])
ap.no_errors(trace)
Root cause analysis
When things fail, AgentVal traces back to find where it actually went wrong:
import agentval as ap
trace = ap.Trace.from_file("traces/failing_agent.json")
report = ap.analyze(trace)
print(report)
AgentVal Analysis: failing_search_agent
Steps: 5 | Failures: 1
------------------------------------------------------------
Failure #1:
Step 4 (delete_files) failed. Root cause traced to step 1 (scan_directory):
Produced empty output (possible upstream data issue)
Failed: step 4 (delete_files)
Root cause: step 1 (scan_directory)
Causal chain:
[1] scan_directory: Produced empty output (possible upstream data issue)
[2] find_duplicates: Produced empty output (possible upstream data issue)
[4] delete_files: Failed with error: TypeError: Cannot iterate over NoneType
Step 4 failed, but the real problem was step 1 returning empty results.
Assertions Reference
Tool assertions
ap.tool_called(trace, "web_search") # tool was used
ap.tool_called(trace, "search", times=2) # called exactly N times
ap.tool_not_called(trace, "delete_file") # tool was NOT used
ap.tool_called_with(trace, "search", query="python") # called with specific args
Step order assertions
ap.step_order(trace, ["search", "summarize"]) # relative order (non-consecutive ok)
ap.exact_step_order(trace, ["a", "b", "c"]) # exact match
ap.step_not_after(trace, "delete", "search") # safety: delete never after search
Output assertions
ap.output_contains(trace, "python") # last step output
ap.output_contains(trace, "results", step_name="web_search") # specific step
ap.output_matches(trace, r"Python.*language") # regex
Status assertions
ap.no_errors(trace) # zero failures
ap.step_succeeded(trace, "web_search") # specific step ok
ap.step_failed(trace, "bad_step") # expected failure (negative testing)
Count assertions
ap.max_steps(trace, 10) # agent shouldn't take more than 10 steps
ap.min_steps(trace, 2) # agent should do at least 2 things
Custom assertions
ap.step_matches(trace, "search", lambda s: len(s.output["results"]) > 0)
ap.trace_matches(trace, lambda t: t.duration_ms < 5000)
ap.no_repeated_tool_calls(trace, "search", max_repeats=2) # catch infinite loops
Trace Format
AgentVal uses a simple JSON trace format:
{
"trace_id": "abc123",
"name": "my_agent",
"steps": [
{
"step_id": "step_001",
"name": "web_search",
"step_type": "tool_call",
"input": {"kwargs": {"query": "python"}},
"output": {"result": [{"title": "Python.org"}]},
"status": "success",
"timestamp": 1708000000.0,
"duration_ms": 450
}
]
}
Step types: llm_call, tool_call, handoff, decision, error, custom
Framework Adapters
OpenAI Agents SDK
pip install agentval[openai]
from agentval.adapters.openai_adapter import OpenAIAgentsAdapter
from agents import Agent, Runner
adapter = OpenAIAgentsAdapter()
agent = Agent(name="my_agent", instructions="...")
result = Runner.run_sync(agent, "hello")
trace = adapter.from_run_result(result)
ap.tool_called(trace, "web_search")
Custom adapter
from agentval.adapters.base import BaseAdapter
class MyFrameworkAdapter(BaseAdapter):
def capture(self, **kwargs):
# implement trace capture for your framework
...
def parse_trace(self, raw_data):
# convert framework data to AgentVal Trace
...
pytest Integration
AgentVal registers as a pytest plugin automatically. Built-in fixtures:
def test_with_fixture(trace_from_file):
trace = trace_from_file("traces/my_agent.json")
ap.no_errors(trace)
def test_with_analysis(analyze_trace, trace_from_file):
trace = trace_from_file("traces/my_agent.json")
report = analyze_trace(trace)
assert not report.has_failures
Why not DeepEval / Promptfoo / Ragas?
Those are great tools. They evaluate LLM outputs — accuracy, hallucination, relevance.
AgentVal evaluates agent behavior — what tools it called, in what order, whether it followed safety rules, and where things went wrong in multi-step workflows.
| DeepEval/Promptfoo/Ragas | AgentVal | |
|---|---|---|
| Tests | LLM output quality | Agent decision-making |
| Scope | Single LLM call | Multi-step workflows |
| When fails | "Output was wrong" | "Step 3 returned empty → Step 7 crashed" |
| Approach | Black box (input → output) | White box (full execution trace) |
Use them together. They're complementary.
Roadmap
- Multi-agent handoff testing
- Drift detection (scheduled eval runs)
- Visual trace explorer (web UI)
- CI/CD GitHub Action
- LangChain/LangGraph adapter
- CrewAI adapter
- Smart sampling (statistical confidence with fewer LLM calls)
- Cost tracking per test run
Contributing
Contributions welcome. See CONTRIBUTING.md for guidelines.
Areas where help is most needed:
- Framework adapters (LangGraph, CrewAI, AutoGen, Google ADK)
- More assertion types
- Documentation and examples
- Real-world trace examples
License
MIT
Metadata
Release files for agentval 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| agentval-0.1.0.tar.gz | 21.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agentval-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.3 kB
Release files / agentval-0.1.0.tar.gz
| Download URL | agentval-0.1.0.tar.gz |
|---|---|
| Size | 21.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
caab492658853aade337b1f3e07ee23a6d9018934ba5d5870cc68f8354c69926
|
|
BLAKE2b-256 checksum How to use checksums |
843408c0097c9ad62d802f9a9f076a97ad1cbe314b4a1c29b66dee7a662f4a58
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 21, 2026.
Transparency logRelease files / agentval-0.1.0-py3-none-any.whl
| Download URL | agentval-0.1.0-py3-none-any.whl |
|---|---|
| Size | 17.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c9309f66465482ec7568e5194f2918ecf387b0d39aa0e96cb0435aac3e99da95
|
|
BLAKE2b-256 checksum How to use checksums |
49e19c2f5f176bb5939b668fb4df75962eab256cf3ce6b291c6e533f76ee3400
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 21, 2026.
Transparency logSecurity for agentval 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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for agentval-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 21, 2026.
Transparency logA 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.
To learn more about protecting yourself from malicious actors, visit our package security documentation.