BLEIBT INFORMIERT

Meldet euch für unseren Newsletter an und erhaltet exklusive Updates zu Blog, IT-Trends und Neuigkeiten der ORDIX AG.

BLEIBEN SIE INFORMIERT

Melden Sie sich für unsere Newsletter an und erhalten Sie exklusive Updates zu IT-Trends und Neuigkeiten der ORIDX AG.

Moderne Datentransformation: von Stored Procedures...
12 Minuten Lesezeit (2425 Worte)

RAG ohne externe Systeme mit Oracle 26ai

Im ersten Teil dieser Reihe haben wir gezeigt, wie SELECT AI natürlichsprachliche Anfragen in SQL übersetzen kann, ohne dass Anwender:innen SQL beherrschen müssen. Dieser zweite Teil geht einen Schritt weiter.

SELECT AI übersetzt Fragen in SQL und führt diese gegen vorhandene Tabellen aus. Das Ergebnis hängt davon ab, ob die Frage präzise genug ist und ob die Tabellenstruktur zum Anwendungsfall passt. Für Szenarien, in denen die relevante Information nicht tabellarisch vorliegt oder die Frage thematisch formuliert ist, braucht es einen anderen Ansatz.

Oracle Database 26ai bietet dafür Oracle AI Vector Search in Kombination mit Retrieval Augmented Generation (RAG). Dieser Artikel zeigt, wie beides in einer Autonomous Database aufgebaut wird, welche Stolperstellen dabei auftreten und wie der gesamte Stack ohne externe Vektordatenbank funktioniert.

Was ist die Oracle AI Vector Search und warum ist sie relevant?

Klassische SQL-Abfragen arbeiten mit exakten Werten: Gleichheit, Bereiche, Mustererkennung per LIKE. Eine Suchanfrage wie „Welches Seminar hilft mir, Datenbanken abzusichern?“ lässt sich damit nicht sinnvoll abbilden, weil kein Datensatz diese Zeichenkette enthält.

Vektorsuche löst dieses Problem auf andere Weise. Texte werden durch ein Embedding-Modell in numerische Vektoren umgewandelt. Semantisch ähnliche Texte landen dabei nah beieinander im hochdimensionalen Raum. Eine Suchanfrage wird mit demselben Embedding-Modell in einen Vektor umgewandelt. Anschließend werden die Datensätze gesucht, deren Vektoren dem Anfragevektor semantisch am nächsten liegen.

Oracle 26ai integriert dieses Konzept direkt in die relationale Datenbank. Der native VECTOR-Datentyp speichert Embeddings transaktionssicher neben klassischen Spalten. Abfragen und Indexerstellung erfolgen per SQL, die Embedding-Generierung über PL/SQL-Pakete. Eine separate Vektordatenbank ist nicht notwendig.

Der VECTOR-Datentyp

Oracle 26ai führt mit VECTOR einen nativen Spaltentyp ein, der Gleitkomma-Arrays direkt in der Datenbank speichert. 

VECTOR(<dimensionen>, <format>)

VECTOR(1024, FLOAT32)   -- Cohere embed-multilingual-v3.0
VECTOR(1536, FLOAT32)   -- OpenAI text-embedding-3-small 

Die Dimensionsanzahl muss exakt zum verwendeten Embedding-Modell passen. Oracle erzwingt hier Konsistenz. Stimmen die Dimensionen nicht überein, wird der Vorgang mit ORA-51803 abgebrochen.

Für die Abstandsberechnung stehen mehrere Metriken zur Verfügung:

VECTOR_DISTANCE(v1, v2, COSINE)       -- Winkelabstand, häufigste Wahl für Text
VECTOR_DISTANCE(v1, v2, EUCLIDEAN)    -- euklidischer Abstand im n-dim. Raum
VECTOR_DISTANCE(v1, v2, DOT)          -- Skalarprodukt (grösser = ähnlicher)
VECTOR_DISTANCE(v1, v2, MANHATTAN)    -- Summe absoluter Differenzen 

Für Text Embeddings ist Cosine-Ähnlichkeit meist die richtige Wahl. Sie vergleicht die Richtung der Embedding-Vektoren statt ihrer Länge und fokussiert damit auf die semantische Ähnlichkeit der Texte. 

Voraussetzungen

Die folgenden Schritte setzen voraus:

  • Oracle Autonomous Database 26ai
  • OCI-Account mit aktiviertem Generative AI Service und ausreichenden Berechtigungen für Modellaufrufe
  • Datenbankzugang als Admin und als Schema-User (in den Beispielen: VECTEST)
  • Die Tabelle SEMINARE_TEST aus Teil 1 dieser Reihe ist vorhanden und befüllt

Schritt 1: Tabelle um Vector-Spalten erweitern

Die Tabelle SEMINARE_TEST aus Teil 1 wird um eine Beschreibungsspalte sowie eine VECTOR-Spalte für das Embedding erweitert.

ALTER TABLE seminare_test ADD beschreibung VARCHAR2(2000);
ALTER TABLE seminare_test ADD embedding    VECTOR(1024, FLOAT32);

COMMENT ON COLUMN seminare_test.beschreibung IS 'Inhaltliche Beschreibung des Seminars.';
COMMENT ON COLUMN seminare_test.embedding    IS 'Vektor-Embedding (Cohere embed-multilingual-v3.0, 1024 Dim.)'; 

Schritt 2: Beschreibungen eintragen

Jedes Seminar bekommt eine inhaltliche Beschreibung. Diese wird später als Grundlage für das Embedding verwendet und entscheidet wesentlich über die Qualität der Suche. Je präziser die Beschreibung, desto besser die Ergebnisse. 

UPDATE seminare_test SET beschreibung =
    'Einführung in die Oracle-Datenbanksprache SQL. Themen: SELECT, DML, DDL, '
    || 'Joins, Unterabfragen, Gruppierung, Funktionen und Transaktionskontrolle.'
WHERE id = 1;

UPDATE seminare_test SET beschreibung =
    'KI-Features der Oracle 26ai Datenbank: Vector Search, Select AI, '
    || 'DBMS_VECTOR_CHAIN, ONNX-Modelle, Property Graphs und JSON Relational Duality.'
WHERE id = 2;

-- ... weitere Updates für id 3 bis 10 (analog)
COMMIT; 

Schritt 3: Oracle Cloud Infrastructure (OCI) API Key anlegen

Der OCI Generative AI Service, über den Embedding-Modell und Chat-Modell laufen, erfordert eine Authentifizierung per API Key. Dafür wird folgendes benötigt:

  • Ein RSA-Schlüsselpaar. Der öffentliche Schlüssel wird im eigenen OCI-Benutzerprofil hinterlegt. Der private Schlüssel (.pem-Datei) wird beim Erstellen einmalig heruntergeladen und sicher verwahrt.
  • Vier Konfigurationswerte, die nach dem Hinterlegen des öffentlichen Schlüssels in der Configuration angezeigt werden: user, fingerprint, tenancy und region.
  • Die Compartment ID, also die eindeutige Kennung des OCI-Compartments, in dem der Generative AI Service genutzt wird. Sie ist im Identity-Bereich der OCI Console bei den Compartments zu finden.

Schritt 4: Credential anlegen

Für DBMS_VECTOR und DBMS_VECTOR_CHAIN muss zwingend DBMS_VECTOR.CREATE_CREDENTIAL verwendet werden. Wer stattdessen DBMS_CLOUD.CREATE_CREDENTIAL nutzt, bekommt den Fehler ORA-20000: The credential specified does not match the URL provided.

DECLARE
    jo JSON_OBJECT_T;
BEGIN
    jo := JSON_OBJECT_T();
    jo.put('user_ocid',        'ocid1.user.oc1..xxx');
    jo.put('tenancy_ocid',     'ocid1.tenancy.oc1..xxx');
    jo.put('compartment_ocid', 'ocid1.compartment.oc1..xxx');
    jo.put('private_key',      '###......');
    jo.put('fingerprint',      '11:11:11...');

    DBMS_VECTOR.CREATE_CREDENTIAL(
        credential_name => 'OCI_VEC_CRED',
        params          => JSON(jo.to_string)
    );
END;
/ 

Das Credential kann mit folgendem Statement geprüft werden: 

SELECT credential_name, username, enabled
FROM   user_credentials; 

Schritt 5: Netzwerk-Access-Control-List (ACL) setzen

Ohne ACL-Freigabe scheitert jeder API-Aufruf mit ORA-24247: Network access denied. Die ACL muss als ADMIN gesetzt werden und richtet sich gegen den OCI-Endpunkt für den Generative AI Service. 

-- Als ADMIN ausführen
BEGIN
    DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
        host => 'inference.generativeai.eu-frankfurt-1.oci.oraclecloud.com',
        ace  => xs$ace_type(
                    privilege_list => xs$name_list('connect', 'resolve'),
                    principal_name => 'VECTEST',
                    principal_type => xs_acl.ptype_db
                )
    );
    COMMIT;
END;
/ 

Im Unterschied zu SELECT AI aus Teil 1 (dort wurden http und http_proxy benötigt) sind hier connect und resolve die relevanten Privilegien.

Vor der Massenverarbeitung empfiehlt sich ein einfacher Test, ob der API-Aufruf grundsätzlich funktioniert: 

SELECT DBMS_VECTOR.UTL_TO_EMBEDDING(
           'Das ist ein Test.',
           JSON('{
               "provider"        : "ocigenai",
               "credential_name" : "OCI_VEC_CRED",
               "url" : "https://inference.generativeai... ",
               "model" : "cohere.embed-multilingual-v3.0"
           }')
       ) AS test_vector
FROM DUAL; 

Schritt 6: Embeddings für alle Datensätze erzeugen

Das folgende PL/SQL-Skript verarbeitet alle Datensätze ohne vorhandenes Embedding. Das Embedding besteht aus einer Kombination von Bezeichnung, Beschreibung und Kategorie, damit der Vektor möglichst viel semantischen Kontext trägt. 

DECLARE
    CURSOR c_seminare IS
        SELECT id, bezeichnung, beschreibung, kategorie
        FROM   seminare_test
        WHERE  embedding IS NULL;
BEGIN
    FOR r IN c_seminare LOOP
        UPDATE seminare_test
        SET    embedding = DBMS_VECTOR.UTL_TO_EMBEDDING(
                           r.bezeichnung || '. ' || r.beschreibung              ' Kategorie: ' || r.kategorie,
                           JSON('{
                            "provider": "ocigenai",
                            "credential_name": "OCI_VEC_CRED",
                            "url": "https://inference.genera.... ",
                            "model": "cohere.embed-multilingual-v3.0"
                               }')
                           )
        WHERE  id = r.id;
        COMMIT;
        DBMS_OUTPUT.PUT_LINE('Embedding generiert für ID: ' || r.id || ' - ' || r.bezeichnung);
    END LOOP;
END;
/ 

Schritt 7: Vector Index anlegen

Für performante Suchen bei größeren Datenmengen wird ein Vector Index angelegt. Der INMEMORY NEIGHBOR GRAPH Typ (HNSW) bietet sehr gute Antwortzeiten bei hoher Trefferqualität. 

CREATE VECTOR INDEX idx_seminare_vec
ON     seminare_test (embedding)
ORGANIZATION INMEMORY NEIGHBOR GRAPH
DISTANCE     COSINE
WITH TARGET ACCURACY 95; 

TARGET ACCURACY 95 beschreibt die angestrebte Treffergenauigkeit bei Approximate-Nearest-Neighbor-Abfragen. Bei größeren Datenmengen ist der Index relevant für die Performance. 

Schritt 8: Semantische Suche per SQL

Die eigentliche Stärke des Setups zeigt sich bei der Suche. Eine thematische Frage wird in einen Vektor umgewandelt und dann gegen alle gespeicherten Embeddings verglichen. Als Ergebnis kommen die thematisch ähnlichsten Seminare.

Der Query-Vektor wird per CTE erzeugt, damit der API-Aufruf nur einmal ausgeführt wird und nicht für jede Zeile der Tabelle wiederholt wird.

WITH query_vec AS (
    SELECT DBMS_VECTOR.UTL_TO_EMBEDDING(
               'Datenbank absichern und Zugriffsrechte verwalten',
               JSON('{
                   "provider"        : "ocigenai",
                   "credential_name" : "OCI_VEC_CRED",
                   "url"             : "https://inference.generativeai.eu-frankfurt-1.oci.oraclecloud.com/20231130/actions/embedText",
                   "model"           : "cohere.embed-multilingual-v3.0"
               }')
           ) AS vec
    FROM DUAL
)
SELECT
    s.id, s.bezeichnung, s.kategorie, s.ort, s.preis,
    ROUND(1 - VECTOR_DISTANCE(s.embedding, q.vec, COSINE), 4) AS aehnlichkeit
FROM   seminare_test s
CROSS  JOIN query_vec q
WHERE  s.embedding IS NOT NULL
ORDER  BY aehnlichkeit DESC
FETCH  FIRST 3 ROWS ONLY; 

Die Ähnlichkeit wird als Wert zwischen 0 und 1 ausgegeben. Die folgende Tabelle gibt Orientierung bei der Einordnung der Ergebnisse: 

WERT BEDEUTUNG
Größer 0,7 Sehr guter Treffer
0,5 bis 0,7Guter Treffer, thematisch verwandt
0,3 bis 0,5Schwacher Treffer
Kleiner 0,3Kein relevanter Treffer

Die Suchanfrage oben liefert erwartbar die Seminare zu IT-Security Grundlagen und Penetration Testing als Top-Treffer, obwohl die Suchphrase keine einzige Zeichenkette aus dem Datensatz exakt enthält. 

Schritt 9: RAG-Prozedur aufbauen

Die semantische Suche liefert relevante Datensätze, aber noch keine Antwort in natürlicher Sprache. Retrieval Augmented Generation (RAG) verbindet beide Schritte: Die Vektorsuche holt die relevanten Informationen aus der Datenbank, das Sprachmodell formuliert daraus eine Antwort auf die ursprüngliche Frage.

Der gesamte Ablauf läuft in einer einzigen PL/SQL-Prozedur ab:

  • Nutzerfrage wird in einen Vektor umgewandelt
  • Top-N thematisch passende Seminare werden per Vektorsuche ermittelt
  • Die Seminardetails werden zu einem Kontext-Text zusammengestellt
  • Ein Prompt wird aus Kontext und Ursprungsfrage aufgebaut
  • Das Sprachmodell (in unserem Beispiel: „cohere.command-a-03-2025“) wird mit dem Prompt aufgerufen und gibt eine Antwort

Bei RAG verlassen Daten die Datenbank bewusst, nämlich als Teil des Prompts an das Sprachmodell. Das muss bei datenschutzrechtlichen Überlegungen berücksichtigt werden.

CREATE OR REPLACE PROCEDURE seminare_rag (
    p_frage     IN  VARCHAR2,
    p_antwort   OUT CLOB,
    p_top_n     IN  NUMBER DEFAULT 3
)
AS
    v_query_vec  VECTOR(1024, FLOAT32);
    v_kontext    CLOB := EMPTY_CLOB();
    v_prompt     CLOB;
    v_response   CLOB;

    CURSOR c_treffer IS
        SELECT bezeichnung, kategorie, beschreibung,
               ort, preis, dauer_tage,
               max_tn - gebuchte_tn AS freie_plaetze
        FROM   seminare_test
        WHERE  embedding IS NOT NULL
        ORDER  BY VECTOR_DISTANCE(embedding, v_query_vec, COSINE)
        FETCH  FIRST p_top_n ROWS ONLY;
BEGIN
    -- 1. Nutzerfrage in Vektor umwandeln
    v_query_vec := DBMS_VECTOR.UTL_TO_EMBEDDING(
        p_frage,
        JSON('{...}')  -- wie in Schritt 6
    );

    -- 2. Kontext aus Top-N Treffern aufbauen
    FOR r IN c_treffer LOOP
        v_kontext := v_kontext
            || 'Seminar: '       || r.bezeichnung  || CHR(10)
            || 'Kategorie: '     || r.kategorie    || CHR(10)
            || 'Ort: '           || r.ort          || CHR(10)
            || 'Preis: '         || r.preis        || ' EUR' || CHR(10)
            || 'Dauer: '         || r.dauer_tage   || ' Tag(e)' || CHR(10)
            || 'Freie Plätze: ' || r.freie_plaetze || CHR(10)
            || 'Beschreibung: '  || r.beschreibung  || CHR(10)
            || '---'             || CHR(10);
    END LOOP;

    -- 3. Prompt aufbauen
    v_prompt :=
        'Du bist ein freundlicher Seminar-Assistent. '
        || 'Beantworte die Frage ausschließlich anhand der bereitgestellten Seminarinformationen.' || CHR(10)
        || 'Seminarinformationen:' || CHR(10) || v_kontext || CHR(10)
        || 'Frage: ' || p_frage;

    -- 4. LLM aufrufen
    v_response := DBMS_VECTOR_CHAIN.UTL_TO_GENERATE_TEXT(
        v_prompt,
        JSON('{
            "provider"        : "ocigenai",
            "credential_name" : "OCI_VEC_CRED",
            "url"             : "https://inference.generativeai.eu...
            "model"           : "cohere.command-a-03-2025",
            "chatRequest"     : { "maxTokens": 600, "temperature": 0.2 }
        }')
    );

    p_antwort := v_response;
EXCEPTION
    WHEN OTHERS THEN
        p_antwort := 'Fehler: ' || SQLERRM;
END seminare_rag;
/ 

Der chatRequest-Block: maxTokens und temperature

Der Aufruf von DBMS_VECTOR_CHAIN.UTL_TO_GENERATE_TEXT akzeptiert im JSON-Parameter neben Provider, Credential und URL auch modellspezifische Steuerparameter. Bei OCI Generative AI müssen diese in einen chatRequest-Block übergeben werden.

maxTokens begrenzt die Länge der Modellantwort.

temperature steuert, wie zufällig das Modell bei der Wortwahl vorgeht. Der Wert liegt typischerweise zwischen 0 und 1, bei einigen Modellen bis 2.

Bei temperature 0 wählt das Modell immer das wahrscheinlichste nächste Wort. Die Ausgabe ist reproduzierbar und vorhersagbar. Das ist für RAG-Szenarien sinnvoll. Das Modell soll den gegebenen Kontext wiedergeben, nicht frei interpretieren.

Bei temperature 1 werden auch weniger wahrscheinliche Wortwahlen zugelassen. Die Antworten werden abwechslungsreicher, aber auch weniger berechenbar und anfälliger für Fehler.

Für unser Beispiel wurde 0.2 gewählt. Das ist nah an deterministisch, aber mit genug Spielraum, damit die Antworten natürlich klingen.

Schritt 10: Prozedur aufrufen

Mit der fertigen Prozedur lassen sich beliebige Fragen stellen. Die folgenden zwei Beispiele zeigen echte Ausgaben des Modells auf Basis der Seminardaten. 

Beispiel 1: Verfügbarkeit prüfen

DECLARE
    v_antwort CLOB;
BEGIN
    seminare_rag(
        p_frage   => 'Welche Cloud-Seminare haben noch freie Plätze?',
        p_antwort => v_antwort
    );
    DBMS_OUTPUT.PUT_LINE(v_antwort);
END;
/ 

Antwort des Modells:

Basierend auf den bereitgestellten Seminarinformationen haben folgende
Cloud-Seminare noch freie Plätze:

1. Kubernetes Intensiv
   Ort: München
   Preis: 2390 EUR
   Daür: 4 Tage
   Freie Plätze: 2

2. AWS Cloud Practitioner
   Ort: Frankfurt
   Preis: 1490 EUR
   Daür: 3 Tage
   Freie Plätze: 3

Beide Seminare gehören zur Kategorie Cloud und haben noch verfügbare Plätze. 

Beispiel 2: Empfehlung für Einsteiger

DECLARE
    v_antwort CLOB;
BEGIN
    seminare_rag(
        p_frage   => 'Ich bin Einsteiger im Thema Datenbanken,
                      welche Seminare würdest du empfehlen?',
        p_antwort => v_antwort
    );
    DBMS_OUTPUT.PUT_LINE(v_antwort);
END;
/ 

Antwort des Modells: 

Basierend auf den bereitgestellten Seminarinformationen würde ich dir als Einsteiger im Thema Datenbanken folgende Seminare empfehlen:

1. PostgreSQL für Einsteiger (Berlin, 990 EUR, 2 Tage)
   Dieses Seminar bietet einen Einstieg in PostgreSQL, einschließlich
   Installation, Datentypen, SQL-Syntax, Benutzerverwaltung und Backup.
   Es ist ideal für Datenbankeinsteiger und auch für Oracle-Umsteiger
   geeignet.
   Hinweis: Aktuell sind keine freien Plätze verfügbar.

2. Oracle SQL Grundlagen (Paderborn, 1290 EUR, 3 Tage)
   Hier wird die Oracle-Datenbanksprache SQL von Grund auf vermittelt,
   mit Themen wie SELECT, DML, DDL, Joins, Unterabfragen und
   Transaktionskontrolle. Es ist explizit für Einsteiger ohne
   Vorkenntnisse geeignet.
   Hinweis: Es gibt noch 2 freie Plätze.

Beide Seminare sind gut für Einsteiger geeignet, wobei das PostgreSQL-
Seminar aktuell ausgebucht ist. Das Oracle-Seminar ist eine gute
Alternative, falls Sie flexibel sind. 

Das Modell empfiehlt zuerst das günstigere PostgreSQL-Seminar, obwohl es ausgebucht ist, und nennt dann die Alternative mit freien Plätzen. Fachlich korrekt, aber aus Vertriebssicht etwas optimierungsfreudig. Wer das ändern möchte, kann den Prompt um den Hinweis erweitern: „Bevorzuge Seminare mit freien Plätzen.“ Das ist der eigentliche Hebel bei RAG: nicht der Code, sondern der Prompt. 

Vergleich: SELECT AI und Vector Search RAG

Beide Ansätze ermöglichen natürlichsprachliche Interaktion mit Datenbankdaten, arbeiten aber grundlegend unterschiedlich.

SELECT AI eignet sich für strukturierte, tabellarische Daten und Fragen, die sich in SQL ausdrücken lassen. Im Regelfall sieht das Modell das Datenbankschema und Metadaten, greift jedoch nicht direkt auf die eigentlichen Datenbestände zu. Die Abfragegüte hängt stark von der Qualität der Tabellenkommentare und der Klarheit der Frage ab.

Vector Search mit RAG eignet sich für thematische Suchen und Szenarien, bei denen die Antwort aus Fließtext oder langen Beschreibungen zusammengestellt werden soll. Das Modell bekommt die relevanten Daten direkt als Kontext übergeben und kann daraus eine Antwort formulieren. Der Nachteil: Die Datensätze verlassen die Datenbank als Teil des Prompts.

Die Wahl hängt vom Anwendungsfall ab. Für SQL-generierbare Auswertungen liefert SELECT AI schnellere und präzisere Ergebnisse. Für inhaltliche Empfehlungen und thematische Suchen ist Vector Search RAG das richtige Werkzeug.

Fazit

Oracle 26ai ermöglicht einen vollständigen RAG-Stack direkt in der relationalen Datenbank. Embeddings werden neben klassischen Spalten gespeichert, Vektorsuche läuft per SQL, und die LLM-Anbindung erfolgt über PL/SQL-Pakete. Eine separate Infrastruktur für die Vektordatenbank ist nicht notwendig.

Wer den Stack einmal aufgebaut hat, kann semantische Suche und KI-gestützte Antworten für unterschiedlichste Anwendungsfälle realisieren, ohne die gewohnte Oracle-Infrastruktur verlassen zu müssen.

Die passenden Seminare für den praktischen Einstieg in Oracle 26ai:

 GS-Leiter / Senior Chief Consultant

Ähnliche Beiträge

 

Kommentare

Derzeit gibt es keine Kommentare. Schreibe den ersten Kommentar!
Dienstag, 08. September 2026

Sicherheitscode (Captcha)