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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42a028f558e6127ab44ee1dfcecf21b993bf57b15940f42965c80a82783e5574
|
|
| MD5 |
eae5b9559165b33557030a6916b96717
|
|
| BLAKE2b-256 |
329914c9592b43db81152ab5d5a5a482cd3be75e1cbb2a63d48210e9027e3ad1
|
Provenance
The following attestation bundles were made for agent_walkforward-0.1.2.tar.gz:
Publisher:
publish.yml on Starlight143/agent-walkforward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agent_walkforward-0.1.2.tar.gz -
Subject digest:
42a028f558e6127ab44ee1dfcecf21b993bf57b15940f42965c80a82783e5574 - Sigstore transparency entry: 2173890698
- Sigstore integration time:
-
Permalink:
Starlight143/agent-walkforward@2897fa804e4cfee8937f684b620cd1121ece6344 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/Starlight143
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2897fa804e4cfee8937f684b620cd1121ece6344 -
Trigger Event:
release
-
Statement type:
File details
Details for the file agent_walkforward-0.1.2-py3-none-any.whl.
File metadata
- Download URL: agent_walkforward-0.1.2-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
442fd4c47e5be309df070e45593a7c5126dd9f4a45f80b608ab2763c59a768d2
|
|
| MD5 |
50b67a8df88efc867cc2cbb0f616a9b0
|
|
| BLAKE2b-256 |
1c74049961d7925ff8fa000e40e4bfda4e4c7e9fb0e4e45ac0f18cd2d85c9399
|
Provenance
The following attestation bundles were made for agent_walkforward-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on Starlight143/agent-walkforward
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agent_walkforward-0.1.2-py3-none-any.whl -
Subject digest:
442fd4c47e5be309df070e45593a7c5126dd9f4a45f80b608ab2763c59a768d2 - Sigstore transparency entry: 2173890710
- Sigstore integration time:
-
Permalink:
Starlight143/agent-walkforward@2897fa804e4cfee8937f684b620cd1121ece6344 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/Starlight143
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2897fa804e4cfee8937f684b620cd1121ece6344 -
Trigger Event:
release
-
Statement type: