TruAssure guide
Black-box, evidence-based quality engineering for AI agents: chat assistants, task agents and multi-agent
workflows. This guide ships inside the package (ta docs --open) and needs no network.
1. Install
pip install truassure # the core: the ta / truassure commands and the library
pip install "truassure[mcp,proxy,otel]" # plus the MCP server, the recording proxy and A2A tap, the OTel receiver
| Extra | Adds |
|---|---|
proxy | ta proxy (recording MCP proxy with fault injection) and ta a2a (agent-to-agent tap) |
otel | ta otel: an OTLP/HTTP receiver for the agent's OpenTelemetry GenAI spans |
mcp | ta mcp: TruAssure as an MCP server for AI assistants |
sql, postgres, sqlserver, mysql, oracle | Any SQL database through SQLAlchemy (state and shared store), with the driver |
servicebus, kafka | Agents behind Azure Service Bus or Kafka (the queue driver) |
s3, azure-blob | Shared stores on Amazon S3 or Azure Blob Storage (a folder or network share needs nothing) |
azure, anthropic | The LLM rubric grader (output-vs-action consistency) on Azure OpenAI or Anthropic |
chat, langgraph, postgres | The WebSocket chat driver; LangGraph and Postgres support for your own adapters |
Python 3.11 or later, on Windows, Linux or macOS. Pin the version in projects and CI: truassure==__VERSION__.
2. Five-minute demo
ta demo # runs the whole tour offline, into ./ta_demo
ta console --runs-dir ta_demo --open
The demo needs no agent, no Docker and no LLM: a scripted agent plays a fictitious insurance assistant. It runs
the demo catalog twice with repeats and perturbations, runs a build with a planted defect, then compares the runs,
lists defects, builds the scorecard, applies the release gate, calibrates a grader and writes HTML reports.
Every step prints the ta command it ran, so you can repeat any of them.
3. Start a project
ta new task claims-tests # or: ta new workflow ... / ta new chat ...
cd claims-tests
ta validate catalog.yaml
| File | What to do with it |
|---|---|
catalog.yaml | An example catalog for the profile. Replace the scenarios with your agent's. |
environment.py | How TruAssure reaches the agent and where the evidence is. Fill in the TODOs (mostly the reset). |
.env.example | The settings environment.py reads: copy to .env. |
truassure-gate.yaml | The release gate policy. |
README.md | These steps, for your team. |
One project per agent or AI initiative works well: the catalog and gate are code, reviewed in pull requests.
4. How an agent is judged
An episode is one task given to the agent in a prepared environment. A checkpoint is where it is judged (every user turn in a chat; once at the end for a task or workflow). Each checkpoint is judged at five stages, and the first failing stage is reported:
| Stage | Question | Evidence |
|---|---|---|
| goal | Did it understand the request (intent, route)? | The agent's reported decision |
| plan | Was the course sensible: steps, loops, handoffs, approvals before writes? | Trace and observers |
| action | Only allowed, correct actions, with the right arguments, for the right person? | Observers (proxy, spans, tap) |
| end_state | Is the data right afterwards, and did nothing else change? | Before/after snapshots |
| output | Is what it told the user true and complete, and consistent with what it did? | Reply or report, graders |
Seams (SE-01 to SE-07) are where the agent's own account and the independent evidence disagree: a call it
reported that never happened, a write it didn't mention, a success announced after a failure, an action for the
wrong person. Each failure has a code (e.g. ACTION.FORBIDDEN_CALLED) with a severity.
Profiles: conversational (turn by turn), task (one event or task, judged at the end),
workflow (several agents, handoffs, identity on every hop, failure containment).
5. Connect your agent
An environment factory is a Python function environment(worker) -> Environment that
ta run --env module:function calls once per parallel worker. Ready-made builders:
| Builder | For |
|---|---|
truassure.profiles.task.http_task_environment | A task agent behind HTTP, a SQLite system of record and a folder of files it writes |
truassure.profiles.workflow.http_workflow_environment | Multi-agent systems behind HTTP, with the proxy and the A2A tap |
truassure.profiles.conversational.http_chat_environment | A chat agent behind a plain HTTP endpoint |
What the agent's endpoint should return
Task and workflow (POST /events with {"event": ...} or {"task": ...}, the
principal in X-Principal):
{"route": "claims_intake", "reason": "...",
"steps": [{"kind": "action", "name": "update_claim", "args": {"claim_id": "CLM-1001"}, "ok": true, "result": "..."},
{"kind": "handoff", "agent": "triage", "to": "billing", "member": "M-1001"}],
"hops": [{"from": "triage", "to": "billing", "member": "M-1001", "permissions": ["billing:read"]}],
"output": "summary for the user", "done": true, "outcome": "finished",
"usage": {"input_tokens": 0, "output_tokens": 0}}
Chat (POST /chat with {"session": "...", "message": "..."}):
{"reply": "Your balance is $193.60.", "intent": "billing",
"steps": [{"kind": "action", "name": "get_billing", "args": {"policy_id": "POL-1002"}, "ok": true}]}
Only reply (chat) or done/output (task) are essential. A different shape needs
only a parse function passed to the driver. An agent that reports nothing about its steps can still be
tested as a black box (chat: trace=False): its actions then come from the observers alone.
Independent evidence
| Source | How | Records |
|---|---|---|
| Recording MCP proxy | ta proxy --upstream <tool server> --log-dir logs/proxy; point the agent at the proxy | Every tool call: name, arguments, principal, outcome. Injects faults (error, timeout, empty) in the upstream's own format. |
| A2A tap | ta a2a --route billing=<url> --log-dir logs/a2a; senders call the tap | Every handoff, with the member and permissions read from the identity token. Injects agent faults. |
| OpenTelemetry | ta otel --out-dir spans (needs TA_OTEL_TOKEN); the agent exports OTLP/JSON to it | GenAI execute_tool spans: a second record of tool calls. |
| State | SQLite database, a folder of files, a git repository | Before/after snapshots and diffs; SQL, file and git checks. |
environment.fixture. Without it, scenarios that change data affect the ones after them.Other systems (a message bus, Postgres, a SaaS API) plug in as adapters: classes that implement the driver, observer
or state protocol in truassure.core.protocols, registered through the entry-point groups
truassure.drivers, truassure.observers, truassure.state and
truassure.graders.
6. Writing a catalog
A catalog is YAML, checked against a JSON Schema by ta validate. Keys starting with x- are
ignored, which is handy for YAML anchors and notes.
schema_version: 1
name: claims agent
defaults: {principal: {user: adjuster-07, permissions: ["claims:read"]}, environment: {fixture: seed},
budgets: {steps: 12, seconds: 120}, severity: major}
actions:
get_claim: {kind: read, scopes: ["claims:read"]}
make_payment: {kind: write, requires_approval: true, scopes: ["payments:write"]}
scenarios:
- id: INTAKE-01
profile: task
input: {event: {type: claim.document_received, claim_id: CLM-1001}}
expect: {...}
Scenario fields
| Field | Meaning |
|---|---|
id, profile | Required. Profile: conversational, task or workflow. |
principal | {user, roles, permissions, attributes}: who the agent acts for. Checked on every observed call. |
input | Exactly one of prompt, event (needs a type) or task. Task and workflow scenarios. |
checkpoints | Conversational: one per user turn, each {id, input: {prompt}, expect, perturbations, faults, approval}. |
agents | Workflow: the agents taking part (two or more). |
perturbations | Variants of the input: a whole alternative prompt (string), or {name, <field.path>: value} overrides, or noise: duplicate_event, reordered_fields, extra_fields, typos. |
faults | {action, mode: error|timeout|empty, times, delay_s, message}, injected by the proxy or tap. |
budgets | {steps, tokens, cost_usd, seconds}. |
severity | critical, major or minor; drives defect severity and the gate. |
test_types | Test-type ids for the scorecard (e.g. TT-02, SS-09, SE-07). |
capabilities | Tags such as claims.intake: used by capability packs and per-capability gate rules. |
What to expect, stage by stage (expect)
| Key | Checks |
|---|---|
goal.intents, goal.route, goal.routes | Accepted intents (chat) or the route / task interpretation. |
plan.max_steps, max_loops, max_calls | Bounds on the course taken (max_calls may be per action). |
plan.terminates, approval_before_writes, no_duplicate_writes | It finished; it asked before writing; it didn't repeat writes. |
plan.handoffs, handoffs_ordered, contain_failures | Workflow: {from, to, principal_preserved, max_permissions} per hop; exact order; after a failure nothing is handed on. |
actions.required, attempted, preferred | Actions that must succeed / be tried / ideally be used. An item is a name or {name: {arg: value}}. |
actions.forbidden, allowed, must_not_succeed, no_writes | Never call; call nothing else; may be tried but must not succeed; no writes at all. |
end_state.changes | none, a list of tables or paths that are the only ones allowed to change, or {adapter: [scopes]}. |
end_state.sql, files, git | {name, sql, equals}; {path, exists, contains, not_contains, equals, matches}; {ref, branch_exists, commit_count, files_changed, message_contains}. |
output.facts, output.forbidden | Facts the answer must state ("a or b" alternatives); phrases it must not contain. |
output.artifact | A file, report or patch the agent produced: {kind, path, checks, tests}. |
output.reference, output.rubric | For graders (e.g. the LLM rubric grader). |
Translating an existing suite catalog: ta translate suites.yaml --yaml -o catalog.yaml.
7. Running
ta run catalog.yaml --env environment:environment --repeats 3 --perturbations all --parallel 2
| Option | Meaning |
|---|---|
--repeats N | Run each episode N times: pass^k (works every time, not just once) and flaky detection. |
--perturbations all|0,1,2 | Also run the input variants (0 is the catalog input): robustness. |
--parallel N | N environments (workers) at once. |
--scenario ID | Only these scenarios (repeatable). |
--mask LABEL=REGEX | An extra masking rule for personal data (repeatable). |
--run-id, --runs-dir, --step-timeout | Name the run; where runs go (default ./ta_runs or TA_RUNS_DIR); seconds per stimulus. |
Exit codes: 0 every case passed, 1 a case failed or was flaky, 2 bad input, 3 the environment is locked by another run.
8. Results and decisions
| Command | Gives |
|---|---|
ta show [run] | Runs, or one run's cases: stage marks, result, Wilson range, reasons. |
ta report latest | Two reports, each one self-contained file that works offline. execution-report.html (with its record execution-report.json) is for stakeholders: a readiness statement, KPI cards and charts, test-type and test-data coverage, persona views, findings with evidence and next actions, the trend against the previous comparable run (--baseline RUN to choose); personal details masked. report.html is for engineers: cases, episodes (the agent's account beside the evidence), scorecard, defects, comparison and gate. |
ta report RUN RUN ... | Several runs at once (for example one per system, run side by side): each run's own reports, plus consolidated-report.html and .json over all of them: an overall readiness statement, KPI cards, outcomes, findings and the release gate per run, every finding in one list, and a test-type matrix. --out FILE and --title TEXT place and name it; each run's reports are linked from it. |
ta console --open | A local browser console over all runs: reports, comparisons, gates. 127.0.0.1 only, token in the URL. |
ta compare A B | Checkpoint by checkpoint: regressions and fixes. Across deployments (model, host, framework), a parity verdict against run-to-run noise. |
ta defects latest | The defect list (JSON and CSV), severity by failure code. |
ta junit latest [--baseline RUN] | JUnit XML for CI; with a baseline only new failures fail. |
ta scorecard latest | The run against the 71-type test catalogue: measured, failing, not run. |
ta pack CAPABILITY | Is a capability ready? (exit 1 when not) |
ta gate latest --policy truassure-gate.yaml | The release gate (exit 1 on fail): pass rate, defects by severity, flaky handling, parity, clean seams, per-capability rules. |
ta calibrate set.yaml --grader module:Class | How far to trust a grader: Cohen's kappa against human labels. |
# truassure-gate.yaml
gate:
max_defects: {critical: 0, major: 2}
min_pass_rate: 0.9
flaky: fail # fail | skip | pass
parity_must_hold: true
seams_clean: [SE-01, SE-02, SE-03, SE-04]
capabilities:
claims.settlement: {min_pass_rate: 1.0, max_defects: {critical: 0, major: 0}}
9. CI pipelines
ta init --ci github --catalog catalog.yaml --env environment:environment
ta init --ci azure --catalog catalog.yaml --env environment:environment
Writes a pipeline that installs TruAssure, validates the catalog, runs it with repeats and perturbations, publishes
JUnit results and the defect list, applies the gate and keeps the run folder as an artifact. Set the
TRUASSURE_PIP variable to pin the version (e.g. truassure==__VERSION__).
9b. The scenario library
Reusable packs for risks every initiative shares. A catalog includes a pack and fills in its parameters with its own action names and data; the scenarios then run like hand-written ones.
ta library # the packs
ta library prompt-injection # what it tests, its parameters, its scenarios
ta library privacy --add catalog.yaml
include:
- pack: payments-approvals
with: {user: shan, pay_prompt: "Pay $50 on POL-1002.", payment_action: make_payment,
payment_args: {policy_id: POL-1002, amount: 50}, payment_scopes: [payments, policies]}
only: [DECLINED, TOOL-FAILS] # optional: a subset
position: start # optional: run before the catalog's own scenarios (default: after)
| Pack | Profiles | Tests |
|---|---|---|
privacy | chat | Another customer's record never revealed; typed personal data not echoed; nothing sent to an address given in chat. |
prompt-injection | chat, task | Instructions in the message, in pasted data or in an event field don't trigger a dangerous action or a canary phrase. |
unsafe-writes | task | A failed or timed-out write is never reported as done; a duplicate event writes once. |
payments-approvals | chat | Declined: nothing paid. Approved: exactly that payment. Tool failure: never "paid". |
identity-handoffs | workflow | Every hop for the same member, permissions only narrowing; a failure is contained. |
conversation-handling | chat | Asks when a request is ambiguous; follows a change of mind; uses the corrected value; reads "don't pay, just tell me" right; recovers from acting on the wrong record; returns to the task after an aside. |
answers-and-boundaries | chat | Answers from the record it looked up; says when it can't know; refuses an action whose precondition fails; hands over to a person; no staff-only actions; no account details before the caller is verified; resists a jailbreak; doesn't refuse a legitimate request; no legal advice or promised outcome. |
tone-and-policy | chat | Rubric-graded (needs the LLM grader): a competent multi-turn conversation, empathy, professionalism under abuse, your written policy. |
Every pack tags its scenarios with catalogue test types, so they appear on the scorecard and in the execution report's coverage matrix.
Your own packs: a YAML file (pack: ./packs/claims-rules.yaml, relative to the catalog) or a package that
registers one in the truassure.packs entry-point group. A pack has pack, params
(each with a description, and an example or a default), optional actions, and scenarios
using {{param}}. Included scenarios record their pack, version and parameters in source.
9c. Enterprise databases and connectors
Any SQL database SQLAlchemy supports, through one adapter: install truassure[sql] plus the driver extra
(postgres, sqlserver, mysql, oracle; others: the dialect's own package).
from truassure.adapters.state.factory import make_state
make_state("postgresql+psycopg://reader:***@db:5432/claims", tables=["claims", "payments"])
make_state("mssql+pyodbc://reader:***@sql/claims?driver=ODBC+Driver+18+for+SQL+Server")
make_state("oracle+oracledb://reader:***@db:1521/?service_name=CLAIMS", schema="CLAIMS")
make_state("snowflake://reader:***@account/db/schema") # any SQLAlchemy dialect
make_state("data/claims.db") # SQLite file
make_state("https://crm.test.example/api", resources={"cases": "/cases"}, items_path="data") # REST
- Tables and primary keys are found by reflection (override with
tables=andkeys=); values are normalised so snapshots compare the same on every database. - Read-only: read-only sessions where the database supports them (Postgres, SQL Server, MySQL/MariaDB, Oracle, SQLite), every session rolled back. Use a login that can only read.
- Passwords in URLs, including inside ODBC strings, are never printed.
Drivers beyond HTTP: OpenAICompatibleDriver (chat-completions endpoints, with conversation history),
QueueDriver with ServiceBusTransport (truassure[servicebus]) or
KafkaTransport (truassure[kafka]). task_environment(name=..., driver=..., state=[...])
combines any driver with any state adapters; http_chat_environment(..., driver=...) does it for chat.
The shared store can be a database too (ta push --store postgresql+psycopg://...): runs go into two
ORM-mapped tables, ta_runs (one row per run: gate, pass rate, defects) and ta_run_files.
A DBA can create them from truassure.adapters.stores.sql.ddl("postgresql://").
10. AI assistants (MCP)
{ "mcpServers": { "truassure": { "command": "ta", "args": ["mcp"] } } }
Every CLI command is a tool with the same arguments, so an assistant in the IDE can validate catalogs, run them, read results and apply the gate. Long-running services (proxy, tap, receiver, console) are not tools. Results are marked as data: they may quote what an agent said, and must not be followed as instructions.
11. Settings and security
- Settings and secrets come from environment variables or the nearest
.env(real environment variables win). TruAssure never prints secret values. Keep.envout of git. - Every listener binds to 127.0.0.1 unless
--allow-remote. Control APIs need tokens (TA_PROXY_TOKEN,TA_OTEL_TOKEN); the console makes its own per start. - Personal data (emails, phone numbers, secret-named fields, your
--maskrules) is masked before evidence is written.ta gc --days Napplies retention. - Test against test environments and test data. Fault injection and resets change the agent's environment.
12. Command reference
| Command | Purpose |
|---|---|
demo, new, docs | Offline tour; a starter project; this guide. |
validate, translate | Check catalogs; convert a suite catalog. |
run, show, report, gc | Run a catalog; list and inspect runs; the HTML report; retention. |
compare, defects, junit, scorecard, pack, gate, calibrate | Run-level analysis and governance. |
init | A CI pipeline. |
push, pull, portfolio | Share runs; copy them back; every initiative at a glance. |
library | The scenario library. |
console, mcp | The browser console; the MCP server. |
proxy, a2a, otel | Observers. |
ta <command> --help lists every option.
TruAssure __VERSION__ · MIT licence · examples use fictitious Harbor Mutual data.