Octomind
Same model as opencode, same endpoint, same prices — octomind solved 24/25 to its 19/25, at half the cost. Open-source agent runtime: one binary, any model, runs unattended in CI. Apache 2.0.
翻译准备中
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Understudy is a scenario-driven testing framework for AI agents that simulates realistic multi-turn users, runs those scenes against an agent through a simple app adapter, records a structured execution trace of messages, tool calls, and handoffs, and then evaluates behavior with deterministic checks, optional LLM judges, and run reports.
Testing with understudy is 4 steps:
The key insight: assert against the trace, not the prose. Don't check what the agent said—check what it did (tool calls).
Simulate multi-turn conversations with personas to test dialog agents.
trace.called("tool_name")Evaluate autonomous agents executing multi-step tasks.
trace.performed("action")See examples/README.md for complete examples of both paradigms.
See real examples:
Restore a completed checkpoint, inject synthetic history/state, or replay through
exchange X, then execute and assert only against exchange X+1. ADK and LangGraph
support isolated checkpoint forks; LangChainApp builds an instrumented LangChain
agent with tool middleware. Traces retain nested invocation identities.
See the state-testing guide
for run_turn, replay_to, scoped tool mocks, pytest fixtures, CLI commands, and
framework-specific restoration limits.
pip install "understudy[all]"
from understudy.adk import ADKApp
from my_agent import agent
app = ADKApp(agent=agent)
Your agent has tools that call external services. Mock them for testing:
from understudy.mocks import MockToolkit
mocks = MockToolkit()
@mocks.handle("lookup_order")
def lookup_order(order_id: str) -> dict:
return {
"order_id": order_id,
"items": [{"sku": "BACKPACK-01", "quantity": 1}],
"status": "delivered",
}
@mocks.handle("create_return")
def create_return(order_id: str, item_sku: str, reason: str) -> dict:
return {"return_id": "RET-001", "status": "created"}
Create scenes/return_backpack.yaml:
id: return_eligible_backpack
description: Customer wants to return a backpack
starting_prompt: "I'd like to return an item please."
conversation_plan: |
Goal: Return the hiking backpack from order ORD-10031.
- Provide order ID when asked
- Return reason: too small
persona: cooperative
max_turns: 15
expectations:
required_tools:
- lookup_order
- create_return
forbidden_tools:
- issue_refund
from understudy import Scene, run
scene = Scene.from_file("scenes/return_backpack.yaml")
trace = run(app, scene, mocks=mocks)
assert trace.called("lookup_order")
assert trace.called("create_return")
assert not trace.called("issue_refund")
Or with pytest (define app and mocks fixtures in conftest.py):
pytest test_returns.py -v
Run multiple scenes with multiple simulations per scene:
from understudy import Suite, RunStorage
suite = Suite.from_directory("scenes/")
storage = RunStorage()
# Run each scene 3 times and tag for comparison
results = suite.run(
app,
mocks=mocks,
storage=storage,
n_sims=3,
tags={"version": "v1"},
)
print(f"{results.pass_count}/{len(results.results)} passed")
Understudy separates simulation (generating traces) from evaluation (checking traces). Use together or separately:
understudy run \
--app mymodule:agent_app \
--scene ./scenes/ \
--n-sims 3 \
--junit results.xml
Generate traces only:
understudy simulate \
--app mymodule:agent_app \
--scenes ./scenes/ \
--output ./traces/ \
--n-sims 3
Evaluate existing traces:
understudy evaluate \
--traces ./traces/ \
--output ./results/ \
--junit results.xml
Python API:
from understudy import simulate_batch, evaluate_batch
# Generate traces
traces = simulate_batch(
app=agent_app,
scenes="./scenes/",
n_sims=3,
output="./traces/",
)
# Evaluate later
results = evaluate_batch(
traces="./traces/",
output="./results/",
)
# Run simulations
understudy run --app mymodule:app --scene ./scenes/
understudy simulate --app mymodule:app --scenes ./scenes/
understudy evaluate --traces ./traces/
# View results
understudy list
understudy show <run_id>
understudy summary
# Compare runs by tag
understudy compare --tag version --before v1 --after v2
# Generate reports
understudy report -o report.html
understudy compare --tag version --before v1 --after v2 --html comparison.html
# Interactive browser
understudy serve --port 8080
# HTTP simulator server (for browser/UI testing)
understudy serve-api --port 8000
# Cleanup
understudy delete <run_id>
understudy clear
For qualities that can't be checked deterministically:
from understudy.judges import Judge
empathy_judge = Judge(
rubric="The agent acknowledged frustration and was empathetic while enforcing policy.",
samples=5,
)
result = empathy_judge.evaluate(trace)
assert result.score == 1
Built-in rubrics:
from understudy.prompts import (
TOOL_USAGE_CORRECTNESS,
POLICY_COMPLIANCE,
TONE_EMPATHY,
ADVERSARIAL_ROBUSTNESS,
TASK_COMPLETION,
)
The understudy summary command shows:
The HTML report (understudy report) includes:
See the full documentation for:
MIT