1607c89459
Deterministic reasoning layer ON TOP of the Legal Knowledge Graph (obligation
registry) and the Compliance Execution Graph (control mapping/evidence). Answers
which regulations apply to a concrete product, which obligations follow, whether
the customer's implementation covers them, and whether a customer interpretation
is too narrow/broad/plausible.
- ProductProfile with tri-state facts (Optional[bool]=None => uncertain, never
false security); safe predicate evaluator (no eval).
- 6 regulation triggers (CRA/MaschinenVO/RED/EMV/DataAct/NIS2) with missing-fact
prompts; 24 obligation scope rules.
- CRA obligation_ids RE-USED verbatim from the registry (93 ids) — never re-minted
(control_uuid trap); Machine/Data-Act flagged proposed=True.
- required_evidence constrained to the framework-agnostic shared evidence catalog;
capabilities echo the planned Obligation->Capability layer.
- Overlap groups (CRA<->MaschinenVO cyber-safety) + evidence-for-multiple (USP).
- 4 endpoints POST /reasoning/{scope,obligations,implementation-assessment,
interpretation-assessment}; thin handlers, registered in api/__init__.py.
- 22 tests (5 machine-builder scenarios + 10 acceptance questions). No DB
migration, no RAG, no new controls.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""Interpretation review engine (spec Modus 4).
|
|
|
|
Evaluates whether a customer's legal interpretation is plausible, too narrow,
|
|
too broad, etc. Matches the interpretation against a curated pattern library;
|
|
no match -> `uncertain` plus a request for the missing context (never invent a
|
|
verdict, spec §6.3).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
from typing import Optional
|
|
|
|
from .enums import Confidence, InterpretationVerdict
|
|
from .schemas import InterpretationAssessment, ProductProfile
|
|
from .taxonomy_interpretations import INTERPRETATION_PATTERNS, InterpretationPattern
|
|
|
|
|
|
def _interpretation_id(raw: str) -> str:
|
|
digest = hashlib.sha1(raw.strip().lower().encode("utf-8")).hexdigest()
|
|
return "interp_%s" % digest[:10]
|
|
|
|
|
|
def _best_match(text: str) -> Optional[InterpretationPattern]:
|
|
low = text.lower()
|
|
best: Optional[InterpretationPattern] = None
|
|
best_score = 0
|
|
for pattern in INTERPRETATION_PATTERNS:
|
|
score = sum(1 for t in pattern.triggers if t in low)
|
|
if score > best_score:
|
|
best, best_score = pattern, score
|
|
return best
|
|
|
|
|
|
def assess_interpretation(
|
|
raw_interpretation: str, profile: Optional[ProductProfile] = None
|
|
) -> InterpretationAssessment:
|
|
interp_id = _interpretation_id(raw_interpretation)
|
|
pattern = _best_match(raw_interpretation)
|
|
|
|
if pattern is None:
|
|
return InterpretationAssessment(
|
|
interpretation_id=interp_id,
|
|
raw_interpretation=raw_interpretation,
|
|
assessment=InterpretationVerdict.UNCERTAIN,
|
|
corrected_interpretation=(
|
|
"Diese Auslegung lässt sich ohne weitere Angaben nicht bewerten. Bitte Produkt, "
|
|
"Rolle, Marktzugang und die konkret betroffene Pflicht benennen."
|
|
),
|
|
explanation="Kein bekanntes Auslegungsmuster erkannt — bewusst keine Scheinsicherheit.",
|
|
confidence=Confidence.LOW,
|
|
)
|
|
|
|
return InterpretationAssessment(
|
|
interpretation_id=interp_id,
|
|
raw_interpretation=raw_interpretation,
|
|
affected_regulations=pattern.affected_regulations,
|
|
affected_obligations=pattern.affected_obligations,
|
|
assessment=pattern.verdict,
|
|
risks=pattern.risks,
|
|
corrected_interpretation=pattern.corrected_interpretation,
|
|
legal_basis_refs=pattern.legal_basis_refs,
|
|
explanation=pattern.explanation,
|
|
confidence=pattern.confidence,
|
|
)
|