The README/INTRODUCTION headlines (recall 100% / precision 71% vs 36% / root-cause fix 100%) come from a real LLM run, not a synthetic claim. This doc shows how to reproduce it with your own model access. Baseline numbers are in
RESULT-REPORT.md.
| Group | Question it answers | Script |
|---|---|---|
| A | Does methodology injection (adversarial verification + confidence ≥80 + 9 security patterns) improve review quality over a bare prompt? | run_group_a.py |
| B | Can the bug-fixer (root-cause 5-step) actually fix bugs — including a hard race — and fix root cause not symptom? | run_group_b.py |
The gold standard is bug_seeds.py: 10 known bugs (easy/medium/hard, across security/correctness/boundary). Group B fixes 5 of them.
You need access to a model that speaks the Anthropic messages API (a relay/gateway with x-api-key auth works; the original run used deepseek-v4 for review/fix and qwen3.7 as judge, both via relay). Two cost tiers:
- A strong/"reasoning" model for review + fix (group A + B) — set as
ANTHROPIC_DEFAULT_SONNET_MODEL - A cheaper model for the LLM-judge (group B) — set as
ANTHROPIC_DEFAULT_HAIKU_MODEL
A reasoning model is not required — any Anthropic-API-compatible model works. If yours isn't a reasoning model, results still reproduce (the thinking-block handling in llm_call.py is a no-op for non-reasoning models).
cd loop-engine/experiment
# 1. Set model access (relay/gateway with x-api-key auth)
export ANTHROPIC_BASE_URL="https://your-relay.example.com"
export ANTHROPIC_AUTH_TOKEN="sk-..."
export ANTHROPIC_DEFAULT_SONNET_MODEL="deepseek-v4" # review + fix (strong)
export ANTHROPIC_DEFAULT_HAIKU_MODEL="qwen3.7" # judge (cheaper)
# 2. Smoke: one call works + auth is x-api-key (not Bearer)
python -c "from llm_call import make_call_fn; c=make_call_fn(); print(c('ping', None)[:80])"
# 3. Group A: bare vs enhanced reviewer (≈40 calls)
python run_group_a.py
# 4. Group B: fix + judge (≈10 calls)
python run_group_b.pyTotal: ~50 calls, slowest single call ~30s (reasoning thinking).
| Metric | Bare | Enhanced | Decision gate | Pass? |
|---|---|---|---|---|
| Recall | 100% (10/10) | 100% (10/10) | ≥70% | ✅ |
| Findings reported | 28 | 14 | — | enhanced −50% |
| False positives | 18 | 4 | — | −78% |
| Precision | 36% | 71% | ≤30% FP | ✅ (+35pp) |
| Group B bug | Difficulty | Fixed correctly | Root-cause fix |
|---|---|---|---|
| B01 SQL injection | easy | ✓ | ✓ parameterized query |
| B04 null | easy | ✓ | ✓ explicit entry guard |
| B06 discount logic | medium | ✓ | ✓ add-not-subtract |
| B08 race | hard | ✓ | ✓ double-checked locking (DCL) |
| B10 divide-by-zero | easy | ✓ | ✓ denominator check |
If your numbers are far off, see "Gotchas" below and RESULT-REPORT.md §四.
These are the production-integration pitfalls the original run exposed. They are handled in llm_call.py; if you adapt it, preserve them:
- Reasoning-model thinking blocks —
content[0]is athinkingblock, not text;max_tokensgets eaten by thinking. Fix: parse onlytype == "text"blocks;max_tokens=4096. - Chinese vs English keyword mismatch — the model may answer in Chinese ("空值") while
expectedis English ("null") → false miss. Fix:SYNONYMSmap inrun_group_a.py(extend if your model uses other phrasings). - Relay auth header unknown — some relays want
x-api-key, othersBearer. Fix: the Step-2 smoke confirmsx-api-keyworks before the full run.
You cannot reproduce the LLM numbers without model access — that's the point (it's a real run, not a mock). But you can validate the gold standard and parsing logic offline:
# bug seeds parse correctly
python -c "from bug_seeds import SEEDS; print(len(SEEDS), 'seeds'); [print(s.id, s.difficulty, s.expected_finding) for s in SEEDS]"
# JSON extraction is robust
python -c "from llm_call import extract_json_array; print(extract_json_array('\`\`\`json\n[{\"a\":1}]\n\`\`\`'))"The headline 71% precision is the enhanced reviewer at a self-reported confidence ≥80 gate. Two improvements are implemented:
- Calibrate the threshold from data —
calibrate_threshold.pyruns the reviewer on the 10-seed gold standard with confidence per finding (no internal filter), sweeps thresholds 60→95, and prints a precision@threshold curve. It recommends the lowest threshold that hits your target precision (default 90%). Run:python calibrate_threshold.py --target-precision 0.90. Requires the same env vars as Group A. - Ablate the structural levers (③④) —
run_precision_ablation.pymeasures the real precision gain of the finding-verifier (③) and skeptic-consensus (④) against a baseline enhanced reviewer, on the 10-seed gold standard. It reuses the production_merge_findings/_apply_verdictsso it tests the real logic. Conditions: baseline / ③ verify / ④ consensus / ③+④. Run:python run_precision_ablation.py. Same env vars as Group A. - Cross-session false-positive memory —
qa_loop.pynow persists findings the bug-fixer markedskippedinto.qa-memory.jsonunderknown_false_positive, and the review phase suppresses those signatures on subsequent runs (same mechanism asknown_fixed). Curation: humans can edit.qa-memory.jsondirectly. Backward-compatible with old memory files.
Note: threshold calibration raises single-run precision; the false-positive memory reduces repeated false positives across runs (team-level accumulation), not single-run precision. The ablation script tells you whether ③/④ beat a self-reported confidence threshold (they bypass LLM over-confidence, so they help most in the "pessimistic" case where calibration alone fails).
LLM runs are stochastic. Expect ±1 finding per seed across runs; the direction of the effect (enhanced ≪ bare false-positives, root-cause fixes) is stable. If recall drops below ~70% on a seed, that's a model-quality issue — try a stronger SONNET model, not a code change.