Skip to main content

Walk-forward / out-of-sample validation for agent evals — detect eval-set overfitting the way quants detect backtest overfitting.

Project description

agent-walkforward

Stop overfitting your agent to its eval set.

Agent eval tools (agentevals, RAGAS, DeepEval, your own JSONL log) score your agent against a fixed set of cases. You tweak a prompt, the score goes up, you ship. Sound familiar? It's the same trap quants fell into for decades: tune a strategy against one slice of history, the backtest looks great, live trading falls apart. The score went up because you overfit the test, not because the agent got better.

agent-walkforward borrows the fix quants already built — walk-forward validation with purge and embargo — and applies it to eval logs. It splits your time-ordered eval records into in-sample (IS) and out-of-sample (OOS) folds, then measures how much the score degrades out-of-sample. A large, persistent IS→OOS gap means your gains are overfit to the eval set.

  • Zero dependencies. Pure standard library — no dependency tree to fight.
  • Bring your own eval tool. Reads JSONL / JSON / CSV with configurable field names — it sits on top of whatever you already use, not instead of it.
  • Leakage-aware. Purge (drop IS records sharing a session/group with OOS) and embargo (temporal buffer) prevent the split from lying to you.

Install

⚠️ Not yet published to PyPI — install from source until the first PyPI release goes out (tracked in CHANGELOG).

git clone https://github.com/Starlight143/agent-walkforward
cd agent-walkforward
pip install -e ".[dev]"

Quickstart

Your eval log is a list of scored records, ordered in time (by run, by version, by commit — anything monotonic). One record per line for JSONL:

{"id": "case-001", "timestamp": 1, "score": 0.92, "group": "sessionA"}
{"id": "case-002", "timestamp": 2, "score": 0.88, "group": "sessionA"}

Run it:

agent-walkforward run --traces examples/sample_traces.jsonl --n-splits 4 --oos-size 8
agent-walkforward 0.1.0  |  records=50
fold  IS_n  OOS_n   IS_mean  OOS_mean      gap   degr  purged  embargo
   1    18      8    0.886    0.787    0.099   0.889       0        0
   2    26      8    0.856    0.729    0.127   0.851       0        0
   3    34      8    0.826    0.670    0.156   0.811       0        0
   4    42      8    0.796    0.608    0.189   0.763       0        0
------------------------------------------------------------------
overall IS_mean= 0.841  OOS_mean= 0.698  mean_gap= 0.143
VERDICT: OVERFIT_RISK

The OOS score sits well below IS and the gap widens fold over fold — a textbook sign that improvements are being tuned into the eval set rather than into the agent.

As a library

from agent_walkforward import load_records, walk_forward_splits, evaluate

records = load_records("evals.jsonl")
sorted_records, folds = walk_forward_splits(records, n_splits=4, oos_size=8, embargo=2)
report = evaluate(sorted_records, folds)

print(report.verdict, report.mean_gap)
for f in report.folds:
    print(f.index, f.is_mean, f.oos_mean, f.gap)

Concepts

Term What it means here
In-sample (IS) Records you'd have tuned against — everything before an OOS block.
Out-of-sample (OOS) A held-out block of later records the tuning never saw.
Walk-forward Consecutive OOS blocks tile the tail of the series; IS expands (or rolls) up to each block.
Purge Drop IS records whose group (session / conversation / seed) also appears in the OOS block, so a shared context can't leak across the boundary.
Embargo Drop the N IS records immediately before an OOS block, a buffer against temporal spillover.
Gap IS_mean − OOS_mean. Positive and growing ⇒ overfitting.
Degradation OOS_mean / IS_mean (only meaningful for non-negative scores).

Interop

The loader maps whatever field names your tool emits:

agent-walkforward run --traces run.csv \
  --id-field case_id --time-field ts --score-field response_match_score \
  --group-field conversation_id

That makes it a drop-in post-processor for agentevals OTel exports, RAGAS result tables, DeepEval logs, or a hand-rolled JSONL.

Agent integrations

Built to be called by coding agents (Claude Code, Cursor, Cline, ...), three ways:

1. One-call Python API — returns a plain JSON-serializable dict:

from agent_walkforward import analyze

result = analyze("evals.jsonl", n_splits=5, oos_size=10, embargo=2)
# also accepts a list of dicts or EvalRecord objects instead of a path
print(result["verdict"], result["mean_gap"])

2. CLI with --json — for agents that shell out and parse stdout:

agent-walkforward run --traces evals.jsonl --json

3. MCP server — expose it as a native tool to any MCP client:

pip install -e ".[mcp]"        # from a source checkout, see Install above
agent-walkforward-mcp          # stdio transport

Example Claude Desktop / Cursor config:

{
  "mcpServers": {
    "agent-walkforward": { "command": "agent-walkforward-mcp" }
  }
}

The server exposes one tool, walkforward_analyze, taking either a traces_path or inline records.

Claude Code skill — a ready-made skill lives in integrations/claude-code-skill/. Copy it to ~/.claude/skills/agent-walkforward/ and Claude Code will reach for it whenever you ask "are these eval gains real or overfit?".

When this helps — and when it doesn't

Walk-forward assumes your eval records have temporal / versioned order and that you iterate against them over time. It shines when you have a growing eval log across many runs or agent versions.

It does not help a one-shot eval of a static set with no ordering — there's no "later" to hold out. And the verdict is a heuristic on the score scale (default tuned for 0–1 scores), not a statistical significance test. Treat it as a smoke alarm, not a p-value.

Development

pip install -e ".[dev]"
pytest -q

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agent_walkforward-0.1.2.tar.gz (16.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agent_walkforward-0.1.2-py3-none-any.whl (14.9 kB view details)

Uploaded Python 3

File details

Details for the file agent_walkforward-0.1.2.tar.gz.

File metadata

  • Download URL: agent_walkforward-0.1.2.tar.gz
  • Upload date:
  • Size: 16.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for agent_walkforward-0.1.2.tar.gz
Algorithm Hash digest
SHA256 42a028f558e6127ab44ee1dfcecf21b993bf57b15940f42965c80a82783e5574
MD5 eae5b9559165b33557030a6916b96717
BLAKE2b-256 329914c9592b43db81152ab5d5a5a482cd3be75e1cbb2a63d48210e9027e3ad1

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_walkforward-0.1.2.tar.gz:

Publisher: publish.yml on Starlight143/agent-walkforward

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agent_walkforward-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for agent_walkforward-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 442fd4c47e5be309df070e45593a7c5126dd9f4a45f80b608ab2763c59a768d2
MD5 50b67a8df88efc867cc2cbb0f616a9b0
BLAKE2b-256 1c74049961d7925ff8fa000e40e4bfda4e4c7e9fb0e4e45ac0f18cd2d85c9399

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_walkforward-0.1.2-py3-none-any.whl:

Publisher: publish.yml on Starlight143/agent-walkforward

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page