Create test scenarios¶
Generate realistic conversations from a short seed and run each one against your agent:
# test_scenarios.py
from monocle_test_tools import MonocleValidator
from travel_agent import build_travel_agent
SEED = {
"agent": "A travel booking agent that books flights between airports and hotels in a city.",
"goals": [
"Book a flight from Bombay to Hyderabad on 22 Oct 2026",
"Book a hotel in Mumbai for 27 Nov 2026",
],
"out_of_scope": ["refunds", "visa advice"],
"max_turns": 8,
}
@MonocleValidator().monocle_scenario_testcase(SEED)
async def test_scenarios(scenario):
await MonocleValidator().test_scenario_async(build_travel_agent(), "langgraph", scenario)
export MONOCLE_TEST_AGENT_MODEL=openai:gpt-4.1-mini # plays the user (required)
export MONOCLE_TEST_JUDGE_MODEL=openai:gpt-4.1-mini # judges each reply (defaults to the same)
export MONOCLE_EXPORTER=okahu OKAHU_API_KEY=okh_...
pytest test_scenarios.py
A model plays the user, and a judge decides after every reply whether the scenario's success criteria are met. Each conversation lands in Okahu as a session you can evaluate and label.
Preview — not yet in a published release
Scenario generation (monocle_scenario_testcase) is on Monocle main and is not in a
released package yet (the current release is 0.8.15). Install the development version from the
Monocle repo main branch to use this page. Everything
in the other pages works on the released packages.
Write the seed¶
Before you write tests, write down who uses the agent, what they want, what they should not do, and the data those situations involve. The seed captures exactly that.
- Goals — phrase them the way a user would, with real values. The generator splits each goal into its details (source, destination, date) and builds variations around them.
out_of_scope— what the agent should decline. Each entry becomes a scope-escape test.max_turns— optional, defaults to 8.
Hand-written scenarios¶
When you need precise control, write the scenarios yourself and pass the list as the seed:
SEED = [{
"test_name": "book_flight_impatient",
"scenario": "Book a flight from source to destination.",
"persona": "An impatient user who gives one detail at a time.",
"params": [
{"name": "source", "value": "San Francisco", "description": "source airport", "is_initial": True},
{"name": "date", "value": "22 Oct 2026", "description": "travel date"},
],
"success_criteria": "The agent confirms the flight is booked.",
"max_turns": 6,
"target_description": "A travel booking agent that books flights and hotels.",
}]
- Held-back details — a param without
is_initialis withheld until the agent asks for it. - Optional details — set
"required": falsefor a detail the agent may skip. - Test data — vary the values to cover what your users bring: ordinary, edge, and invalid.
What gets generated per goal¶
About fifteen scenarios per goal, each its own pytest case:
| Kind | Test id contains | What it checks |
|---|---|---|
| Personas | persona.<name> |
Five styles: power user, newcomer, developer, manager, terse |
| Held-back details | withhold.<detail> |
It asks for a missing detail instead of guessing |
| False premise | false_premise |
It handles a request for a capability it does not have |
| Red team | redteam.<attack> |
Prompt extraction, unauthorized actions, indirect injection |
| Out of scope | scope.scope_escape.<topic> |
It declines each out_of_scope topic |
Run a subset with -k, e.g. pytest -k redteam or pytest -k withhold.
When a scenario fails¶
A failed scenario reports the full turn-by-turn transcript with the judge's verdicts. The reasons: criteria violated, maximum turns used up, the agent raised an error, or a required detail was never asked for (the agent made it up).
Notes¶
- Fresh agent per scenario — build the agent inside the test so each scenario gets its own memory.
- Cached set — scenarios are generated on the first run and cached in
scenarios.json, reused until the seed changes, so runs stay comparable. - Plan for cost — each scenario is a multi-turn conversation with a judge call per turn, so cost
grows with goals × scenarios × turns. Start with one goal and
-k, then widen. - Manual testing counts too — run your app with tracing on and use it as your users would; those traces land in Okahu next to the generated ones and you label them the same way.
With Claude Code
The okahu@skills plugin can plan and write these tests: monocle-test-plan reads your app and
writes a scenario/checks table; monocle-test-gen turns a scenario into a pytest test.
Next¶
Check agent behaviour — the assertions and evals you put inside these tests.