persistaibench 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- persistaibench-0.1.0/LICENSE +21 -0
- persistaibench-0.1.0/PKG-INFO +141 -0
- persistaibench-0.1.0/README.md +130 -0
- persistaibench-0.1.0/persistaibench.egg-info/PKG-INFO +141 -0
- persistaibench-0.1.0/persistaibench.egg-info/SOURCES.txt +28 -0
- persistaibench-0.1.0/persistaibench.egg-info/dependency_links.txt +1 -0
- persistaibench-0.1.0/persistaibench.egg-info/entry_points.txt +2 -0
- persistaibench-0.1.0/persistaibench.egg-info/top_level.txt +1 -0
- persistaibench-0.1.0/persistbench/__init__.py +6 -0
- persistaibench-0.1.0/persistbench/agents.py +95 -0
- persistaibench-0.1.0/persistbench/chains.py +351 -0
- persistaibench-0.1.0/persistbench/cli.py +208 -0
- persistaibench-0.1.0/persistbench/faults.py +55 -0
- persistaibench-0.1.0/persistbench/g2_smoke.py +808 -0
- persistaibench-0.1.0/persistbench/llm_agent.py +954 -0
- persistaibench-0.1.0/persistbench/memory.py +392 -0
- persistaibench-0.1.0/persistbench/pilot.py +374 -0
- persistaibench-0.1.0/persistbench/runner.py +142 -0
- persistaibench-0.1.0/persistbench/scorer.py +113 -0
- persistaibench-0.1.0/persistbench/world.py +138 -0
- persistaibench-0.1.0/pyproject.toml +24 -0
- persistaibench-0.1.0/setup.cfg +4 -0
- persistaibench-0.1.0/tests/test_cli.py +59 -0
- persistaibench-0.1.0/tests/test_dependents.py +132 -0
- persistaibench-0.1.0/tests/test_exit_criterion.py +150 -0
- persistaibench-0.1.0/tests/test_g2_smoke.py +359 -0
- persistaibench-0.1.0/tests/test_llm_dependent.py +385 -0
- persistaibench-0.1.0/tests/test_llm_pilot.py +396 -0
- persistaibench-0.1.0/tests/test_week2_policies.py +389 -0
- persistaibench-0.1.0/tests/test_world.py +67 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Karmendra Pandey
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: persistaibench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks — cross-session propagation of silent agent failures through long-term memory
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/karmendra8386/persistbench-bench
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# PersistAIBench
|
|
13
|
+
|
|
14
|
+
**An evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks.**
|
|
15
|
+
|
|
16
|
+
PersistAIBench (harness/repo name: PersistBench) measures one unmeasured chain: a tool fails silently in Session 1 → the agent writes its own account of the outcome into long-term memory → different tasks in Sessions 2–4 act on that memory → whether a write-time guard stops the propagation. There is no attacker and no prompt injection; the failure mode is benign self-poisoning.
|
|
17
|
+
|
|
18
|
+
Paper: Karmendra Pandey, *"Fail-Plausible, Then Persistent"* (working title). Outline approved 2026-10-23; filed in Google Drive EB1 §03 (`persistbench-outline.pdf`, `persistbench-outline-record-2026-10-23.md`).
|
|
19
|
+
|
|
20
|
+
## Quickstart
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone https://github.com/karmendra8386/persistbench-bench.git
|
|
24
|
+
cd persistbench-bench
|
|
25
|
+
python3 -m venv .venv
|
|
26
|
+
.venv/bin/pip install .
|
|
27
|
+
.venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend fake
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The last command runs one pilot cell (chain × fault × policy) end-to-end and prints the scored cell as JSON. `--backend fake` is a deterministic scripted backend — a plumbing smoke test, **not a result**: the model id in the output is stamped `fake/...` so it can never be mistaken for a real model run. `--chain` accepts a full chain id or a unique prefix (`chain01`); `--policy` is one of M0–M5; `--fault` one of the five fault conditions.
|
|
31
|
+
|
|
32
|
+
To run a cell with a real model (requires a GPU and `torch` + `transformers`, which the Kaggle notebook installs):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
.venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend hf --model Qwen/Qwen2.5-7B-Instruct
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To run the full pilot grid (2 models × 6 chains × 5 faults × M0–M5), use `persistaibench pilot --config configs/pilot.json --out results/pilot --backend hf` — or `--backend fake` for a plumbing-only pass. The Week 2 pilot results are in `docs/results.md`; the design rule they support is written up in `docs/verified-memory-writes.md`.
|
|
39
|
+
|
|
40
|
+
## Provenance — read this first
|
|
41
|
+
|
|
42
|
+
**Weeks 1–2 = harness, design, policy machinery, and the Week 2 formation pilot.** The pilot ran on 2026-11-02 (Kaggle, GPU T4 x2) and its results — including a failed model arm, reported as such — are in **`docs/results.md`** with raw outputs in `results/pilot/`. Everything before the pilot (scripted-agent evidence below) is machinery validation, not model findings. What exists:
|
|
43
|
+
|
|
44
|
+
- `docs/design-freeze.md` — the frozen design: RQ1–RQ5 / H1–H5, environment, fault conditions, memory policies M0–M5, six metrics, paired statistics plan.
|
|
45
|
+
- `docs/preregistration-draft.md` — hypotheses, conditions, primary metrics, analysis plan, and the four pre-registered falsification checks.
|
|
46
|
+
- `docs/results.md` — the Week 2 pilot: formation rates by policy × model and by fault, the classifier-gate result, and the Qwen3-8B write-generation anomaly.
|
|
47
|
+
- `docs/verified-memory-writes.md` — the transferable design rule the pilot supports: verify memory writes against current state, not against the observation that produced them.
|
|
48
|
+
- A working deterministic harness with scripted agents, validated by the test suite below.
|
|
49
|
+
- **Week 2 (code half, complete):** memory policies **M0–M5 all implemented** under the frozen interface — M2 (self-summary + disclosure/provenance annotation), M3 (structured evidence-contract write), M4 (state-verified write with correction/rejection accounting) join Week 1's M0/M1/M5. Every policy records a per-write log (entries + cost placeholders) sufficient to compute the frozen metrics, and `runner.run_chain` executes a full chain under any policy × any fault.
|
|
50
|
+
- **Week 2 pilot execution package:** `persistbench/llm_agent.py` (LLM origin agent behind a backend interface — deterministic fake backend for tests, HF transformers backend for real models; conservative JSON action parsing and claim extraction), real per-policy memory-write generation (M1 free-form summary, M2 under the disclosure instruction, M3 claims parsed into the evidence-contract format — unparseable claims are logged as `unknown`, never verified; M4 = model write through the state-verified policy), `persistbench/pilot.py` + `configs/pilot.json` + `scripts/run_pilot.py` (2 models × 6 chains × 5 faults × M0–M5, per-cell md/csv/jsonl outputs, and the 50-write hand-labelling sample export for the ≥90%-agreement classifier gate), the `persistaibench` CLI (`persistbench/cli.py`), and `kaggle/week2-pilot.ipynb` + `kaggle/RUNBOOK.md`. Plumbing was additionally smoke-tested with a real tiny model from the local cache (Qwen2.5-0.5B-Instruct, CPU): see `results/smoke-llm-2026-10-23.md` — labelled SMOKE, not a result.
|
|
51
|
+
|
|
52
|
+
Two implementation notes that matter for reading the machinery (both follow the design freeze; flagged here so they are not mistaken for findings):
|
|
53
|
+
|
|
54
|
+
- **M3's evidence is tool observations, never DB state** (checking state is M4's definition). Under explicit-error and empty/partial faults the contract therefore stores no false claim as fact. Under silent-wrong-value and stale faults, the corrupted response *is* the supporting observation, so M3 marks the false claim `verified` — the contract's boundary, asserted in the tests, and exactly why H3 orders M3 below M4.
|
|
55
|
+
- **M4 corrects rather than merely rejects** where the DB holds a value (rejection is reserved for facts the DB does not hold and for unverifiable keys). Its write log accounts for every falsified field as corrected-or-rejected, and on no-fault writes it corrects/rejects nothing — pre-registration falsification check 3 (a guard that destroys legitimate utility is not viable) is tested directly.
|
|
56
|
+
|
|
57
|
+
The full live-model runs (Qwen2.5-7B, Qwen3-8B, Llama-3.1-8B, Mistral-7B; ~3,600 sessions) are Weeks 3–4 of the plan. Numbers from the Week 2 pilot are in `docs/results.md`; any other number quoted from this repo is a property of the scripted harness, not of any model.
|
|
58
|
+
|
|
59
|
+
## Layout
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
persistbench/
|
|
63
|
+
world.py deterministic retail world; DB ground truth; agent tools
|
|
64
|
+
chains.py the 12 task chains (Session 1 + 3 dependents + correction fact)
|
|
65
|
+
faults.py seeded fault injection: no_fault / explicit_error /
|
|
66
|
+
empty_partial / silent_wrong_value / stale_value
|
|
67
|
+
agents.py scripted oracle + naive origin agents; dependent agent
|
|
68
|
+
memory.py memory policies M0–M5 (all implemented, Week 2) + write logs
|
|
69
|
+
scorer.py programmatic grading vs DB end-state — no LLM judge
|
|
70
|
+
runner.py origin / dependent trajectory runners
|
|
71
|
+
llm_agent.py LLM origin agent + per-policy write generation (Week 2
|
|
72
|
+
pilot prep; fake + HF transformers backends)
|
|
73
|
+
pilot.py pilot config validation, grid runner, formation tables,
|
|
74
|
+
hand-labelling sample export + agreement gate
|
|
75
|
+
cli.py the `persistaibench` command (run one cell / run the pilot)
|
|
76
|
+
configs/ pilot.json — the frozen pilot configuration
|
|
77
|
+
scripts/ run_pilot.py — pilot entry point (the CLI wraps the same code)
|
|
78
|
+
kaggle/ week2-pilot.ipynb + RUNBOOK.md (launch procedure)
|
|
79
|
+
results/ smoke evidence (labelled); pilot/ — Week 2 pilot outputs
|
|
80
|
+
tests/ pytest suite, incl. the Week 1 exit criterion
|
|
81
|
+
docs/ design freeze + pre-registration draft; results.md —
|
|
82
|
+
Week 2 pilot results; verified-memory-writes.md — the
|
|
83
|
+
design rule
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Week 1 exit criterion — evidence
|
|
87
|
+
|
|
88
|
+
> The harness grades hand-written/scripted trajectories correctly in all 5 fault conditions.
|
|
89
|
+
|
|
90
|
+
Verified by `tests/test_exit_criterion.py` (plus `test_world.py`, `test_dependents.py`):
|
|
91
|
+
|
|
92
|
+
- Oracle agent under no-fault: task success + truthful claim on **12/12 chains**.
|
|
93
|
+
- Naive (unverifying) agent: scorer detects a falsified claim under **each of the 4 injected faults × 12 chains (48/48)**, and names the falsified fields.
|
|
94
|
+
- Oracle agent: claim **never falsified under any fault (60/60 chain × fault cases)**.
|
|
95
|
+
- The dissociation the paper rests on is mechanically demonstrated: silent/stale faults leave the DB correct but the naive claim false (24/24); explicit/empty faults fail both the task and the claim (24/24).
|
|
96
|
+
- Propagation machinery: an M1 write of the naive claim is classified false on the corrupted fact (12/12); the dependent task that needs that fact then fails under M1 memory and succeeds under M5 (oracle) and M0 (query the world); the pre-Session-4 correction event repairs a revising agent and not a stubborn one (12/12).
|
|
97
|
+
- Hand-written false trajectory (claims success for work never executed) is detected, including the `task_success` falsification.
|
|
98
|
+
|
|
99
|
+
Run it:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
python3 -m venv .venv && .venv/bin/pip install pytest
|
|
103
|
+
.venv/bin/python -m pytest -q # 1164 passed, 10 skipped (skips: Session-2 task
|
|
104
|
+
# does not depend on that chain's corrupted fact)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Standard library only at run time; pytest is the sole dev dependency.
|
|
108
|
+
|
|
109
|
+
## Week 2 policy evidence (machinery, not results)
|
|
110
|
+
|
|
111
|
+
Verified by `tests/test_week2_policies.py` (811 cases) over all 12 chains × 5 fault conditions, with scripted agents:
|
|
112
|
+
|
|
113
|
+
- **M4:** the stored write is never falsified, under any fault, for either scripted agent; its content matches the M5 oracle store; its log's corrected+rejected entries are exactly the scorer's falsified fields; on no-fault writes it accepts everything and its store equals M1's.
|
|
114
|
+
- **M3:** under explicit-error / empty-partial faults the whole write classifies clean (success claims stored `failed` / `unknown`, never verified); under no fault every claim is verified and downstream behaviour matches M1. Under silent/stale faults the false claim is stored `verified` on the corrupted observation's evidence — the documented boundary above.
|
|
115
|
+
- **M2:** the claim is stored verbatim with provenance on every entry (`verified_against_state: False` throughout); observed tool errors appear in the disclosures, and a success claim past an observed error is annotated `contradicted_by_observation`. Downstream behaviour matches M1 exactly — the annotation, not filtering, is M2's guard.
|
|
116
|
+
- **Correction:** where a guard kept the false memory out (M3/M4 under evidence-visible faults; M4 under all faults), even a stubborn agent's Session 4 is correct. Where the lie reached the store (M2/M3 under silent/stale), the Week-1 pattern holds: stubborn resists, revising is repaired.
|
|
117
|
+
|
|
118
|
+
## Next (per the approved plan)
|
|
119
|
+
|
|
120
|
+
- **Week 2 pilot — DONE (2026-11-02):** 2 models × 6 chains × 5 faults × M0–M5, Session-1 formation rates measured; classifier gate passed (50/50 = 100% hand-label agreement). Results: `docs/results.md`. One arm (Qwen3-8B) produced almost no parseable writes — fixing its write generation (token cap / `<think>` handling) and re-piloting that arm, or replacing it, is a precondition for Week 3; scale-up is not cleared as a 2-model run until then.
|
|
121
|
+
- **Weeks 3–4:** full runs; correction analysis; first results memo.
|
|
122
|
+
- **Month 2:** LongMemEval + Mem0 transfer arms; write-up. Zenodo preprint week 8–10 after author review; NeurIPS 2027 Datasets & Benchmarks is the primary venue target.
|
|
123
|
+
|
|
124
|
+
## Cite
|
|
125
|
+
|
|
126
|
+
If you use PersistAIBench in your work, please cite the software record on Zenodo:
|
|
127
|
+
|
|
128
|
+
```bibtex
|
|
129
|
+
@software{pandey_persistaibench_2026,
|
|
130
|
+
author = {Pandey, Karmendra},
|
|
131
|
+
title = {PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks},
|
|
132
|
+
version = {0.1.0},
|
|
133
|
+
doi = {10.5281/zenodo.23893975},
|
|
134
|
+
url = {https://github.com/karmendra8386/persistbench-bench},
|
|
135
|
+
date = {2026-11-02}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A `CITATION.cff` is included for automated citation tooling.
|
|
140
|
+
|
|
141
|
+
**DOI:** the v0.1.0 software release is archived on Zenodo as [10.5281/zenodo.23893975](https://doi.org/10.5281/zenodo.23893975) (concept DOI: [10.5281/zenodo.23893974](https://doi.org/10.5281/zenodo.23893974), which always resolves to the latest version). The accompanying paper (*"Fail-Plausible, Then Persistent"*, working title) is unpublished.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# PersistAIBench
|
|
2
|
+
|
|
3
|
+
**An evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks.**
|
|
4
|
+
|
|
5
|
+
PersistAIBench (harness/repo name: PersistBench) measures one unmeasured chain: a tool fails silently in Session 1 → the agent writes its own account of the outcome into long-term memory → different tasks in Sessions 2–4 act on that memory → whether a write-time guard stops the propagation. There is no attacker and no prompt injection; the failure mode is benign self-poisoning.
|
|
6
|
+
|
|
7
|
+
Paper: Karmendra Pandey, *"Fail-Plausible, Then Persistent"* (working title). Outline approved 2026-10-23; filed in Google Drive EB1 §03 (`persistbench-outline.pdf`, `persistbench-outline-record-2026-10-23.md`).
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
git clone https://github.com/karmendra8386/persistbench-bench.git
|
|
13
|
+
cd persistbench-bench
|
|
14
|
+
python3 -m venv .venv
|
|
15
|
+
.venv/bin/pip install .
|
|
16
|
+
.venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend fake
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The last command runs one pilot cell (chain × fault × policy) end-to-end and prints the scored cell as JSON. `--backend fake` is a deterministic scripted backend — a plumbing smoke test, **not a result**: the model id in the output is stamped `fake/...` so it can never be mistaken for a real model run. `--chain` accepts a full chain id or a unique prefix (`chain01`); `--policy` is one of M0–M5; `--fault` one of the five fault conditions.
|
|
20
|
+
|
|
21
|
+
To run a cell with a real model (requires a GPU and `torch` + `transformers`, which the Kaggle notebook installs):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
.venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend hf --model Qwen/Qwen2.5-7B-Instruct
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
To run the full pilot grid (2 models × 6 chains × 5 faults × M0–M5), use `persistaibench pilot --config configs/pilot.json --out results/pilot --backend hf` — or `--backend fake` for a plumbing-only pass. The Week 2 pilot results are in `docs/results.md`; the design rule they support is written up in `docs/verified-memory-writes.md`.
|
|
28
|
+
|
|
29
|
+
## Provenance — read this first
|
|
30
|
+
|
|
31
|
+
**Weeks 1–2 = harness, design, policy machinery, and the Week 2 formation pilot.** The pilot ran on 2026-11-02 (Kaggle, GPU T4 x2) and its results — including a failed model arm, reported as such — are in **`docs/results.md`** with raw outputs in `results/pilot/`. Everything before the pilot (scripted-agent evidence below) is machinery validation, not model findings. What exists:
|
|
32
|
+
|
|
33
|
+
- `docs/design-freeze.md` — the frozen design: RQ1–RQ5 / H1–H5, environment, fault conditions, memory policies M0–M5, six metrics, paired statistics plan.
|
|
34
|
+
- `docs/preregistration-draft.md` — hypotheses, conditions, primary metrics, analysis plan, and the four pre-registered falsification checks.
|
|
35
|
+
- `docs/results.md` — the Week 2 pilot: formation rates by policy × model and by fault, the classifier-gate result, and the Qwen3-8B write-generation anomaly.
|
|
36
|
+
- `docs/verified-memory-writes.md` — the transferable design rule the pilot supports: verify memory writes against current state, not against the observation that produced them.
|
|
37
|
+
- A working deterministic harness with scripted agents, validated by the test suite below.
|
|
38
|
+
- **Week 2 (code half, complete):** memory policies **M0–M5 all implemented** under the frozen interface — M2 (self-summary + disclosure/provenance annotation), M3 (structured evidence-contract write), M4 (state-verified write with correction/rejection accounting) join Week 1's M0/M1/M5. Every policy records a per-write log (entries + cost placeholders) sufficient to compute the frozen metrics, and `runner.run_chain` executes a full chain under any policy × any fault.
|
|
39
|
+
- **Week 2 pilot execution package:** `persistbench/llm_agent.py` (LLM origin agent behind a backend interface — deterministic fake backend for tests, HF transformers backend for real models; conservative JSON action parsing and claim extraction), real per-policy memory-write generation (M1 free-form summary, M2 under the disclosure instruction, M3 claims parsed into the evidence-contract format — unparseable claims are logged as `unknown`, never verified; M4 = model write through the state-verified policy), `persistbench/pilot.py` + `configs/pilot.json` + `scripts/run_pilot.py` (2 models × 6 chains × 5 faults × M0–M5, per-cell md/csv/jsonl outputs, and the 50-write hand-labelling sample export for the ≥90%-agreement classifier gate), the `persistaibench` CLI (`persistbench/cli.py`), and `kaggle/week2-pilot.ipynb` + `kaggle/RUNBOOK.md`. Plumbing was additionally smoke-tested with a real tiny model from the local cache (Qwen2.5-0.5B-Instruct, CPU): see `results/smoke-llm-2026-10-23.md` — labelled SMOKE, not a result.
|
|
40
|
+
|
|
41
|
+
Two implementation notes that matter for reading the machinery (both follow the design freeze; flagged here so they are not mistaken for findings):
|
|
42
|
+
|
|
43
|
+
- **M3's evidence is tool observations, never DB state** (checking state is M4's definition). Under explicit-error and empty/partial faults the contract therefore stores no false claim as fact. Under silent-wrong-value and stale faults, the corrupted response *is* the supporting observation, so M3 marks the false claim `verified` — the contract's boundary, asserted in the tests, and exactly why H3 orders M3 below M4.
|
|
44
|
+
- **M4 corrects rather than merely rejects** where the DB holds a value (rejection is reserved for facts the DB does not hold and for unverifiable keys). Its write log accounts for every falsified field as corrected-or-rejected, and on no-fault writes it corrects/rejects nothing — pre-registration falsification check 3 (a guard that destroys legitimate utility is not viable) is tested directly.
|
|
45
|
+
|
|
46
|
+
The full live-model runs (Qwen2.5-7B, Qwen3-8B, Llama-3.1-8B, Mistral-7B; ~3,600 sessions) are Weeks 3–4 of the plan. Numbers from the Week 2 pilot are in `docs/results.md`; any other number quoted from this repo is a property of the scripted harness, not of any model.
|
|
47
|
+
|
|
48
|
+
## Layout
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
persistbench/
|
|
52
|
+
world.py deterministic retail world; DB ground truth; agent tools
|
|
53
|
+
chains.py the 12 task chains (Session 1 + 3 dependents + correction fact)
|
|
54
|
+
faults.py seeded fault injection: no_fault / explicit_error /
|
|
55
|
+
empty_partial / silent_wrong_value / stale_value
|
|
56
|
+
agents.py scripted oracle + naive origin agents; dependent agent
|
|
57
|
+
memory.py memory policies M0–M5 (all implemented, Week 2) + write logs
|
|
58
|
+
scorer.py programmatic grading vs DB end-state — no LLM judge
|
|
59
|
+
runner.py origin / dependent trajectory runners
|
|
60
|
+
llm_agent.py LLM origin agent + per-policy write generation (Week 2
|
|
61
|
+
pilot prep; fake + HF transformers backends)
|
|
62
|
+
pilot.py pilot config validation, grid runner, formation tables,
|
|
63
|
+
hand-labelling sample export + agreement gate
|
|
64
|
+
cli.py the `persistaibench` command (run one cell / run the pilot)
|
|
65
|
+
configs/ pilot.json — the frozen pilot configuration
|
|
66
|
+
scripts/ run_pilot.py — pilot entry point (the CLI wraps the same code)
|
|
67
|
+
kaggle/ week2-pilot.ipynb + RUNBOOK.md (launch procedure)
|
|
68
|
+
results/ smoke evidence (labelled); pilot/ — Week 2 pilot outputs
|
|
69
|
+
tests/ pytest suite, incl. the Week 1 exit criterion
|
|
70
|
+
docs/ design freeze + pre-registration draft; results.md —
|
|
71
|
+
Week 2 pilot results; verified-memory-writes.md — the
|
|
72
|
+
design rule
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Week 1 exit criterion — evidence
|
|
76
|
+
|
|
77
|
+
> The harness grades hand-written/scripted trajectories correctly in all 5 fault conditions.
|
|
78
|
+
|
|
79
|
+
Verified by `tests/test_exit_criterion.py` (plus `test_world.py`, `test_dependents.py`):
|
|
80
|
+
|
|
81
|
+
- Oracle agent under no-fault: task success + truthful claim on **12/12 chains**.
|
|
82
|
+
- Naive (unverifying) agent: scorer detects a falsified claim under **each of the 4 injected faults × 12 chains (48/48)**, and names the falsified fields.
|
|
83
|
+
- Oracle agent: claim **never falsified under any fault (60/60 chain × fault cases)**.
|
|
84
|
+
- The dissociation the paper rests on is mechanically demonstrated: silent/stale faults leave the DB correct but the naive claim false (24/24); explicit/empty faults fail both the task and the claim (24/24).
|
|
85
|
+
- Propagation machinery: an M1 write of the naive claim is classified false on the corrupted fact (12/12); the dependent task that needs that fact then fails under M1 memory and succeeds under M5 (oracle) and M0 (query the world); the pre-Session-4 correction event repairs a revising agent and not a stubborn one (12/12).
|
|
86
|
+
- Hand-written false trajectory (claims success for work never executed) is detected, including the `task_success` falsification.
|
|
87
|
+
|
|
88
|
+
Run it:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
python3 -m venv .venv && .venv/bin/pip install pytest
|
|
92
|
+
.venv/bin/python -m pytest -q # 1164 passed, 10 skipped (skips: Session-2 task
|
|
93
|
+
# does not depend on that chain's corrupted fact)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Standard library only at run time; pytest is the sole dev dependency.
|
|
97
|
+
|
|
98
|
+
## Week 2 policy evidence (machinery, not results)
|
|
99
|
+
|
|
100
|
+
Verified by `tests/test_week2_policies.py` (811 cases) over all 12 chains × 5 fault conditions, with scripted agents:
|
|
101
|
+
|
|
102
|
+
- **M4:** the stored write is never falsified, under any fault, for either scripted agent; its content matches the M5 oracle store; its log's corrected+rejected entries are exactly the scorer's falsified fields; on no-fault writes it accepts everything and its store equals M1's.
|
|
103
|
+
- **M3:** under explicit-error / empty-partial faults the whole write classifies clean (success claims stored `failed` / `unknown`, never verified); under no fault every claim is verified and downstream behaviour matches M1. Under silent/stale faults the false claim is stored `verified` on the corrupted observation's evidence — the documented boundary above.
|
|
104
|
+
- **M2:** the claim is stored verbatim with provenance on every entry (`verified_against_state: False` throughout); observed tool errors appear in the disclosures, and a success claim past an observed error is annotated `contradicted_by_observation`. Downstream behaviour matches M1 exactly — the annotation, not filtering, is M2's guard.
|
|
105
|
+
- **Correction:** where a guard kept the false memory out (M3/M4 under evidence-visible faults; M4 under all faults), even a stubborn agent's Session 4 is correct. Where the lie reached the store (M2/M3 under silent/stale), the Week-1 pattern holds: stubborn resists, revising is repaired.
|
|
106
|
+
|
|
107
|
+
## Next (per the approved plan)
|
|
108
|
+
|
|
109
|
+
- **Week 2 pilot — DONE (2026-11-02):** 2 models × 6 chains × 5 faults × M0–M5, Session-1 formation rates measured; classifier gate passed (50/50 = 100% hand-label agreement). Results: `docs/results.md`. One arm (Qwen3-8B) produced almost no parseable writes — fixing its write generation (token cap / `<think>` handling) and re-piloting that arm, or replacing it, is a precondition for Week 3; scale-up is not cleared as a 2-model run until then.
|
|
110
|
+
- **Weeks 3–4:** full runs; correction analysis; first results memo.
|
|
111
|
+
- **Month 2:** LongMemEval + Mem0 transfer arms; write-up. Zenodo preprint week 8–10 after author review; NeurIPS 2027 Datasets & Benchmarks is the primary venue target.
|
|
112
|
+
|
|
113
|
+
## Cite
|
|
114
|
+
|
|
115
|
+
If you use PersistAIBench in your work, please cite the software record on Zenodo:
|
|
116
|
+
|
|
117
|
+
```bibtex
|
|
118
|
+
@software{pandey_persistaibench_2026,
|
|
119
|
+
author = {Pandey, Karmendra},
|
|
120
|
+
title = {PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks},
|
|
121
|
+
version = {0.1.0},
|
|
122
|
+
doi = {10.5281/zenodo.23893975},
|
|
123
|
+
url = {https://github.com/karmendra8386/persistbench-bench},
|
|
124
|
+
date = {2026-11-02}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
A `CITATION.cff` is included for automated citation tooling.
|
|
129
|
+
|
|
130
|
+
**DOI:** the v0.1.0 software release is archived on Zenodo as [10.5281/zenodo.23893975](https://doi.org/10.5281/zenodo.23893975) (concept DOI: [10.5281/zenodo.23893974](https://doi.org/10.5281/zenodo.23893974), which always resolves to the latest version). The accompanying paper (*"Fail-Plausible, Then Persistent"*, working title) is unpublished.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: persistaibench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks — cross-session propagation of silent agent failures through long-term memory
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/karmendra8386/persistbench-bench
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# PersistAIBench
|
|
13
|
+
|
|
14
|
+
**An evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks.**
|
|
15
|
+
|
|
16
|
+
PersistAIBench (harness/repo name: PersistBench) measures one unmeasured chain: a tool fails silently in Session 1 → the agent writes its own account of the outcome into long-term memory → different tasks in Sessions 2–4 act on that memory → whether a write-time guard stops the propagation. There is no attacker and no prompt injection; the failure mode is benign self-poisoning.
|
|
17
|
+
|
|
18
|
+
Paper: Karmendra Pandey, *"Fail-Plausible, Then Persistent"* (working title). Outline approved 2026-10-23; filed in Google Drive EB1 §03 (`persistbench-outline.pdf`, `persistbench-outline-record-2026-10-23.md`).
|
|
19
|
+
|
|
20
|
+
## Quickstart
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone https://github.com/karmendra8386/persistbench-bench.git
|
|
24
|
+
cd persistbench-bench
|
|
25
|
+
python3 -m venv .venv
|
|
26
|
+
.venv/bin/pip install .
|
|
27
|
+
.venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend fake
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The last command runs one pilot cell (chain × fault × policy) end-to-end and prints the scored cell as JSON. `--backend fake` is a deterministic scripted backend — a plumbing smoke test, **not a result**: the model id in the output is stamped `fake/...` so it can never be mistaken for a real model run. `--chain` accepts a full chain id or a unique prefix (`chain01`); `--policy` is one of M0–M5; `--fault` one of the five fault conditions.
|
|
31
|
+
|
|
32
|
+
To run a cell with a real model (requires a GPU and `torch` + `transformers`, which the Kaggle notebook installs):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
.venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend hf --model Qwen/Qwen2.5-7B-Instruct
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To run the full pilot grid (2 models × 6 chains × 5 faults × M0–M5), use `persistaibench pilot --config configs/pilot.json --out results/pilot --backend hf` — or `--backend fake` for a plumbing-only pass. The Week 2 pilot results are in `docs/results.md`; the design rule they support is written up in `docs/verified-memory-writes.md`.
|
|
39
|
+
|
|
40
|
+
## Provenance — read this first
|
|
41
|
+
|
|
42
|
+
**Weeks 1–2 = harness, design, policy machinery, and the Week 2 formation pilot.** The pilot ran on 2026-11-02 (Kaggle, GPU T4 x2) and its results — including a failed model arm, reported as such — are in **`docs/results.md`** with raw outputs in `results/pilot/`. Everything before the pilot (scripted-agent evidence below) is machinery validation, not model findings. What exists:
|
|
43
|
+
|
|
44
|
+
- `docs/design-freeze.md` — the frozen design: RQ1–RQ5 / H1–H5, environment, fault conditions, memory policies M0–M5, six metrics, paired statistics plan.
|
|
45
|
+
- `docs/preregistration-draft.md` — hypotheses, conditions, primary metrics, analysis plan, and the four pre-registered falsification checks.
|
|
46
|
+
- `docs/results.md` — the Week 2 pilot: formation rates by policy × model and by fault, the classifier-gate result, and the Qwen3-8B write-generation anomaly.
|
|
47
|
+
- `docs/verified-memory-writes.md` — the transferable design rule the pilot supports: verify memory writes against current state, not against the observation that produced them.
|
|
48
|
+
- A working deterministic harness with scripted agents, validated by the test suite below.
|
|
49
|
+
- **Week 2 (code half, complete):** memory policies **M0–M5 all implemented** under the frozen interface — M2 (self-summary + disclosure/provenance annotation), M3 (structured evidence-contract write), M4 (state-verified write with correction/rejection accounting) join Week 1's M0/M1/M5. Every policy records a per-write log (entries + cost placeholders) sufficient to compute the frozen metrics, and `runner.run_chain` executes a full chain under any policy × any fault.
|
|
50
|
+
- **Week 2 pilot execution package:** `persistbench/llm_agent.py` (LLM origin agent behind a backend interface — deterministic fake backend for tests, HF transformers backend for real models; conservative JSON action parsing and claim extraction), real per-policy memory-write generation (M1 free-form summary, M2 under the disclosure instruction, M3 claims parsed into the evidence-contract format — unparseable claims are logged as `unknown`, never verified; M4 = model write through the state-verified policy), `persistbench/pilot.py` + `configs/pilot.json` + `scripts/run_pilot.py` (2 models × 6 chains × 5 faults × M0–M5, per-cell md/csv/jsonl outputs, and the 50-write hand-labelling sample export for the ≥90%-agreement classifier gate), the `persistaibench` CLI (`persistbench/cli.py`), and `kaggle/week2-pilot.ipynb` + `kaggle/RUNBOOK.md`. Plumbing was additionally smoke-tested with a real tiny model from the local cache (Qwen2.5-0.5B-Instruct, CPU): see `results/smoke-llm-2026-10-23.md` — labelled SMOKE, not a result.
|
|
51
|
+
|
|
52
|
+
Two implementation notes that matter for reading the machinery (both follow the design freeze; flagged here so they are not mistaken for findings):
|
|
53
|
+
|
|
54
|
+
- **M3's evidence is tool observations, never DB state** (checking state is M4's definition). Under explicit-error and empty/partial faults the contract therefore stores no false claim as fact. Under silent-wrong-value and stale faults, the corrupted response *is* the supporting observation, so M3 marks the false claim `verified` — the contract's boundary, asserted in the tests, and exactly why H3 orders M3 below M4.
|
|
55
|
+
- **M4 corrects rather than merely rejects** where the DB holds a value (rejection is reserved for facts the DB does not hold and for unverifiable keys). Its write log accounts for every falsified field as corrected-or-rejected, and on no-fault writes it corrects/rejects nothing — pre-registration falsification check 3 (a guard that destroys legitimate utility is not viable) is tested directly.
|
|
56
|
+
|
|
57
|
+
The full live-model runs (Qwen2.5-7B, Qwen3-8B, Llama-3.1-8B, Mistral-7B; ~3,600 sessions) are Weeks 3–4 of the plan. Numbers from the Week 2 pilot are in `docs/results.md`; any other number quoted from this repo is a property of the scripted harness, not of any model.
|
|
58
|
+
|
|
59
|
+
## Layout
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
persistbench/
|
|
63
|
+
world.py deterministic retail world; DB ground truth; agent tools
|
|
64
|
+
chains.py the 12 task chains (Session 1 + 3 dependents + correction fact)
|
|
65
|
+
faults.py seeded fault injection: no_fault / explicit_error /
|
|
66
|
+
empty_partial / silent_wrong_value / stale_value
|
|
67
|
+
agents.py scripted oracle + naive origin agents; dependent agent
|
|
68
|
+
memory.py memory policies M0–M5 (all implemented, Week 2) + write logs
|
|
69
|
+
scorer.py programmatic grading vs DB end-state — no LLM judge
|
|
70
|
+
runner.py origin / dependent trajectory runners
|
|
71
|
+
llm_agent.py LLM origin agent + per-policy write generation (Week 2
|
|
72
|
+
pilot prep; fake + HF transformers backends)
|
|
73
|
+
pilot.py pilot config validation, grid runner, formation tables,
|
|
74
|
+
hand-labelling sample export + agreement gate
|
|
75
|
+
cli.py the `persistaibench` command (run one cell / run the pilot)
|
|
76
|
+
configs/ pilot.json — the frozen pilot configuration
|
|
77
|
+
scripts/ run_pilot.py — pilot entry point (the CLI wraps the same code)
|
|
78
|
+
kaggle/ week2-pilot.ipynb + RUNBOOK.md (launch procedure)
|
|
79
|
+
results/ smoke evidence (labelled); pilot/ — Week 2 pilot outputs
|
|
80
|
+
tests/ pytest suite, incl. the Week 1 exit criterion
|
|
81
|
+
docs/ design freeze + pre-registration draft; results.md —
|
|
82
|
+
Week 2 pilot results; verified-memory-writes.md — the
|
|
83
|
+
design rule
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Week 1 exit criterion — evidence
|
|
87
|
+
|
|
88
|
+
> The harness grades hand-written/scripted trajectories correctly in all 5 fault conditions.
|
|
89
|
+
|
|
90
|
+
Verified by `tests/test_exit_criterion.py` (plus `test_world.py`, `test_dependents.py`):
|
|
91
|
+
|
|
92
|
+
- Oracle agent under no-fault: task success + truthful claim on **12/12 chains**.
|
|
93
|
+
- Naive (unverifying) agent: scorer detects a falsified claim under **each of the 4 injected faults × 12 chains (48/48)**, and names the falsified fields.
|
|
94
|
+
- Oracle agent: claim **never falsified under any fault (60/60 chain × fault cases)**.
|
|
95
|
+
- The dissociation the paper rests on is mechanically demonstrated: silent/stale faults leave the DB correct but the naive claim false (24/24); explicit/empty faults fail both the task and the claim (24/24).
|
|
96
|
+
- Propagation machinery: an M1 write of the naive claim is classified false on the corrupted fact (12/12); the dependent task that needs that fact then fails under M1 memory and succeeds under M5 (oracle) and M0 (query the world); the pre-Session-4 correction event repairs a revising agent and not a stubborn one (12/12).
|
|
97
|
+
- Hand-written false trajectory (claims success for work never executed) is detected, including the `task_success` falsification.
|
|
98
|
+
|
|
99
|
+
Run it:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
python3 -m venv .venv && .venv/bin/pip install pytest
|
|
103
|
+
.venv/bin/python -m pytest -q # 1164 passed, 10 skipped (skips: Session-2 task
|
|
104
|
+
# does not depend on that chain's corrupted fact)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Standard library only at run time; pytest is the sole dev dependency.
|
|
108
|
+
|
|
109
|
+
## Week 2 policy evidence (machinery, not results)
|
|
110
|
+
|
|
111
|
+
Verified by `tests/test_week2_policies.py` (811 cases) over all 12 chains × 5 fault conditions, with scripted agents:
|
|
112
|
+
|
|
113
|
+
- **M4:** the stored write is never falsified, under any fault, for either scripted agent; its content matches the M5 oracle store; its log's corrected+rejected entries are exactly the scorer's falsified fields; on no-fault writes it accepts everything and its store equals M1's.
|
|
114
|
+
- **M3:** under explicit-error / empty-partial faults the whole write classifies clean (success claims stored `failed` / `unknown`, never verified); under no fault every claim is verified and downstream behaviour matches M1. Under silent/stale faults the false claim is stored `verified` on the corrupted observation's evidence — the documented boundary above.
|
|
115
|
+
- **M2:** the claim is stored verbatim with provenance on every entry (`verified_against_state: False` throughout); observed tool errors appear in the disclosures, and a success claim past an observed error is annotated `contradicted_by_observation`. Downstream behaviour matches M1 exactly — the annotation, not filtering, is M2's guard.
|
|
116
|
+
- **Correction:** where a guard kept the false memory out (M3/M4 under evidence-visible faults; M4 under all faults), even a stubborn agent's Session 4 is correct. Where the lie reached the store (M2/M3 under silent/stale), the Week-1 pattern holds: stubborn resists, revising is repaired.
|
|
117
|
+
|
|
118
|
+
## Next (per the approved plan)
|
|
119
|
+
|
|
120
|
+
- **Week 2 pilot — DONE (2026-11-02):** 2 models × 6 chains × 5 faults × M0–M5, Session-1 formation rates measured; classifier gate passed (50/50 = 100% hand-label agreement). Results: `docs/results.md`. One arm (Qwen3-8B) produced almost no parseable writes — fixing its write generation (token cap / `<think>` handling) and re-piloting that arm, or replacing it, is a precondition for Week 3; scale-up is not cleared as a 2-model run until then.
|
|
121
|
+
- **Weeks 3–4:** full runs; correction analysis; first results memo.
|
|
122
|
+
- **Month 2:** LongMemEval + Mem0 transfer arms; write-up. Zenodo preprint week 8–10 after author review; NeurIPS 2027 Datasets & Benchmarks is the primary venue target.
|
|
123
|
+
|
|
124
|
+
## Cite
|
|
125
|
+
|
|
126
|
+
If you use PersistAIBench in your work, please cite the software record on Zenodo:
|
|
127
|
+
|
|
128
|
+
```bibtex
|
|
129
|
+
@software{pandey_persistaibench_2026,
|
|
130
|
+
author = {Pandey, Karmendra},
|
|
131
|
+
title = {PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks},
|
|
132
|
+
version = {0.1.0},
|
|
133
|
+
doi = {10.5281/zenodo.23893975},
|
|
134
|
+
url = {https://github.com/karmendra8386/persistbench-bench},
|
|
135
|
+
date = {2026-11-02}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A `CITATION.cff` is included for automated citation tooling.
|
|
140
|
+
|
|
141
|
+
**DOI:** the v0.1.0 software release is archived on Zenodo as [10.5281/zenodo.23893975](https://doi.org/10.5281/zenodo.23893975) (concept DOI: [10.5281/zenodo.23893974](https://doi.org/10.5281/zenodo.23893974), which always resolves to the latest version). The accompanying paper (*"Fail-Plausible, Then Persistent"*, working title) is unpublished.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
persistaibench.egg-info/PKG-INFO
|
|
5
|
+
persistaibench.egg-info/SOURCES.txt
|
|
6
|
+
persistaibench.egg-info/dependency_links.txt
|
|
7
|
+
persistaibench.egg-info/entry_points.txt
|
|
8
|
+
persistaibench.egg-info/top_level.txt
|
|
9
|
+
persistbench/__init__.py
|
|
10
|
+
persistbench/agents.py
|
|
11
|
+
persistbench/chains.py
|
|
12
|
+
persistbench/cli.py
|
|
13
|
+
persistbench/faults.py
|
|
14
|
+
persistbench/g2_smoke.py
|
|
15
|
+
persistbench/llm_agent.py
|
|
16
|
+
persistbench/memory.py
|
|
17
|
+
persistbench/pilot.py
|
|
18
|
+
persistbench/runner.py
|
|
19
|
+
persistbench/scorer.py
|
|
20
|
+
persistbench/world.py
|
|
21
|
+
tests/test_cli.py
|
|
22
|
+
tests/test_dependents.py
|
|
23
|
+
tests/test_exit_criterion.py
|
|
24
|
+
tests/test_g2_smoke.py
|
|
25
|
+
tests/test_llm_dependent.py
|
|
26
|
+
tests/test_llm_pilot.py
|
|
27
|
+
tests/test_week2_policies.py
|
|
28
|
+
tests/test_world.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
persistbench
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Scripted agents for Week 1 harness validation.
|
|
2
|
+
|
|
3
|
+
Week 1 has no LLM runs. Two scripted origin agents exercise the harness:
|
|
4
|
+
|
|
5
|
+
- ``OracleAgent`` builds its end-of-session claim by re-reading database
|
|
6
|
+
ground truth, so its claim is truthful by construction, under every fault.
|
|
7
|
+
- ``NaiveAgent`` is the optimistic summariser: it records what it set out to
|
|
8
|
+
do (the plan's intended facts), overwritten by whatever the tool responses
|
|
9
|
+
reported, and never verifies against the DB. It models the false-success
|
|
10
|
+
behaviour FTA measured at response time, in scripted form.
|
|
11
|
+
|
|
12
|
+
A scripted ``DependentAgent`` resolves later-session tasks from a memory
|
|
13
|
+
dict, from a correction event, or — with no memory — by querying the world.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from .chains import resolve_fact
|
|
18
|
+
|
|
19
|
+
# Tool-response field -> fact key, used by the naive agent's claim builder.
|
|
20
|
+
RESPONSE_FACT_MAP = {
|
|
21
|
+
"tracking": "tracking",
|
|
22
|
+
"order_status": "order_status",
|
|
23
|
+
"payment_status": "payment_status",
|
|
24
|
+
"amount_cents": "charged_cents",
|
|
25
|
+
"refund_amount_cents": "refund_cents",
|
|
26
|
+
"address": "address",
|
|
27
|
+
"stock": "stock",
|
|
28
|
+
"cancellation_fee_cents": "cancellation_fee_cents",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class OracleAgent:
|
|
33
|
+
name = "oracle"
|
|
34
|
+
|
|
35
|
+
def build_claim(self, chain: dict, steps: list, world) -> dict:
|
|
36
|
+
claim = {key: resolve_fact(world, chain, key) for key in chain["intended_facts"]}
|
|
37
|
+
claim["task_success"] = all(
|
|
38
|
+
claim[key] == value for key, value in chain["intended_facts"].items()
|
|
39
|
+
)
|
|
40
|
+
return claim
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class NaiveAgent:
|
|
44
|
+
name = "naive"
|
|
45
|
+
|
|
46
|
+
def build_claim(self, chain: dict, steps: list, world) -> dict:
|
|
47
|
+
claim = dict(chain["intended_facts"])
|
|
48
|
+
claim["task_success"] = True
|
|
49
|
+
for step in steps:
|
|
50
|
+
response = step["response"]
|
|
51
|
+
for response_field, fact_key in RESPONSE_FACT_MAP.items():
|
|
52
|
+
if response_field in response and response[response_field] is not None:
|
|
53
|
+
claim[fact_key] = response[response_field]
|
|
54
|
+
return claim
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DependentAgent:
|
|
58
|
+
"""Resolves one dependent task.
|
|
59
|
+
|
|
60
|
+
``memory`` is the fact dict carried from Session 1 (``None`` = M0, the
|
|
61
|
+
agent queries the world directly). ``correction`` is the verified
|
|
62
|
+
observation delivered before Session 4; a ``revising`` agent adopts it
|
|
63
|
+
for the chain's primary fact, a ``stubborn`` agent ignores it.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, memory: dict | None, mode: str = "revising"):
|
|
67
|
+
if mode not in ("revising", "stubborn"):
|
|
68
|
+
raise ValueError(f"unknown dependent-agent mode {mode!r}")
|
|
69
|
+
self.memory = memory
|
|
70
|
+
self.mode = mode
|
|
71
|
+
|
|
72
|
+
def belief(self, chain: dict, world, fact: str, correction: dict | None):
|
|
73
|
+
if (
|
|
74
|
+
correction
|
|
75
|
+
and self.mode == "revising"
|
|
76
|
+
and fact == chain["primary_fact"]
|
|
77
|
+
and fact in correction
|
|
78
|
+
):
|
|
79
|
+
return correction[fact]
|
|
80
|
+
if self.memory is not None and fact in self.memory:
|
|
81
|
+
return self.memory[fact]
|
|
82
|
+
return resolve_fact(world, chain, fact)
|
|
83
|
+
|
|
84
|
+
def run(self, chain: dict, world, spec: dict, correction: dict | None = None) -> dict:
|
|
85
|
+
fact = spec["fact"]
|
|
86
|
+
believed = self.belief(chain, world, fact, correction if spec["session"] == 4 else None)
|
|
87
|
+
if spec["kind"] == "report":
|
|
88
|
+
return {"answer": believed}
|
|
89
|
+
if spec["kind"] == "decision":
|
|
90
|
+
return {"answer": believed >= spec["threshold"]}
|
|
91
|
+
if spec["kind"] == "action":
|
|
92
|
+
action_world = world.clone()
|
|
93
|
+
result = getattr(action_world, spec["tool"])(**{spec["arg"]: believed})
|
|
94
|
+
return {"answer": result, "acted_value": believed}
|
|
95
|
+
raise ValueError(f"unknown dependent kind {spec['kind']!r}")
|