Loading Insight...
Loading Insight...
Loading Insight...
Insights
Insights

Large Language Models (LLMs) sind leistungsfähig, aber ihre Ausgaben können unvorhersehbar sein. Selbst kleine Inkonsistenzen oder mehrdeutige Werte können nachgelagerte Systeme stillschweigend beschädigen und versteckte Fehler erzeugen, die schwer zu erkennen sind.
Die Adaptive Anti‑Corruption Layer (AACL) adressiert diese Herausforderung mit einer zweischichtigen Grenze, die LLM‑Ausgaben normalisiert und typensichere Verarbeitung erzwingt. Durch strukturierte Korrektursignale ermöglicht die AACL Echtzeit‑Selbstkorrektur und erlaubt probabilistischen LLMs, zuverlässig mit deterministischer Geschäftslogik zu interagieren.
Gelöstes Problem: von LLM erzeugte Format‑Halluzinationen
Das Synpulse8‑Team hat das AACL‑Designmuster formalisiert, um probabilistische LLM‑Agenten über einen Selbstheilungsmechanismus mit deterministischen Systemen zu integrieren. Eine Version dieses Musters ist auch im README auf GitHub verfügbar.
Das AACL bietet eine Normalisierungsgrenze, die mehrdeutige LLM‑Ausgaben in strikt typisierte Eingaben umwandelt und strukturierte Korrektursignale zurückgibt, die es dem Modell ermöglichen, sich zur Laufzeit selbst zu korrigieren. Dies eliminiert stille Formatbeschädigungen und verbessert zuverlässiges agentisches Verhalten, ohne das Modell neu trainieren zu müssen.
| Designmuster: Adaptive Anti‑Corruption Layer (AACL) |
| Kontext: Integration probabilistischer LLM‑Agenten mit deterministischen Systemen |
| Problem: LLMs erzeugen chaotische, nicht typensichere Ausgaben; direkte Integration verursacht stille Formatbeschädigungen |
| Lösung: Zweischichtige Architektur mit Normalisierungsgrenze, die strukturiertes Feedback bereitstellt |
| Ergebnis: Selbstkorrigierendes System, in dem strukturiertes Feedback Fehlerkorrektur zur Laufzeit ermöglicht |
Referenzimplementierung und adversarielle Tests sind auf GitHub verfügbar.
Der Selbstheilungsmechanismus erfordert eine agentische Loop‑Architektur, in der das LLM Feedback aus der Tool‑Ausführung erhalten und mit korrigierten Eingaben erneut ausführen kann. Konkret muss das System Folgendes unterstützen:
Framework‑Beispiel:
Wenn Ihr System keinen agentischen Loop besitzt (d. h. One‑Shot‑Tool‑Aufrufe ohne Retry), bietet das AACL‑Muster dennoch Mehrwert, indem es stille Formatbeschädigungen verhindert, aber Selbstheilung erfordert den Retry‑Mechanismus.
Warum das wichtig ist
LLMs sind semantische Sequenzmodelle. Sie sind keine typensicheren, schemastabilen und zuverlässigen Datenserialisierer. Daher müssen LLMs Werte liefern, während Code Struktur bereitstellen muss.
Die korrekte Architektur ist eine zweischichtige Grenze, die freie Modell‑Ausgaben von deterministischer Geschäftslogik trennt.
LLM (semantischer Planer) ↓ Interface Layer (Normalisierung + Validierung + strukturierte Fehler) ↓ Implementation Layer (strikte Typen, reine Logik)
An dieser Grenze wird das System selbstkorrigierend. Die Interface‑Grenze ist der einzige Ort, an dem Mehrdeutigkeit existieren darf. Sobald die Ausführung in die Implementierungsschicht übergeht, muss die Mehrdeutigkeit NULL sein.
Strukturierte Ausgaben gehören in Funktionsresultate, nicht in Token‑Streams. Verwenden Sie Function Calling, um strukturierte Daten vom Code zu erhalten, nicht um sie aus LLM‑generiertem Text zu parsen. Müssen Benutzer das JSON sehen. Platzieren Sie das Funktionsresultat in Ihrer Output‑Queue – genau dort, wo auch der Response‑Stream landet.
Häufige LLM‑Ausgabeprobleme (nicht vollständig)
LLMs tauschen frei untereinander aus: • "true", "True", "yes", "1", True • 5, "05", "five", "5" • "null", "none", None, "n/a", "" • "a.com b.com", "a.com,b.com", ["a.com", "b.com"] • Daten in jeder menschenlesbaren Variante
Diese direkt an eine API‑Schicht weiterzugeben, führt zu stiller Formatbeschädigung – der schlimmsten Art von Systemfehler, weil sie eine gewisse Wahrscheinlichkeit hat zu „funktionieren“, und dann bricht es plötzlich ohne erkennbaren Grund.
Architektur
Diese zweischichtige Grenze ist der Kern der AACL. Das LLM arbeitet in einem semantischen Raum; die Implementierungsschicht arbeitet in einem typisierten, deterministischen Raum. Die AACL ist die Grenze, die zwischen beiden durch Normalisierung + strukturierte Fehlersignale übersetzt.
1. Interface Layer (LLM‑seitig)
Funktion: Beliebige Eingaben in typisierte Eingaben umwandeln.
Anforderungen:
Diese Schicht muss total sein: Jede Eingabe wird entweder normalisiert oder schlägt mit einem für das LLM nutzbaren Korrektursignal fehl.
2. Implementation Layer (logikseitig)
Funktion: Geschäftsvorgänge mit strikter Typisierung ausführen.
Wenn falsche Werte diese Schicht erreichen, ist die Architektur falsch.
Minimales Beispiel (Python)
Das Muster verwendet eine Zwei‑Dateien‑Struktur. Die vollständige Referenzimplementierung ist auf GitHub verfügbar.
Interface Layer (LLM‑seitig)
from fastmcp import FastMCP
from .implementations.tavily_impl import tavily_search_impl
mcp = FastMCP("My MCP Server")
@mcp.tool()
def search_web(
query,
search_depth="basic",
max_results=5,
include_domains=None,
time_range=None
) -> dict:
"""
Search the web using Tavily's search API.
Args:
query: Search query (required)
search_depth: "basic" or "advanced" (Optional, defaults to "basic")
max_results: Number of results, 1-10 (Optional, defaults to 5)
include_domains: Domain filter (comma/space-separated or list) (Optional)
time_range: Time filter ("day", "week", "month", "year") (Optional)
Returns:
Dictionary containing search results
Raises:
ValueError: With structured correction signals for invalid inputs
"""
# Normalize ambiguous inputs to canonical forms
search_depth = _normalize_search_depth(search_depth)
include_domains = _normalize_optional_string(include_domains)
time_range = _normalize_optional_string(time_range)
# Validate with structured error messages
if time_range and time_range not in ("day", "week", "month", "year"):
raise ValueError(
"INVALID_TIME_RANGE: expected one of ['day', 'week', 'month', 'year']; "
f"received '{time_range}'. Retry with a valid value."
)
# Pass typed, normalized inputs to implementation
return tavily_search_impl(
query=query,
search_depth=search_depth,
max_results=int(max_results),
include_domains=include_domains,
time_range=time_range
)
def _normalize_optional_string(value):
"""Normalize null-like values to None."""
if value is None:
return None
if isinstance(value, str):
s = value.strip().lower()
if s in ("", "null", "none", "n/a", "na"):
return None
return value
def _normalize_search_depth(depth):
"""Normalize search depth to 'basic' or 'advanced'."""
if not depth:
return "basic"
d = str(depth).strip().lower()
if d in ("advanced", "deep", "thorough"):
return "advanced"
return "basic"Implementierungsschicht (Logik‑Ebene)
def tavily_search_impl(
query: str,
search_depth: str,
max_results: int,
include_domains: list[str] | None,
time_range: str | None
) -> dict:
"""
Pure implementation - expects strictly typed, normalized inputs.
No validation or normalization should happen here.
"""
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
params = {
"query": query,
"search_depth": search_depth,
"max_results": max_results
}
if include_domains:
params["include_domains"] = include_domains
if time_range:
params["time_range"] = time_range
return client.search(**params)Warum dies selbstheilend ist
Der Loop:
Dies erzeugt adaptive Konvergenz: Das System heilt sich zur Laufzeit selbst, indem es das LLM zu korrekten Eingaben ohne menschliche Aufsicht führt.
Warum man das LLM niemals JSON erzeugen lässt
Es geht nicht um Syntaxfehler. Es geht um Verantwortungsgrenzen.
JSON ist ein deterministisches Serialisierungsformat. LLMs sind probabilistische Sequenzmodelle.
Wenn das LLM dafür verantwortlich ist, formatiertes JSON zu erzeugen, garantieren Sie:
Dies sind keine Fehler. Es ist die statistische Natur der Token‑Generierung. Sobald das Modell Struktur definieren darf, wird die Struktur nicht‑deterministisch.
Anwendung derselben zweischichtigen Architektur
LLM → untypisierte Werte Interface Layer → Normalisierung + Schema‑Erzwingung Implementation Layer → konstruiert JSON deterministisch
Das Modell formatiert niemals JSON.
Müssen die strukturierten Daten in der Benutzeroberfläche sichtbar sein. Kein Problem – Ihre Funktion gibt sie zurück, und Ihre Anwendungsschicht zeigt sie an. Der Punkt ist: Das LLM erzeugt nicht die Struktur, Ihr Code tut es.
Beispiel
Die vollständige Referenzimplementierung ist auf GitHub verfügbar.
Interface‑Schicht (LLM‑seitig)
from fastmcp import FastMCP
from .implementations.json_writer_impl import create_structured_data_impl
mcp = FastMCP("My MCP Server")
@mcp.tool()
def create_json(x, y, flag) -> dict:
"""
Create and return structured JSON from LLM-provided values.
Args:
x: Integer value (accepts "5", "05", 5)
y: String value
flag: Boolean flag (accepts "true", "yes", "1", True, etc.)
Returns:
Dictionary with deterministic structure (ready for JSON serialization)
Raises:
ValueError: With structured correction signals for invalid inputs
"""
# Normalize integer with structured error
try:
x = int(x)
except (ValueError, TypeError):
raise ValueError(
f"TYPE_ERROR: field 'x' must be an integer; "
f"received {repr(x)} (type: {type(x).__name__}). "
"Retry with a valid integer value."
)
# Normalize string
y = str(y)
# Normalize boolean from various representations
if isinstance(flag, str):
flag = flag.strip().lower() in ("true", "1", "yes", "on")
else:
flag = bool(flag)
# Pass typed inputs to implementation
return create_structured_data_impl(x=x, y=y, flag=flag)Implementation Layer (Geschäftslogik | strikt, deterministisches JSON)
def create_structured_data_impl(x: int, y: str, flag: bool) -> dict:
"""
Construct JSON structure deterministically from typed values.
The LLM never generates JSON - it only provides values.
Code defines keys, order, and types.
"""
# Structure is defined by code, not LLM
return {
"x": x,
"y": y,
"flag": flag
}Warum das funktioniert
Verantwortlichkeit
| Verantwortlichkeit | LLM | Interface‑Schicht | Implementierungs‑Schicht |
|---|---|---|---|
| Intent interpretieren | Yes | No | No |
| Werte normalisieren | No | Yes | No |
| Schema erzwingen | No | Yes | No |
| Datenstrukturen konstruieren | No | No | Yes |
| Daten serialisieren | No | No | Yes |
Kernprinzip
LLMs planen. Code typisiert. Lassen Sie niemals das Modell Struktur definieren. Erzwingen Sie Struktur immer an der Grenze.
LLMs planen. Code typisiert. Lassen Sie niemals das Modell Struktur definieren. Erzwingen Sie Struktur immer an der Grenze.
Zusammenfassung
| Layer | Handles | Must Be | Failure Mode | Output |
|---|---|---|---|---|
| LLM | Semantics | Flexible | Format Hallucination | Unstructured Values |
| Interface Layer | Normalisation + Validation | Total / Deterministic | Structured Correction (Intentional Exception Raised) | Typed Inputs |
| Implementation Layer | Business Logic | Pure / Strict | Hard Failure (if reached incorrectly) | Stable Data / JSON / YAML |
Die Invariante: Wenn die Implementierungsschicht Müll sieht, ist die Interface‑Schicht fehlerhaft.
Dieses Muster ist allgemein und gilt für jede LLM‑Tooling‑Integration, einschliesslich MCP, ReAct, Function‑Calling‑APIs und agentische Planungssysteme. Diese Architektur ist kein Workaround für LLM‑Schwächen. Die AACL ist die korrekte Trennung von Verantwortlichkeiten für jedes System, in dem ein probabilistischer Sprachgenerator mit deterministischen Softwarekomponenten interagiert.
Verwandte Musters
| Muster | Relationship |
|---|---|
| DDD Anti‑Corruption Layer | Konzeptioneller Vorfahre — geht jedoch von einem deterministischen Upstream‑Domain aus |
| Adapter Muster | Behandelt Interface‑Mismatch, aber nicht semantische Mehrdeutigkeit |
| Retry with Backoff | Behandelt Fehler, aber nicht Interpretation |
| ReAct | Behandelt iterative Konvergenz, fokussiert jedoch auf LLM‑Output statt auf das bekannte gute System namens Code |
Für die Aufnahme in einen Muster‑Katalog.
Anwendbarkeit
Verwenden Sie das AACL‑Muster, wenn:
Nicht verwenden, wenn:
Konsequenzen
Vorteile:
Trade‑offs:
Bekannte Verwendungen
Aufgelöste Kräfte
Das Muster balanciert:
Die Auflösung: Die Interface‑Schicht ist total (behandelt alle Eingaben), während die Implementierung rein ist (setzt Korrektheit voraus).
Über dieses Muster
Autor: Morgan Lee Organisation: Synpulse8 Erstveröffentlichung: 12. November 2025


Large Language Models (LLMs) sind leistungsfähig, aber ihre Ausgaben können unvorhersehbar sein. Selbst kleine Inkonsistenzen oder mehrdeutige Werte können nachgelagerte Systeme stillschweigend beschädigen und versteckte Fehler erzeugen, die schwer zu erkennen sind.
Die Adaptive Anti‑Corruption Layer (AACL) adressiert diese Herausforderung mit einer zweischichtigen Grenze, die LLM‑Ausgaben normalisiert und typensichere Verarbeitung erzwingt. Durch strukturierte Korrektursignale ermöglicht die AACL Echtzeit‑Selbstkorrektur und erlaubt probabilistischen LLMs, zuverlässig mit deterministischer Geschäftslogik zu interagieren.
Gelöstes Problem: von LLM erzeugte Format‑Halluzinationen
Das Synpulse8‑Team hat das AACL‑Designmuster formalisiert, um probabilistische LLM‑Agenten über einen Selbstheilungsmechanismus mit deterministischen Systemen zu integrieren. Eine Version dieses Musters ist auch im README auf GitHub verfügbar.
Das AACL bietet eine Normalisierungsgrenze, die mehrdeutige LLM‑Ausgaben in strikt typisierte Eingaben umwandelt und strukturierte Korrektursignale zurückgibt, die es dem Modell ermöglichen, sich zur Laufzeit selbst zu korrigieren. Dies eliminiert stille Formatbeschädigungen und verbessert zuverlässiges agentisches Verhalten, ohne das Modell neu trainieren zu müssen.
| Designmuster: Adaptive Anti‑Corruption Layer (AACL) |
| Kontext: Integration probabilistischer LLM‑Agenten mit deterministischen Systemen |
| Problem: LLMs erzeugen chaotische, nicht typensichere Ausgaben; direkte Integration verursacht stille Formatbeschädigungen |
| Lösung: Zweischichtige Architektur mit Normalisierungsgrenze, die strukturiertes Feedback bereitstellt |
| Ergebnis: Selbstkorrigierendes System, in dem strukturiertes Feedback Fehlerkorrektur zur Laufzeit ermöglicht |
Referenzimplementierung und adversarielle Tests sind auf GitHub verfügbar.
Der Selbstheilungsmechanismus erfordert eine agentische Loop‑Architektur, in der das LLM Feedback aus der Tool‑Ausführung erhalten und mit korrigierten Eingaben erneut ausführen kann. Konkret muss das System Folgendes unterstützen:
Framework‑Beispiel:
Wenn Ihr System keinen agentischen Loop besitzt (d. h. One‑Shot‑Tool‑Aufrufe ohne Retry), bietet das AACL‑Muster dennoch Mehrwert, indem es stille Formatbeschädigungen verhindert, aber Selbstheilung erfordert den Retry‑Mechanismus.
Warum das wichtig ist
LLMs sind semantische Sequenzmodelle. Sie sind keine typensicheren, schemastabilen und zuverlässigen Datenserialisierer. Daher müssen LLMs Werte liefern, während Code Struktur bereitstellen muss.
Die korrekte Architektur ist eine zweischichtige Grenze, die freie Modell‑Ausgaben von deterministischer Geschäftslogik trennt.
LLM (semantischer Planer) ↓ Interface Layer (Normalisierung + Validierung + strukturierte Fehler) ↓ Implementation Layer (strikte Typen, reine Logik)
An dieser Grenze wird das System selbstkorrigierend. Die Interface‑Grenze ist der einzige Ort, an dem Mehrdeutigkeit existieren darf. Sobald die Ausführung in die Implementierungsschicht übergeht, muss die Mehrdeutigkeit NULL sein.
Strukturierte Ausgaben gehören in Funktionsresultate, nicht in Token‑Streams. Verwenden Sie Function Calling, um strukturierte Daten vom Code zu erhalten, nicht um sie aus LLM‑generiertem Text zu parsen. Müssen Benutzer das JSON sehen. Platzieren Sie das Funktionsresultat in Ihrer Output‑Queue – genau dort, wo auch der Response‑Stream landet.
Häufige LLM‑Ausgabeprobleme (nicht vollständig)
LLMs tauschen frei untereinander aus: • "true", "True", "yes", "1", True • 5, "05", "five", "5" • "null", "none", None, "n/a", "" • "a.com b.com", "a.com,b.com", ["a.com", "b.com"] • Daten in jeder menschenlesbaren Variante
Diese direkt an eine API‑Schicht weiterzugeben, führt zu stiller Formatbeschädigung – der schlimmsten Art von Systemfehler, weil sie eine gewisse Wahrscheinlichkeit hat zu „funktionieren“, und dann bricht es plötzlich ohne erkennbaren Grund.
Architektur
Diese zweischichtige Grenze ist der Kern der AACL. Das LLM arbeitet in einem semantischen Raum; die Implementierungsschicht arbeitet in einem typisierten, deterministischen Raum. Die AACL ist die Grenze, die zwischen beiden durch Normalisierung + strukturierte Fehlersignale übersetzt.
1. Interface Layer (LLM‑seitig)
Funktion: Beliebige Eingaben in typisierte Eingaben umwandeln.
Anforderungen:
Diese Schicht muss total sein: Jede Eingabe wird entweder normalisiert oder schlägt mit einem für das LLM nutzbaren Korrektursignal fehl.
2. Implementation Layer (logikseitig)
Funktion: Geschäftsvorgänge mit strikter Typisierung ausführen.
Wenn falsche Werte diese Schicht erreichen, ist die Architektur falsch.
Minimales Beispiel (Python)
Das Muster verwendet eine Zwei‑Dateien‑Struktur. Die vollständige Referenzimplementierung ist auf GitHub verfügbar.
Interface Layer (LLM‑seitig)
from fastmcp import FastMCP
from .implementations.tavily_impl import tavily_search_impl
mcp = FastMCP("My MCP Server")
@mcp.tool()
def search_web(
query,
search_depth="basic",
max_results=5,
include_domains=None,
time_range=None
) -> dict:
"""
Search the web using Tavily's search API.
Args:
query: Search query (required)
search_depth: "basic" or "advanced" (Optional, defaults to "basic")
max_results: Number of results, 1-10 (Optional, defaults to 5)
include_domains: Domain filter (comma/space-separated or list) (Optional)
time_range: Time filter ("day", "week", "month", "year") (Optional)
Returns:
Dictionary containing search results
Raises:
ValueError: With structured correction signals for invalid inputs
"""
# Normalize ambiguous inputs to canonical forms
search_depth = _normalize_search_depth(search_depth)
include_domains = _normalize_optional_string(include_domains)
time_range = _normalize_optional_string(time_range)
# Validate with structured error messages
if time_range and time_range not in ("day", "week", "month", "year"):
raise ValueError(
"INVALID_TIME_RANGE: expected one of ['day', 'week', 'month', 'year']; "
f"received '{time_range}'. Retry with a valid value."
)
# Pass typed, normalized inputs to implementation
return tavily_search_impl(
query=query,
search_depth=search_depth,
max_results=int(max_results),
include_domains=include_domains,
time_range=time_range
)
def _normalize_optional_string(value):
"""Normalize null-like values to None."""
if value is None:
return None
if isinstance(value, str):
s = value.strip().lower()
if s in ("", "null", "none", "n/a", "na"):
return None
return value
def _normalize_search_depth(depth):
"""Normalize search depth to 'basic' or 'advanced'."""
if not depth:
return "basic"
d = str(depth).strip().lower()
if d in ("advanced", "deep", "thorough"):
return "advanced"
return "basic"Implementierungsschicht (Logik‑Ebene)
def tavily_search_impl(
query: str,
search_depth: str,
max_results: int,
include_domains: list[str] | None,
time_range: str | None
) -> dict:
"""
Pure implementation - expects strictly typed, normalized inputs.
No validation or normalization should happen here.
"""
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
params = {
"query": query,
"search_depth": search_depth,
"max_results": max_results
}
if include_domains:
params["include_domains"] = include_domains
if time_range:
params["time_range"] = time_range
return client.search(**params)Warum dies selbstheilend ist
Der Loop:
Dies erzeugt adaptive Konvergenz: Das System heilt sich zur Laufzeit selbst, indem es das LLM zu korrekten Eingaben ohne menschliche Aufsicht führt.
Warum man das LLM niemals JSON erzeugen lässt
Es geht nicht um Syntaxfehler. Es geht um Verantwortungsgrenzen.
JSON ist ein deterministisches Serialisierungsformat. LLMs sind probabilistische Sequenzmodelle.
Wenn das LLM dafür verantwortlich ist, formatiertes JSON zu erzeugen, garantieren Sie:
Dies sind keine Fehler. Es ist die statistische Natur der Token‑Generierung. Sobald das Modell Struktur definieren darf, wird die Struktur nicht‑deterministisch.
Anwendung derselben zweischichtigen Architektur
LLM → untypisierte Werte Interface Layer → Normalisierung + Schema‑Erzwingung Implementation Layer → konstruiert JSON deterministisch
Das Modell formatiert niemals JSON.
Müssen die strukturierten Daten in der Benutzeroberfläche sichtbar sein. Kein Problem – Ihre Funktion gibt sie zurück, und Ihre Anwendungsschicht zeigt sie an. Der Punkt ist: Das LLM erzeugt nicht die Struktur, Ihr Code tut es.
Beispiel
Die vollständige Referenzimplementierung ist auf GitHub verfügbar.
Interface‑Schicht (LLM‑seitig)
from fastmcp import FastMCP
from .implementations.json_writer_impl import create_structured_data_impl
mcp = FastMCP("My MCP Server")
@mcp.tool()
def create_json(x, y, flag) -> dict:
"""
Create and return structured JSON from LLM-provided values.
Args:
x: Integer value (accepts "5", "05", 5)
y: String value
flag: Boolean flag (accepts "true", "yes", "1", True, etc.)
Returns:
Dictionary with deterministic structure (ready for JSON serialization)
Raises:
ValueError: With structured correction signals for invalid inputs
"""
# Normalize integer with structured error
try:
x = int(x)
except (ValueError, TypeError):
raise ValueError(
f"TYPE_ERROR: field 'x' must be an integer; "
f"received {repr(x)} (type: {type(x).__name__}). "
"Retry with a valid integer value."
)
# Normalize string
y = str(y)
# Normalize boolean from various representations
if isinstance(flag, str):
flag = flag.strip().lower() in ("true", "1", "yes", "on")
else:
flag = bool(flag)
# Pass typed inputs to implementation
return create_structured_data_impl(x=x, y=y, flag=flag)Implementation Layer (Geschäftslogik | strikt, deterministisches JSON)
def create_structured_data_impl(x: int, y: str, flag: bool) -> dict:
"""
Construct JSON structure deterministically from typed values.
The LLM never generates JSON - it only provides values.
Code defines keys, order, and types.
"""
# Structure is defined by code, not LLM
return {
"x": x,
"y": y,
"flag": flag
}Warum das funktioniert
Verantwortlichkeit
| Verantwortlichkeit | LLM | Interface‑Schicht | Implementierungs‑Schicht |
|---|---|---|---|
| Intent interpretieren | Yes | No | No |
| Werte normalisieren | No | Yes | No |
| Schema erzwingen | No | Yes | No |
| Datenstrukturen konstruieren | No | No | Yes |
| Daten serialisieren | No | No | Yes |
Kernprinzip
LLMs planen. Code typisiert. Lassen Sie niemals das Modell Struktur definieren. Erzwingen Sie Struktur immer an der Grenze.
LLMs planen. Code typisiert. Lassen Sie niemals das Modell Struktur definieren. Erzwingen Sie Struktur immer an der Grenze.
Zusammenfassung
| Layer | Handles | Must Be | Failure Mode | Output |
|---|---|---|---|---|
| LLM | Semantics | Flexible | Format Hallucination | Unstructured Values |
| Interface Layer | Normalisation + Validation | Total / Deterministic | Structured Correction (Intentional Exception Raised) | Typed Inputs |
| Implementation Layer | Business Logic | Pure / Strict | Hard Failure (if reached incorrectly) | Stable Data / JSON / YAML |
Die Invariante: Wenn die Implementierungsschicht Müll sieht, ist die Interface‑Schicht fehlerhaft.
Dieses Muster ist allgemein und gilt für jede LLM‑Tooling‑Integration, einschliesslich MCP, ReAct, Function‑Calling‑APIs und agentische Planungssysteme. Diese Architektur ist kein Workaround für LLM‑Schwächen. Die AACL ist die korrekte Trennung von Verantwortlichkeiten für jedes System, in dem ein probabilistischer Sprachgenerator mit deterministischen Softwarekomponenten interagiert.
Verwandte Musters
| Muster | Relationship |
|---|---|
| DDD Anti‑Corruption Layer | Konzeptioneller Vorfahre — geht jedoch von einem deterministischen Upstream‑Domain aus |
| Adapter Muster | Behandelt Interface‑Mismatch, aber nicht semantische Mehrdeutigkeit |
| Retry with Backoff | Behandelt Fehler, aber nicht Interpretation |
| ReAct | Behandelt iterative Konvergenz, fokussiert jedoch auf LLM‑Output statt auf das bekannte gute System namens Code |
Für die Aufnahme in einen Muster‑Katalog.
Anwendbarkeit
Verwenden Sie das AACL‑Muster, wenn:
Nicht verwenden, wenn:
Konsequenzen
Vorteile:
Trade‑offs:
Bekannte Verwendungen
Aufgelöste Kräfte
Das Muster balanciert:
Die Auflösung: Die Interface‑Schicht ist total (behandelt alle Eingaben), während die Implementierung rein ist (setzt Korrektheit voraus).
Über dieses Muster
Autor: Morgan Lee Organisation: Synpulse8 Erstveröffentlichung: 12. November 2025

Insights
Insights

Large Language Models (LLMs) sind leistungsfähig, aber ihre Ausgaben können unvorhersehbar sein. Selbst kleine Inkonsistenzen oder mehrdeutige Werte können nachgelagerte Systeme stillschweigend beschädigen und versteckte Fehler erzeugen, die schwer zu erkennen sind.
Die Adaptive Anti‑Corruption Layer (AACL) adressiert diese Herausforderung mit einer zweischichtigen Grenze, die LLM‑Ausgaben normalisiert und typensichere Verarbeitung erzwingt. Durch strukturierte Korrektursignale ermöglicht die AACL Echtzeit‑Selbstkorrektur und erlaubt probabilistischen LLMs, zuverlässig mit deterministischer Geschäftslogik zu interagieren.
Gelöstes Problem: von LLM erzeugte Format‑Halluzinationen
Das Synpulse8‑Team hat das AACL‑Designmuster formalisiert, um probabilistische LLM‑Agenten über einen Selbstheilungsmechanismus mit deterministischen Systemen zu integrieren. Eine Version dieses Musters ist auch im README auf GitHub verfügbar.
Das AACL bietet eine Normalisierungsgrenze, die mehrdeutige LLM‑Ausgaben in strikt typisierte Eingaben umwandelt und strukturierte Korrektursignale zurückgibt, die es dem Modell ermöglichen, sich zur Laufzeit selbst zu korrigieren. Dies eliminiert stille Formatbeschädigungen und verbessert zuverlässiges agentisches Verhalten, ohne das Modell neu trainieren zu müssen.
| Designmuster: Adaptive Anti‑Corruption Layer (AACL) |
| Kontext: Integration probabilistischer LLM‑Agenten mit deterministischen Systemen |
| Problem: LLMs erzeugen chaotische, nicht typensichere Ausgaben; direkte Integration verursacht stille Formatbeschädigungen |
| Lösung: Zweischichtige Architektur mit Normalisierungsgrenze, die strukturiertes Feedback bereitstellt |
| Ergebnis: Selbstkorrigierendes System, in dem strukturiertes Feedback Fehlerkorrektur zur Laufzeit ermöglicht |
Referenzimplementierung und adversarielle Tests sind auf GitHub verfügbar.
Der Selbstheilungsmechanismus erfordert eine agentische Loop‑Architektur, in der das LLM Feedback aus der Tool‑Ausführung erhalten und mit korrigierten Eingaben erneut ausführen kann. Konkret muss das System Folgendes unterstützen:
Framework‑Beispiel:
Wenn Ihr System keinen agentischen Loop besitzt (d. h. One‑Shot‑Tool‑Aufrufe ohne Retry), bietet das AACL‑Muster dennoch Mehrwert, indem es stille Formatbeschädigungen verhindert, aber Selbstheilung erfordert den Retry‑Mechanismus.
Warum das wichtig ist
LLMs sind semantische Sequenzmodelle. Sie sind keine typensicheren, schemastabilen und zuverlässigen Datenserialisierer. Daher müssen LLMs Werte liefern, während Code Struktur bereitstellen muss.
Die korrekte Architektur ist eine zweischichtige Grenze, die freie Modell‑Ausgaben von deterministischer Geschäftslogik trennt.
LLM (semantischer Planer) ↓ Interface Layer (Normalisierung + Validierung + strukturierte Fehler) ↓ Implementation Layer (strikte Typen, reine Logik)
An dieser Grenze wird das System selbstkorrigierend. Die Interface‑Grenze ist der einzige Ort, an dem Mehrdeutigkeit existieren darf. Sobald die Ausführung in die Implementierungsschicht übergeht, muss die Mehrdeutigkeit NULL sein.
Strukturierte Ausgaben gehören in Funktionsresultate, nicht in Token‑Streams. Verwenden Sie Function Calling, um strukturierte Daten vom Code zu erhalten, nicht um sie aus LLM‑generiertem Text zu parsen. Müssen Benutzer das JSON sehen. Platzieren Sie das Funktionsresultat in Ihrer Output‑Queue – genau dort, wo auch der Response‑Stream landet.
Häufige LLM‑Ausgabeprobleme (nicht vollständig)
LLMs tauschen frei untereinander aus: • "true", "True", "yes", "1", True • 5, "05", "five", "5" • "null", "none", None, "n/a", "" • "a.com b.com", "a.com,b.com", ["a.com", "b.com"] • Daten in jeder menschenlesbaren Variante
Diese direkt an eine API‑Schicht weiterzugeben, führt zu stiller Formatbeschädigung – der schlimmsten Art von Systemfehler, weil sie eine gewisse Wahrscheinlichkeit hat zu „funktionieren“, und dann bricht es plötzlich ohne erkennbaren Grund.
Architektur
Diese zweischichtige Grenze ist der Kern der AACL. Das LLM arbeitet in einem semantischen Raum; die Implementierungsschicht arbeitet in einem typisierten, deterministischen Raum. Die AACL ist die Grenze, die zwischen beiden durch Normalisierung + strukturierte Fehlersignale übersetzt.
1. Interface Layer (LLM‑seitig)
Funktion: Beliebige Eingaben in typisierte Eingaben umwandeln.
Anforderungen:
Diese Schicht muss total sein: Jede Eingabe wird entweder normalisiert oder schlägt mit einem für das LLM nutzbaren Korrektursignal fehl.
2. Implementation Layer (logikseitig)
Funktion: Geschäftsvorgänge mit strikter Typisierung ausführen.
Wenn falsche Werte diese Schicht erreichen, ist die Architektur falsch.
Minimales Beispiel (Python)
Das Muster verwendet eine Zwei‑Dateien‑Struktur. Die vollständige Referenzimplementierung ist auf GitHub verfügbar.
Interface Layer (LLM‑seitig)
from fastmcp import FastMCP
from .implementations.tavily_impl import tavily_search_impl
mcp = FastMCP("My MCP Server")
@mcp.tool()
def search_web(
query,
search_depth="basic",
max_results=5,
include_domains=None,
time_range=None
) -> dict:
"""
Search the web using Tavily's search API.
Args:
query: Search query (required)
search_depth: "basic" or "advanced" (Optional, defaults to "basic")
max_results: Number of results, 1-10 (Optional, defaults to 5)
include_domains: Domain filter (comma/space-separated or list) (Optional)
time_range: Time filter ("day", "week", "month", "year") (Optional)
Returns:
Dictionary containing search results
Raises:
ValueError: With structured correction signals for invalid inputs
"""
# Normalize ambiguous inputs to canonical forms
search_depth = _normalize_search_depth(search_depth)
include_domains = _normalize_optional_string(include_domains)
time_range = _normalize_optional_string(time_range)
# Validate with structured error messages
if time_range and time_range not in ("day", "week", "month", "year"):
raise ValueError(
"INVALID_TIME_RANGE: expected one of ['day', 'week', 'month', 'year']; "
f"received '{time_range}'. Retry with a valid value."
)
# Pass typed, normalized inputs to implementation
return tavily_search_impl(
query=query,
search_depth=search_depth,
max_results=int(max_results),
include_domains=include_domains,
time_range=time_range
)
def _normalize_optional_string(value):
"""Normalize null-like values to None."""
if value is None:
return None
if isinstance(value, str):
s = value.strip().lower()
if s in ("", "null", "none", "n/a", "na"):
return None
return value
def _normalize_search_depth(depth):
"""Normalize search depth to 'basic' or 'advanced'."""
if not depth:
return "basic"
d = str(depth).strip().lower()
if d in ("advanced", "deep", "thorough"):
return "advanced"
return "basic"Implementierungsschicht (Logik‑Ebene)
def tavily_search_impl(
query: str,
search_depth: str,
max_results: int,
include_domains: list[str] | None,
time_range: str | None
) -> dict:
"""
Pure implementation - expects strictly typed, normalized inputs.
No validation or normalization should happen here.
"""
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
params = {
"query": query,
"search_depth": search_depth,
"max_results": max_results
}
if include_domains:
params["include_domains"] = include_domains
if time_range:
params["time_range"] = time_range
return client.search(**params)Warum dies selbstheilend ist
Der Loop:
Dies erzeugt adaptive Konvergenz: Das System heilt sich zur Laufzeit selbst, indem es das LLM zu korrekten Eingaben ohne menschliche Aufsicht führt.
Warum man das LLM niemals JSON erzeugen lässt
Es geht nicht um Syntaxfehler. Es geht um Verantwortungsgrenzen.
JSON ist ein deterministisches Serialisierungsformat. LLMs sind probabilistische Sequenzmodelle.
Wenn das LLM dafür verantwortlich ist, formatiertes JSON zu erzeugen, garantieren Sie:
Dies sind keine Fehler. Es ist die statistische Natur der Token‑Generierung. Sobald das Modell Struktur definieren darf, wird die Struktur nicht‑deterministisch.
Anwendung derselben zweischichtigen Architektur
LLM → untypisierte Werte Interface Layer → Normalisierung + Schema‑Erzwingung Implementation Layer → konstruiert JSON deterministisch
Das Modell formatiert niemals JSON.
Müssen die strukturierten Daten in der Benutzeroberfläche sichtbar sein. Kein Problem – Ihre Funktion gibt sie zurück, und Ihre Anwendungsschicht zeigt sie an. Der Punkt ist: Das LLM erzeugt nicht die Struktur, Ihr Code tut es.
Beispiel
Die vollständige Referenzimplementierung ist auf GitHub verfügbar.
Interface‑Schicht (LLM‑seitig)
from fastmcp import FastMCP
from .implementations.json_writer_impl import create_structured_data_impl
mcp = FastMCP("My MCP Server")
@mcp.tool()
def create_json(x, y, flag) -> dict:
"""
Create and return structured JSON from LLM-provided values.
Args:
x: Integer value (accepts "5", "05", 5)
y: String value
flag: Boolean flag (accepts "true", "yes", "1", True, etc.)
Returns:
Dictionary with deterministic structure (ready for JSON serialization)
Raises:
ValueError: With structured correction signals for invalid inputs
"""
# Normalize integer with structured error
try:
x = int(x)
except (ValueError, TypeError):
raise ValueError(
f"TYPE_ERROR: field 'x' must be an integer; "
f"received {repr(x)} (type: {type(x).__name__}). "
"Retry with a valid integer value."
)
# Normalize string
y = str(y)
# Normalize boolean from various representations
if isinstance(flag, str):
flag = flag.strip().lower() in ("true", "1", "yes", "on")
else:
flag = bool(flag)
# Pass typed inputs to implementation
return create_structured_data_impl(x=x, y=y, flag=flag)Implementation Layer (Geschäftslogik | strikt, deterministisches JSON)
def create_structured_data_impl(x: int, y: str, flag: bool) -> dict:
"""
Construct JSON structure deterministically from typed values.
The LLM never generates JSON - it only provides values.
Code defines keys, order, and types.
"""
# Structure is defined by code, not LLM
return {
"x": x,
"y": y,
"flag": flag
}Warum das funktioniert
Verantwortlichkeit
| Verantwortlichkeit | LLM | Interface‑Schicht | Implementierungs‑Schicht |
|---|---|---|---|
| Intent interpretieren | Yes | No | No |
| Werte normalisieren | No | Yes | No |
| Schema erzwingen | No | Yes | No |
| Datenstrukturen konstruieren | No | No | Yes |
| Daten serialisieren | No | No | Yes |
Kernprinzip
LLMs planen. Code typisiert. Lassen Sie niemals das Modell Struktur definieren. Erzwingen Sie Struktur immer an der Grenze.
LLMs planen. Code typisiert. Lassen Sie niemals das Modell Struktur definieren. Erzwingen Sie Struktur immer an der Grenze.
Zusammenfassung
| Layer | Handles | Must Be | Failure Mode | Output |
|---|---|---|---|---|
| LLM | Semantics | Flexible | Format Hallucination | Unstructured Values |
| Interface Layer | Normalisation + Validation | Total / Deterministic | Structured Correction (Intentional Exception Raised) | Typed Inputs |
| Implementation Layer | Business Logic | Pure / Strict | Hard Failure (if reached incorrectly) | Stable Data / JSON / YAML |
Die Invariante: Wenn die Implementierungsschicht Müll sieht, ist die Interface‑Schicht fehlerhaft.
Dieses Muster ist allgemein und gilt für jede LLM‑Tooling‑Integration, einschliesslich MCP, ReAct, Function‑Calling‑APIs und agentische Planungssysteme. Diese Architektur ist kein Workaround für LLM‑Schwächen. Die AACL ist die korrekte Trennung von Verantwortlichkeiten für jedes System, in dem ein probabilistischer Sprachgenerator mit deterministischen Softwarekomponenten interagiert.
Verwandte Musters
| Muster | Relationship |
|---|---|
| DDD Anti‑Corruption Layer | Konzeptioneller Vorfahre — geht jedoch von einem deterministischen Upstream‑Domain aus |
| Adapter Muster | Behandelt Interface‑Mismatch, aber nicht semantische Mehrdeutigkeit |
| Retry with Backoff | Behandelt Fehler, aber nicht Interpretation |
| ReAct | Behandelt iterative Konvergenz, fokussiert jedoch auf LLM‑Output statt auf das bekannte gute System namens Code |
Für die Aufnahme in einen Muster‑Katalog.
Anwendbarkeit
Verwenden Sie das AACL‑Muster, wenn:
Nicht verwenden, wenn:
Konsequenzen
Vorteile:
Trade‑offs:
Bekannte Verwendungen
Aufgelöste Kräfte
Das Muster balanciert:
Die Auflösung: Die Interface‑Schicht ist total (behandelt alle Eingaben), während die Implementierung rein ist (setzt Korrektheit voraus).
Über dieses Muster
Autor: Morgan Lee Organisation: Synpulse8 Erstveröffentlichung: 12. November 2025


Large Language Models (LLMs) sind leistungsfähig, aber ihre Ausgaben können unvorhersehbar sein. Selbst kleine Inkonsistenzen oder mehrdeutige Werte können nachgelagerte Systeme stillschweigend beschädigen und versteckte Fehler erzeugen, die schwer zu erkennen sind.
Die Adaptive Anti‑Corruption Layer (AACL) adressiert diese Herausforderung mit einer zweischichtigen Grenze, die LLM‑Ausgaben normalisiert und typensichere Verarbeitung erzwingt. Durch strukturierte Korrektursignale ermöglicht die AACL Echtzeit‑Selbstkorrektur und erlaubt probabilistischen LLMs, zuverlässig mit deterministischer Geschäftslogik zu interagieren.
Gelöstes Problem: von LLM erzeugte Format‑Halluzinationen
Das Synpulse8‑Team hat das AACL‑Designmuster formalisiert, um probabilistische LLM‑Agenten über einen Selbstheilungsmechanismus mit deterministischen Systemen zu integrieren. Eine Version dieses Musters ist auch im README auf GitHub verfügbar.
Das AACL bietet eine Normalisierungsgrenze, die mehrdeutige LLM‑Ausgaben in strikt typisierte Eingaben umwandelt und strukturierte Korrektursignale zurückgibt, die es dem Modell ermöglichen, sich zur Laufzeit selbst zu korrigieren. Dies eliminiert stille Formatbeschädigungen und verbessert zuverlässiges agentisches Verhalten, ohne das Modell neu trainieren zu müssen.
| Designmuster: Adaptive Anti‑Corruption Layer (AACL) |
| Kontext: Integration probabilistischer LLM‑Agenten mit deterministischen Systemen |
| Problem: LLMs erzeugen chaotische, nicht typensichere Ausgaben; direkte Integration verursacht stille Formatbeschädigungen |
| Lösung: Zweischichtige Architektur mit Normalisierungsgrenze, die strukturiertes Feedback bereitstellt |
| Ergebnis: Selbstkorrigierendes System, in dem strukturiertes Feedback Fehlerkorrektur zur Laufzeit ermöglicht |
Referenzimplementierung und adversarielle Tests sind auf GitHub verfügbar.
Der Selbstheilungsmechanismus erfordert eine agentische Loop‑Architektur, in der das LLM Feedback aus der Tool‑Ausführung erhalten und mit korrigierten Eingaben erneut ausführen kann. Konkret muss das System Folgendes unterstützen:
Framework‑Beispiel:
Wenn Ihr System keinen agentischen Loop besitzt (d. h. One‑Shot‑Tool‑Aufrufe ohne Retry), bietet das AACL‑Muster dennoch Mehrwert, indem es stille Formatbeschädigungen verhindert, aber Selbstheilung erfordert den Retry‑Mechanismus.
Warum das wichtig ist
LLMs sind semantische Sequenzmodelle. Sie sind keine typensicheren, schemastabilen und zuverlässigen Datenserialisierer. Daher müssen LLMs Werte liefern, während Code Struktur bereitstellen muss.
Die korrekte Architektur ist eine zweischichtige Grenze, die freie Modell‑Ausgaben von deterministischer Geschäftslogik trennt.
LLM (semantischer Planer) ↓ Interface Layer (Normalisierung + Validierung + strukturierte Fehler) ↓ Implementation Layer (strikte Typen, reine Logik)
An dieser Grenze wird das System selbstkorrigierend. Die Interface‑Grenze ist der einzige Ort, an dem Mehrdeutigkeit existieren darf. Sobald die Ausführung in die Implementierungsschicht übergeht, muss die Mehrdeutigkeit NULL sein.
Strukturierte Ausgaben gehören in Funktionsresultate, nicht in Token‑Streams. Verwenden Sie Function Calling, um strukturierte Daten vom Code zu erhalten, nicht um sie aus LLM‑generiertem Text zu parsen. Müssen Benutzer das JSON sehen. Platzieren Sie das Funktionsresultat in Ihrer Output‑Queue – genau dort, wo auch der Response‑Stream landet.
Häufige LLM‑Ausgabeprobleme (nicht vollständig)
LLMs tauschen frei untereinander aus: • "true", "True", "yes", "1", True • 5, "05", "five", "5" • "null", "none", None, "n/a", "" • "a.com b.com", "a.com,b.com", ["a.com", "b.com"] • Daten in jeder menschenlesbaren Variante
Diese direkt an eine API‑Schicht weiterzugeben, führt zu stiller Formatbeschädigung – der schlimmsten Art von Systemfehler, weil sie eine gewisse Wahrscheinlichkeit hat zu „funktionieren“, und dann bricht es plötzlich ohne erkennbaren Grund.
Architektur
Diese zweischichtige Grenze ist der Kern der AACL. Das LLM arbeitet in einem semantischen Raum; die Implementierungsschicht arbeitet in einem typisierten, deterministischen Raum. Die AACL ist die Grenze, die zwischen beiden durch Normalisierung + strukturierte Fehlersignale übersetzt.
1. Interface Layer (LLM‑seitig)
Funktion: Beliebige Eingaben in typisierte Eingaben umwandeln.
Anforderungen:
Diese Schicht muss total sein: Jede Eingabe wird entweder normalisiert oder schlägt mit einem für das LLM nutzbaren Korrektursignal fehl.
2. Implementation Layer (logikseitig)
Funktion: Geschäftsvorgänge mit strikter Typisierung ausführen.
Wenn falsche Werte diese Schicht erreichen, ist die Architektur falsch.
Minimales Beispiel (Python)
Das Muster verwendet eine Zwei‑Dateien‑Struktur. Die vollständige Referenzimplementierung ist auf GitHub verfügbar.
Interface Layer (LLM‑seitig)
from fastmcp import FastMCP
from .implementations.tavily_impl import tavily_search_impl
mcp = FastMCP("My MCP Server")
@mcp.tool()
def search_web(
query,
search_depth="basic",
max_results=5,
include_domains=None,
time_range=None
) -> dict:
"""
Search the web using Tavily's search API.
Args:
query: Search query (required)
search_depth: "basic" or "advanced" (Optional, defaults to "basic")
max_results: Number of results, 1-10 (Optional, defaults to 5)
include_domains: Domain filter (comma/space-separated or list) (Optional)
time_range: Time filter ("day", "week", "month", "year") (Optional)
Returns:
Dictionary containing search results
Raises:
ValueError: With structured correction signals for invalid inputs
"""
# Normalize ambiguous inputs to canonical forms
search_depth = _normalize_search_depth(search_depth)
include_domains = _normalize_optional_string(include_domains)
time_range = _normalize_optional_string(time_range)
# Validate with structured error messages
if time_range and time_range not in ("day", "week", "month", "year"):
raise ValueError(
"INVALID_TIME_RANGE: expected one of ['day', 'week', 'month', 'year']; "
f"received '{time_range}'. Retry with a valid value."
)
# Pass typed, normalized inputs to implementation
return tavily_search_impl(
query=query,
search_depth=search_depth,
max_results=int(max_results),
include_domains=include_domains,
time_range=time_range
)
def _normalize_optional_string(value):
"""Normalize null-like values to None."""
if value is None:
return None
if isinstance(value, str):
s = value.strip().lower()
if s in ("", "null", "none", "n/a", "na"):
return None
return value
def _normalize_search_depth(depth):
"""Normalize search depth to 'basic' or 'advanced'."""
if not depth:
return "basic"
d = str(depth).strip().lower()
if d in ("advanced", "deep", "thorough"):
return "advanced"
return "basic"Implementierungsschicht (Logik‑Ebene)
def tavily_search_impl(
query: str,
search_depth: str,
max_results: int,
include_domains: list[str] | None,
time_range: str | None
) -> dict:
"""
Pure implementation - expects strictly typed, normalized inputs.
No validation or normalization should happen here.
"""
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
params = {
"query": query,
"search_depth": search_depth,
"max_results": max_results
}
if include_domains:
params["include_domains"] = include_domains
if time_range:
params["time_range"] = time_range
return client.search(**params)Warum dies selbstheilend ist
Der Loop:
Dies erzeugt adaptive Konvergenz: Das System heilt sich zur Laufzeit selbst, indem es das LLM zu korrekten Eingaben ohne menschliche Aufsicht führt.
Warum man das LLM niemals JSON erzeugen lässt
Es geht nicht um Syntaxfehler. Es geht um Verantwortungsgrenzen.
JSON ist ein deterministisches Serialisierungsformat. LLMs sind probabilistische Sequenzmodelle.
Wenn das LLM dafür verantwortlich ist, formatiertes JSON zu erzeugen, garantieren Sie:
Dies sind keine Fehler. Es ist die statistische Natur der Token‑Generierung. Sobald das Modell Struktur definieren darf, wird die Struktur nicht‑deterministisch.
Anwendung derselben zweischichtigen Architektur
LLM → untypisierte Werte Interface Layer → Normalisierung + Schema‑Erzwingung Implementation Layer → konstruiert JSON deterministisch
Das Modell formatiert niemals JSON.
Müssen die strukturierten Daten in der Benutzeroberfläche sichtbar sein. Kein Problem – Ihre Funktion gibt sie zurück, und Ihre Anwendungsschicht zeigt sie an. Der Punkt ist: Das LLM erzeugt nicht die Struktur, Ihr Code tut es.
Beispiel
Die vollständige Referenzimplementierung ist auf GitHub verfügbar.
Interface‑Schicht (LLM‑seitig)
from fastmcp import FastMCP
from .implementations.json_writer_impl import create_structured_data_impl
mcp = FastMCP("My MCP Server")
@mcp.tool()
def create_json(x, y, flag) -> dict:
"""
Create and return structured JSON from LLM-provided values.
Args:
x: Integer value (accepts "5", "05", 5)
y: String value
flag: Boolean flag (accepts "true", "yes", "1", True, etc.)
Returns:
Dictionary with deterministic structure (ready for JSON serialization)
Raises:
ValueError: With structured correction signals for invalid inputs
"""
# Normalize integer with structured error
try:
x = int(x)
except (ValueError, TypeError):
raise ValueError(
f"TYPE_ERROR: field 'x' must be an integer; "
f"received {repr(x)} (type: {type(x).__name__}). "
"Retry with a valid integer value."
)
# Normalize string
y = str(y)
# Normalize boolean from various representations
if isinstance(flag, str):
flag = flag.strip().lower() in ("true", "1", "yes", "on")
else:
flag = bool(flag)
# Pass typed inputs to implementation
return create_structured_data_impl(x=x, y=y, flag=flag)Implementation Layer (Geschäftslogik | strikt, deterministisches JSON)
def create_structured_data_impl(x: int, y: str, flag: bool) -> dict:
"""
Construct JSON structure deterministically from typed values.
The LLM never generates JSON - it only provides values.
Code defines keys, order, and types.
"""
# Structure is defined by code, not LLM
return {
"x": x,
"y": y,
"flag": flag
}Warum das funktioniert
Verantwortlichkeit
| Verantwortlichkeit | LLM | Interface‑Schicht | Implementierungs‑Schicht |
|---|---|---|---|
| Intent interpretieren | Yes | No | No |
| Werte normalisieren | No | Yes | No |
| Schema erzwingen | No | Yes | No |
| Datenstrukturen konstruieren | No | No | Yes |
| Daten serialisieren | No | No | Yes |
Kernprinzip
LLMs planen. Code typisiert. Lassen Sie niemals das Modell Struktur definieren. Erzwingen Sie Struktur immer an der Grenze.
LLMs planen. Code typisiert. Lassen Sie niemals das Modell Struktur definieren. Erzwingen Sie Struktur immer an der Grenze.
Zusammenfassung
| Layer | Handles | Must Be | Failure Mode | Output |
|---|---|---|---|---|
| LLM | Semantics | Flexible | Format Hallucination | Unstructured Values |
| Interface Layer | Normalisation + Validation | Total / Deterministic | Structured Correction (Intentional Exception Raised) | Typed Inputs |
| Implementation Layer | Business Logic | Pure / Strict | Hard Failure (if reached incorrectly) | Stable Data / JSON / YAML |
Die Invariante: Wenn die Implementierungsschicht Müll sieht, ist die Interface‑Schicht fehlerhaft.
Dieses Muster ist allgemein und gilt für jede LLM‑Tooling‑Integration, einschliesslich MCP, ReAct, Function‑Calling‑APIs und agentische Planungssysteme. Diese Architektur ist kein Workaround für LLM‑Schwächen. Die AACL ist die korrekte Trennung von Verantwortlichkeiten für jedes System, in dem ein probabilistischer Sprachgenerator mit deterministischen Softwarekomponenten interagiert.
Verwandte Musters
| Muster | Relationship |
|---|---|
| DDD Anti‑Corruption Layer | Konzeptioneller Vorfahre — geht jedoch von einem deterministischen Upstream‑Domain aus |
| Adapter Muster | Behandelt Interface‑Mismatch, aber nicht semantische Mehrdeutigkeit |
| Retry with Backoff | Behandelt Fehler, aber nicht Interpretation |
| ReAct | Behandelt iterative Konvergenz, fokussiert jedoch auf LLM‑Output statt auf das bekannte gute System namens Code |
Für die Aufnahme in einen Muster‑Katalog.
Anwendbarkeit
Verwenden Sie das AACL‑Muster, wenn:
Nicht verwenden, wenn:
Konsequenzen
Vorteile:
Trade‑offs:
Bekannte Verwendungen
Aufgelöste Kräfte
Das Muster balanciert:
Die Auflösung: Die Interface‑Schicht ist total (behandelt alle Eingaben), während die Implementierung rein ist (setzt Korrektheit voraus).
Über dieses Muster
Autor: Morgan Lee Organisation: Synpulse8 Erstveröffentlichung: 12. November 2025
