Startseite / Artikel / LLM-Ausgaben mit einem ausführbaren Knowledge Graph und zwei Gates prüfen

LLM-Ausgaben mit einem ausführbaren Knowledge Graph und zwei Gates prüfen

RelationalAI als Verifikationsschicht: strukturierte LLM-Aussagen, Schemavalidierung, ein Graph für Erstattungsrichtlinien und ein zweites Gate, das Antworten gegen Geschäftsregeln abweist.

1546 Wörter

Flüssig ist nicht gleich richtig

Sprachmodelle schreiben glatte Antworten, aber glatt heißt nicht richtig. Angenommen, ein Modell sagt: „Dieser Kunde hat Anspruch auf eine volle Erstattung.“ Klingt gut. Doch das Modell könnte eine Richtlinie erfunden, ein Datum falsch gelesen, eine Ausnahme übersehen oder fehlende Fakten geraten haben. In kundennahen Abläufen ist das inakzeptabel, also müssen Sprache erzeugen und Fakten prüfen getrennte Aufgaben sein.

Es gibt einen echten Präzedenzfall. In Moffatt v. Air Canada, 2024 BCCRT 149, erklärte ein Chatbot einer Airline, ein Trauerfalltarif könne nach der Reise beantragt werden, obwohl die tatsächliche Richtlinie der Airline keine rückwirkenden Anträge erlaubte. Das Civil Resolution Tribunal von British Columbia sah Air Canada wegen fahrlässiger Falschdarstellung in der Haftung und sprach Schadenersatz, Zinsen und Gebühren zu. Die Lehre: Sobald ein kundennahes System eine Richtlinie zitiert, kann das Unternehmen darauf festgenagelt werden.

Ziel ist also, die Flexibilität eines LLM zu behalten und trotzdem etwas die Fakten prüfen zu lassen, bevor eine Antwort beim Nutzer ankommt. Der hier beschriebene Ansatz nutzt RelationalAI als ausführbaren Knowledge Graph, der die strukturierten Aussagen eines LLM validiert.

Ein Agent-Loop mit zwei Gates

Das Muster stammt aus einem Vortrag von Frank Coyle von der UC Berkeley auf der AI Engineer World's Fair über die Kombination von LLMs mit Validierung und Verifikation. Kern ist ein Agent-Loop mit je einem Gate auf beiden Seiten der Tool-Ausführung. Er läuft so ab:

1. The user asks a question.
2. The LLM decides whether it needs to call a tool.
3. Before the tool runs, the system validates the tool input.
4. The tool runs against an authoritative system.
5. After the tool runs, the system validates the output.
6. The result is appended back into the conversation.
7. The loop repeats until the model produces a final answer.

Als Bild:

LLM proposes an action
   |
Gate 1 validates the tool input
   |
Tool runs
   |
Gate 2 validates the tool output
   |
LLM continues or finishes

Gate 1 prüft die Form. Es fragt, ob das Modell eine wohlgeformte Anfrage erzeugt hat, und das ist ein Fall für Schemavalidierung: Bevor irgendein Tool läuft, wird sichergestellt, dass die Pflichtfelder vorhanden sind und die Werte zu Aussagetypen passen, die das System kennt.

Gate 2 prüft den Inhalt, also ob das Ergebnis mit den Fakten und Regeln der Domäne übereinstimmt. Genau an dieser Stelle ist ein ausführbarer Knowledge Graph nützlich.

Was RelationalAI beiträgt

Mit RelationalAI legt man einen ausführbaren Knowledge Graph über die eigenen Daten. Er speichert Fakten, und zusätzlich schreibt man Regeln, die daraus weitere Fakten ableiten. Das unterscheidet einen Graph, in dem man nachschlagen kann, von einem, mit dem man schlussfolgern kann.

In diesem Muster dient er als Verifikationsschicht. Das LLM deutet, was der Nutzer will, und gibt eine strukturierte Aussage aus. RelationalAI entscheidet, ob Geschäftsfakten und Regeln diese Aussage stützen, und die Anwendung akzeptiert sie dann, lehnt sie ab oder markiert sie als nicht belegt:

LLM output
   |
Pydantic validates the tool-call shape
   |
RelationalAI derives the authoritative value
   |
Claim is marked entailed, contradicted, or unsupported

Ein konkretes Beispiel zeigt, wie das zusammenspielt.

Schritt 1: strukturierte Aussagen vom Modell holen

Das LLM darf nicht nur mit freier Prosa antworten. Es muss zusätzlich strukturierte Aussagen ausgeben, die sich prüfen lassen. Zum Beispiel:

{
  "claim_type": "refund_method",
  "subject_id": "A-91",
  "value": "ORIGINAL_PAYMENT",
  "evidence": "Refund policy for GOLD uses ORIGINAL_PAYMENT."
}

Damit hat das System etwas Konkretes zum Testen. Die Frage lautet nicht mehr „Klingt dieser Absatz richtig?“, sondern „Stimmt genau diese Aussage für genau diese Bestellung?“. Das ist Gate 1 bei der Arbeit. Ein unbekannter Aussagetyp oder ein fehlendes Pflichtfeld stoppt den Tool-Aufruf, bevor der Graph überhaupt befragt wird.

Schritt 2: die Erstattungsrichtlinie im Graph abbilden

Als Nächstes modelliert RelationalAI die maßgebliche Geschäftslogik. Die Demo nutzt eine kleine Erstattungsdomäne mit drei Konzepten: Customer, Order und RefundPolicy. Beziehungen und abgeleitete Eigenschaften beschreiben dann, wie sie zusammenhängen:

- an order belongs to a customer
- a refund policy applies to a customer tier
- refund eligibility depends on final-sale status and return window
- refund amount depends on eligibility and restocking fee
- refund method comes from the applicable policy

Aus diesen Fakten und Regeln leitet der Graph die Werte ab, die geprüft werden müssen:

- customer tier
- refund eligibility
- refund amount
- restocking fee
- refund method
- return window
- whether shipping is refundable
- whether the order was defective

Dem LLM sollte man nicht zutrauen, diese Werte zu erfinden oder aus dem Gedächtnis abzurufen; es sollte sie beim Graph abfragen. Angenommen, die bekannten Fakten lauten:

Order A-91 belongs to Customer C-184
Customer C-184 has tier GOLD
GOLD customers have a 30-day return window

Dann kann RelationalAI ableiten:

Order A-91 is refund eligible
Order A-91 refund amount is 129.99
Order A-91 refund method is ORIGINAL_PAYMENT

Der Graph wird zur Quelle ausführbarer Wahrheit. Eine gekürzte Fassung des Modells sieht so aus:

model = Model("refund_policy_kg")

Customer = model.Concept("Customer", identify_by={"customer_id": String})
Order = model.Concept("Order", identify_by={"order_id": String})
RefundPolicy = model.Concept("RefundPolicy", identify_by={"policy_id": String})

Customer.tier = ...

Order.customer = ...
Order.amount = ...
Order.final_sale = ...
Order.request_age_days = ...

RefundPolicy.tier = ...
RefundPolicy.return_window_days = ...
RefundPolicy.refund_method = ...

Dann folgen die abgeleiteten Werte, die Gate 2 abfragen wird:

Order.expected_refund_method = model.Property(
    f"{Order} has {String:expected_refund_method}"
)
Order.expected_eligible_for_refund = model.Property(
    f"{Order} has {Boolean:expected_eligible_for_refund}"
)

model.where(
    Order.customer(Customer),
    Customer.tier == RefundPolicy.tier,
).define(
    Order.expected_refund_method(RefundPolicy.refund_method)
)

model.where(
    Order.customer(Customer),
    Customer.tier == RefundPolicy.tier,
    Order.final_sale == False,
    Order.request_age_days <= RefundPolicy.return_window_days,
).define(
    Order.expected_eligible_for_refund(True)
)

Weder der Erstattungsanspruch noch die Erstattungsart kommen vom LLM. Sie werden von einem Knowledge Graph berechnet, der auf Geschäftsregeln beruht, und diese Regeln können Fachleute und Entwickler reviewen, versionieren und steuern.

Schritt 3: prüfen, was das Modell gesagt hat

Steht der Graph, lassen sich Aussagen testen. Angenommen, das Modell antwortet: „Erstatte Bestellung A-91 als Store-Guthaben, weil der Kunde Platinum ist.“ Klingt plausibel, aber der Graph kann etwas anderes zeigen:

  • der Kunde ist GOLD und nicht PLATINUM
  • die Erstattungsart ist ORIGINAL_PAYMENT und nicht STORE_CREDIT
  • die Bestellung ist nicht als defekt markiert
  • laut Richtlinie sind Versandkosten nicht erstattungsfähig

Das System weist die Antwort zurück. Diese Entscheidung hat kein zweites LLM getroffen, sondern ausführbare Geschäftslogik auf Basis belegter Fakten. Der Validierungscode ist bewusst einfach: Für jede Aussage die passende erwartete Eigenschaft in RelationalAI suchen, sie abfragen und das Ergebnis mit dem vom Modell vorgeschlagenen Wert vergleichen.

EXPECTED_PROPERTY_BY_CLAIM = {
    "refund_method": "expected_refund_method",
    "eligible_for_refund": "expected_eligible_for_refund",
    "refund_amount": "expected_refund_amount",
    "customer_tier": "expected_customer_tier",
}

def check_claims(model, concepts, claims):
    Order = concepts["Order"]
    results = []

    for claim in claims:
        expected_property = getattr(
            Order,
            EXPECTED_PROPERTY_BY_CLAIM[claim.claim_type],
        )

        df = model.where(
            Order.order_id == claim.subject_id
        ).select(
            Order.order_id.alias("subject_id"),
            expected_property.alias("expected"),
        ).to_df()

        if df.empty:
            status = "unsupported"
            expected = None
        else:
            expected = str(df.iloc[0]["expected"])
            status = "entailed" if claim.value == expected else "contradicted"

        results.append((claim, status, expected))

     return results

In Produktion würde man Werte sorgfältig normalisieren, nicht unterstützte Aussagen explizit behandeln und die Belege hinter jeder Entscheidung protokollieren. Der Kernablauf bleibt aber gleich: Das LLM schlägt vor, RelationalAI verifiziert.

Warum man die Arbeit so aufteilt

LLMs sind stark im Ausdruck. Sie verstehen Absichten, entwerfen Antworten und erklären Ergebnisse in natürlicher Sprache. Das letzte Wort bei Richtlinien, Preisen, Anspruchsberechtigung, Compliance oder anderen operativen Fakten sollten sie aber nicht haben. Die bessere Reihenfolge:

  1. Das LLM die Anfrage deuten lassen.
  2. Es strukturierte Aussagen ausgeben lassen.
  3. Das erzeugte Objekt mit Pydantic validieren.
  4. Die maßgebliche Antwort in RelationalAI ableiten.
  5. Nur Aussagen zurückgeben, die der Knowledge Graph stützt.

Das LLM kümmert sich um Sprache und Interaktion, RelationalAI um die Verifikation.

Der Loop im Code

Grob betrachtet sieht das System so aus:

context = [user_question]

while True:
    response = llm.generate(context)

    if response.stop_reason != "tool_use":
        return response.text

    tool_call = response.tool_call

    # Gate 1: validate the model's requested action.
    claims = validate_tool_input(tool_call)

    # Tool execution: query authoritative facts and rules.
    graph = relationalai_model()
    checked_claims = graph.validate_claims(claims)

    # Gate 2: only grounded claims move forward.
    if checked_claims.has_contradictions():
        context.append(rejection_message(checked_claims))
        continue

    context.append(tool_result(checked_claims.entailed()))

Der Loop selbst ist Routine. Entscheidend ist, wer welche Aufgabe hat:

  • das Modell schlägt eine Aussage oder eine Abfrage vor
  • das erste Gate prüft die Form des Vorschlags
  • RelationalAI ermittelt, welche Fakten gelten
  • das zweite Gate verwirft alles, was widerlegt oder nicht belegt ist
  • nur Aussagen, die das überstanden haben, landen in der Antwort

Mehr als Erstattungen

Der Erstattungsfall ist absichtlich klein, aber dieselbe Struktur funktioniert für Vertragsbedingungen, regulatorische Regeln, Rabatt- und Preislogik, Lieferkettengrenzen, die Frage, wer für welches Produkt berechtigt ist, und was jedem Kunden zusteht. Jeder Ablauf, in dem ein LLM für ein Unternehmen spricht, kann eine Verifikationsschicht gebrauchen, und je teurer ein Fehler, desto wichtiger ist es, erzeugte Sprache von ausführbarer Wahrheit zu trennen.

Quellen:

  • https://www.youtube.com/watch?v=Sir59K8ZDPU (der Konferenzvortrag)
  • https://www.canlii.org/en/bc/bccrt/doc/2024/2024bccrt149/2024bccrt149.html (Moffatt v. Air Canada)
  • https://docs.relational.ai/ (Dokumentation von RelationalAI)