Bringt es was, das ganze Firmenwissen als PDF in die KI zu kippen?

Gerade liegt bei einem Mittelständler die Entscheidung an, das komplette Firmenwissen in ein lokales KI-System zu laden. Der Plan klingt naheliegend: alle PDFs hochladen, fertig. Und weil ein großer Teil des Wissens in einer Datenbank steckt, soll die vorher als PDF exportiert und gleich mit hochgeladen werden.

Das ist der teuerste Weg zum schlechtesten Ergebnis. Nicht, weil die KI zu dumm wäre. Sondern weil PDF das falsche Format ist, um Daten für RAG aufzubereiten.

Das eigentliche Problem sitzt nicht im Modell

Wer ein lokales RAG-System wie AnythingLLM, LM Studio oder Ollama aufsetzt, kümmert sich meist intensiv um das Modell: Welches läuft auf der Hardware, wie viel Kontext, welche Quantisierung. Die Datenaufbereitung wird zur Nebensache erklärt — man kippt rein, was da ist.

Genau hier kippt die Qualität. RAG zerlegt jedes Dokument in Häppchen („Chunks“), wandelt die in Vektoren um und sucht bei einer Frage die passenden Häppchen heraus. Wie gut diese Häppchen geschnitten sind, hängt am Eingangsformat. Schlecht geschnittene Häppchen bedeuten schlechte Antworten — egal, wie gut das Modell ist.

Aus meiner Praxis im norddeutschen Mittelstand zeigt sich: Die Antwortqualität eines RAG-Systems wird zu einem überraschend großen Teil schon entschieden, bevor das Modell überhaupt eine Zeile sieht.

Daten für RAG aufbereiten heißt: Struktur erhalten, nicht Layout

PDF ist ein Druckformat. Es beschreibt, wo auf einer Seite welche Buchstaben stehen — nicht, was inhaltlich zusammengehört. Beim Auslesen passiert deshalb regelmäßig das hier:

  • Mehrspaltiger Satz wird in falscher Lesereihenfolge zusammengewürfelt.
  • Tabellen zerfallen, Spalten landen wild durcheinander.
  • Kopf- und Fußzeilen, Seitenzahlen und Wasserzeichen rutschen als Rauschen in die Chunks.
  • Eingescannte Seiten sind reine Bilder und brauchen erst OCR — mit eigenen Fehlern.

Eine als PDF exportierte Datenbankabfrage ist der Extremfall: Du nimmst sauber strukturierte Daten und presst sie in ein Format, das diese Struktur wieder zerstört. Die Maschine muss anschließend mühsam rekonstruieren, was vorher schon ordentlich vorlag.

Es gibt eine brauchbare Rangfolge fürs Mitnehmen, wenn es um Material für KI-Systeme geht:

strukturiertes Markdown / Plaintext > sauberes semantisches HTML > rohes oder JavaScript-lastiges HTML > PDF.

Markdown gewinnt aus zwei getrennten Gründen. Erstens der Token-Aufwand: Eine Überschrift ist ein #, eine Liste ein -, eine Tabelle ein paar |. Kaum Ballast. HTML schleppt für denselben Inhalt Tags, schließende Elemente, div-Verschachtelungen, Klassen und Inline-Styles mit — der Tokenizer zählt das alles mit, du bezahlst Layout-Müll. Zweitens die Schnitt-Qualität: Markdown hat verlässliche Grenzen. Eine Überschrift ist ein sauberer Schnittpunkt, eine Tabelle bleibt eine Tabelle.

Wichtig zur Einordnung, damit es nicht zu absolut klingt: Sauberes, semantisches HTML ist für KI durchaus brauchbar — es ist die Muttersprache des Webs. Das Problem sind verschachteltes JavaScript-HTML und eben PDF. Markdown ist nur die schlankere und zuverlässigere Variante.

Was Markdown und OKF eigentlich sind

Markdown ist ein leichtgewichtiges Textformat, das Struktur mit minimalen Sonderzeichen abbildet — lesbar für Menschen und für Maschinen, ohne Spezial-Software. Eine Markdown-Datei lässt sich in jedem Editor öffnen und gleichzeitig zuverlässig von einer KI verarbeiten.

Im November 2025 hat Google dazu das Open Knowledge Format (OKF) veröffentlicht — derzeit als Entwurf in Version 0.1. OKF ist eine schlanke Konvention, um Wissen als Verzeichnis von Markdown-Dateien mit YAML-Kopfzeile abzulegen, sodass sowohl Menschen als auch KI-Agenten es lesen und durchsuchen können. Keine zentrale Instanz, keine Pflicht-Software. Wer eine Datei mit cat anzeigen kann, kann OKF lesen.

Der Kern besteht aus wenigen Bausteinen: Jede .md-Datei beschreibt ein Konzept (eine Tabelle, eine Kennzahl, ein Prozess). Oben steht ein kleiner YAML-Block mit Feldern wie type (Pflicht), title, description und timestamp. Darunter folgt der Inhalt als normales Markdown, idealerweise mit einer # Schema-Überschrift und einer Tabelle. Verlinkt wird über normale Markdown-Links.

OKF ist nicht der einzige Ansatz dieser Art, und es ist auch keine technische Neuerung. Verwandt sind:

  • llms.txt — eine Markdown-Datei im Wurzelverzeichnis einer Website, die einer KI die wichtigsten Inhalte strukturiert anbietet.
  • Obsidian und Notion — persönliche Wissensspeicher auf Basis von Markdown mit Querverweisen.
  • dbt docs und ähnliche „Metadata as Code“-Ansätze — Katalog-Metadaten direkt neben dem Code statt in einer separaten Datenbank.
  • MCP (Model Context Protocol) — liegt auf einer anderen Ebene: Das ist die Tür zum Zugriff, nicht das Lagerformat. OKF und MCP konkurrieren nicht, sie ergänzen sich.

Der einzige echte Mehrwert von OKF ist, dass diese ohnehin verbreitete Praxis jetzt einen Namen, eine Spezifikation und einen Google-Stempel hat. Für die Praxis zählt nicht das Etikett, sondern die Haltung dahinter: Struktur vor KI.

Für die Entwickler: Eine Access-Abfrage als OKF-Markdown exportieren

Ab hier wird es technisch. Wen nicht der Code interessiert, sondern die Wirtschaftlichkeit, springt zum nächsten Abschnitt.

Der Punkt ist: Eine Datenbank ist bereits ein OKF-Bundle — sie weiß es nur noch nicht. Spalten, Datentypen, Beziehungen, Beschreibungen: Die Struktur, die ein RAG-System braucht, liegt längst im Schema. Statt sie über einen PDF-Export zu zerstören, exportiert man sie direkt als OKF-konformes Markdown.

Die folgende VBA-Funktion nimmt eine gespeicherte Access-Abfrage (oder Ad-hoc-SQL), setzt optional einen Parameter und schreibt das Ergebnis als OKF-Markdown-Datei: YAML-Frontmatter plus # Schema-Tabelle. Geschrieben wird per ADODB.Stream in UTF-8 — damit Lübeck auch Lübeck bleibt und nicht als Zeichensalat im Chunk landet. Die API-Deklaration für den Zeitstempel ist 32- und 64-Bit-sicher gehalten.

Option Compare Database
Option Explicit

' --- Windows-API für echten UTC-Zeitstempel (32/64-Bit-sicher) ---
Private Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

#If VBA7 Then
    Private Declare PtrSafe Sub GetSystemTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
#Else
    Private Declare Sub GetSystemTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
#End If

'==============================================================
' ExportAbfrageAlsOKF
'   Exportiert das Ergebnis einer (parametrisierten) Abfrage als
'   OKF-konforme Markdown-Datei (Frontmatter + # Schema-Tabelle).
'
'   sAbfrage      : Name einer gespeicherten Abfrage ODER SQL-Text
'   sZielDatei    : vollständiger Pfad der .md-Zieldatei
'   sType         : OKF-Pflichtfeld, z. B. "Access Query"
'   sTitel        : Anzeigename
'   sBeschreibung : optionaler Einzeiler
'   sResource     : optionale URI zur Quelle
'   sTags         : optional, kommagetrennt (z. B. "kunden, vertrieb")
'   vParameter    : optionaler Wert für den ersten Abfrageparameter
'
'   Rückgabe: Anzahl exportierter Datensätze, -1 bei Fehler
'==============================================================
Public Function ExportAbfrageAlsOKF( _
        ByVal sAbfrage As String, _
        ByVal sZielDatei As String, _
        ByVal sType As String, _
        ByVal sTitel As String, _
        Optional ByVal sBeschreibung As String = "", _
        Optional ByVal sResource As String = "", _
        Optional ByVal sTags As String = "", _
        Optional ByVal vParameter As Variant) As Long

    Dim db As DAO.Database
    Dim qd As DAO.QueryDef
    Dim rs As DAO.Recordset
    Dim sb As String
    Dim sLine As String
    Dim i As Long, n As Long
    Dim bTempQd As Boolean

    On Error GoTo Fehler
    Set db = CurrentDb

    ' Abfrage auflösen: gespeicherte Abfrage oder Ad-hoc-SQL
    If QueryDefVorhanden(db, sAbfrage) Then
        Set qd = db.QueryDefs(sAbfrage)
    Else
        Set qd = db.CreateQueryDef("", sAbfrage)   ' temporär, nicht gespeichert
        bTempQd = True
    End If

    ' Parameter setzen, falls vorhanden und übergeben
    If Not IsMissing(vParameter) Then
        If qd.Parameters.Count > 0 Then qd.Parameters(0).Value = vParameter
    End If

    Set rs = qd.OpenRecordset(dbOpenSnapshot)

    ' --- YAML-Frontmatter (OKF, Abschnitt 4.1) ---
    sb = "---" & vbLf
    sb = sb & "type: " & sType & vbLf                       ' Pflichtfeld
    sb = sb & "title: " & sTitel & vbLf
    If Len(sBeschreibung) > 0 Then sb = sb & "description: " & sBeschreibung & vbLf
    If Len(sResource) > 0 Then sb = sb & "resource: " & sResource & vbLf
    If Len(sTags) > 0 Then sb = sb & "tags: [" & sTags & "]" & vbLf
    sb = sb & "timestamp: " & ISO8601UTC() & vbLf
    sb = sb & "---" & vbLf & vbLf

    ' --- Body: # Schema als Markdown-Tabelle (OKF, Abschnitt 4.2) ---
    sb = sb & "# Schema" & vbLf & vbLf

    ' Kopfzeile aus Feldnamen
    sLine = "|"
    For i = 0 To rs.Fields.Count - 1
        sLine = sLine & " " & rs.Fields(i).Name & " |"
    Next i
    sb = sb & sLine & vbLf

    ' Trennzeile
    sLine = "|"
    For i = 0 To rs.Fields.Count - 1
        sLine = sLine & " --- |"
    Next i
    sb = sb & sLine & vbLf

    ' Datenzeilen
    n = 0
    Do While Not rs.EOF
        sLine = "|"
        For i = 0 To rs.Fields.Count - 1
            sLine = sLine & " " & MDZelle(rs.Fields(i).Value) & " |"
        Next i
        sb = sb & sLine & vbLf
        n = n + 1
        rs.MoveNext
    Loop

    SchreibeUTF8 sZielDatei, sb
    rs.Close
    ExportAbfrageAlsOKF = n

Aufraeumen:
    On Error Resume Next
    If Not rs Is Nothing Then rs.Close
    Set rs = Nothing
    If bTempQd And Not qd Is Nothing Then qd.Close
    Set qd = Nothing
    Set db = Nothing
    Exit Function

Fehler:
    MsgBox "Export fehlgeschlagen: " & Err.Number & " - " & Err.Description, _
           vbExclamation, "ExportAbfrageAlsOKF"
    ExportAbfrageAlsOKF = -1
    Resume Aufraeumen
End Function

' Prüft, ob eine gespeicherte Abfrage mit dem Namen existiert
Private Function QueryDefVorhanden(ByVal db As DAO.Database, ByVal sName As String) As Boolean
    Dim qd As DAO.QueryDef
    On Error Resume Next
    Set qd = db.QueryDefs(sName)
    QueryDefVorhanden = (Err.Number = 0) And (Not qd Is Nothing)
End Function

' Macht einen Feldwert tabellensicher: Null -> "", Pipe escapen, Umbrüche raus
Private Function MDZelle(ByVal v As Variant) As String
    Dim s As String
    If IsNull(v) Then MDZelle = "": Exit Function
    s = CStr(v)
    s = Replace(s, "|", "\|")
    s = Replace(s, vbCrLf, " ")
    s = Replace(s, vbCr, " ")
    s = Replace(s, vbLf, " ")
    MDZelle = Trim$(s)
End Function

' ISO-8601-Zeitstempel in UTC, z. B. 2026-06-13T09:30:00Z
Private Function ISO8601UTC() As String
    Dim st As SYSTEMTIME
    GetSystemTime st
    ISO8601UTC = Format$(st.wYear, "0000") & "-" & Format$(st.wMonth, "00") & "-" & _
                 Format$(st.wDay, "00") & "T" & Format$(st.wHour, "00") & ":" & _
                 Format$(st.wMinute, "00") & ":" & Format$(st.wSecond, "00") & "Z"
End Function

' Schreibt Text als UTF-8 (late-bound, keine Referenz nötig)
Private Sub SchreibeUTF8(ByVal sPfad As String, ByVal sInhalt As String)
    Dim stm As Object
    Set stm = CreateObject("ADODB.Stream")
    stm.Type = 2                 ' adTypeText
    stm.Charset = "utf-8"
    stm.Open
    stm.WriteText sInhalt
    stm.SaveToFile sPfad, 2      ' adSaveCreateOverWrite
    stm.Close
    Set stm = Nothing
End Sub

Entscheidend ist die letzte Idee: Der Export erzeugt keine schöne Seite zum Ausdrucken, sondern eine maschinenlesbare Tabelle mit Kontext im Kopf. Genau das, was ein RAG-System braucht.

Die zugehörige Abfrage qry_KundenNachRegion deklariert ihren Parameter explizit, damit die Funktion ihn setzen kann:

PARAMETERS [prmRegion] Text(255);
SELECT KundenNr, Firma, Ort, Umsatz
FROM   tblKunden
WHERE  Region = [prmRegion] AND Aktiv = True;

Der Aufruf bleibt überschaubar:

Dim n As Long
n = ExportAbfrageAlsOKF( _
        sAbfrage:="qry_KundenNachRegion", _
        sZielDatei:="C:\okf-bundle\tables\kunden-ostholstein.md", _
        sType:="Access Query", _
        sTitel:="Kunden Region Ostholstein", _
        sBeschreibung:="Aktive Kunden im Kreis Ostholstein, eine Zeile pro Kunde.", _
        sResource:="access://Kundendb/qry_KundenNachRegion", _
        sTags:="kunden, ostholstein, vertrieb", _
        vParameter:="Ostholstein")
Debug.Print n & " Datensaetze exportiert."

Wer das pro Region, pro Standort oder pro Produktgruppe in einer Schleife laufen lässt, hat in wenigen Minuten ein ganzes OKF-Bundle aus der Datenbank gezogen — verlustfrei, durchsuchbar und versionierbar. Dasselbe Prinzip lässt sich für SQL Server über eine entsprechende Prozedur abbilden.

Wo der Ansatz an Grenzen stößt

Markdown löst nicht alles. Tabellarische Daten gehen sauber, lange Freitext-Verträge oder gescannte Altdokumente bleiben Handarbeit und brauchen weiterhin OCR. Die Metadaten im Kopf müssen jemand pflegen — ein guter description-Satz pro Datei kostet Zeit, zahlt sich aber bei jeder Abfrage aus. Und OKF ist Stand jetzt ein Entwurf in Version 0.1; wer darauf baut, baut auf etwas, das sich noch ändern kann. Für die eigene RAG-Aufbereitung ist das unkritisch, weil es ohnehin nur normale Markdown-Dateien sind. Ein kleiner Stolperstein am Rande: ADODB.Stream schreibt UTF-8 standardmäßig mit BOM — die meisten RAG-Parser stört das nicht, einzelne schon. Im Zweifel das BOM entfernen.

Was das für dich bedeutet, wenn du das nicht selbst baust

Die Struktur, die deine KI braucht, liegt seit Jahren in deiner Datenbank. Spalten, Beziehungen, Beschreibungen — alles da. Der Reflex, das vor dem KI-Projekt als PDF zu exportieren, vergräbt diese Struktur ausgerechnet in dem Moment, in dem du sie am dringendsten brauchst.

Für ein RAG-Projekt heißt das konkret: Bevor Budget in Hardware und Modelle fließt, lohnt eine Stunde Nachdenken über das Eingangsformat. Wer Datenbank-Inhalte strukturiert exportiert statt als PDF, bekommt bessere Antworten bei weniger Rechenaufwand — und spart sich die Enttäuschung, dass die teure KI „irgendwie nicht richtig funktioniert“.

Struktur vor KI ist kein Slogan. Es ist die Reihenfolge, in der ein RAG-Projekt gelingt oder scheitert.

Wenn du ein RAG-Projekt mit eigenen Daten planst

Wer vor der Entscheidung steht, wie das Firmenwissen in ein KI-System kommt, sollte das Eingangsformat klären, bevor das erste PDF hochgeladen wird. Wer sich das einmal anschauen lassen möchte, erreicht mich über sesoft.de/kontakt.

Quellen

Über den Autor

Sönke Schäfer ist selbstständiger IT-Berater und Datenarchitekt aus Sierksdorf in Ostholstein und betreut seit über 25 Jahren norddeutsche KMU. Sein Schwerpunkt liegt auf der Anbindung von KI an bestehende Datenbanken — Microsoft Access, VBA und SQL Server als Fundament, nicht als Altlast. Mehr unter Der Datenschäfer.

PS:

AnythingLLM kann SQL Server übrigens auch direkt — aber als Agent-Skill, nicht als RAG. Der SQL-Connector ist Text-to-SQL: Das Modell schaut sich per @agent die Tabellen an, formuliert daraus eine Abfrage und zieht die Zeilen live aus der Datenbank. Nichts davon wird eingebettet. Das ist der zweite Weg neben dem hier gezeigten Export — und beide haben ihren Platz: Für exakte, tagesaktuelle Auswertungen ist der SQL-Agent stark, für durchsuchbares Wissen neben Dokumenten der eingebettete Markdown-Export. Wichtig fürs Mitnehmen: PDF ist für beide Wege falsch. Beim RAG zerstört es die Struktur, beim SQL-Agenten ist es schlicht überflüssig, weil der die lebende Datenbank abfragt und gar keinen Export braucht. Wer den Agent-Weg geht, sollte ihm in jedem Fall einen nur-lesenden Datenbankbenutzer geben.

Nach oben scrollen