Spätestens mit dem Aufkommen großer Sprachmodelle (LLMs) wie GPT stellt sich vielen die Frage, wie man solchen Modellen strukturierte, präzise Informationen zur Verfügung stellen kann. Denn obwohl LLMs in der Lage sind, Fragen sehr überzeugend zu beantworten, beruhen viele ihrer Antworten lediglich auf sprachstatistischen Wahrscheinlichkeiten, nicht auf logischem Schließen oder explizitem Faktenwissen. An dieser Stelle bietet der Einsatz einer Ontologie einen systematischen Mehrwert.
Im folgenden Beitrag wird anhand einer fiktiven Mission im „Herr der Ringe“-Universum gezeigt, wie eine Ontologie ein LLM bei der Beantwortung komplexer Fragen unterstützen kann.
Was ist eine Ontologie?
Eine Ontologie ist ein formales Modell zur strukturierten Repräsentation von Wissen. Sie dient dazu, reale Sachverhalte, Zusammenhänge und Abhängigkeiten durch Konzepte und deren Beziehungen abzubilden. Technisch basiert sie in der Regel auf dem RDF-Modell (Resource Description Framework), bei dem Wissen in Form von Tripeln dargestellt wird: ein Subjekt, ein Prädikat und ein Objekt. Diese Tripel bilden die grundlegende Einheit zur Beschreibung von Fakten und Beziehungen in einer Wissensdomäne.
Statt Begriffe einfach aufzulisten, modelliert eine Ontologie die Realität in ihrer Abhängigkeit. Dabei wird beschrieben, wie Entitäten zueinander in Beziehung stehen. So kann etwa festgehalten werden, dass ein Subjekt („Aragorn“) über ein bestimmtes Merkmal („Schwertkampf“) verfügt. Diese Beziehung wird durch ein Prädikat („hatSkill“) ausgedrückt. Aragorn, ein Hauptcharakter aus Herr der Ringe, ist ein erfahrener Kämpfer mit ausgeprägten Fähigkeiten im Schwertkampf und in der Führung.
Dieses strukturierte Wissen kann durch maschinelle Systeme wie ein LLM (Large Language Model) genutzt werden, um präzisere Antworten zu geben, vor allem dann, wenn das Modell nicht auf rein statistische Wahrscheinlichkeiten zurückgreift, sondern auf explizit modelliertes Hintergrundwissen. Solche Tripel lassen sich in Graphdatenbanken wie Neo4j speichern und mit Python-Bibliotheken wie py2neo, rdflib oder networkx analysieren und visualisieren.
Beispiel Ontologie:
Nehmen wir an, wir modellieren eine Ontologie mit Charakteren aus Herr der Ringe. In dieser Ontologie sind folgende Informationen enthalten:
Was leistet eine Ontologie im Zusammenspiel mit einem LLM?
Statt einem LLM einfach eine offene Frage zu stellen (und damit dem Modell zu überlassen, ob es auf Halluzinationen oder Fragmentwissen zurückgreift), kann man das LLM so steuern, dass es zunächst strukturiertes Wissen aus einer Ontologie berücksichtigt. Das hat mehrere Vorteile:
Konsistenz Das LLM bezieht sich nur auf definierte und überprüfbare Fakten.
Einschränkung des Suchraums Die Antwortsuche wird gezielt geführt.
Explainability Entscheidungen können über den zugrunde liegenden Graph nachvollzogen werden.
Kombination von Logik und Sprache Die Ontologie liefert die Fakten, das LLM formuliert elegant.
Beispiel: Eine Mission im Herr-der-Ringe-Universum
Angenommen, wir haben die folgende Frage und fragen ein LLM nach der Antwort:
Welcher Charakter aus 'Herr Der Ringe' verfügt über die höchste Fähigkeit in Heimlichkeit und Willenskraft für eine gefährliche Mission in Mordor?
ChatGPT bzw. das LLM Antwort auf diese Frage wie folgt:
Im gezeigten Beispiel wird deutlich, wie ein LLM, in diesem Fall ChatGPT, zwar inhaltlich nachvollziehbare Antwort liefert, dabei jedoch nicht zwingend korrekt auf eine spezifische Faktenlage reagiert. Die gegebene Antwort basiert auf einem allgemeinen Weltwissen, das das Modell während seines Trainings aus einer Vielzahl öffentlich zugänglicher Quellen gelernt hat. Sie reflektiert damit eine wahrscheinliche Sichtweise, aber nicht notwendigerweise die tatsächlichen, im aktuellen Kontext geltenden Fakten.
LLMs arbeiten im Kern auf der Basis von Wahrscheinlichkeiten. Sie berechnen, welche Antwort auf eine Eingabe statistisch am wahrscheinlichsten erscheint, basierend auf dem Sprach- und Wissensraum, in dem sie trainiert wurden. In vielen Fällen führt das zu plausiblen Ergebnissen. Doch sobald die Fragestellung auf domänenspezifisches Wissen abzielt, etwa internes Fachwissen, projektbezogene Zusammenhänge oder speziell kuratierte Fakten, stoßen solche Modelle ohne zusätzliche Steuerung an Grenzen.
Hier kommt der Mehrwert einer Ontologie ins Spiel. Eine Ontologie ermöglicht es, solches Spezialwissen strukturiert zu modellieren, also welche Entitäten existieren, welche Eigenschaften sie haben und wie sie zueinander stehen. In einer Ontologie kann exakt festgelegt werden, dass Frodo sowohl über die Fähigkeit Heimlichkeit als auch über Willensstärke verfügt, während Samweis zwar Heimlichkeit, aber keine Willensstärke besitzt. Dieses Wissen ist explizit hinterlegt und maschinenlesbar.
Durch die Einbindung eines solchen strukturierten Wissensmodells in die LLM-Verarbeitung wird die ursprüngliche Wahrscheinlichkeitsverteilung gezielt beeinflusst. Das LLM muss nicht mehr erraten, wer geeignet sein könnte, sondern es erhält klare Fakten, auf deren Basis die Antwort generiert wird. Die Wahrscheinlichkeit für eine korrekte und spezialisierte Antwort steigt deutlich, weil das Modell nicht mehr ausschließlich auf seinem generalisierten Trainingswissen basiert, sondern zusätzlich auf spezialisierte, kontextspezifische Informationen zugreift.
Die Ontologie wirkt somit als semantische Leitstruktur. Sie erhöht die faktische Treffsicherheit und zwingt das Modell, innerhalb eines Bedeutungskontexts zu argumentieren. Besonders in realen Anwendungsszenarien, etwa in Unternehmen, medizinischen Systemen oder technischen Assistenzlösungen, kann diese Integration über den Unterschied zwischen plausibel und korrekt entscheiden.
Erweiterung von Prompts durch Ontologie-Wissen zur präziseren LLM-Antwortgenerierung
Bevor eine Frage an das Sprachmodell gesendet wird, wird der ursprüngliche Prompt durch strukturiertes Wissen aus der Ontologie ergänzt. Dieses Wissen besteht aus Tripeln, die relevante Informationen über die Domäne enthalten, zum Beispiel über Eigenschaften von Figuren oder Zusammenhänge zwischen Konzepten. Durch die Einbettung dieser Fakten kann das Sprachmodell nicht nur auf statistische Muster zurückgreifen, sondern gezielt auf das bereitgestellte Spezialwissen zugreifen. Das führt zu besseren, nachvollziehbaren und domänenspezifisch korrekten Antworten.
INFO - ------ LLM Prompt Start ------ INFO - Nutze die folgenden Fakten, um die Frage m�glichst pr�zise zu beantworten. INFO - INFO - Fakten: INFO - Legolas HATSKILL Beweglichkeit INFO - Legolas HATSKILLLEVEL Experte INFO - Gimli HATSKILL Axtkampf INFO - Gimli HATSKILL Standfestigkeit INFO - Gimli HATSKILLLEVEL Experte INFO - Frodo HATSKILL Heimlichkeit INFO - Frodo HATSKILL Willensst�rke INFO - Frodo HATSKILLLEVEL Fortgeschritten INFO - Samweis HATSKILL Heimlichkeit INFO - Samweis HATSKILL Durchhalteverm�gen INFO - Samweis HATSKILLLEVEL Fortgeschritten INFO - Aragorn LOYALIT�T Mittelerde INFO - Legolas LOYALIT�T Elbenallianz INFO - Gimli LOYALIT�T Zwerge INFO - Frodo LOYALIT�T Frodo selbst INFO - Samweis LOYALIT�T Frodo INFO - Schattenlager Infiltration ERFORDERTSKILL Heimlichkeit INFO - Schattenlager Infiltration MISSIONSTYP Sabotage INFO - Orks am Fluss bek�mpfen ERFORDERTSKILL Fernkampf INFO - Orks am Fluss bek�mpfen MISSIONSTYP Kampf INFO - Verteidigung des Tors ERFORDERTSKILL Axtkampf INFO - Verteidigung des Tors MISSIONSTYP Verteidigung INFO - Legolas HATERFAHRUNGMIT Orks am Fluss bek�mpfen INFO - Gimli HATERFAHRUNGMIT Verteidigung des Tors INFO - Frodo HATERFAHRUNGMIT Schattenlager Infiltration INFO - Samweis HATERFAHRUNGMIT Schattenlager Infiltration INFO - Gimli VERLETZUNGSGRAD leicht INFO - Aragorn HATSKILL Schwertkampf INFO - Aragorn HATSKILL F�hrung INFO - Aragorn HATSKILLLEVEL Experte INFO - Legolas HATSKILL Bogenschie�en INFO - INFO - Frage: INFO - Welcher Charakter aus 'Herr Der Ringe' verfügt über die höchste Fähigkeit in Heimlichkeit und Willenskraft für eine gefährliche Mission in Mordor? INFO - INFO - Antwort: INFO - ------ LLM Prompt Ende ------
Nach der Erweiterung des Prompts durch die Metadaten erhalten wir als Antwort „Frodo“ statt wie zuvor „Samweis“.
INFO - Antwort des LLM: Frodo
Zur Beantwortung der Frage wurde im aktuellen Beispiel die gesamte Ontologie in den Prompt eingebunden. Alle verfügbaren Fakten wurden dem LLM vollständig mitgegeben, um sicherzustellen, dass die Antwort ausschließlich auf dem modellierten Wissen basiert. Dieses Vorgehen funktioniert in kleineren Beispielen, eignet sich aber nicht für reale, umfangreiche Anwendungen.
In professionellen Umgebungen enthalten Ontologien oft zehntausende oder sogar Millionen Einträge. Es wäre ineffizient, jedes Mal alle Informationen zu übertragen, da
die maximale Eingabelänge des Modells überschritten werden kann
unnötige Informationen die Rechenzeit verlängern
viele irrelevante Fakten im Prompt enthalten wären
Deshalb wird in der Praxis nur ein relevanter Ausschnitt aus der Ontologie verwendet, der zur aktuellen Frage passt.
Wie eine sinnvolle Auswahl funktioniert
Eine Ontologie ist ein strukturiertes Modell aus Subjekten, Prädikaten und Objekten. Die Auswahl der relevanten Informationen zur Beantwortung einer konkreten Frage erfolgt nicht durch statistische Ähnlichkeit oder semantisches Raten, sondern durch strukturierte Traversierung der modellierten Beziehungen.
Entscheidend ist, dass man aus der Ontologie gezielt jene Teilmengen auswählt, die inhaltlich in Abhängigkeit zur Fragestellung stehen. Dafür werden keine ML-Modelle benötigt, sondern regelbasierte Mechanismen:
Beziehungsorientierte Navigation Über Abfragen wie „MATCH (c:Charakter)-[:HATSKILL]->(s:Skill) WHERE s.name = ‚Heimlichkeit’“ kann man gezielt alle Entitäten finden, die mit dem gesuchten Konzept in Verbindung stehen.
Pfadbasierte Selektion Es kann definiert werden, dass nur Entitäten berücksichtigt werden, die mit bestimmten Missionsanforderungen über mehrere Kanten hinweg verknüpft sind, z. B. über „erfordertSkill“ und „hatSkill“.
Einschränkung nach Attributen Durch gezielte Filterung auf Knotenattribute wie „hatSkillLevel = Experte“ wird die Auswahl weiter eingegrenzt.
Graphorientierte Nachbarschaftsanalyse Man kann mit festen Regeln entscheiden, welche benachbarten Knoten entlang definierter Prädikate betrachtet werden sollen, um zum Beispiel Erfahrungen, Loyalitäten oder Verfügbarkeiten in die Auswahl einzubeziehen.
Diese Form der Selektion nutzt die Struktur der Ontologie, also ihre expliziten Kanten und Knoten, um genau die Wissenselemente zu extrahieren, die für die Fragestellung notwendig sind.
Ein LLM kommt erst nachgelagert ins Spiel, um auf Basis dieser verdichteten Faktenlage eine sprachliche Antwort zu formulieren. Die Qualität der Antwort hängt daher wesentlich von der Qualität und Relevanz der zuvor ausgewählten Teilstruktur der Ontologie ab.
neo4j
Neo4j ist eine Graphdatenbank, die speziell dafür entwickelt wurde, Beziehungen zwischen Daten effizient abzubilden und abzufragen. Im Gegensatz zu klassischen relationalen Datenbanken speichert Neo4j Informationen nicht in Tabellen, sondern in Form von Knoten (Entities), Kanten (Beziehungen) und Eigenschaften.
Mit Neo4j lassen sich komplexe, vernetzte Strukturen modellieren und analysieren:
Ontologien und Wissensgraphen (z. B. welche Person hat welche Fähigkeit und welche Aufgabe erfordert sie)
Soziale Netzwerke (z. B. wer kennt wen)
Empfehlungssysteme (z. B. welche Produkte passen zum Nutzerverhalten
Betrugserkennung (z. B. ungewöhnliche Muster in Zahlungsströmen)
IT-Architekturen (z. B. welche Systeme sind wie miteinander verbunden)
Unsere Ontologie sieht innerhalb von neo4j wie folgt aus:
Finde alle Charaktere (oder allgemein: Entitäten), die den Skill „Heimlichkeit“ besitzen.
MATCH (c:Entity)-[:HATSKILL]->(s:Entity) WHERE s.name = 'Heimlichkeit' RETURN c
Durch diese Abfrage betrachten wir gezielt nur den für die Fragestellung relevanten Ausschnitt der Ontologie, um das Sprachmodell gezielt mit passenden Metadaten anzureichern.
Kontextuelle Reduktion der Ontologie für LLM-Abfragen
Nachdem die Ontologie gezielt nach relevanten Zusammenhängen durchsucht wurde, können dem Sprachmodell anstelle der vollständigen Ontologie nur die inhaltlich relevanten Abhängigkeiten als Kontext übergeben werden.
Lade Konfiguration und initialisiere OpenAI-Client. Initialisiere Neo4j-Treiber. ===== Starte intelligente RAG-Pipeline (Version: Final mit Synonym-Matching) ===== Bereinige alte Daten, säubere neue Tripel und füge sie in Neo4j ein. Bestehende Graphen-Datenbank wurde bereinigt. 31 bereinigte Tripel wurden erfolgreich eingefügt. Ontologie-Wissensbasis: Verfügbare Fähigkeiten sind ['Axtkampf', 'Beweglichkeit', 'Bogenschießen', 'Durchhaltevermögen', 'Führung', 'Heimlichkeit', 'Schwertkampf', 'Standfestigkeit', 'Willensstärke'] Extrahiere und matche Fähigkeiten aus der Frage: 'Welcher Charakter aus 'Herr Der Ringe' verfügt über die höchste Fähigkeit in Heimlichkeit und Willenskraft für eine gefährliche Mission in Mordor?' HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Vom LLM extrahierte UND korrigierte Fähigkeiten: ['Heimlichkeit', 'Willensstärke']
Lese relevanten Teilgraph mit einem mehrstufigen, robusten Query-Ansatz. Führe Sub-Query aus für Fähigkeit: 'Heimlichkeit' -> Gefundene Charaktere: {'Samweis', 'Frodo'} Führe Sub-Query aus für Fähigkeit: 'Willensstärke' -> Gefundene Charaktere: {'Frodo'} Schnittmenge der Charaktere, die alle Fähigkeiten besitzen: {'Frodo'} Hole alle Fakten für die finalen Charaktere: ['Frodo'] Anzahl relevanter Fakten für den Kontext gefunden: 5 Erzeuge Graphvisualisierung. Suche nach pyvis-Vorlage im Verzeichnis: C:\sources\ontology\venv\Lib\site-packages\pyvis\templates Lade Vorlagendatei: template.html Graph als HTML gespeichert: ontology_graph.html
Sende Frage an LLM mit dynamisch erstelltem Kontext.
------ Finaler LLM Prompt Start ------ Du bist ein 'Herr der Ringe'-Experte. Nutze AUSSCHLIESSLICH die folgenden Fakten, um die Frage präzise zu beantworten. Begründe deine Antwort kurz basierend auf den Fakten.
Frage: Welcher Charakter aus 'Herr Der Ringe' verfügt über die höchste Fähigkeit in Heimlichkeit und Willenskraft für eine gefährliche Mission in Mordor?
Antwort: ------ Finaler LLM Prompt Ende ------
HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK" Antwort des LLM: Basierend auf den gegebenen Fakten ist Frodo der Charakter aus 'Herr der Ringe', der über die höchste Fähigkeit in Heimlichkeit und Willenskraft für eine gefährliche Mission in Mordor verfügt. Dies wird durch seine Erfahrung mit Schattenlager Infiltration, sein fortgeschrittenes Skilllevel und seine spezifischen Fähigkeiten in Heimlichkeit und Willensstärke belegt.
Basierend auf den gegebenen Fakten ist Frodo der Charakter aus 'Herr der Ringe', der über die höchste Fähigkeit in Heimlichkeit und Willenskraft für eine gefährliche Mission in Mordor verfügt. Dies wird durch seine Erfahrung mit Schattenlager Infiltration, sein fortgeschrittenes Skilllevel und seine spezifischen Fähigkeiten in Heimlichkeit und Willensstärke belegt.
# Import der notwendigen Bibliotheken from neo4j import GraphDatabase from pyvis.network import Network import networkx as nx import os from jinja2 import Environment, FileSystemLoader from data import triples import json from openai import OpenAI import logging import pyvis
# --- Logging-Konfiguration --- logger = logging.getLogger() logger.setLevel(logging.INFO) if logger.hasHandlers(): logger.handlers.clear() file_handler = logging.FileHandler("logfile.txt", mode='w', encoding='utf-8') formatter = logging.Formatter('%(message)s') file_handler.setFormatter(formatter) logger.addHandler(file_handler) # --- Ende der Logging-Konfiguration ---
# Konfiguration und OpenAI-Client initialisieren logging.info("Lade Konfiguration und initialisiere OpenAI-Client.") with open("config.json", "r", encoding="utf-8") as f: config = json.load(f) client = OpenAI(api_key=config["openai_api_key"])
# Verbindungskonfiguration zu Neo4j uri = config["neo4j_uri"] user = config["neo4j_user"] password = config["neo4j_password"]
def insert_data(triples_data): """Löscht alte Daten, bereinigt sie von Leerzeichen und fügt sie dann in Neo4j ein.""" logging.info("Bereinige alte Daten, säubere neue Tripel und füge sie in Neo4j ein.") with driver.session() as session: session.run("MATCH (n) DETACH DELETE n") logging.info("Bestehende Graphen-Datenbank wurde bereinigt.")
def get_all_skills_from_ontology(triples_data): """Extrahiert eine eindeutige Liste aller Fähigkeiten aus den Rohdaten.""" all_skills = set() for s, p, o in triples_data: if p.strip().lower() == 'hatskill': all_skills.add(o.strip()) logging.info(f"Ontologie-Wissensbasis: Verfügbare Fähigkeiten sind {sorted(list(all_skills))}") return sorted(list(all_skills))
def extract_and_match_skills(question, available_skills): """ Nutzt das LLM, um Fähigkeiten zu extrahieren UND sie mit den verfügbaren Fähigkeiten aus der Ontologie abzugleichen. """ logging.info(f"Extrahiere und matche Fähigkeiten aus der Frage: '{question}'")
available_skills_str = ", ".join(available_skills) prompt = ( "Du bist ein Experte für die Analyse von Anfragen. Deine Aufgabe besteht aus zwei Schritten:\n" "1. Extrahiere die Schlüssel-Fähigkeiten (skills), die ein Charakter aus der Frage besitzen muss.\n" "2. Vergleiche jede extrahierte Fähigkeit mit der folgenden Liste von verfügbaren Fähigkeiten und finde die exakte Übereinstimmung. " "Korrigiere Synonyme oder kleine Tippfehler (z.B. wenn der Nutzer 'Willenskraft' sagt, solltest du 'Willensstärke' zurückgeben).\n\n" f"Verfügbare Fähigkeiten in der Datenbank: [{available_skills_str}]\n\n" f"Frage des Nutzers: \"{question}\"\n\n" "Gib das Ergebnis als ein JSON-Objekt zurück, das einen einzigen Schlüssel namens 'korrigierte_faehigkeiten' hat, der ein Array der exakten, korrigierten Fähigkeitsnamen aus der Liste ist." )
try: response = client.chat.completions.create( model="gpt-4-turbo", response_format={"type": "json_object"}, messages=[ {"role": "system", "content": "You are a helpful assistant designed to output JSON."}, {"role": "user", "content": prompt} ], temperature=0 )
extracted_text = response.choices[0].message.content data = json.loads(extracted_text) skills = data.get("korrigierte_faehigkeiten", [])
if not isinstance(skills, list) or not all(isinstance(s, str) for s in skills): logging.error(f"LLM hat kein valides JSON-Array von Strings zurückgegeben. Antwort: {extracted_text}") return []
logging.info(f"Vom LLM extrahierte UND korrigierte Fähigkeiten: {skills}") return skills except Exception as e: logging.error(f"Fehler bei der Extraktion/Matching der Fähigkeiten mit dem LLM: {e}") return []
def get_relevant_facts(skills): """Fragt die Datenbank mit dem robusten, mehrstufigen Ansatz ab.""" if not skills: logging.info("Keine Fähigkeiten zur Abfrage vorhanden.") return []
logging.info("\nLese relevanten Teilgraph mit einem mehrstufigen, robusten Query-Ansatz.") character_sets = [] with driver.session() as session: for skill in skills: query = "MATCH (c:Entity)-[:HATSKILL]->(:Entity {name: $skill_param}) RETURN c.name AS name" logging.info(f"Führe Sub-Query aus für Fähigkeit: '{skill}'") result = session.run(query, skill_param=skill) characters_with_skill = {record["name"] for record in result} logging.info(f" -> Gefundene Charaktere: {characters_with_skill if characters_with_skill else 'Keine'}") character_sets.append(characters_with_skill)
if not character_sets: final_characters = set() else: final_characters = character_sets[0].intersection(*character_sets[1:]) logging.info(f"Schnittmenge der Charaktere, die alle Fähigkeiten besitzen: {final_characters if final_characters else 'Keine'}")
if not final_characters: logging.info("Anzahl relevanter Fakten für den Kontext gefunden: 0") return []
with driver.session() as session: query = "MATCH (c:Entity)-[r]->(o:Entity) WHERE c.name IN $characters_param RETURN c.name AS source, type(r) AS rel, o.name AS target" logging.info(f"Hole alle Fakten für die finalen Charaktere: {list(final_characters)}") result = session.run(query, characters_param=list(final_characters)) triples = [(r["source"], r["rel"], r["target"]) for r in result] logging.info(f"Anzahl relevanter Fakten für den Kontext gefunden: {len(triples)}") return triples
def visualize_graph(triples_data): """Erstellt eine HTML-Visualisierung des Graphen mit expliziter Template-Pfad-Logik.""" if not triples_data: logging.info("Keine Daten zur Visualisierung vorhanden. Es wird keine Graph-Datei erzeugt.") return logging.info("Erzeuge Graphvisualisierung.") G = nx.DiGraph() for s, p, o in triples_data: G.add_edge(s, str(o), label=p)
net = Network(notebook=False, directed=True, height="750px", width="100%") net.from_nx(G)
# FINALE KORREKTUR: Explizites Laden der pyvis-Vorlage zur Behebung des 'NoneType'-Fehlers. # Diese Methode ist robust und stellt sicher, dass die HTML-Vorlage gefunden wird, # unabhängig von der Installationsumgebung des Pakets. try: template_dir = os.path.join(os.path.dirname(pyvis.__file__), 'templates') logging.info(f"Suche nach pyvis-Vorlage im Verzeichnis: {template_dir}")
# In neueren pyvis-Versionen heißt die Hauptvorlage 'index.html'. # Wir prüfen deren Existenz und greifen für die Kompatibilität auf 'template.html' zurück. template_filename = "index.html" if not os.path.exists(os.path.join(template_dir, template_filename)): template_filename = "template.html"
except Exception as e: logging.error(f"Konnte pyvis-Vorlage nicht laden, Visualisierung könnte fehlschlagen. Fehler: {e}")
net.show_buttons(filter_=['physics']) output_file = "ontology_graph.html" net.show(output_file) logging.info(f"Graph als HTML gespeichert: {output_file}")
def answer_question_with_ontology(question, context_triples): logging.info("\nSende Frage an LLM mit dynamisch erstelltem Kontext.") if not context_triples: context = "Keine spezifischen Fakten in der Wissensdatenbank gefunden, die exakt zur Frage passen. Antworte basierend auf deinem Allgemeinwissen." else: context = "\n".join([f"- {s} {p.replace('_', ' ').lower()} {o}" for s, p, o in context_triples]) prompt = (f"Du bist ein 'Herr der Ringe'-Experte. Nutze AUSSCHLIESSLICH die folgenden Fakten, um die Frage präzise zu beantworten. Begründe deine Antwort kurz basierend auf den Fakten.\n\nFakten:\n{context}\n\nFrage: {question}\n\nAntwort:") logging.info("\n------ Finaler LLM Prompt Start ------") for line in prompt.splitlines(): logging.info(line) logging.info("------ Finaler LLM Prompt Ende ------\n") response = client.chat.completions.create(model="gpt-4", messages=[{"role": "user", "content": prompt}], temperature=0) antwort = response.choices[0].message.content logging.info(f"Antwort des LLM:\n{antwort}") print("\n--- Antwort des LLM ---\n") print(antwort) print("\n-----------------------\n")
# Hauptausführung if __name__ == "__main__": try: logging.info("===== Starte intelligente RAG-Pipeline (Version: Final mit Synonym-Matching) =====") insert_data(triples)
# NEUER SCHRITT: Hole alle verfügbaren Fähigkeiten aus der Ontologie available_skills = get_all_skills_from_ontology(triples)
frage = config["frage"] print(f"Analysiere die Frage: '{frage}'")
except Exception as e: logging.error(f"Ein schwerwiegender Fehler ist aufgetreten: {e}", exc_info=True) finally: logging.info("\nSchließe Datenbankverbindung.") driver.close() logging.info("===== Pipeline-Durchlauf beendet =====")
app.py
{ "neo4j_uri": "bolt://localhost:7687", "neo4j_user": "neo4j", "neo4j_password": "secret123", "cypher_query": "MATCH (c:Entity)-[r:HATSKILL]->(s:Entity) WHERE s.name IN ['Heimlichkeit', 'Willensstärke'] WITH c, collect(s.name) AS skills WHERE 'Heimlichkeit' IN skills AND 'Willensstärke' IN skills MATCH (c)-[r2:HATSKILL]->(s2:Entity) RETURN c.name AS source, type(r2) AS rel, s2.name AS target", "frage": "Welcher Charakter aus 'Herr Der Ringe' verfügt über die höchste Fähigkeit in Heimlichkeit und Willenskraft für eine gefährliche Mission in Mordor?", "openai_api_key": "sk-...oF" }
config.json
OWL (Web Ontology Language)
In den vorherigen Abschnitten haben wir gesehen, wie eine Ontologie in Form eines Graphen mit einfachen Tripeln (Subjekt-Prädikat-Objekt) die Qualität der Antworten eines LLMs drastisch verbessern kann. Wir haben Entitäten wie Frodo und Heimlichkeit und die Beziehung hatSkill zwischen ihnen modelliert.
Dieser Ansatz ist bereits sehr mächtig. Allerdings weiß unser System bisher nur, dass eine Verbindung besteht, aber nicht, was diese Verbindung logisch bedeutet oder welchen Regeln sie unterliegt. Um diese Lücke zu schließen und unserer Ontologie eine tiefere Ebene der „Intelligenz“ zu verleihen, nutzen wir die Web Ontology Language (OWL).
OWL ist ein vom W3C standardisiertes Format zur Erstellung von umfassenden, formalen Ontologien. Man kann es sich als eine Erweiterung von RDF (dem Framework, auf dem unser Tripel-Modell basiert) vorstellen.
Wenn unser bisheriger Graph das Vokabular und die einfachen Sätze liefert, dann ist OWL die Grammatik und das Regelwerk, das die logischen Zusammenhänge dazwischen beschreibt. OWL erlaubt es uns, die Bedeutung der Begriffe und Beziehungen in unserer „Herr der Ringe“-Welt so zu definieren, dass eine Maschine sie nicht nur lesen, sondern auch verstehen und darüber schlussfolgern kann. Die Umstellung oder Erweiterung unserer Ontologie mit OWL würde uns mehrere entscheidende Vorteile bringen, die die Qualität der an das LLM übergebenen Fakten nochmals deutlich steigern.
Problem heute In unserem Graphen sind Frodo, Heimlichkeit und Schattenlager Infiltration allesamt Knoten vom gleichen Typ (Entity). Das System weiß nicht, dass es sich um einen Charakter, eine Fähigkeit und eine Mission handelt.
Lösung mit OWL Wir können formale Klassen definieren: Charakter, Faehigkeit und Mission. Anschließend können wir sogar Hierarchien bilden, z.B. könnten Hobbit und Elb Unterklassen von Charakter sein. Frodo wäre dann eine Instanz der Klasse Hobbit.
Vorteil Das System kann viel präzisere Abfragen durchführen („Finde alle Hobbits, die die Fähigkeit ‚Heimlichkeit‘ haben“). Logische Fehler, wie eine Mission einer Loyalität zuzuweisen, werden unmöglich, und der Kontext für das LLM wird unmissverständlich klar.
Indem wir OWL verwenden, verwandeln wir unseren bestehenden Wissensgraphen von einer reinen Datensammlung in ein dynamisches, logisches Modell unserer „Herr der Ringe“-Welt. Die Fakten, die wir dann an das LLM übergeben, sind nicht nur präziser und kontextreicher, sondern können auch Wissen enthalten, das durch logische Schlussfolgerungen erst generiert wurde. Dies ist der nächste, entscheidende Schritt zur Maximierung der Antwortqualität.