# Evals Behavioural evaluations for `deepagents`. Each subdirectory is an independent workspace package containing vitest tests that run a real agent against LLM APIs and assert on the resulting trajectory. Results are streamed to [LangSmith](https://smith.langchain.com) as experiments so you can compare runs across models and track regressions over time. ## Available eval suites | Suite | Description | | ---------------------------------------------------- | -------------------------------------------------------------------------------- | | [`basic/`](./basic/) | System prompt adherence, simple reasoning, avoiding unnecessary tool calls | | [`all/`](./all/) | Aggregated run that executes all eval suites in a single Vitest + LangSmith session | | [`files/`](./files/) | File operations — read, write, edit, ls, grep, glob, parallel I/O, deep nesting | | [`followup-quality/`](./followup-quality/) | Clarifying question quality for underspecified user requests | | [`hitl/`](./hitl/) | Human-in-the-loop interrupt behavior, review configs, resume after approval | | [`external-benchmarks/`](./external-benchmarks/) | Curated hard-set from FRAMES, Nexus, and BFCL v3 benchmark samples | | [`memory/`](./memory/) | AGENTS.md memory injection — recall, guided behavior, multiple sources, graceful fallback | | [`memory-agent-bench/`](./memory-agent-bench/) | MemoryAgentBench-style long-context memorization and retrieval scenarios | | [`memory-multiturn/`](./memory-multiturn/) | Multi-turn memory persistence: implicit preferences, explicit instructions, transient filtering | | [`skills/`](./skills/) | Skill file discovery, reading, selection, combination, and editing via skill source paths | | [`subagents/`](./subagents/) | Subagent delegation — `task` tool routing to named and general-purpose subagents | | [`summarization/`](./summarization/) | Summarization middleware behavior and conversation-history offloading | | [`tau2-airline/`](./tau2-airline/) | Tau2-airline inspired policy-grounded airline support tasks | | [`todos/`](./todos/) | Sequential `write_todos` state updates and completion behavior | | [`tool-selection/`](./tool-selection/) | Direct/indirect tool routing and multi-step chaining across mock integrations | | [`tool-usage-relational/`](./tool-usage-relational/) | Multi-step tool chaining with relational data lookups (users, locations, foods) | ## Running evals Evals require the `EVAL_RUNNER` environment variable to select a model runner. Available runners are registered in [`internal/eval-harness/src/setup.ts`](../internal/eval-harness/src/setup.ts): You also need `LANGSMITH_API_KEY` set for result tracking (and the appropriate `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` for the model you choose). ```bash # Run all eval suites with Sonnet 4.5 EVAL_RUNNER=sonnet-4-5 pnpm test:eval # Run a single suite EVAL_RUNNER=sonnet-4-5 pnpm --filter @deepagents/eval-basic test:eval # Run every suite in one execution (single reporter session) EVAL_RUNNER=sonnet-4-5 pnpm --filter @deepagents/eval-all test:eval # Run with a different model EVAL_RUNNER=gpt-4.1 pnpm --filter @deepagents/eval-files test:eval ``` ## Writing a new eval 1. Create a new directory under `evals/` (e.g. `evals/my-eval/`). 2. Add a `package.json`: ```json { "name": "@deepagents/eval-my-eval", "private": true, "type": "module", "scripts": { "test:eval": "vitest run" }, "dependencies": { "@deepagents/evals": "workspace:*", "deepagents": "workspace:*", "langsmith": "^0.5.4", "vitest": "^4.0.18" } } ``` 3. Add a `vitest.config.ts`: ```ts import { defineConfig } from "vitest/config"; export default defineConfig({ test: { environment: "node", globals: false, testTimeout: 120_000, hookTimeout: 60_000, teardownTimeout: 60_000, include: ["**/*.test.ts"], setupFiles: ["@deepagents/evals/setup"], reporters: ["default", "langsmith/vitest/reporter"], }, }); ``` 4. Write your test in `eval.test.ts`: ```ts import * as ls from "langsmith/vitest"; import { expect } from "vitest"; import { getDefaultRunner } from "@deepagents/evals"; const runner = getDefaultRunner(); const evalName = "deepagents-js-my-eval"; ls.describe( evalName, () => { ls.test( "my test case", { inputs: { query: "Hello" } }, async ({ inputs }) => { const result = await runner.run({ query: inputs.query }); expect(result).toHaveAgentSteps(1); }, ); }, { projectName: runner.name, upsert: true }, ); ``` 5. Run `pnpm install` from the repo root to link the new workspace. ### Customising the agent per test Use `runner.extend()` to create a derived runner with different agent configuration. The `run()` method only takes invocation params (`query`, `initialFiles`). ```ts // Custom system prompt const result = await runner .extend({ systemPrompt: "Your name is Foo Bar." }) .run({ query: "What is your name?" }); // Custom tools const result = await runner .extend({ tools: [myTool] }) .run({ query: "Use the tool." }); // Custom subagents const result = await runner .extend({ subagents: [ { name: "helper", description: "A helper agent", tools: [myTool] }, ], }) .run({ query: "Delegate to the helper." }); // Seed files const result = await runner.run({ query: "Read /data.txt", initialFiles: { "/data.txt": "hello world" }, }); ``` ### Custom matchers The harness provides vitest matchers that also log LangSmith feedback: - `toHaveAgentSteps(n)` — assert exact step count - `toHaveToolCallRequests(n)` — assert total tool-call count - `toHaveToolCallInStep(step, { name, argsContains?, argsEquals? })` — assert a specific tool call in a step (1-indexed) - `toHaveFinalTextContaining(text, caseInsensitive?)` — assert the final response contains text ## Architecture See [`internal/eval-harness/README.md`](../internal/eval-harness/README.md) for details on the harness internals.