Suites¶
A suite names the agent under test (module:callable), the cases and the evaluators. Suite
defaults apply to every case; a case evaluator with the same name replaces the default one:
# suites/support.yaml
name: support-agent
agent: support_agent:run_agent # imported from the cwd or the suite's folder
repetitions: 3
defaults:
evaluators: [no_error, {max_steps: 8}]
cases:
- id: refund-simple
input: "I want a refund for order 1234"
expected: {tools: [lookup_order, issue_refund]}
evaluators:
- {tool_sequence: {mode: ordered}}
- {max_steps: 4} # overrides the default max_steps: 8
from agentlens.suite import load_suite
suite = load_suite("suites/support.yaml")
agent = suite.load_agent()
for case in suite.resolved_cases():
print(case.id, [spec.name for spec in case.evaluators], agent(case.input))
# refund-simple ['no_error', 'tool_sequence', 'max_steps'] ...
Every problem is reported at once, with its position: suites/support.yaml:13:9:
cases[0].evaluators[1]: unknown evaluator 'max_step' (did you mean 'max_steps'?). Cases can live
in their own file (cases: data/support.jsonl), and Suite(name=..., agent=run_agent,
cases=[...]) builds a suite in Python. suite.content_hash() fingerprints what the suite runs
(agent, model, repetitions, resolved cases), so experiments can tell whether the suite changed.
Running a suite¶
run_suite runs every case repetitions times (at most concurrency at once, sync or async
agents), records each run as a trace, scores it and saves everything under a new experiment. An
agent that raises or exceeds timeout_s fails its run, not the suite. If the agent has a model
parameter it receives the model under test (model=, else the suite's model:, else
$AGENTLENS_MODEL):
import agentlens
from agentlens.llm import LLMRequest, Message, get_client
from agentlens.store import SQLiteStore
from agentlens.suite import Suite, get_baseline, run_suite, set_baseline
def echo_agent(question: str, model: str | None = None) -> str:
llm = agentlens.wrap_llm(get_client(model or "mock")) # "mock" echoes the question
return llm.complete(LLMRequest(messages=[Message.user(question)])).content
suite = Suite(name="echo", agent=echo_agent, model="mock", repetitions=3, concurrency=4,
defaults={"evaluators": ["no_error", "exact_match"]},
cases=[{"id": "hi", "input": "hi", "expected": "hi"}])
with SQLiteStore("runs.db") as store:
result = run_suite(suite, store=store, name="main", config={"prompt_version": "v1"})
print(f"{result.passed}/{result.total} passed ({result.pass_rate:.0%})") # 3/3 passed (100%)
print(result.experiment.config["git_sha"], result.experiment.config["suite_hash"][:12])
set_baseline(store, "main") # the one baseline of suite "echo"
print(get_baseline(store, "echo").name) # main
Each trace carries case_id, repetition, suite and experiment metadata; arun_suite is the
async version, and progress=lambda result, done, total: ... reports each finished run.
See also: design §6, API: agentlens.suite.