feat(checkers): platform router + Haiku sufficiency tier; cookie is first consumer

Generalise "Embedding finds, Claude decides" into the shared Pruefer-Library:
- router.route_and_check dispatches control -> sensor_classification -> Checker.
- build_spec reads sensor_classification (CONTENT/LLM -> judge=haiku, the
  validated sufficiency tier; the Qwen-first cascade is disproven for sufficiency).
- LLMChecker gains a Haiku-direct tier (reuses the validated deep_check prompt).
- Cookie Layer-3 now routes through route_and_check instead of bespoke code, so
  cookie is the first real router consumer -- proves the architecture end-to-end.

Reproduces the validated result via the shared path: FN 159->14, recall
0.13->0.92, precision 0.89 (vs bespoke 12/0.93/0.90 -- within Haiku noise).
Tests: 10/10 (router dispatch + build_spec + haiku tier + cookie rewire).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-22 12:56:14 +02:00
parent e809d0bc1c
commit 3e3644f83d
5 changed files with 170 additions and 19 deletions
@@ -8,6 +8,7 @@ from compliance.services.specialist_agents.cookie_policy._sufficiency_judge impo
)
_ANTHROPIC = "compliance.services.llm_cascade._call_anthropic"
_DOC = "Volltext der Cookie-Richtlinie mit ausreichend Inhalt. " * 4
def _r(cid, source, passed=True):
@@ -20,7 +21,7 @@ async def test_rescued_unpassed_when_judge_fehlt():
results = [_r("A", "keyword+embedding")]
fake = AsyncMock(return_value='{"erfuellt": false, "confidence": 0.9, "begruendung": "fehlt"}')
with patch(_ANTHROPIC, new=fake):
n = await judge_rescued("text", results)
n = await judge_rescued(_DOC, results)
assert n == 1
assert results[0]["passed"] is False
assert "+llm_failed" in results[0]["source"]
@@ -31,7 +32,7 @@ async def test_rescued_kept_when_judge_erfuellt():
results = [_r("A", "keyword+embedding")]
fake = AsyncMock(return_value='{"erfuellt": true, "confidence": 0.9}')
with patch(_ANTHROPIC, new=fake):
n = await judge_rescued("text", results)
n = await judge_rescued(_DOC, results)
assert n == 0
assert results[0]["passed"] is True
@@ -42,7 +43,7 @@ async def test_keyword_pass_not_judged():
results = [_r("A", "keyword")]
fake = AsyncMock(return_value='{"erfuellt": false}')
with patch(_ANTHROPIC, new=fake):
n = await judge_rescued("text", results)
n = await judge_rescued(_DOC, results)
assert n == 0
assert results[0]["passed"] is True
assert fake.call_count == 0
@@ -53,7 +54,7 @@ async def test_boost_rescue_is_judged():
results = [_r("A", "keyword+regex_boost")]
fake = AsyncMock(return_value='{"erfuellt": false}')
with patch(_ANTHROPIC, new=fake):
n = await judge_rescued("text", results)
n = await judge_rescued(_DOC, results)
assert n == 1 and results[0]["passed"] is False
@@ -63,5 +64,5 @@ async def test_failed_controls_ignored():
results = [_r("A", "keyword+embedding", passed=False)]
fake = AsyncMock(return_value='{"erfuellt": false}')
with patch(_ANTHROPIC, new=fake):
n = await judge_rescued("text", results)
n = await judge_rescued(_DOC, results)
assert n == 0 and fake.call_count == 0