Critic-driven correction loop for large LLM-generated JSON state. Surgical RFC 6902 patching with sub-agents (path_finder + context narrowing). Works with OpenAI, Anthropic, OpenRouter.
Project description
json-correction-loop
A Python library for iteratively correcting large LLM-generated JSON and knowledge graphs via RFC 6902 patches and a multi-agent sub-task stack (
path_finder, context narrowing, request validator, patch evaluator). Composable critic loop with convergence policies. Works with OpenAI, Anthropic, OpenRouter.
TL;DR. When an LLM regenerates a 100-entity / 183-edge JSON
knowledge graph on critic feedback (the prevailing "full-regen"
pattern), gpt-4o-mini fixes 0 / 8 flagged defects and burns 73K
tokens. This library — a critic loop with surgical RFC 6902 patching
and sub-agent decomposition — fixes 8 / 8 at 17K tokens.
Why this exists
When an LLM produces a large JSON artifact — an agent's memory, a generated knowledge graph, a structured config tree, a multi-step plan — the prevailing pattern is to re-emit the entire object on every critic pass. This is a problem.
We measured it: on a synthetic 100-entity / 183-edge knowledge graph,
gpt-4o-mini saturates its 8K max_tokens ceiling mid-array, returns
truncated JSON, and across five hardcapped loop iterations fixes 0
of 8 critic-flagged defects while burning 73K tokens and 8.5 minutes
of wall clock.
size=100 (100 entities, 183 edges, 8 defects):
full-regen baseline: fix=0% tokens=73,740 wall=435s
this library (O2N): fix=100% tokens=17,117 wall=26s
Surgical patching alone isn't enough either: a naive RFC 6902 patcher inside a critic loop only fixes 35–64% of defects, because the LLM acts on critic-flagged symptoms (an edge with the wrong predicate) rather than root causes (the entity whose type was actually flipped). Closing that gap requires sub-agents.
What this library is
A composable gather → plan → execute loop with four sub-agent
slots:
- Critics (you supply) report defects against stable item IDs (JSON pointers, entity IDs).
- path_finder maps each critic-flagged symptom pointer to its root-cause pointer.
- Context narrowing scopes both the sub-agent and the patcher to the slice of state implicated by flagged paths — turns out to be a correctness component, not just a cost optimization.
- Surgical patcher emits RFC 6902 ops via tool calling, validated and applied with a standard JSON Patch library.
- Convergence policies (quality-stable, hardcap) compose as Protocols.
The library imports no specific LLM client, persistence layer, or event sink. Storage backends and event sinks are Protocols you plug in.
Install
pip install json-correction-loop # coming soon to PyPI
Or from source:
git clone https://github.com/warpspaceinc/json-correction-loop
cd json-correction-loop
pip install -e ".[dev]"
pytest
Quickstart
The smallest end-to-end example wires fakes through the full loop — no LLM required — to show how the pieces compose:
from json_correction_loop import (
CorrectionLoopConfig,
CriticIssue, CriticReport,
ExecuteResult,
make_callback_executor,
make_identity_planner,
run_correction_loop,
)
# 1. Define your state and a critic.
state = {"items": [{"id": "a", "ok": False}, {"id": "b", "ok": True}]}
def gather(state, iteration, model):
issues = [
CriticIssue(
target_id=f"/items/{i}/ok",
severity="major",
issue_type="needs_fix",
description=f"set ok=True on item {item['id']}",
)
for i, item in enumerate(state["items"]) if not item["ok"]
]
return [CriticReport(issues=issues, score=10 if not issues else 4)]
# 2. Define an executor that applies one correction at a time.
def apply_one(state, flagged_paths, feedback_by_path, model):
traces = []
for path in flagged_paths:
# In production this is your LLM patcher; here we just patch.
idx = int(path.strip("/").split("/")[1])
state["items"][idx]["ok"] = True
traces.append(type("T", (), {
"id": f"t-{idx}", "requirement_id": path,
"addressed": True, "reason": "set ok=True",
})())
return traces
# 3. Run the loop.
def parse(issues):
return ([iss.target_id for iss in issues],
{iss.target_id: iss.description for iss in issues})
cfg = CorrectionLoopConfig(level="items", max_loops=5)
ok = run_correction_loop(
state, cfg,
gather_fn=gather,
plan_fn=make_identity_planner(parse),
execute_fn=make_callback_executor(apply_one),
)
assert ok and all(item["ok"] for item in state["items"])
For a real LLM-driven example with a knowledge-graph correction
workload, see examples/kg_correction/ and tests/test_loop.py.
The end-to-end ablations (full-regen vs single-shot patch vs full
sub-agent stack) and size-sweep numbers are documented in
EXPERIMENTS.md.
What's inside
Core
| Module | Purpose |
|---|---|
json_correction_loop.loop |
The run_correction_loop driver |
json_correction_loop.models |
Correction, CorrectionPlan, CriticIssue, CriticReport |
json_correction_loop.planners |
Identity + oscillation-aware planners |
json_correction_loop.executors |
make_callback_executor factory |
json_correction_loop.convergence |
QualityStablePolicy, HardcapPolicy |
json_correction_loop.events |
EventSink Protocol + NullEventSink |
json_correction_loop.storage |
StorageBackend Protocol + NullStorageBackend |
Sub-agents
| Module | Sub-agent | Status |
|---|---|---|
json_correction_loop.path_finder |
Symptom → root cause pointer redirect | Stable |
json_correction_loop.template_filler |
Empty-container enumeration filler | Stable |
json_correction_loop.request_validator |
Reject malformed patch requests upstream | Stable |
json_correction_loop.patch_evaluator |
Score patches against intent before commit | Stable |
json_correction_loop.patcher |
Surgical RFC 6902 patcher | Stable |
Status
- Alpha (v0.1). Public API may change before 1.0.
- Domain-neutral library — bring your own critic, patcher prompt, and storage backend.
Experiments
These design choices are measured on a synthetic knowledge-graph perturbation benchmark. Headline result: at 100 entities, full-regeneration achieves 0% fix rate while the full library stack achieves 100% at ~5× fewer tokens.
See EXPERIMENTS.md for the setup, ablations, and size-sweep numbers.
Contributing
Issues and PRs welcome. Please run pytest and ruff before
submitting.
License
Apache-2.0. 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 json_correction_loop-0.1.0.tar.gz.
File metadata
- Download URL: json_correction_loop-0.1.0.tar.gz
- Upload date:
- Size: 93.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9f1d4b8faed187a0c33eef7aa0d6dbc995e89baeb4119384ee0e26984c95006
|
|
| MD5 |
57884cabfe1787cd0b26cc19e79c32c5
|
|
| BLAKE2b-256 |
abec9c1b82b681f9bc0b7ce01781bac53e9bc1a6c9482156934edab62eb35d8a
|
Provenance
The following attestation bundles were made for json_correction_loop-0.1.0.tar.gz:
Publisher:
release.yml on warpspaceinc/json-correction-loop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
json_correction_loop-0.1.0.tar.gz -
Subject digest:
f9f1d4b8faed187a0c33eef7aa0d6dbc995e89baeb4119384ee0e26984c95006 - Sigstore transparency entry: 1474688450
- Sigstore integration time:
-
Permalink:
warpspaceinc/json-correction-loop@10b390885e513bc48d58ad4d40eb7f1894eefe12 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/warpspaceinc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@10b390885e513bc48d58ad4d40eb7f1894eefe12 -
Trigger Event:
push
-
Statement type:
File details
Details for the file json_correction_loop-0.1.0-py3-none-any.whl.
File metadata
- Download URL: json_correction_loop-0.1.0-py3-none-any.whl
- Upload date:
- Size: 92.8 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 |
559982f78286cf6ffd5c9b6958196062992a10aea5b054ea9983c60bfd1bf560
|
|
| MD5 |
59bc276a3dd24d0f79d8675ad851713d
|
|
| BLAKE2b-256 |
1971bde9b022a864f02bc7ecd703bd78c8ba1139a08494804848d5c5258c89c9
|
Provenance
The following attestation bundles were made for json_correction_loop-0.1.0-py3-none-any.whl:
Publisher:
release.yml on warpspaceinc/json-correction-loop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
json_correction_loop-0.1.0-py3-none-any.whl -
Subject digest:
559982f78286cf6ffd5c9b6958196062992a10aea5b054ea9983c60bfd1bf560 - Sigstore transparency entry: 1474688481
- Sigstore integration time:
-
Permalink:
warpspaceinc/json-correction-loop@10b390885e513bc48d58ad4d40eb7f1894eefe12 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/warpspaceinc
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@10b390885e513bc48d58ad4d40eb7f1894eefe12 -
Trigger Event:
push
-
Statement type: