"""Welche Norm eine Frage entscheidet — kuratiert statt erraten. Dieselbe Bauweise wie die vier Listenmodule (`amrl_substitution`, `amrl_austauschbarkeit`, `amrl_biosimilars`, `amrl_otc`), aber eine Ebene höher. Die Listen sichern die **Stoffebene**: steht ein Wirkstoff auf einer Anlage. Dieses Modul sichert die **Normebene**: welche Vorschrift eine Frage überhaupt beantwortet. Der Anlass war eine Livemessung mit fünf normbezogenen Fragen — keine nannte einen Wirkstoff, alle vier Lookups meldeten deshalb planmäßig `nicht_pruefbar`, und zwei Antworten nannten die falsche maßgebliche Norm: OTC zu Lasten der GKV beantwortet mit § 31 Abs. 1 SGB V. Diese Norm gewährt den Anspruch aber nur, „soweit die Arzneimittel nicht nach § 34 … ausgeschlossen sind“ — sie verweist auf die Antwort, sie ist sie nicht. Entschieden wird die Frage von § 34 Abs. 1. Zuweisungsverbot beantwortet mit § 360 Abs. 2 SGB V, einer Ausnahmeklausel zur E-Rezept-Pflicht. Das Ergebnis stimmte zufällig, die Herleitung war verkehrt herum. Das Verbot steht in § 31 Abs. 1 Satz 6. Beide Male standen die richtigen Normen im Index. Das Retrieval hat sie nicht gefunden, weil Ähnlichkeit zur Frage etwas anderes ist als Einschlägigkeit — und kein Ähnlichkeitsmaß kann den Unterschied kennen. Deshalb dieselbe Antwort wie bei den Listen: eine kuratierte Tabelle und eine Substring-Prüfung, kein Modell. Das Register tut zwei Dinge: 1. **Pflichtabruf.** `pflichtabruf()` benennt Korpus, Container, § und Absatz der tragenden Normen. Der `FederatedRetriever` holt sie unabhängig vom Ähnlichkeitswettbewerb — die entscheidende Norm steht damit im Kontext, auch wenn sie sich schlecht einbettet. 2. **Abgleich und Richtigstellung.** `pruefe()` liest den Abschnitt „Maßgebliche Norm“ der fertigen Antwort. Fehlt ein Glied der Kette, wird es nachgetragen. Steht dort eine Vorschrift, die die Frage nicht entscheidet, setzt `in_antwort_einsetzen()` die Abschnitte „Kurzantwort“ und „Maßgebliche Norm“ neu. **Warum die Antwort neu gesetzt und nicht nur ergänzt wird.** Die erste Fassung hängte den richtigen Befund unter die falsche Antwort. Damit standen beide nebeneinander und nichts im Text sagte, welcher gilt — dieselbe Lage, die `amrl_substitution.antwort_mit_befunden` für die Listen und `corpus_amendments.reconcile_negative_answer` für spätere Änderungen auflöst, und sie wird hier genauso aufgelöst. **Eine Richtigstellung, die den Widerspruch stehen lässt, ist keine.** Der Ausdruck vom 20.08.2026 zeigte die zweite Fassung von außen: die Antwort zur Austauschbarkeit von Darreichungsformen begann mit der Meldung, das Modell habe danebengelegen, setzte Kurzantwort und maßgebliche Norm neu — und ließ darunter den vollständigen Wortlaut des verworfenen § 40c AM-RL stehen, mitsamt seinem Quellenmarker und als erste Quelle der Liste. Angekündigt wurde das im Text sogar („können sich noch auf die verworfene Vorschrift beziehen“); aufgelöst wurde es nicht. Drei Dinge folgen daraus, und alle drei stehen unten im Code: * Der Hinweis steht **hinter** der Antwort, nicht davor (`in_antwort_einsetzen`). * Absätze, die die verworfene Vorschrift **zitieren**, fallen weg; ihre Erwähnung im Wortlaut einer anderen Norm bleibt (`_ohne_verworfene_bloecke`). * Der neu gesetzte Normabschnitt übernimmt die Quellenmarker, die das Modell denselben Vorschriften gegeben hat (`_marker_je_norm`) — sonst verlöre gerade die richtiggestellte Norm ihren Beleg. **Die `kernaussage` ist die Grenze dieses Moduls.** Sie gibt wieder, was die tragende Norm sagt — sie subsumiert nicht. „Vertragsärzte dürfen nicht zuweisen“ steht im Gesetz; ob eine konkrete Praxis zugewiesen hat, entscheidet dieses Modul nicht und darf es nicht entscheiden. Alles Fallbezogene bleibt beim Modell, dessen übrige Abschnitte unangetastet stehen bleiben. **Bedingte Einträge gegen Übergenauigkeit.** Eine Norm, ein Hinweis oder ein Fehlgriff kann an Signale gebunden werden (`nur_bei`, `nicht_bei`). Ohne das trug die OTC-Antwort für eine erwachsene Versicherte den Hinweis auf Kinder bis 12, und die allgemeine Frage nach Darreichungsformen bekam § 40c AM-RL aufgeladen, der nur biotechnologisch hergestellte Arzneimittel betrifft. Ein Register, das zu jeder Frage alles nennt, verwischt den Hauptfall. **Die Grenze des Registers ist Absicht.** Es enthält nur Fälle, die in der Auswertung tatsächlich falsch beantwortet wurden. Jeder Eintrag ist eine Rechtsbehauptung, die gepflegt werden muss; ein Register, das „vorsichtshalber“ jede denkbare Frage abdeckt, ist bei der nächsten Novelle stiller Ballast. Trifft kein Anker, ist der Befund `nicht_pruefbar` und die Pipeline verhält sich wie zuvor. **Der Lifestyle-Anker ist derselbe Fehler an einer zweiten Stelle.** Auch die Frage nach der Erstattungsfähigkeit von Sildenafil, Wegovy oder Champix wurde auf § 31 SGB V gestützt statt auf § 34 Abs. 1 Satz 7 SGB V. Er steht neben dem OTC-Anker, weil beide aus § 34 Abs. 1 kommen und trotzdem verschiedene Ausschlüsse sind: Satz 1 schließt nach der Verschreibungspflicht aus, Satz 7 nach der Zweckbestimmung. Zwei Dinge trägt nur dieser Anker. Erstens hat der Ausschluss genau eine Ausnahme — die Tabakentwöhnung nach § 34 Abs. 2 SGB V, § 14a AM-RL, Anlage IIa —, und sie hängt an `nur_bei=_TABAK`, weil sie in einer Frage nach Sildenafil nichts zu suchen hat. Zweitens führt der Weg über den medizinisch begründeten Einzelfall hier *nicht* hinaus: § 16 Abs. 5 AM-RL öffnet sich wörtlich nur für die nach § 16 Abs. 1 und 2 ausgeschlossenen Arzneimittel. Das steht in der `kernaussage`, aber bewusst **nicht** als `Fehlgriff` — der Listenblock von `amrl_lifestyle` schreibt denselben Satz in seiner Verneinung („… greift hier nicht“), und ein Muster auf `§ 16` schlüge an einer Antwort an, die gerade das Richtige sagt. **Die Fundstellen sind gegen den Index geprüft**, nicht aus dem Gedächtnis notiert: `container_id`, `section_id` und `subsection` stammen aus der Chunk-Metadata der laufenden Collections. `tests/test_norm_anchors.py` hält das nach — ein Anker, der auf nichts zeigt, ist schlimmer als kein Anker. """ from __future__ import annotations import re from dataclasses import dataclass, field from typing import Any, Dict, List, Optional, Tuple # --------------------------------------------------------------------------- # Datenmodell # --------------------------------------------------------------------------- # Eine UND-Verknüpfung von ODER-Gruppen: jede Gruppe muss treffen, innerhalb # einer Gruppe genügt ein Treffer. Ein Anker kann mehrere davon führen; er # greift, sobald einer vollständig trifft. Signalsatz = Tuple[Tuple[str, ...], ...] @dataclass(frozen=True) class Bedingung: """Wann ein Eintrag zur Frage gehört — und wann er nur Ballast wäre.""" nur_bei: Tuple[str, ...] = () nicht_bei: Tuple[str, ...] = () def gilt(self, haystack: str) -> bool: if self.nur_bei and not any(_trifft(s, haystack) for s in self.nur_bei): return False if self.nicht_bei and any(_trifft(s, haystack) for s in self.nicht_bei): return False return True @dataclass(frozen=True) class Norm: """Eine Vorschrift, so wie sie zu zitieren und so wie sie zu finden ist. `zitat` ist die Form, in der die Antwort sie nennen soll. Die übrigen Felder sind die Adresse im Index; sie sind leer, wenn die Norm nicht indiziert ist (ApoG, Technische Anlagen, AM-RL-Anlagen). Eine nicht indizierte Norm wird benannt, aber nicht abgerufen — sonst behauptete der Pflichtabruf einen Bestand, den es nicht gibt. """ zitat: str corpus_id: str = "" container_id: str = "" section_id: str = "" # Der Absatz grenzt den Abruf ein. Ohne ihn holt „§ 129 SGB V“ achtzig # Chunks; mit ihm sind es zwei, und beide tragen. absatz: str = "" tragend: bool = True warum: str = "" wenn: Bedingung = Bedingung() # Wie viele Chunks diese Norm braucht. 0 heißt: was der Aufrufer vorgibt. # # Der Grund ist ein Budget, keine Feinjustierung. Der Kontext fasst zehn # Treffer; ein Anker mit vier tragenden Normen belegte bei zwei Chunks je # Norm acht davon und verdrängte damit genau die Suche, die er ergänzen # soll. Gemessen am 19.08.2026 an der Verweisauflösung, die an derselben # Grenze scheiterte. Wo der Absatz eng gefasst ist, genügt ein Chunk: der # Parent trägt den ganzen Absatz, das Kind wiederholt Teile davon. chunks: int = 0 @property def abrufbar(self) -> bool: return bool(self.corpus_id and self.section_id) def als_ziel(self, max_chunks: int = 2) -> Dict[str, Any]: """Die Adresse als schlichtes dict — der Retriever kennt dieses Modul nicht.""" return { "corpus_id": self.corpus_id, "container_id": self.container_id, "section_id": self.section_id, "subsection": self.absatz, "max_chunks": self.chunks or max_chunks, "zitat": self.zitat, } @dataclass(frozen=True) class Fehlgriff: """Eine Vorschrift, die zu dieser Frage regelmäßig fälschlich genannt wird. Zwei Sorten, und die Unterscheidung entscheidet, wann angeschlagen wird: `nur_ohne_tragende=True` — die Vorschrift *gehört* in die Antwort, nur nicht als die entscheidende. § 31 SGB V ist in einer OTC-Antwort die Anspruchs- norm; ein Fehler ist sie erst, wenn sie **statt** § 34 dasteht. `nur_ohne_tragende=False` — die Vorschrift ist zu eng oder zu speziell und stört auch neben der richtigen Norm. § 40c AM-RL betrifft ausschließlich biotechnologisch hergestellte Arzneimittel; in einer allgemeinen Frage nach Darreichungsformen verengt er die Antwort auf einen Sonderfall. `im_kurzabschnitt=True` — das Muster wird gegen die **Kurzantwort** geprüft, nicht gegen den Normabschnitt. Manche Fehler stehen nicht in der Wahl der Vorschrift, sondern in ihrer Zusammenfassung: die OTC-Kurzantwort nennt die richtige Norm (§ 34) und fasst ihre Absätze trotzdem falsch zusammen. Ein solcher Fehlgriff treibt keine Blockentfernung (`_ohne_verworfene_bloecke`); er löst allein die Neusetzung der Kurzantwort aus der `kernaussage` aus. """ muster: str warum: str nur_ohne_tragende: bool = True im_kurzabschnitt: bool = False wenn: Bedingung = Bedingung() @dataclass(frozen=True) class Hinweis: """Was eine richtige Antwort nicht verschweigen darf — soweit es der Fall aufwirft.""" text: str wenn: Bedingung = Bedingung() @dataclass(frozen=True) class Kernaussage: """Eine Kurzantwort für einen Teilfall, die die allgemeine verdrängt. Der Anlass ist gemessen: die Frage nach einem **zehnjährigen** Kind mit einer OTC-Verordnung bekam am 21.08.2026 die allgemeine Kurzantwort — „zu Lasten der GKV verordnungsfähig nur, soweit … Anlage I …". Für ein Kind bis zum vollendeten 12. Lebensjahr ist das falsch: der Ausschluss greift dort gar nicht, und die Liste entscheidet nichts. Ein Hinweis am Ende reichte dafür nicht. Er stünde unter einer Kurzantwort, die das Gegenteil sagt — dieselbe Lage, die `in_antwort_einsetzen` für die Richtigstellung auflöst, eine Ebene tiefer. Deshalb verdrängt die bedingte Kernaussage die allgemeine, statt neben ihr zu stehen. """ text: str wenn: Bedingung = Bedingung() @dataclass(frozen=True) class Anker: """Ein Fragetyp und die Normenkette, die ihn entscheidet.""" key: str thema: str signale: Tuple[Signalsatz, ...] normen: Tuple[Norm, ...] # Was die tragende Norm sagt — als Satz, ohne Subsumtion. Ersetzt die # Kurzantwort, wenn die Antwort auf der falschen Vorschrift stand. kernaussage: str = "" # Teilfälle, die eine eigene Kurzantwort brauchen. Der erste zutreffende # gewinnt; trifft keiner, gilt `kernaussage`. kernaussagen: Tuple[Kernaussage, ...] = () fehlgriffe: Tuple[Fehlgriff, ...] = () hinweise: Tuple[Hinweis, ...] = () def trifft(self, haystack: str) -> bool: return any( all(any(_trifft(s, haystack) for s in gruppe) for gruppe in satz) for satz in self.signale ) def geltende_normen(self, haystack: str) -> Tuple[Norm, ...]: return tuple(n for n in self.normen if n.wenn.gilt(haystack)) def tragende(self, haystack: str) -> Tuple[Norm, ...]: return tuple(n for n in self.geltende_normen(haystack) if n.tragend) def geltende_hinweise(self, haystack: str) -> Tuple[str, ...]: return tuple(h.text for h in self.hinweise if h.wenn.gilt(haystack)) def geltende_kernaussage(self, haystack: str) -> str: for eintrag in self.kernaussagen: if eintrag.wenn.gilt(haystack): return eintrag.text return self.kernaussage # --------------------------------------------------------------------------- # Das Register # --------------------------------------------------------------------------- # Fundstellen im Index (Stand der geprüften Collections): # sgb5 container_id = "Drittes/Viertes/Zehntes Kapitel", section_id = "§ 34" # rv129 container_id = "Vertrag" — § 10 gibt es auch in Anlage 11! # amrl container_id = "Richtlinie" # amabrv container_id = "Vertrag" — § 2 gibt es auch in Anlage 1, 2 und 4! # # Der Container ist deshalb Pflicht, wo ein § mehrfach vorkommt. Beim SGB V ist # er es nicht (§§ sind über die Kapitel hinweg eindeutig), steht aber trotzdem # da: er ist die billigste Kontrolle dafür, dass der Anker noch stimmt. # --------------------------------------------------------------------------- # Das Alter als eigenes Merkmal # # § 34 Abs. 1 SGB V staffelt nach Alter — Satz 5 Nr. 1 nimmt Kinder bis zum # vollendeten 12. Lebensjahr vom Ausschluss aus, Nr. 2 Jugendliche mit # Entwicklungsstörungen bis 18. Ein Register, das nur nach Wörtern sucht, kann # das nicht: „jährige" steht in „45-jährige Versicherte" **und** in # „zehnjähriges Kind". Genau daran ist der Kinderhinweis am 21.08.2026 # gescheitert, und zwar in der Frage, für die er gebaut ist. # # Gelesen wird deshalb die Zahl, und das Ergebnis hängt als Marke am Haystack. # Die Marken sind absichtlich unaussprechlich: sie sollen nie zufällig in einer # Frage stehen. # --------------------------------------------------------------------------- _MARKE_ERWACHSEN = "__alter_ab_18__" _MARKE_KIND = "__alter_bis_11__" _MARKE_MINDERJAEHRIG = "__alter_bis_17__" # Zahlwörter bis zwanzig — darüber hinaus schreibt niemand ein Alter aus, und # jede weitere Zeile wäre eine Zeile, die gepflegt werden muss. _ZAHLWORT: Dict[str, int] = { "ein": 1, "eine": 1, "einem": 1, "einjährig": 1, "zwei": 2, "drei": 3, "vier": 4, "fünf": 5, "fuenf": 5, "sechs": 6, "sieben": 7, "acht": 8, "neun": 9, "zehn": 10, "elf": 11, "zwölf": 12, "zwoelf": 12, "dreizehn": 13, "vierzehn": 14, "fünfzehn": 15, "fuenfzehn": 15, "sechzehn": 16, "siebzehn": 17, "achtzehn": 18, "neunzehn": 19, "zwanzig": 20, } # „10-jährig", „10 jährige", „zehnjähriges", „10 Jahre alt", „zehn Jahre alt". # Bewusst **nicht** „vollendetes 12. Lebensjahr": das ist die Regel, nicht der # Patient — eine Frage, die den Gesetzeswortlaut zitiert, hat damit noch kein # Alter genannt. _ALTER_RX = ( re.compile(r"(\d{1,3})\s*-?\s*j[äa]hrig"), re.compile(r"(\d{1,3})\s+jahre?\s+alt"), re.compile(r"([a-zäöüß]+)j[äa]hrig"), re.compile(r"([a-zäöüß]+)\s+jahre?\s+alt"), ) def alter_in_jahren(question: str) -> Optional[int]: """Das Alter, das die Frage nennt — oder None. Gefunden wird das **erste** Alter. Eine Frage mit zwei Altersangaben ist ein Sachverhalt, den das Register nicht entscheidet; dann trägt es lieber das erste und der Fachanwender sieht die Marke im Befund. """ text = " ".join(str(question or "").lower().split()) for erkenner in _ALTER_RX: treffer = erkenner.search(text) if not treffer: continue roh = treffer.group(1) if roh.isdigit(): jahre = int(roh) else: jahre = _ZAHLWORT.get(roh, _ZAHLWORT.get(roh.rstrip("e"), -1)) if 0 <= jahre <= 120: return jahre return None # Eine Verneinung, die ein Signal enthält, ist kein Signal. „… und **nicht** zur # Herstellung einer parenteralen Zubereitung verwendet" beschreibt gerade den # Fall, den `_PARENTERAL` ausschließen soll — für eine Substring-Prüfung steht # dort aber „parenteral". Gemessen am 21.08.2026: der § 40b-Fehlgriff schwieg an # der Frage, für die er gebaut ist. # # Entfernt wird deshalb die verneinte Wendung, bevor die Signale greifen. Eng # gefasst: nur die Verneinung unmittelbar vor dem Signalwort. _VERNEINT = re.compile( r"\b(?:nicht|kein\w*)\s+(?:zur\s+herstellung\s+(?:einer\s+)?)?" r"(?:parenteral\w*)(?:\s+zubereitung\w*)?" ) def _alterssignale(question: str) -> str: jahre = alter_in_jahren(question) if jahre is None: return "" marken = [] if jahre >= 18: marken.append(_MARKE_ERWACHSEN) else: marken.append(_MARKE_MINDERJAEHRIG) if jahre < 12: marken.append(_MARKE_KIND) return " " + " ".join(marken) # Signale, die einen Fall als Erwachsenenfall ausweisen. Sie unterdrücken den # Kinderhinweis bei § 34 — gemessen an einer Frage, die ausdrücklich von einer # erwachsenen Versicherten handelte und trotzdem die Ausnahme für Kinder bis 12 # vorgesetzt bekam. # `"jährige"` stand hier bis zum 21.08.2026 und war der Fehler, den Fall # `kette_kind_otc_anlage_i` sichtbar gemacht hat: „ein **zehnjähriges** Kind" # enthält das Signal und galt damit als Erwachsenenfall — der Kinderhinweis # wurde unterdrückt, und zwar in genau der Frage, für die er gebaut ist. Ein # Altersmerkmal als blanker Substring ist altersblind. # # Jetzt wird das Alter gelesen (`alter_in_jahren`) und als Marke in den # Haystack gehängt. Die Wortsignale bleiben für die Fälle ohne Zahl. _ERWACHSEN = ("erwachsen", "volljährig", "volljaehrig", "rentner", _MARKE_ERWACHSEN) # Dass nach der Verordnung zu Lasten der GKV gefragt ist. Beide § 34-Anker # brauchen dieses Signal: ohne es spränge der Ausschluss auf jede Frage an, in # der ein ausgeschlossener Stoff bloß vorkommt. _ZU_LASTEN = ( "zu lasten", "zulasten", "kasse", "gkv", "erstattung", "erstattungsfähig", "erstattungsfaehig", "verordnungsfähig", "verordnungsfaehig", "kassenrezept", "verordnet werden", "verordnen", ) # Dieselbe Frage in den Wendungen der Offizin. Beim Lifestyle-Ausschluss wird # sie regelmäßig von der anderen Seite gestellt — nicht „zahlt die Kasse", sondern # „muss der Patient das selbst zahlen". `amrl_lifestyle.VERORDNUNGS_SIGNALE` # kennt dieselben Wendungen auf der Stoffebene. _SELBST_ZAHLEN = ( "privatrezept", "grünes rezept", "gruenes rezept", "grüne rezept", "selbstzahler", "selbst zahlen", "selbst bezahlen", "ausgeschlossen", ) # Signale für ein biologisches/biotechnologisch hergestelltes Arzneimittel. _BIOLOGISCH = ("biolog", "biosimilar", "biotechnolog", "referenzarzneimittel", "§ 40c", "40c") # Die parenterale Zubereitung — die Konstellation, die § 40b von § 40c trennt. # „zubereitung“ steht bewusst nicht allein darin: das Wort kommt in # Abgabefragen auch ohne Bezug zur Herstellung vor. _PARENTERAL = ( "parenteral", "zytostatik", "antineoplast", "applikationsfertig", "unmittelbaren ärztlichen anwendung", "unmittelbaren aerztlichen anwendung", "zubereitung aus fertigarzneimitteln", ) # Die Anwendungsgebiete, die § 34 Abs. 1 Satz 8 SGB V selbst aufzählt, in den # Wendungen, in denen danach gefragt wird. Signale der Normebene: sie stehen im # Gesetz. Welcher Wirkstoff unter sie fällt, entscheidet Anlage II — dieses # Register entscheidet das nicht und behauptet es auch nicht. _LIFESTYLE_GEBIETE = ( "erektil", "dysfunktion", "sexuelle potenz", "sexuellen potenz", "sexuellen verlangen", "sexuellen verlangens", "potenzmittel", "libido", "raucherentwöhn", "raucherentwoehn", "tabakentwöhn", "tabakentwoehn", "tabakabhängig", "tabakabhaengig", "nikotinabhängig", "nikotinabhaengig", "rauchstopp", "abmagerung", "appetitzügl", "appetitzuegl", "zügelung des appetits", "zuegelung des appetits", "gewichtsreduktion", "gewichtsabnahme", "gewichtsregulierung", "regulierung des körpergewichts", "regulierung des koerpergewichts", "abnehmspritze", "adipositas", "übergewicht", "uebergewicht", "haarwuchs", "haarausfall", "verbesserung des aussehens", "kosmetisch", "schlaf-wach-rhythmus", ) # Die Namen, unter denen die Frage in der Offizin gestellt wird. Das ist **nicht** # Anlage II — die steht in `data/amrl_anlage_ii.json` und wird von # `amrl_lifestyle` geführt, mit Stand, Fußnoten und den Maßgaben, an denen der # Ausschluss wirklich hängt. Hier stehen nur so viele Namen, dass eine Frage nach # ihnen den Weg zur richtigen Norm findet: gemessen wurde „Ist Sildenafil zu # Lasten der GKV erstattungsfähig?" — beantwortet aus § 31 SGB V. # # Diese Doppelung ist ungefährlich, weil sie nichts behauptet. Fällt ein Name # aus Anlage II heraus, bleibt § 34 Abs. 1 Satz 7 SGB V trotzdem die Vorschrift, # an der sich die Frage nach ihm entscheidet; die Auskunft, ob er noch gelistet # ist, gibt allein der Lookup. _LIFESTYLE_LEITFAELLE = ( "sildenafil", "viagra", "tadalafil", "cialis", "vardenafil", "levitra", "avanafil", "alprostadil", "dapoxetin", "semaglutid", "wegovy", "liraglutid", "saxenda", "tirzepatid", "mounjaro", "orlistat", "xenical", "mysimba", "finasterid", "propecia", "minoxidil", "regaine", "vareniclin", "champix", "zyban", "cytisin", ) # Wann die eine Ausnahme überhaupt im Raum steht. Nur dann gehören § 34 Abs. 2 # SGB V, § 14a AM-RL und Anlage IIa in die Kette — bei einer Frage nach Sildenafil # wäre die Tabakentwöhnung Ballast. _TABAK = ( "raucherentwöhn", "raucherentwoehn", "tabakentwöhn", "tabakentwoehn", "tabakabhängig", "tabakabhaengig", "nikotinabhängig", "nikotinabhaengig", "rauchstopp", "raucher", "rauchen", "tabak", "nikotin", "nicotin", "vareniclin", "champix", "zyban", "cytisin", ) # Wer steuern könnte. Nur diese Akteure machen aus einer Empfehlung eine # Zuweisung im Sinne des § 31 Abs. 1 Satz 6 SGB V. _AKTEURE = ( "arzt", "ärzt", "aerzt", "praxis", "krankenkasse", "kasse", "verordner", "vertragsarzt", ) ANKER: Tuple[Anker, ...] = ( Anker( key="otc_zu_lasten_gkv", thema="Nicht verschreibungspflichtiges Arzneimittel zu Lasten der GKV", signale=( ( ( "nicht verschreibungspflichtig", "nicht-verschreibungspflichtig", "nicht rezeptpflichtig", "rezeptfrei", "freiverkäuflich", "freiverkaeuflich", "otc", ), _ZU_LASTEN, ), ), kernaussage=( "Nicht verschreibungspflichtige Arzneimittel sind nach § 34 Abs. 1 Satz 1 SGB V " "von der Versorgung ausgeschlossen. Zu Lasten der GKV verordnungsfähig sind sie " "nur, soweit der Gemeinsame Bundesausschuss sie in § 12 und Anlage I der AM-RL " "als Therapiestandard bei der Behandlung einer schwerwiegenden Erkrankung " "ausweist und der Vertragsarzt sie für diese Erkrankung mit Begründung verordnet." ), kernaussagen=( Kernaussage( # Gemessen am 21.08.2026: die Frage nach einem zehnjährigen Kind # bekam die allgemeine Kurzantwort und damit die Anlage I als # Voraussetzung vorgesetzt — bei einem Kind bis zum vollendeten # 12. Lebensjahr entscheidet sie nichts, weil der Ausschluss # selbst nicht greift. text=( "Der Ausschluss des § 34 Abs. 1 Satz 1 SGB V gilt hier nicht: nach " "§ 34 Abs. 1 Satz 5 Nr. 1 SGB V sind versicherte Kinder bis zum " "vollendeten 12. Lebensjahr von ihm ausgenommen, § 12 Abs. 12 AM-RL " "wiederholt das für die Richtlinie. Ein Eintrag in Anlage I ist " "deshalb keine Voraussetzung der Verordnungsfähigkeit — die Anlage " "führt die Ausnahmen **vom** Ausschluss, und der greift in diesem " "Fall nicht. Zu prüfen bleibt die Verordnung im Übrigen, nicht die " "Liste." ), wenn=Bedingung(nur_bei=(_MARKE_KIND,)), ), ), normen=( Norm( zitat="§ 34 Abs. 1 SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 34", absatz="1", warum=( "Satz 1 schließt nicht verschreibungspflichtige Arzneimittel von der " "Versorgung aus, Satz 2 ermächtigt den G-BA zu den Ausnahmen." ), ), Norm( zitat="§ 12 AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 12", warum=( "Setzt die Ermächtigung des § 34 Abs. 1 Satz 2 um und regelt nach " "Absatz 10 abschließend, wann ausnahmsweise verordnet werden darf." ), ), Norm( zitat="AM-RL Anlage I", warum=( "Führt die ausnahmsweise verordnungsfähigen Stoffe samt Indikation. " "Die konkrete Verordnungsfähigkeit ist an ihr zu prüfen." ), ), Norm( zitat="§ 31 Abs. 1 SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 31", absatz="1", tragend=False, warum=( "Anspruchsnorm — gewährt die Versorgung ausdrücklich nur, soweit sie " "nicht nach § 34 ausgeschlossen ist. Gehört in die Antwort, " "entscheidet sie aber nicht." ), ), ), fehlgriffe=( Fehlgriff( muster=r"§\s*31\b", warum=( "§ 31 Abs. 1 Satz 1 SGB V gewährt den Anspruch nur, „soweit die " "Arzneimittel nicht nach § 34 oder durch Richtlinien nach § 92 " "Absatz 1 Satz 2 Nummer 6 ausgeschlossen sind“. Die Norm verweist " "auf die Antwort; die Ausschlussentscheidung trifft § 34." ), ), Fehlgriff( # Gemessen am realen Lauf: die Kurzantwort nannte die richtige Norm # (§ 34), machte aber die in § 34 Abs. 1 Satz 5 ff. genannten Gruppen # zum Negativmaßstab der OTC-Ausnahme („… nicht zu den ausgeschlossenen # Gruppen wie Abführmittel … gehört“). Das vermischt zwei Regelungs- # komplexe. Der Fehlgriff sucht deshalb die Kurzantwort, nicht den # Normabschnitt, und feuert nicht, wenn die Frage selbst eine der # Gruppen benennt (dann ist ihre Nennung sachgerecht). muster=( r"(?is)nicht zu (?:den|einer)\b.{0,80}ausgeschlossen" r".{0,120}(abführmittel|abfuehrmittel|reisekrankheit|erkältung" r"|erkaeltung|rachentherapeutika|grippal)" ), warum=( "Die in § 34 Abs. 1 Satz 5 ff. SGB V genannten Gruppen (Erkältungs- " "und Grippemittel, Mund- und Rachentherapeutika, Abführmittel, Mittel " "gegen Reisekrankheit) sind ein eigener Ausschluss bestimmter " "verschreibungspflichtiger Arzneimittel für Erwachsene — nicht der " "Negativmaßstab der OTC-Ausnahme. Ob ein nicht verschreibungspflichtiges " "Arzneimittel ausnahmsweise verordnungsfähig ist, entscheidet allein " "§ 12 in Verbindung mit Anlage I der AM-RL; ein nicht " "verschreibungspflichtiges Abführmittel etwa kann für eine dort " "gelistete schwerwiegende Erkrankung verordnungsfähig sein." ), nur_ohne_tragende=False, im_kurzabschnitt=True, wenn=Bedingung( nicht_bei=( "abführmittel", "abfuehrmittel", "reisekrankheit", "rachentherapeutika", ) ), ), ), hinweise=( Hinweis( text=( "Neben Anlage I bleibt verordnungsfähig, was § 12 AM-RL gesondert regelt: " "Begleitmedikation, die die Fachinformation des Hauptarzneimittels " "voraussetzt (Absatz 7), die Behandlung schwerwiegender unerwünschter " "Arzneimittelwirkungen (Absatz 8) und Arzneimittel der Anthroposophie und " "Homöopathie für die dort gelisteten Indikationsgebiete (Absatz 6)." ), ), Hinweis( # Für einen ausdrücklichen Erwachsenenfall ist das nur Rauschen. # Die Nummern stehen hier, seit die Messung vom 21.08.2026 zeigte, # dass die Antwort den Inhalt traf und die Fundstelle nicht: „Satz 5" # allein sagt nicht, welche der beiden Gruppen gemeint ist, und bei # einem zehnjährigen Kind ist das Nr. 1 und nicht Nr. 2. text=( "Für versicherte Kinder bis zum vollendeten 12. Lebensjahr " "(§ 34 Abs. 1 Satz 5 Nr. 1 SGB V) und Jugendliche mit " "Entwicklungsstörungen bis zum vollendeten 18. Lebensjahr " "(§ 34 Abs. 1 Satz 5 Nr. 2 SGB V) gilt der Ausschluss überhaupt " "nicht; § 12 Abs. 12 AM-RL wiederholt das für die Richtlinie." ), wenn=Bedingung(nicht_bei=_ERWACHSEN), ), ), ), Anker( key="lifestyle_arzneimittel", thema="Arzneimittel zur Erhöhung der Lebensqualität (Lifestyle-Arzneimittel)", # Drei Zugänge, weil dieselbe Frage auf drei Höhen gestellt wird: über den # Namen der Sache, über das Anwendungsgebiet und über den Wirkstoff. Die # letzten beiden verlangen zusätzlich, dass nach der Verordnung zu Lasten # der GKV gefragt ist — „Wie wirkt Sildenafil?" ist keine Normfrage. signale=( (("lifestyle", "life-style", "erhöhung der lebensqualität", "erhoehung der lebensqualitaet"),), (_LIFESTYLE_GEBIETE, (*_ZU_LASTEN, *_SELBST_ZAHLEN)), (_LIFESTYLE_LEITFAELLE, (*_ZU_LASTEN, *_SELBST_ZAHLEN)), ), kernaussage=( "Arzneimittel, bei deren Anwendung eine Erhöhung der Lebensqualität im Vordergrund " "steht, sind nach § 34 Abs. 1 Satz 7 SGB V von der Versorgung ausgeschlossen; Satz 8 " "nennt die Anwendungsgebiete — erektile Dysfunktion, Anreizung sowie Steigerung der " "sexuellen Potenz, Raucherentwöhnung, Abmagerung, Zügelung des Appetits, Regulierung " "des Körpergewichts und Verbesserung des Haarwuchses. Das Nähere regelt § 14 AM-RL, " "dessen Absatz 2 das erste Gebiet zu den sexuellen Dysfunktionen weitet; welche " "Fertigarzneimittel und Anwendungsgebiete darunterfallen, stellt Anlage II der AM-RL " "zusammen. Der Ausschluss beruht damit auf dem Gesetz selbst: der medizinisch " "begründete Einzelfall nach § 16 Abs. 5 AM-RL öffnet sich wörtlich nur für die nach " "§ 16 Abs. 1 und 2 ausgeschlossenen Arzneimittel und trägt hier nicht. Die einzige " "Ausnahme ist die Tabakentwöhnung: bei festgestellter schwerer Tabakabhängigkeit " "besteht nach § 34 Abs. 2 SGB V, § 14a AM-RL Anspruch auf eine einmalige Versorgung " "mit den in Anlage IIa geführten Arzneimitteln im Rahmen evidenzbasierter Programme." ), normen=( Norm( zitat="§ 34 Abs. 1 Satz 7 SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 34", absatz="1", warum=( "Satz 7 schließt Arzneimittel aus, bei deren Anwendung eine Erhöhung der " "Lebensqualität im Vordergrund steht; Satz 8 zählt die Anwendungsgebiete " "auf, Satz 9 verweist auf die Richtlinien nach § 92 Abs. 1 Satz 2 Nr. 6. " "Das ist ein anderer Ausschluss als der des Satzes 1 und hängt nicht an der " "Verschreibungspflicht." ), ), Norm( zitat="§ 14 AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 14", warum=( "Führt Satz 7 aus: Absatz 1 umschreibt die ausgeschlossenen Arzneimittel " "nach ihrer Zweckbestimmung, Absatz 2 wiederholt die Anwendungsgebiete, " "Absatz 3 stellt sie in Anlage II zusammen." ), ), Norm( zitat="AM-RL Anlage II", warum=( "Die Übersicht der ausgeschlossenen Fertigarzneimittel und Anwendungsgebiete " "— hier entscheidet sich die Frage nach dem einzelnen Arzneimittel. Nicht " "indiziert, aber als deterministischer Lookup vorhanden (amrl_lifestyle)." ), ), Norm( zitat="§ 34 Abs. 2 SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 34", absatz="2", warum=( "Der einzige Weg an dem Ausschluss vorbei: Versicherte mit festgestellter " "schwerer Tabakabhängigkeit haben Anspruch auf eine einmalige Versorgung mit " "Arzneimitteln zur Tabakentwöhnung im Rahmen evidenzbasierter Programme; " "erneut frühestens drei Jahre nach Abschluss der Behandlung." ), wenn=Bedingung(nur_bei=_TABAK), ), Norm( zitat="§ 14a AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 14a", warum=( "Setzt § 34 Abs. 2 um: Absatz 1 hält den Ausschluss nach § 34 Abs. 1 Satz 7 " "ff. SGB V ausdrücklich fest, Absatz 2 gewährt die Ausnahme, Absatz 3 nennt " "Anlage IIa und die Voraussetzungen, unter denen eine schwere " "Tabakabhängigkeit festgestellt ist." ), wenn=Bedingung(nur_bei=_TABAK), ), Norm( zitat="AM-RL Anlage IIa", warum=( "Führt die zur Tabakentwöhnung ausnahmsweise verordnungsfähigen Arzneimittel " "— hier entscheidet sich, ob die Ausnahme den Wirkstoff erfasst. Nicht " "indiziert, aber als deterministischer Lookup vorhanden " "(amrl_tabakentwoehnung)." ), wenn=Bedingung(nur_bei=_TABAK), ), Norm( zitat="§ 31 Abs. 1 SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 31", absatz="1", tragend=False, warum=( "Anspruchsnorm — gewährt die Versorgung ausdrücklich nur, soweit sie nicht " "nach § 34 ausgeschlossen ist. Gehört in die Antwort, entscheidet sie aber " "nicht." ), ), ), fehlgriffe=( Fehlgriff( # Der gemessene Fehler: die Erstattungsfähigkeit eines # Lifestyle-Arzneimittels aus der Anspruchsnorm hergeleitet. muster=r"§\s*31\b", warum=( "§ 31 Abs. 1 Satz 1 SGB V gewährt den Anspruch nur, „soweit die Arzneimittel " "nicht nach § 34 oder durch Richtlinien nach § 92 Absatz 1 Satz 2 Nummer 6 " "ausgeschlossen sind“. Für Arzneimittel zur Erhöhung der Lebensqualität " "trifft diese Ausschlussentscheidung § 34 Abs. 1 Satz 7." ), ), ), hinweise=( Hinweis( # Die Verwechslung, die in einer § 34-Antwort naheliegt: Satz 1 # schließt nach der Verschreibungspflicht aus, Satz 7 nach der # Zweckbestimmung. Viagra, Wegovy und Champix sind # verschreibungspflichtig und trotzdem ausgeschlossen. text=( "Der Ausschluss nach § 34 Abs. 1 Satz 7 SGB V trifft auch " "verschreibungspflichtige Arzneimittel: er hängt nicht an der " "Verschreibungspflicht wie der des Satzes 1, sondern an der " "Zweckbestimmung (§ 14 Abs. 1 AM-RL)." ), ), Hinweis( # Ohne Fallbezug ist die Tabakentwöhnung ein Sonderfall und # gehört nicht in die Antwort. text=( "Zur Tabakentwöhnung besteht die eine gesetzliche Ausnahme: Versicherte mit " "festgestellter schwerer Tabakabhängigkeit haben nach § 34 Abs. 2 SGB V, " "§ 14a AM-RL Anspruch auf eine einmalige Versorgung im Rahmen " "evidenzbasierter Programme; die dafür verordnungsfähigen Arzneimittel führt " "Anlage IIa. Ob die Voraussetzungen vorliegen, ist eine ärztliche " "Feststellung nach § 14a Abs. 3 AM-RL und keine Auskunft dieses Registers." ), wenn=Bedingung(nur_bei=_TABAK), ), ), ), Anker( key="apothekenwahl_zuweisung", thema="Freie Apothekenwahl und Zuweisungsverbot", # Drei Signalsätze, weil dieselbe Frage in drei Registern gestellt wird. # Die erste Fassung hatte nur den ersten und traf die gemessene Frage # deshalb nicht: sie sprach von „vorgeben, in welcher Apotheke", nicht # von „zuweisen". # # Satz 2 und 3 verlangen zusätzlich einen Akteur (Arzt, Praxis, Kasse). # Ohne diese Schranke spränge der Anker auf jede Frage an, in der eine # Apotheke irgendetwas empfiehlt. # # Warum Satz 3 eine Richtungsangabe nimmt und kein Verb: „weiterleiten # an" traf zusammen mit „Apotheke" und „Kasse" die Frage „Muss die # Apotheke die Verordnung an die Krankenkasse weiterleiten?" — eine # echte Abrechnungsfrage, die mit dem Zuweisungsverbot nichts zu tun # hat. Entscheidend ist nicht das Verb, sondern dass die Apotheke das # **Ziel** ist. Im Deutschen steht das Verb am Satzende, weshalb ein # Bigramm wie „übermitteln an" ohnehin an der Wortstellung scheitert. signale=( ( ( "zuweis", "zuführ", "zufuehr", "zuleiten", "makeln", "makelverbot", "apothekenwahl", "freie wahl", "freier wahl", "frei wähl", "frei waehl", "bestimmte apotheke", "bestimmten apotheke", "bestimmter apotheke", "bestimmte apotheken", "welcher apotheke", "welche apotheke", ), ("apotheke", "apotheken", "leistungserbringer"), ), ( ( "vorgeb", "vorschreib", "beeinfluss", "steuer", "lenken", "nahelegen", "nahe legen", "empfehl", "verweis", "aussuchen", "auswählen", "auswaehlen", ), ("apotheke", "apotheken"), _AKTEURE, ), ( ( "an eine apotheke", "an die apotheke", "an eine bestimmte apotheke", "an bestimmte apotheken", "zu einer apotheke", "zu einer bestimmten apotheke", "in eine bestimmte apotheke", ), _AKTEURE, ), ), kernaussage=( "Nein. Versicherte wählen die Apotheke frei (§ 31 Abs. 1 Satz 5 SGB V). " "Vertragsärzte und Krankenkassen dürfen sie weder dahin beeinflussen, " "Verordnungen bei einer bestimmten Apotheke einzulösen, noch Verordnungen " "unmittelbar oder mittelbar bestimmten Apotheken zuweisen (Satz 6); für " "elektronische Verordnungen gilt nichts anderes (Satz 7). Ausgenommen ist nur, " "was gesetzlich anders bestimmt ist oder was aus medizinischen Gründen im " "Einzelfall als Empfehlung geboten ist." ), normen=( Norm( zitat="§ 31 Abs. 1 Satz 5 bis 7 SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 31", absatz="1", warum=( "Satz 5 gibt den Versicherten die freie Apothekenwahl, Satz 6 verbietet " "Vertragsärzten und Krankenkassen Beeinflussung und Zuweisung, Satz 7 " "erstreckt beides ausdrücklich auf elektronische Verordnungen." ), ), Norm( zitat="§ 11 ApoG", tragend=False, warum=( "Spiegelt das Verbot auf der Seite der Apotheke (Absprachen mit " "Ärzten). Nicht indiziert — die Antwort kann es benennen, nicht " "zitieren." ), ), ), fehlgriffe=( Fehlgriff( muster=r"§\s*360\b", warum=( "§ 360 SGB V regelt die Pflicht zur elektronischen Verordnung. Aus der " "technischen Ausgestaltung des E-Rezepts folgt kein Zuweisungsverbot — " "es steht in § 31 Abs. 1 Satz 6 und gilt nach Satz 7 auch für " "elektronische Verordnungen." ), ), ), hinweise=( Hinweis( text=( "Das Verbot ist ein Verbot, nicht bloß eine fehlende Erlaubnis: § 31 " "Abs. 1 Satz 6 SGB V untersagt Beeinflussung und Zuweisung ausdrücklich. " "Eine Antwort, die nur feststellt, es gebe keine Berechtigung, verfehlt " "die Rechtslage." ), ), ), ), Anker( key="sonderkennzeichen", thema="PZN oder Sonderkennzeichen in der Abrechnung", signale=( ( ( "sonderkennzeichen", "sonder-pzn", "sonderpzn", "pseudo-pzn", "pseudopzn", "sonder-kennzeichen", ), ), ), kernaussage=( "Zu übermitteln ist die Pharmazentralnummer des tatsächlich abgegebenen " "Fertigarzneimittels (§ 300 Abs. 1 Nr. 1 SGB V, § 2 Abs. 1 " "Arzneimittelabrechnungsvereinbarung). Ein Sonderkennzeichen ist zu verwenden, " "soweit die Technische Anlage 1 zur Vereinbarung für den konkreten Abgabe- oder " "Abrechnungssachverhalt eines vorsieht. Ob es die Produkt-PZN ersetzt oder einen " "zusätzlichen Sachverhalt abbildet, richtet sich nach der dortigen Regelung." ), normen=( Norm( zitat="§ 2 Abs. 1 Arzneimittelabrechnungsvereinbarung", corpus_id="amabrv", container_id="Vertrag", section_id="§ 2", absatz="1", warum=( "Bestimmt das bundeseinheitliche Kennzeichen nach § 300 Abs. 3 Satz 1 " "Nr. 1 SGB V und rechnet die Sonderkennzeichen der Technischen Anlage 1 " "ausdrücklich dazu." ), ), Norm( zitat="§ 300 Abs. 1 und 3 SGB V", corpus_id="sgb5", container_id="Zehntes Kapitel", section_id="§ 300", absatz="1", warum=( "Verpflichtet die Apotheke, das Kennzeichen maschinenlesbar zu " "übertragen, und ist die Ermächtigungsgrundlage der Vereinbarung." ), ), Norm( zitat="Technische Anlage 1 zur Arzneimittelabrechnungsvereinbarung", tragend=False, warum=( "Führt die einzelnen Sonderkennzeichen und bestimmt, für welchen " "Sachverhalt welches gilt. Nicht indiziert — die Vereinbarung verweist " "darauf, das Verzeichnis selbst liegt nicht vor." ), ), ), hinweise=( Hinweis( text=( "Maßgeblich ist das **abgegebene**, nicht das verordnete Fertigarzneimittel. " "Bei einer Ersetzung fallen beide auseinander." ), ), Hinweis( text=( "Welches Sonderkennzeichen im Einzelfall gilt, steht in der Technischen " "Anlage 1 und nicht in der Vereinbarung selbst. Sie ist nicht Teil des " "indizierten Bestandes; die Antwort kann die Regel nennen, nicht das " "einzelne Kennzeichen." ), ), ), ), Anker( key="rabatt_vor_import", thema="Abgaberangfolge: Rabattarzneimittel und preisgünstiger Import", signale=( ( ("import", "reimport", "re-import", "parallelimport", "parallelvertrieb"), ( "rabatt", "130a", "rabattvertrag", "rabattarzneimittel", "rabattbegünstigt", "rabattbeguenstigt", ), ), ), kernaussage=( "Vorrang hat das rabattbegünstigte Fertigarzneimittel; die Abgabe eines " "preisgünstigen Imports tritt dahinter zurück (§ 11 Abs. 1 Rahmenvertrag, " "§ 129 Abs. 1 Satz 8 SGB V). Von der Abgaberangfolge darf nur unter den " "Voraussetzungen der §§ 10 bis 14 Rahmenvertrag abgewichen werden, insbesondere " "bei Nichtverfügbarkeit oder bei ordnungsgemäß geltend gemachten pharmazeutischen " "Bedenken. Welches Arzneimittel dann abzugeben ist, richtet sich nach dem " "Auswahlbereich des § 9 und der weiteren Rangfolge." ), normen=( Norm( zitat="§ 10 Rahmenvertrag", corpus_id="rv129", container_id="Vertrag", section_id="§ 10", warum=( "Die Abgaberangfolge selbst: die Auswahl ist nach Maßgabe der §§ 11 bis " "14 zu treffen. Ohne diese Norm wird eine Rangfolge behauptet, ohne die " "Vorschrift zu nennen, die sie anordnet." ), ), Norm( zitat="§ 11 Abs. 1 Rahmenvertrag", corpus_id="rv129", container_id="Vertrag", section_id="§ 11", absatz="1", warum=( "Vorrang der Rabattverträge: vorrangig ist ein Fertigarzneimittel " "abzugeben, für das ein Rabattvertrag nach § 130a Abs. 8 SGB V besteht." ), ), Norm( zitat="§ 129 Abs. 1 SGB V", corpus_id="sgb5", container_id="Viertes Kapitel", section_id="§ 129", absatz="1", warum=( "Satz 1 Nr. 2 begründet die Importabgabepflicht samt Preisabstand; " "Satz 8 ordnet an, dass die Abgabe eines Rabattarzneimittels ihr " "vorgeht. Das ist die gesetzliche Grundlage der Rangfolge." ), ), Norm( zitat="§ 13 Rahmenvertrag", corpus_id="rv129", container_id="Vertrag", section_id="§ 13", # Die Frage nennt den Import ausdrücklich; ohne § 13 als maßgebliche # Norm fehlt die Vorschrift, die seine Abgabe regelt. Ein Chunk genügt: # der Anker soll ihn benennen und knapp belegen, nicht ausbreiten. chunks=1, warum="Führt die Abgabe preisgünstiger Importe nach § 9 im Einzelnen aus.", ), Norm( zitat="§ 14 Rahmenvertrag", corpus_id="rv129", container_id="Vertrag", section_id="§ 14", # Die Frage fragt ausdrücklich nach der Abweichung von der Rangfolge; # § 14 ist die Vorschrift, die sie regelt, und gehört damit in die # maßgeblichen Normen. Ein Chunk hält das Abrufbudget schmal. chunks=1, warum=( "Regelt abschließend, wann von der Abgaberangfolge abgewichen werden " "darf. Eine Antwort, die nur sagt, die vorrangige Abgabe sei „nicht " "möglich“, bleibt ohne diese Tatbestände unbestimmt." ), ), ), fehlgriffe=( Fehlgriff( # Gemessen am realen Lauf: die Antwort stützte die allgemeine Abweichung # auf § 14 Abs. 5 und zitierte „In den Fällen des § 129 Absätze 2a und # 2b SGB V …“, obwohl die Frage keinen dieser Tatbestände nennt. Der # Fehlgriff feuert deshalb nur, wenn die Frage 2a/2b nicht aufwirft. muster=r"§\s*14\s+Abs(?:atz|\.)?\s*5", warum=( "§ 14 Abs. 5 Rahmenvertrag gilt nur in den Fällen des § 129 Abs. 2a " "und 2b SGB V. Ohne einen dieser Tatbestände trägt er die allgemeine " "Abweichung von der Abgaberangfolge nicht; maßgeblich sind dann die " "§§ 10 bis 14 Rahmenvertrag im Übrigen — insbesondere Nichtverfügbarkeit " "und ordnungsgemäß geltend gemachte pharmazeutische Bedenken." ), nur_ohne_tragende=False, wenn=Bedingung( nicht_bei=( "abs. 2a", "absatz 2a", "abs 2a", "absätze 2a", "absaetze 2a", "abs. 2b", "absatz 2b", "2a und 2b", ) ), ), ), hinweise=( Hinweis( # Ohne Fallbezug ist das ein Sonderfall und gehört nicht in die Kette. text=( "§ 129 Abs. 1 Satz 9 SGB V nimmt biotechnologisch hergestellte und " "antineoplastische Arzneimittel zur parenteralen Anwendung von der " "Importabgabepflicht aus." ), wenn=Bedingung( nur_bei=( "parenteral", "zytostatik", "antineoplast", "biotechnolog", "biolog", "zubereitung", ) ), ), ), ), Anker( key="darreichungsform_austausch", thema="Austauschbarkeit von Darreichungsformen", signale=( ( ("darreichungsform",), ( "austausch", "austauschbar", "ersetz", "aut idem", "aut-idem", "substitu", "abweichen", "abgeben statt", "anstelle", "andere", "anderer", "unterschiedlich", ), ), ), kernaussage=( "Eine andere Darreichungsform darf abgegeben werden, wenn beide Formen nach " "§ 9 Abs. 3 Buchst. d Rahmenvertrag in Verbindung mit § 40 und Anlage VII Teil A " "der AM-RL austauschbar sind. Die Austauschbarkeit folgt aus der Zuordnung in " "Anlage VII Teil A, nicht aus einer Ähnlichkeit der Formen; Einzeldosis- und " "Mehrdosenbehältnis sind untereinander nicht austauschbar. Daneben müssen die " "übrigen Kriterien des § 9 Abs. 3 erfüllt sein: gleicher Wirkstoff, identische " "Wirkstärke, identische Packungsgröße und Zulassung für ein gleiches " "Anwendungsgebiet." ), normen=( Norm( zitat="§ 9 Abs. 3 Buchst. d Rahmenvertrag", corpus_id="rv129", container_id="Vertrag", section_id="§ 9", absatz="3", warum=( "Nennt die Kriterien: identische Bezeichnung heißt gleich; austauschbar " "ist, was Anlage VII Teil A der AM-RL benennt; Einzeldosis- und " "Mehrdosenbehältnis sind nicht austauschbar; bei Importen zählt die Form " "des Referenzarzneimittels. Buchstaben a bis c und e bis f nennen die " "übrigen Voraussetzungen des Austauschs." ), ), Norm( zitat="§ 129 Abs. 1 SGB V", corpus_id="sgb5", container_id="Viertes Kapitel", section_id="§ 129", absatz="1", warum=( "Satz 2 verlangt neben der gleichen oder austauschbaren Darreichungsform " "Identität in Wirkstärke und Packungsgröße sowie die Zulassung für ein " "gleiches Anwendungsgebiet. Die Austauschbarkeit der Form ist also nur " "eine von mehreren Voraussetzungen." ), ), Norm( zitat="§ 40 AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 40", warum=( "Die Hinweise des G-BA nach § 129 Abs. 1a Satz 1 SGB V selbst, mit " "Anlage VII Teil A als Fundstelle." ), ), Norm( zitat="AM-RL Anlage VII Teil A", warum=( "Die Liste der austauschbaren Darreichungsformen — hier entscheidet sich " "die Frage. Nicht indiziert, aber als deterministischer Lookup vorhanden " "(amrl_austauschbarkeit)." ), ), Norm( zitat="§ 40c Abs. 1 AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 40c", tragend=False, warum=( "Zusätzliche Regelungen zu den Darreichungsformen — aber nur für " "biotechnologisch hergestellte biologische Arzneimittel." ), wenn=Bedingung(nur_bei=_BIOLOGISCH), ), ), fehlgriffe=( Fehlgriff( # Steht auch neben der richtigen Norm falsch: § 40c verengt die # allgemeine Frage auf einen Sonderfall, den sie nicht aufwirft. muster=r"§\s*40c\b", warum=( "§ 40c AM-RL betrifft ausschließlich biotechnologisch hergestellte " "biologische Arzneimittel. Für die allgemeine Austauschbarkeit von " "Darreichungsformen ist § 40 in Verbindung mit Anlage VII Teil A " "maßgeblich; § 40c tritt nur hinzu, wenn ein biologisches Arzneimittel " "betroffen ist." ), nur_ohne_tragende=False, wenn=Bedingung(nicht_bei=_BIOLOGISCH), ), ), hinweise=( Hinweis( text=( "Ohne beide konkret benannten Darreichungsformen lässt sich die " "Zugehörigkeit zu einer Gruppe der Anlage VII Teil A nicht feststellen — " "und damit die Frage nicht abschließend beantworten." ), ), ), ), Anker( # Der einzige Anker, dessen Beleg keine falsche Antwort ist, sondern # eine Messung: die Kettenabdeckung des Falls # `kette_verordnungsausschluss_abgabe` lag am 19.08.2026 bei 1 von 4. # § 34 SGB V, § 16 AM-RL und § 6 Rahmenvertrag erreichten den Kontext # nicht, und keiner der gefundenen Texte nennt sie, sodass auch die # Verweisauflösung sie nicht hätte holen können. Das ist dieselbe Lage # wie bei den fünf übrigen Ankern — die Norm steht im Index und die # Ähnlichkeit findet sie nicht —, nur diesmal vor der falschen Antwort # bemerkt statt danach. # # Kein `Fehlgriff`-Eintrag: das Register nimmt Muster nur auf, wenn # eine Auswertung sie als tatsächlich falsch genannte Norm gezeigt hat. # Eine Kettenlücke ist kein solcher Beleg. key="abgabe_nicht_verordnungsfaehig", thema="Abgabe eines nicht verordnungsfähigen Arzneimittels zu Lasten der GKV", signale=( ( ( "nicht verordnungsfähig", "nicht verordnungsfaehig", "nicht verordnungsfähiges", "nicht verordnungsfaehiges", "verordnungsausschluss", "verordnungseinschränkung", "verordnungseinschraenkung", "von der versorgung ausgeschlossen", "ausgeschlossenes arzneimittel", "anlage iii", ), ( "abgeben", "abgabe", "beliefer", "einlösen", "einloesen", "zu lasten", "zulasten", "abrechn", "retax", "vergütungsanspruch", "verguetungsanspruch", ), ), ), kernaussage=( "Der Anspruch der Versicherten besteht nach § 31 Abs. 1 Satz 1 SGB V nur, " "soweit das Arzneimittel nicht nach § 34 SGB V oder durch die Richtlinien " "nach § 92 Abs. 1 Satz 2 Nr. 6 SGB V ausgeschlossen ist. Die arzneimittel" "rechtliche Zulassung und die Verschreibungspflicht sagen darüber nichts: " "sie betreffen die Verkehrsfähigkeit, nicht die sozialrechtliche " "Verordnungsfähigkeit zu Lasten der GKV. Ausgeschlossen oder eingeschränkt " "wird auf Grundlage des § 92 Abs. 1 Satz 1 Halbsatz 3 SGB V in Verbindung " "mit § 16 Abs. 1 und 2 AM-RL; die so erfassten Arzneimittel sind in " "Anlage III der AM-RL zusammengestellt. Für die durch Richtlinie " "ausgeschlossenen Arzneimittel eröffnet § 31 Abs. 1 Satz 4 SGB V die " "Verordnung im medizinisch begründeten Einzelfall mit Begründung; adressiert " "ist dort der Vertragsarzt. Der Vergütungsanspruch der Apotheke entsteht " "nach § 6 Abs. 1 Satz 1 Rahmenvertrag mit der Belieferung einer gültigen " "ordnungsgemäßen vertragsärztlichen Verordnung." ), normen=( Norm( zitat="§ 31 Abs. 1 SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 31", absatz="1", chunks=1, warum=( "Satz 1 trägt den Anspruch samt Ausschlussvorbehalt — „soweit die " "Arzneimittel nicht nach § 34 oder durch Richtlinien nach § 92 Absatz 1 " "Satz 2 Nummer 6 ausgeschlossen sind“. Satz 4 eröffnet für die durch " "Richtlinie ausgeschlossenen Arzneimittel die Verordnung im medizinisch " "begründeten Einzelfall." ), ), Norm( zitat="§ 34 Abs. 1 SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 34", absatz="1", chunks=1, warum=( "Der zweite der beiden Ausschlusswege, die § 31 Abs. 1 Satz 1 nennt. " "Wer nur den Richtlinienweg nennt, gibt die Grundregel unvollständig " "wieder." ), ), # Die Ermächtigung selbst — ohne sie steht die Anlage III für sich, # und genau das ist der Mangel, den dieser Fall messen soll: eine # gefundene Liste muss auf ihre Rechtsgrundlage zurückgeführt werden. # Maßgeblich ist Satz 1 Halbsatz 3 (Einschränkung der Verordnung von # Arzneimitteln), nicht Satz 2 Nr. 6 — der ermächtigt nur, überhaupt # eine Arzneimittel-Richtlinie zu erlassen. § 31 Abs. 1 Satz 1 nennt # deshalb Satz 2 Nr. 6, die Vorbemerkung der Anlage III den Satz 1 # Halbsatz 3; beide Zitate sind an ihrer Stelle richtig. Norm( zitat="§ 92 Abs. 1 Satz 1 Halbsatz 3 SGB V", corpus_id="sgb5", container_id="Viertes Kapitel", section_id="§ 92", absatz="1", chunks=1, warum=( "Die gesetzliche Ermächtigung, die Verordnung von Arzneimitteln " "einzuschränken oder auszuschließen, „wenn die Unzweckmäßigkeit " "erwiesen oder eine andere, wirtschaftlichere Behandlungsmöglichkeit " "mit vergleichbarem diagnostischen oder therapeutischen Nutzen " "verfügbar ist“. Die Vorbemerkung der Anlage III nimmt genau diesen " "Halbsatz in Verbindung mit § 16 Abs. 1 und 2 AM-RL in Bezug." ), ), # Am 21.08.2026 vom Fachanwender auf die Absätze geschärft. Die # Vorbemerkung der Anlage III nimmt ausdrücklich „§ 92 Abs. 1 Satz 1 # Halbsatz 3 SGB V in Verbindung mit § 16 Abs. 1 und 2 AM-RL" in # Bezug — und die beiden Absätze tragen Verschiedenes: Absatz 1 # nennt die drei Gründe, Absatz 2 die Regelbeispiele. Der Anker holte # vorher nur „§ 16" und damit den Abschnittskopf; die Messung # `kette_verordnungseinschraenkung_abgabe` zeigte Abs. 2 als fehlend. Norm( zitat="§ 16 Abs. 1 AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 16", absatz="1", chunks=1, warum=( "„Verordnungseinschränkungen und -ausschlüsse von Arzneimitteln nach " "§ 92“ — die Konkretisierung des Richtlinienwegs. Absatz 1 nennt die " "drei Gründe: fehlender Nutzen, fehlende medizinische Notwendigkeit, " "fehlende Wirtschaftlichkeit." ), ), Norm( zitat="§ 16 Abs. 2 AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 16", absatz="2", chunks=1, warum=( "Die Regelbeispiele, wann diese Voraussetzungen zutreffen — " "Unzweckmäßigkeit, wirtschaftlichere Behandlungsmöglichkeit, fehlende " "medizinische Notwendigkeit, nichtmedikamentöse Alternative, fixe " "Kombination statt Monopräparat. Anlage III ist die Fundstelle, in der " "die so ausgeschlossenen Arzneimittel zusammengestellt sind." ), ), Norm( zitat="§ 6 Abs. 1 Rahmenvertrag", corpus_id="rv129", container_id="Vertrag", section_id="§ 6", absatz="1", chunks=1, warum=( "Die Abgabeebene, nach der gefragt ist: Satz 1 lässt den " "Vergütungsanspruch der Apotheke „mit Belieferung einer gültigen " "ordnungsgemäßen vertragsärztlichen Verordnung“ entstehen. Ohne diese " "Norm beantwortet die Antwort die Verordnungsfrage und nicht die " "gestellte." ), ), Norm( zitat="AM-RL Anlage III", tragend=False, warum=( "Führt die eingeschränkten und ausgeschlossenen Arzneimittel. Nicht " "indiziert, aber als deterministischer Lookup vorhanden " "(amrl_verordnungsausschluss)." ), ), ), hinweise=( # Die beiden folgenden Hinweise stehen hier und nicht nur in der # `kernaussage`, weil die Kernaussage die Antwort **nur bei einer # Abweichung** ersetzt. Am 21.08.2026 nannte die Antwort § 31 und # § 16 richtig — also kein Fehlgriff, also keine Ersetzung — und # zitierte die Ermächtigung trotzdem als § 92 Abs. 1 Satz 2 Nr. 6, # weil § 31 Abs. 1 Satz 1 sie so nennt. Der mittelbare Pfad blieb # damit unaufgelöst. Ein Hinweis wird immer angehängt. Hinweis( text=( "Die Ermächtigung für Anlage III ist § 92 Abs. 1 Satz 1 Halbsatz 3 " "SGB V in Verbindung mit § 16 Abs. 1 und 2 AM-RL — nicht § 92 Abs. 1 " "Satz 2 Nr. 6 SGB V. Satz 2 Nr. 6 ermächtigt dazu, überhaupt eine " "Arzneimittel-Richtlinie zu erlassen; Satz 1 Halbsatz 3 dazu, die " "Verordnung von Arzneimitteln einzuschränken oder auszuschließen. " "§ 31 Abs. 1 Satz 1 SGB V verweist auf die Richtlinie, die " "Vorbemerkung der Anlage III auf die Einschränkungsermächtigung." ), ), Hinweis( text=( "Die arzneimittelrechtliche Zulassung und die Verschreibungspflicht " "entscheiden die Frage nicht: sie betreffen die Verkehrsfähigkeit " "nach dem Arzneimittelrecht. Die sozialrechtliche " "Verordnungsfähigkeit zu Lasten der GKV ist davon getrennt zu " "prüfen." ), ), Hinweis( text=( "Ob das konkrete Arzneimittel betroffen ist, entscheidet Anlage III der " "AM-RL. Anders als bei der Substitutions-Ausschlussliste trägt dort die " "Negativauskunft nicht: die Anlage ist eine Übersicht und nennt sich " "nirgends abschließend." ), ), Hinweis( text=( "Die Verordnung im medizinisch begründeten Einzelfall steht nach der " "Vorbemerkung der Anlage III nur bei den Rechtsfolgen 3 bis 6 offen — " "nicht bei einem gesetzlichen Ausschluss." ), ), ), ), Anker( # Das Spiegelbild des § 40c-Fehlgriffs im Anker # `darreichungsform_austausch`: dort ist § 40c zu speziell, hier ist er # genau richtig — und § 40b zu speziell. Beide Anker können zugleich # greifen; der § 40c-Fehlgriff hängt an `nicht_bei=_BIOLOGISCH` und # schweigt dann, der § 40b-Fehlgriff hier an den Signalen der # parenteralen Zubereitung. # # Der Beleg ist eine gemessene falsche Antwort vom 21.08.2026: auf eine # Frage, die die Abgabekonstellation ausdrücklich als unveränderte # Fertigspritze beschrieb, führte der Normabschnitt „§ 40b Abs. 1, # Abs. 3" an **erster** Stelle und zitierte im Wortlaut den Satz, der # die Pflicht auf parenterale Zubereitungen beschränkt. In zwei # weiteren Läufen schwankte die Antwort — mal mit Anlage VIIa, mal # ohne, mal mit Abgrenzung, mal ohne. Genau diese Schwankung nimmt ein # Anker heraus. key="biosimilar_austausch", thema="Austausch eines biologischen Referenzarzneimittels durch ein Biosimilar", signale=( ( ("biosimilar", "biotechnolog", "biologische", "biologisches", "referenzarzneimittel"), ( "austausch", "austauschbar", "ersetz", "substitu", "abgeben", "abgabe", "aut idem", "aut-idem", ), ), ), kernaussage=( "Vor dem Austausch sind zwei Ebenen zu prüfen. Allgemein gelten die " "Voraussetzungen des § 129 Abs. 1 Satz 2 SGB V und des § 9 Abs. 3 " "Rahmenvertrag: gleicher Wirkstoff, identische Wirkstärke, identische " "Packungsgröße, Zulassung für ein gleiches Anwendungsgebiet und gleiche oder " "austauschbare Darreichungsform. Besonders gilt für biotechnologisch " "hergestellte biologische Fertigarzneimittel § 40c AM-RL: das abzugebende " "Arzneimittel muss nach Anlage VIIa dem verordneten Referenzarzneimittel " "zugeordnet sein, bei gleicher gemeldeter Darreichungsform muss zusätzlich " "das in der Fachinformation angegebene Behältnis übereinstimmen, und die " "Ersetzung darf vom verordnenden Arzt nicht ausgeschlossen sein. Für ein " "unverändert abgegebenes Fertigarzneimittel gilt § 40c, nicht § 40b — der " "betrifft die parenterale Zubereitung zur unmittelbaren ärztlichen Anwendung." ), normen=( # Welche der beiden Parallelvorschriften trägt, entscheidet die # Abgabekonstellation — deshalb zwei Einträge mit gegenläufiger # Bedingung statt eines festen `tragend`. Norm( zitat="§ 40c AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 40c", chunks=2, warum=( "Die Spezialnorm für den Austausch biotechnologisch hergestellter " "biologischer **Fertigarzneimittel** durch die Apotheke. Absatz 1 " "nennt die Abgabekriterien samt Behältnisgleichheit, Absatz 3 die " "Zuordnung von Referenzarzneimittel und Biosimilar über Anlage VIIa." ), wenn=Bedingung(nicht_bei=_PARENTERAL), ), Norm( zitat="§ 129 Abs. 1 SGB V", corpus_id="sgb5", container_id="Viertes Kapitel", section_id="§ 129", absatz="1", chunks=1, warum=( "Die allgemeinen Voraussetzungen jeder Ersetzung: Satz 2 verlangt " "Identität in Wirkstärke und Packungsgröße, eine gleiche oder " "austauschbare Darreichungsform und die Zulassung für ein gleiches " "Anwendungsgebiet." ), ), Norm( zitat="§ 9 Abs. 3 Rahmenvertrag", corpus_id="rv129", container_id="Vertrag", section_id="§ 9", absatz="3", chunks=1, warum=( "Der Auswahlbereich, in dem die allgemeinen Kriterien für die Abgabe " "ausgeführt sind. Buchstabe a stellt Biologika und Bezug nehmend " "zugelassene Arzneimittel ausdrücklich wirkstoffgleich und verweist " "für die Auswahl auf § 40c AM-RL." ), ), Norm( zitat="AM-RL Anlage VIIa", warum=( "Die Zuordnung von Referenzarzneimittel und Biosimilar — hier " "entscheidet sich, ob überhaupt ausgetauscht werden darf. Nicht " "indiziert, aber als deterministischer Lookup vorhanden " "(amrl_biosimilars)." ), ), Norm( zitat="§ 40b AM-RL", corpus_id="amrl", container_id="Richtlinie", section_id="§ 40b", chunks=1, warum=( "Die Parallelvorschrift für parenterale Zubereitungen aus " "Fertigarzneimitteln zur unmittelbaren ärztlichen Anwendung — in " "einem solchen Fall die tragende Norm, sonst gar keine." ), wenn=Bedingung(nur_bei=_PARENTERAL), ), ), fehlgriffe=( Fehlgriff( # Wie bei § 40c in der allgemeinen Darreichungsformfrage: die # Vorschrift stört auch neben der richtigen, weil sie den Fall # auf eine Konstellation verengt, die er nicht aufwirft. muster=r"§\s*40b\b", warum=( "§ 40b AM-RL verpflichtet zur Ersetzung nur, „wenn es sich um eine " "parenterale Zubereitung aus Fertigarzneimitteln zur unmittelbaren " "ärztlichen Anwendung handelt“. Wird das Fertigarzneimittel " "unverändert an die versicherte Person abgegeben, ist § 40c AM-RL " "die maßgebliche Vorschrift; § 40b bleibt als Abgrenzung richtig, " "als tragende Norm dieses Falls nicht." ), nur_ohne_tragende=False, wenn=Bedingung(nicht_bei=_PARENTERAL), ), ), hinweise=( Hinweis( text=( "Die Abgrenzung entscheidet die Abgabekonstellation, nicht das " "Arzneimittel: § 40c AM-RL gilt für das unverändert abgegebene " "Fertigarzneimittel, § 40b AM-RL für das Fertigarzneimittel, das zu " "einer parenteralen Zubereitung zur unmittelbaren ärztlichen " "Anwendung verarbeitet wird." ), ), Hinweis( text=( "Ohne das konkrete Referenzarzneimittel lässt sich die Zuordnung in " "Anlage VIIa nicht feststellen — und ohne sie ist die Frage nicht " "abschließend zu beantworten." ), ), ), ), Anker( # Der zweite Anker aus einer Messung statt aus einer falschen Antwort, # und die Lücke sitzt hier zwischen zwei Kapiteln: die Frage nach der # Aktualisierung des Medikationsplans erreichte am 21.08.2026 § 31a # SGB V im Dritten Kapitel auf Rang 1 — und § 334 SGB V im Elften # Kapitel überhaupt nicht (Kettenabdeckung 1 von 2). An seiner Stelle # kam § 342 Abs. 2a, thematisch benachbart und nicht die Norm, auf die # § 31a verweist. # # Anders als bei den übrigen Ankern nennt der gefundene Text die # fehlende Norm ausdrücklich („elektronischen Medikationsplan nach # § 334 Absatz 1 Satz 2 Nummer 4"). Die Verweisauflösung hätte sie also # holen können — sie ist seit dem 21.08.2026 abgeschaltet, weil sie im # festen Budget mehr verdrängt als sie beibringt. Ein Anker holt genau # diese eine Norm, statt drei beliebige. # # Kein `Fehlgriff`: eine Kettenlücke ist kein Beleg für eine falsch # genannte Norm. key="medikationsplan_aktualisierung", thema="Aktualisierung des Medikationsplans durch die Apotheke", signale=( ( ("medikationsplan",), ( "aktualisier", "fortschreib", "ergänz", "ergaenz", "ändern", "aendern", "pflegen", "eintrag", "elektronisch", "muss", "darf", ), ), ), kernaussage=( "Die Apotheke hat den Medikationsplan nach § 31a Abs. 3 Satz 2 SGB V bei der " "Abgabe eines Arzneimittels auf Wunsch des Versicherten zu aktualisieren, " "soweit die Abgabe eine Aktualisierung erforderlich macht. Für den " "elektronischen Medikationsplan nach § 334 Abs. 1 Satz 2 Nr. 4 SGB V besteht " "der Anspruch nach § 31a Abs. 3 Satz 3 SGB V, wenn der Versicherte der " "abgebenden Apotheke den Zugriff erlaubt; gespeichert wird die Aktualisierung " "nach Satz 5 nur, sofern der Versicherte das wünscht. Seit die elektronische " "Patientenakte nach § 342 Abs. 1 Satz 2 SGB V zur Verfügung steht, ist die " "Aktualisierung und Speicherung nach § 31a Abs. 3a SGB V eine Pflicht, soweit " "der Versicherte dem Zugriff nicht widersprochen hat." ), normen=( Norm( zitat="§ 31a SGB V", corpus_id="sgb5", container_id="Drittes Kapitel", section_id="§ 31a", chunks=2, warum=( "Absatz 3 Satz 2 begründet die Aktualisierung durch die Apotheke auf " "Wunsch des Versicherten, Satz 3 den darüber hinausgehenden Anspruch " "beim elektronischen Medikationsplan, Satz 5 die Speicherung. Absatz 3a " "macht aus dem Anspruch eine Pflicht, sobald die elektronische " "Patientenakte zur Verfügung steht. Ohne Absatz gefasst, weil die " "Antwort auf Absatz 1, 3 und 3a zugleich beruht." ), ), Norm( zitat="§ 334 Abs. 1 SGB V", corpus_id="sgb5", container_id="Elftes Kapitel", section_id="§ 334", absatz="1", chunks=1, warum=( "Satz 2 Nr. 4 ist der elektronische Medikationsplan — die Norm, auf die " "§ 31a für die zweite Hälfte der Frage verweist. Sie steht im Elften " "Kapitel und wird von einer Frage nach der Apotheke nicht gefunden." ), ), ), hinweise=( Hinweis( # Ohne Fallbezug ist die Anspruchsvoraussetzung ein Sonderfall # und gehört nicht in jede Antwort. text=( "Der Anspruch auf den Medikationsplan setzt nach § 31a Abs. 1 Satz 1 " "SGB V voraus, dass der Versicherte gleichzeitig mindestens drei " "verordnete Arzneimittel anwendet; erstellt wird er vom Vertragsarzt, " "die Apotheke aktualisiert ihn." ), ), ), ), ) # --------------------------------------------------------------------------- # Erkennung in der Frage # --------------------------------------------------------------------------- def _norm(text: str) -> str: return " ".join(str(text or "").lower().split()) def _haystack(question: str) -> str: """Die Frage, wie das Register sie liest — samt der Marken aus dem Alter. Getrennt von `_norm`, weil `_trifft` auch die **Signale** normalisiert: ein Signal hat kein Alter, und eine Anreicherung dort wäre nur Rauschen. """ text = _norm(question) if not text: return text text = _VERNEINT.sub(" ", text) return f"{text}{_alterssignale(text)}" # „otc“ als blanker Substring träfe irgendwann in einem längeren Wort. Kurze # Signale bekommen deshalb eine Wortgrenze; lange bleiben Substrings, weil # „zuweis“ gerade „zuweisen/Zuweisung/zugewiesen“ mitnehmen soll. _KURZ = 4 def _trifft(signal: str, haystack: str) -> bool: signal = _norm(signal) if not signal: return False if len(signal) <= _KURZ and signal.isalnum(): return re.search(rf"(? List[Anker]: """Alle Anker, bei denen mindestens ein Signalsatz vollständig trifft.""" haystack = _haystack(question) if not haystack: return [] return [anker for anker in ANKER if anker.trifft(haystack)] def pflichtabruf(question: str, *, max_chunks: int = 2) -> List[Dict[str, Any]]: """Adressen der tragenden Normen, die unabhängig vom Ranking zu holen sind. Bewusst schlichte dicts: der `FederatedRetriever` soll dieses Modul nicht importieren müssen. Er bekommt eine Liste von Zielen und weiß nicht, dass dahinter eine Rechtsbehauptung steht. """ haystack = _haystack(question) ziele: List[Dict[str, Any]] = [] gesehen = set() for anker in passende_anker(question): for norm in anker.tragende(haystack): if not norm.abrufbar: continue schluessel = (norm.corpus_id, norm.container_id, norm.section_id, norm.absatz) if schluessel in gesehen: continue gesehen.add(schluessel) ziele.append(norm.als_ziel(max_chunks=max_chunks)) return ziele def pflicht_korpora(question: str) -> List[str]: """Korpora, die eine Frage erreichen muss, auch ohne im Text genannt zu sein. Das schließt die Lücke, die „Sonderkennzeichen“ aufgedeckt hat: das Wort nennt kein Korpus, die Regelung steht aber in der Abrechnungsvereinbarung — und deren 106 Chunks verlieren im Ähnlichkeitswettbewerb gegen 6.670 des SGB V. Ein zweites Alias-Register braucht es dafür nicht: die Anker nennen ihr Korpus bereits. """ out: List[str] = [] for ziel in pflichtabruf(question): if ziel["corpus_id"] not in out: out.append(ziel["corpus_id"]) return out def anlagen_geprueft(question: str) -> bool: """Nennt ein Anker eine AM-RL-Anlage als Fundstelle? Für den Reichweiten-Hinweis: er meldete die AM-RL-Anlagen als ungeprüft, während die Antwort sich zwei Absätze darüber auf Anlage VII Teil A berief. """ haystack = _haystack(question) return any( "anlage" in norm.zitat.lower() and "am-rl" in norm.zitat.lower() for anker in passende_anker(question) for norm in anker.geltende_normen(haystack) ) # --------------------------------------------------------------------------- # Abgleich mit der Antwort # --------------------------------------------------------------------------- @dataclass class Befund: status: str anker: List[Anker] = field(default_factory=list) fehlend: List[Norm] = field(default_factory=list) genannt: List[Norm] = field(default_factory=list) erwartet: List[Norm] = field(default_factory=list) fehlgriffe: List[Fehlgriff] = field(default_factory=list) hinweise: List[str] = field(default_factory=list) kernaussage: str = "" grund: str = "" @property def ist_belastbar(self) -> bool: return self.status in {"abweichung", "unvollstaendig", "bestaetigt"} @property def korrigiert(self) -> bool: """Der Fall, in dem die Hauptantwort neu gesetzt wird.""" return self.status == "abweichung" def to_dict(self) -> Dict[str, Any]: return { "status": self.status, "anker": [a.key for a in self.anker], "erwartete_normen": [n.zitat for n in self.erwartet], "genannt": [n.zitat for n in self.genannt], "fehlend": [n.zitat for n in self.fehlend], "fehlgriffe": [f.muster for f in self.fehlgriffe], "grund": self.grund, } # Der Abschnitt, in dem die Antwort ihre maßgebliche Norm nennt. Beide Schreib- # weisen kommen vor; `answer_schema.SECTION_HEADINGS` kennt sie ebenfalls, aber # dieses Modul kommt bewusst ohne Import aus — es soll auch auf einem rohen # Antworttext arbeiten können. _NORM_UEBERSCHRIFT = "Maßgebliche Norm" _KURZ_UEBERSCHRIFT = "Kurzantwort" _UEBERSCHRIFT_RE = re.compile( r"^\s*#*\s*(Kurzantwort|Maßgebliche\s+Norm(?:\(en\))?|Wortlaut(?:\s*/\s*Kriterien)?|" r"Einordnung|Ausnahmen|Ergebnis|Heilungen|Retaxationsgrenzen)\s*:?\s*$", re.I, ) def _abschnitte(answer: str) -> List[Tuple[Optional[str], List[str]]]: """(Überschrift | None, Zeilen) in Reihenfolge. Der erste Block trägt None.""" blocks: List[Tuple[Optional[str], List[str]]] = [] heading: Optional[str] = None body: List[str] = [] for line in (answer or "").split("\n"): treffer = _UEBERSCHRIFT_RE.match(line) if treffer is None: body.append(line) continue blocks.append((heading, body)) heading, body = " ".join(treffer.group(1).split()), [] blocks.append((heading, body)) return blocks def _ist(heading: Optional[str], name: str) -> bool: return bool(heading) and str(heading).lower().startswith(name.lower()) def normabschnitt(answer: str) -> str: """Der Text unter „Maßgebliche Norm“, oder die ganze Antwort ohne den Abschnitt. Der Rückfall auf die ganze Antwort ist kein Notbehelf: nennt das Modell die Norm nur in der Kurzantwort, ist sie genannt. Geprüft wird, ob die tragende Norm überhaupt vorkommt — nicht, ob sie unter der richtigen Überschrift steht. """ for heading, body in _abschnitte(answer): if _ist(heading, _NORM_UEBERSCHRIFT): return "\n".join(body) return answer or "" def kurzabschnitt(answer: str) -> str: """Der Text unter „Kurzantwort“ — oder leer, wenn es den Abschnitt nicht gibt. Anders als `normabschnitt` fällt das **nicht** auf die ganze Antwort zurück: ein Fehlgriff `im_kurzabschnitt` sucht gezielt die Zusammenfassung, und ein Rückfall würde ihn auch im Wortlaut anschlagen lassen, der die Norm richtig zitiert. """ for heading, body in _abschnitte(answer): if _ist(heading, _KURZ_UEBERSCHRIFT): return "\n".join(body) return "" # „§ 34 Abs. 1 SGB V“ soll auch auf „§ 34 SGB V“ und „§ 34 Absatz 1“ passen: das # Modell schreibt die Norm selten genau so, wie das Register sie führt. Geprüft # wird deshalb nur der Paragraf — der Absatz ist eine Genauigkeit, die eine # Substring-Prüfung nicht belastbar feststellen kann. _PARAGRAF_RE = re.compile(r"§+\s*(\d+[a-z]?)") # Anlagen haben keine §-Nummer und werden über ihren Namen erkannt. _ANLAGE_RE = re.compile(r"anlage\s+([ivx]+[a-z]?|\d+[a-z]?)(?:\s+teil\s+([ab]))?", re.I) def _paragrafen(text: str) -> set: return {m.group(1).lower() for m in _PARAGRAF_RE.finditer(text or "")} def _anlagen(text: str) -> set: return { (m.group(1).lower(), (m.group(2) or "").lower()) for m in _ANLAGE_RE.finditer(text or "") } def _adresse(zitat: str) -> Optional[tuple]: """Die vergleichbare Kennung eines Zitats: ('§', '34') oder ('anlage', 'vii', 'a').""" anlage = _ANLAGE_RE.search(zitat or "") if anlage and "§" not in (zitat or ""): return ("anlage", anlage.group(1).lower(), (anlage.group(2) or "").lower()) paragraf = _PARAGRAF_RE.search(zitat or "") return ("§", paragraf.group(1).lower()) if paragraf else None def _genannt_in(zitat: str, text: str) -> bool: adresse = _adresse(zitat) if adresse is None: return False if adresse[0] == "anlage": return (adresse[1], adresse[2]) in _anlagen(text) return adresse[1] in _paragrafen(text) def pruefe(question: str = "", answer: str = "") -> Befund: """Nennt die Antwort die Norm, die das Register für maßgeblich hält?""" anker = passende_anker(question) if not anker: return Befund(status="nicht_pruefbar", grund="kein_anker") haystack = _haystack(question) tragende = [n for a in anker for n in a.tragende(haystack)] hinweise = [h for a in anker for h in a.geltende_hinweise(haystack)] kernaussage = "\n\n".join( text for text in (a.geltende_kernaussage(haystack) for a in anker) if text ) if not tragende: return Befund(status="nicht_pruefbar", anker=anker, grund="anker_ohne_tragende_norm") if not (answer or "").strip(): return Befund(status="nicht_pruefbar", anker=anker, grund="keine_antwort") abschnitt = normabschnitt(answer) genannt = [n for n in tragende if _genannt_in(n.zitat, abschnitt) or _genannt_in(n.zitat, answer)] fehlend = [n for n in tragende if n not in genannt] gemeinsam = dict( anker=anker, erwartet=tragende, genannt=genannt, fehlend=fehlend, hinweise=hinweise, kernaussage=kernaussage, ) # Ein Fehlgriff der Sorte „gehört dazu, nur nicht als die entscheidende Norm" # zählt nur, wenn keine tragende Norm neben ihm steht. § 31 SGB V gehört in # eine OTC-Antwort — nur eben nicht als die Norm, die den Ausschluss trifft. kurz = kurzabschnitt(answer) tragend_im_abschnitt = any(_genannt_in(n.zitat, abschnitt) for n in tragende) fehlgriffe = [ f for a in anker for f in a.fehlgriffe if f.wenn.gilt(haystack) and re.search(f.muster, kurz if f.im_kurzabschnitt else abschnitt) and not (f.nur_ohne_tragende and tragend_im_abschnitt) ] if fehlgriffe or not genannt: return Befund( status="abweichung", grund="falsche_norm" if fehlgriffe else "tragende_norm_fehlt", fehlgriffe=fehlgriffe, **gemeinsam, ) if fehlend: return Befund(status="unvollstaendig", grund="normenkette_unvollstaendig", **gemeinsam) return Befund(status="bestaetigt", **gemeinsam) # --------------------------------------------------------------------------- # Darstellung # --------------------------------------------------------------------------- _EINLEITUNG = "Maßgebliche Norm — Abgleich mit dem kuratierten Normregister" # Die Richtigstellung bekommt eine eigene Überschrift, weil sie an einer anderen # Stelle steht als der Nachtrag: der Nachtrag *ergänzt* die maßgebliche Norm, die # Richtigstellung *erklärt* die oben bereits gesetzte. Sie reiht sich damit in # die übrigen Schlusshinweise ein („Hinweis zur Reichweite", „Hinweis zum # Stand") statt eine zweite Überschrift „Maßgebliche Norm" zu eröffnen. _KORREKTUR_EINLEITUNG = "Hinweis zum Normabgleich" # Nur noch, wenn es stimmt: siehe `_ohne_verworfene_bloecke`. Vorher stand der # Satz unter jeder Richtigstellung — auch unter denen, bei denen gar keine # Vorschrift verworfen worden war (`tragende_norm_fehlt`), und über einem Text, # den niemand mehr auf die verworfene Norm hin gelesen hatte. _RESTVERWEIS = ( "Die übrigen Abschnitte stammen unverändert vom Modell und nehmen diese Vorschrift " "stellenweise noch in Bezug." ) # Ein Quellenmarker, wie ihn die Antwort führt. Zu diesem Zeitpunkt sind es # bereits die Anzeigenummern (`app._renumber_sources_for_display` läuft im # Compose-Schritt davor), sie werden hier nur verschoben, nie neu vergeben. _MARKER_RE = re.compile(r"\[Quellen?\s+\d+(?:\s*,\s*\d+)*\]") def _normzeilen(normen: List[Norm], *, einzug: str = " ", marker: Optional[Dict[tuple, str]] = None) -> List[str]: zeilen: List[str] = [] for norm in normen: adresse = _adresse(norm.zitat) beleg = (marker or {}).get(adresse) if adresse else None zeilen.append(f"{einzug}{norm.zitat} {beleg}" if beleg else f"{einzug}{norm.zitat}") if norm.warum: zeilen.append(f"{einzug} {norm.warum}") return zeilen def _marker_je_norm(abschnitt: str) -> Dict[tuple, str]: """Welchen Quellenmarker das Modell neben welche Vorschrift gesetzt hat. Die Richtigstellung ersetzt den Normabschnitt durch die Zitate des Registers — und das Register führt keine Belege. Im Ausdruck vom 20.08.2026 stand die richtiggestellte maßgebliche Norm deshalb ganz ohne Quellenverweis, während die verworfene Vorschrift ihren Marker behielt und die Quellenliste anführte. Wo das Modell dieselbe Vorschrift belegt hat, wandert der Marker mit. Übernommen wird nur aus Segmenten mit genau einer Fundstelle und genau einem Marker. „§ 300 Abs. 3 SGB V in Verbindung mit § 2 der Abrechnungs- vereinbarung — [Quelle 1]" nennt zwei Vorschriften und einen Beleg; welcher zu welcher gehört, steht nicht im Text, und ein geratener Beleg wäre schlimmer als keiner. """ out: Dict[tuple, str] = {} for segment in re.split(r"[;\n]", abschnitt or ""): marker = _MARKER_RE.findall(segment) if len(marker) != 1: continue ohne_marker = _MARKER_RE.sub(" ", segment) adressen = {("§", p) for p in _paragrafen(ohne_marker)} adressen |= {("anlage", a, b) for a, b in _anlagen(ohne_marker)} if len(adressen) != 1: continue out.setdefault(next(iter(adressen)), marker[0]) return out def _ohne_verworfene_bloecke(text: str, fehlgriffe: List[Fehlgriff]) -> str: """Die Zitatblöcke der verworfenen Vorschrift aus dem Modelltext nehmen. Der Anlass steht im Ausdruck vom 20.08.2026: die Richtigstellung setzte Kurzantwort und maßgebliche Norm neu, ließ darunter aber den vollständigen Wortlaut des verworfenen § 40c AM-RL stehen — mitsamt seinem Quellenmarker — und kündigte den Widerspruch im Text auch noch an. Wer die Antwort las, musste selbst entscheiden, welche der beiden Vorschriften gilt. Entfernt wird ein Absatz nur, wenn seine **erste** Zeile die verworfene Vorschrift nennt: dann ist der Absatz ein Zitat von ihr. Steht sie mitten in einem Absatz, gehört sie zum Wortlaut einer anderen Norm — § 9 Abs. 3 Buchst. d Rahmenvertrag verweist in seinem letzten Spiegelstrich selbst auf § 40c —, und dieser Wortlaut ist richtig zitiert und bleibt stehen. Eine Abschnittsüberschrift schützt ihren Absatz: Text unter einer eigenen Überschrift zu löschen hieße, den Abschnitt zu entscheiden, und dafür ist das Register nicht zuständig. `im_kurzabschnitt`-Fehlgriffe bleiben außen vor: sie beanstanden keine zitierte Vorschrift, sondern die Zusammenfassung, und ihr Muster ist ein Satzmuster, kein Fundstellenmuster — es dürfte hier keinen Absatz löschen. """ fehlgriffe = [f for f in fehlgriffe if not f.im_kurzabschnitt] if not fehlgriffe or not (text or "").strip(): return text muster = [re.compile(f.muster) for f in fehlgriffe] behalten: List[str] = [] for absatz in re.split(r"\n[ \t]*\n", text): erste = next((zeile for zeile in absatz.split("\n") if zeile.strip()), "") if not erste or _UEBERSCHRIFT_RE.match(erste): behalten.append(absatz) continue if any(m.search(erste) for m in muster): continue behalten.append(absatz) return "\n\n".join(a for a in behalten if a.strip()) def _nimmt_noch_bezug(text: str, fehlgriffe: List[Fehlgriff]) -> bool: """Argumentiert der verbliebene Modelltext noch aus der verworfenen Vorschrift? Gefragt ist nur nach dem Text, der **stehen bleibt**. „Kurzantwort" und „Maßgebliche Norm" werden ohnehin neu gesetzt; was dort noch steht, ist gleich weg und kein Restbezug. Der Abschnitt „Wortlaut / Kriterien" zählt ebenfalls nicht mit. Er gibt Normtext wieder, und dass eine Vorschrift auf eine andere verweist, ist dann keine Aussage des Modells, sondern die des Gesetzgebers: § 9 Abs. 3 Buchst. d Rahmenvertrag nennt § 40c in seinem letzten Spiegelstrich selbst. Ein Hinweis auf einen „Restbezug" wäre dort eine Warnung vor dem richtigen Zitat. """ fehlgriffe = [f for f in fehlgriffe if not f.im_kurzabschnitt] if not fehlgriffe: return False ersetzt = (_KURZ_UEBERSCHRIFT, _NORM_UEBERSCHRIFT, "Wortlaut") rest = "\n".join( "\n".join(body) for heading, body in _abschnitte(text) if not any(_ist(heading, name) for name in ersetzt) ) return any(re.search(f.muster, rest) for f in fehlgriffe) def befund_block(befund: Befund, *, restverweis: bool = False) -> str: """Der Textblock, den die Antwort bekommt. Leer, wenn nichts zu sagen ist. **Ein `Hinweis` hängt nicht daran, dass die Antwort falsch war.** Bis zum 21.08.2026 gab dieser Block nur bei `abweichung` und `unvollstaendig` etwas zurück; eine im Übrigen richtige Antwort bekam die Hinweise nicht. Das widerspricht dem, wofür sie da sind — „was eine richtige Antwort nicht verschweigen darf". Gemessen an zwei Läufen derselben Frage: im ersten fehlte ein Kettenglied und der Hinweis auf die Altersausnahme kam mit, im zweiten war die Kette vollständig und derselbe Hinweis blieb weg. """ if befund.status == "bestaetigt": hinweise = list(befund.hinweise) if not hinweise: return "" themen = ", ".join(a.thema for a in befund.anker) return "\n".join([f"{_KORREKTUR_EINLEITUNG} ({themen}):", *hinweise]) if befund.status not in {"abweichung", "unvollstaendig"}: return "" themen = ", ".join(a.thema for a in befund.anker) zeilen: List[str] = [] if befund.status == "abweichung": zeilen.append(f"{_KORREKTUR_EINLEITUNG} ({themen}):") if befund.fehlgriffe: if any(not f.im_kurzabschnitt for f in befund.fehlgriffe): zeilen.append( "Kurzantwort und maßgebliche Norm oben stammen aus dem kuratierten " "Normregister; die zunächst herangezogene Vorschrift entscheidet diese " "Frage nicht." ) else: zeilen.append( "Kurzantwort und maßgebliche Norm oben stammen aus dem kuratierten " "Normregister; die ursprüngliche Fassung hat die maßgeblichen " "Regelungen unzutreffend zusammengefasst." ) for fehlgriff in befund.fehlgriffe: zeilen.append(f" {fehlgriff.warum}") else: zeilen.append( "Kurzantwort und maßgebliche Norm oben stammen aus dem kuratierten " "Normregister; die ursprüngliche Fassung nannte die tragende Vorschrift " "nicht." ) if restverweis: zeilen.append(_RESTVERWEIS) else: zeilen.append(f"{_EINLEITUNG} ({themen}):") zeilen.append("Zur Normenkette gehört außerdem:") zeilen.extend(_normzeilen(befund.fehlend)) for hinweis in befund.hinweise: zeilen.append(hinweis) return "\n".join(zeilen) def _normabschnitt_text(befund: Befund, marker: Optional[Dict[tuple, str]] = None) -> str: return "\n".join(_normzeilen(befund.erwartet, einzug="", marker=marker)) def in_antwort_einsetzen(answer: str, befund: Befund) -> str: """Befund und Antwort zusammenführen. Zwei Wege, und der Unterschied ist der Punkt: * `unvollstaendig` — die Antwort ist im Übrigen richtig, ihr fehlt ein Glied der Kette. Der Nachtrag hängt unten an und lässt sie stehen. * `abweichung` — die Antwort steht auf der falschen Vorschrift. Dann genügt ein Nachtrag nicht: er stünde neben der falschen Antwort, und nichts im Text sagte, welcher gilt. „Kurzantwort" und „Maßgebliche Norm" werden deshalb neu gesetzt. **Die Antwort führt in beiden Fällen, nicht der Werkstattbericht.** Bis zum 20.08.2026 stand der Richtigstellungsblock *über* der Antwort: der Leser bekam zuerst zu lesen, dass das Modell danebengelegen habe, und erst danach, was gilt. Der Block wandert deshalb ans Ende, zu den übrigen Hinweisen — die Kurzantwort steht wieder oben, wo sie hingehört. Die übrigen Abschnitte bleiben Modelltext. Sie umzuschreiben hieße, den Fall zu entscheiden, und das ist nicht Aufgabe eines Normregisters. Entfernt werden nur die Absätze, die die verworfene Vorschrift zitieren — siehe `_ohne_verworfene_bloecke`. """ if befund.status not in {"abweichung", "unvollstaendig", "bestaetigt"}: return answer text = (answer or "").strip() if befund.status != "abweichung": # `bestaetigt` läuft hier mit: die Antwort bleibt unangetastet, ihre # Hinweise hängen unten an. Siehe `befund_block`. block = befund_block(befund) if not block: return answer return f"{text}\n\n{block}" if text else block # Erst den Modelltext von den Zitaten der verworfenen Vorschrift befreien, # dann fragen, ob sie darin überhaupt noch vorkommt: der Hinweis auf den # Restbezug soll nur stehen, wenn er zutrifft. marker = _marker_je_norm(normabschnitt(text)) text = _ohne_verworfene_bloecke(text, befund.fehlgriffe) block = befund_block(befund, restverweis=_nimmt_noch_bezug(text, befund.fehlgriffe)) blocks = _abschnitte(text) hat_schema = any( _ist(h, _KURZ_UEBERSCHRIFT) or _ist(h, _NORM_UEBERSCHRIFT) for h, _ in blocks ) if not hat_schema: # Kein erkennbares Schema — dann gibt es keinen Abschnitt zum Ersetzen. # Die Richtigstellung wird trotzdem gesetzt, sonst bestünde sie aus einer # Beanstandung ohne Auskunft: Kurzantwort und maßgebliche Norm gehen dem # Modelltext voran, der Hinweis folgt am Ende. # # `text` kann hier leer sein — dann bestand die ganze Antwort aus einem # Zitat der verworfenen Vorschrift und ist restlos entfallen. Auch dieser # Fall braucht eine Auskunft, keinen alleinstehenden Hinweis. kopf: List[str] = [] if befund.kernaussage: kopf.append(f"{_KURZ_UEBERSCHRIFT}:\n{befund.kernaussage}") kopf.append(f"{_NORM_UEBERSCHRIFT}:\n{_normabschnitt_text(befund, marker)}") return "\n\n".join([t for t in (*kopf, text, block) if t.strip()]) teile: List[str] = [] norm_gesetzt = False for heading, body_lines in blocks: body = "\n".join(body_lines).strip() if heading is None: if body: teile.append(body) elif _ist(heading, _KURZ_UEBERSCHRIFT) and befund.kernaussage: teile.append(f"{heading}:\n{befund.kernaussage}") elif _ist(heading, _NORM_UEBERSCHRIFT): teile.append(f"{heading}:\n{_normabschnitt_text(befund, marker)}") norm_gesetzt = True elif body: teile.append(f"{heading}:\n{body}") # Eine Überschrift ohne Text fällt weg. Sie kann hier neu entstehen: hat # ein Abschnitt nur die verworfene Vorschrift zitiert, ist er nach der # Bereinigung leer, und eine nackte Überschrift sähe nach verlorenem # Inhalt aus. if not norm_gesetzt: # Die Antwort hatte keinen Normabschnitt. Er wird hinter der Kurzantwort # eingesetzt, damit die Reihenfolge des Schemas erhalten bleibt. einfuegen = 1 if teile and teile[0].startswith(_KURZ_UEBERSCHRIFT) else 0 teile.insert(einfuegen, f"{_NORM_UEBERSCHRIFT}:\n{_normabschnitt_text(befund, marker)}") return "\n\n".join(t for t in teile if t.strip()) + f"\n\n{block}"