Freigeben über


Verwenden von LangChain mit Azure-Datenbank für PostgreSQL

Azure Database for PostgreSQL lässt sich nahtlos in führende Orchestrierungspakete für große Sprachmodelle (Large Language Model, LLM) wie LangChain integrieren, sodass Entwickler die Leistungsfähigkeit erweiterter KI-Funktionen in ihren Anwendungen nutzen können. LangChain kann die Verwaltung und Verwendung von LLMs, Einbettungsmodellen und Datenbanken optimieren, wodurch die Entwicklung von generativen KI-Anwendungen noch einfacher wird.

In diesem Lernprogramm erfahren Sie, wie Sie die integrierte Vektordatenbank azure Database for PostgreSQL verwenden, um Dokumente in Sammlungen mit LangChain zu speichern und zu verwalten. Außerdem wird gezeigt, wie Indizes erstellt und Vektorsuchabfragen mithilfe von Algorithmen der ungefähren nächsten Nachbarn wie Cosine Distanz, L2 (Euklidischer Abstand) und IP (inneres Produkt) verwendet werden können, um Dokumente zu finden, die den Abfragevektoren nahe sind.

Vektorunterstützung

Azure Database for PostgreSQL – Flexible Server ermöglicht es Ihnen, Millionen von Vektoreinbettungen in PostgreSQL effizient zu speichern und abzufragen und Ihre KI-Anwendungsfälle von POC (Machbarkeitsnachweis) auf die Produktion zu skalieren:

  • Stellt eine vertraute SQL-Schnittstelle zum Abfragen von Vektoreinbettungen und relationalen Daten bereit.
  • Steigert pgvector durch eine schnellere und präzisere Ähnlichkeitssuche über mehr als 100 Millionen Vektoren mithilfe des DiskANN-Indexierungsalgorithmus.
  • Vereinfacht Vorgänge, indem relationale Metadaten, Vektoreinbettungen und Zeitreihendaten in eine einzelne Datenbank integriert werden.
  • Verwendet die Leistungsfähigkeit des robusten PostgreSQL-Ökosystems und der Azure Cloud for Enterprise-Funktionen, einschließlich Replikation und hoher Verfügbarkeit.

Authentifizierung

Azure-Datenbank für PostgreSQL – Flexible Server unterstützt kennwortbasierte sowie die Microsoft Entra-Authentifizierung (früher Azure Active Directory). Mit entra-Authentifizierung können Sie entra Identity verwenden, um sich bei Ihrem PostgreSQL-Server zu authentifizieren. Die Entra-ID beseitigt die Notwendigkeit, separate Benutzernamen und Kennwörter für Ihre Datenbankbenutzer zu verwalten, und ermöglicht es Ihnen, dieselben Sicherheitsmechanismen zu verwenden, die Sie für andere Azure-Dienste verwenden.

Dieses Notizbuch ist für die Verwendung einer Authentifizierungsmethode eingerichtet. Sie können konfigurieren, ob die Entra-Authentifizierung später im Notebook verwendet werden soll.

Konfiguration

Azure Database for PostgreSQL verwendet die Open-Source-Unterstützung für LangChain-Postgres, um eine Verbindung mit Azure Database for PostgreSQL herzustellen. Laden Sie zuerst das Partnerpaket herunter:

%pip install -qU langchain_postgres
%pip install -qU langchain-openai
%pip install -qU azure-identity

Aktivieren von pgvector auf Azure-Datenbank für PostgreSQL - Flexible Server

Siehe Aktivierungsanweisungen für Azure-Datenbank für PostgreSQL.

Anmeldeinformationen

Zum Ausführen dieses Notizbuchs benötigen Sie Ihre Azure-Datenbank für PostgreSQL-Verbindungsdetails und fügen sie als Umgebungsvariablen hinzu.

Setzen Sie die USE_ENTRA_AUTH Flagge auf True, wenn Sie die Microsoft Entra-Authentifizierung verwenden möchten. Bei Verwendung der Entra-Authentifizierung müssen Sie nur den Host- und Datenbanknamen angeben. Wenn Sie die Kennwortauthentifizierung verwenden, müssen Sie auch den Benutzernamen und das Kennwort festlegen.

import getpass
import os

USE_ENTRA_AUTH = True

# Supply the connection details for the database
os.environ["DBHOST"] = "<server-name>"
os.environ["DBNAME"] = "<database-name>"
os.environ["SSLMODE"] = "require"

if not USE_ENTRA_AUTH:
    # If using a username and password, supply them here
    os.environ["DBUSER"] = "<username>"
    os.environ["DBPASSWORD"] = getpass.getpass("Database Password:")

Einrichten von Azure OpenAI Embeddings

os.environ["AZURE_OPENAI_ENDPOINT"] = "<azure-openai-endpoint>"
os.environ["AZURE_OPENAI_API_KEY"] = getpass.getpass("Azure OpenAI API Key:")
AZURE_OPENAI_ENDPOINT = os.environ["AZURE_OPENAI_ENDPOINT"]
AZURE_OPENAI_API_KEY = os.environ["AZURE_OPENAI_API_KEY"]

from langchain_openai import AzureOpenAIEmbeddings

embeddings = AzureOpenAIEmbeddings(
    model="text-embedding-3-small",
    api_key=AZURE_OPENAI_API_KEY,
    azure_endpoint=AZURE_OPENAI_ENDPOINT,
    azure_deployment="text-embedding-3-small",
)

Initialisierung

Microsoft Entra-Authentifizierung

Die folgende Zelle enthält Funktionen, die LangChain für die Verwendung der Entra-Authentifizierung einrichten. Sie stellt eine get_token_and_username-Funktion bereit, die Token für den Azure Databases for PostgreSQL-Dienst mithilfe von DefaultAzureCredential aus der azure.identity Bibliothek abruft. Es stellt sicher, dass das sqlalchemy-Modul über ein gültiges Token verfügt, mit dem neue Verbindungen erstellt werden können. Außerdem analysiert es das Token, bei dem es sich um ein Java Web Token (JWT) handelt, um den Benutzernamen zu extrahieren, der zum Herstellen einer Verbindung mit der Datenbank verwendet wird.

Die create_postgres_engine-Funktion erstellt eine sqlalchemy Engine , die den Benutzernamen und das Kennwort dynamisch basierend auf dem token festlegt, das vom TokenManager abgerufen wird. Dies Engine kann an den connection Parameter des PGVector LangChain VectorStore übergeben werden.

Anmelden bei Azure

Um sich bei Azure anzumelden, stellen Sie sicher, dass die Azure CLI installiert ist. Sie müssen den folgenden Befehl in Ihrem Terminal ausführen:

az login

Sobald Sie sich angemeldet haben, ruft der folgende Code das Token ab.

import base64
import json
from functools import lru_cache

from azure.identity import DefaultAzureCredential
from sqlalchemy import create_engine, event
from sqlalchemy.engine.url import URL


@lru_cache(maxsize=1)
def get_credential():
    """Memoized function to create the Azure credential, which caches tokens."""
    return DefaultAzureCredential()


def decode_jwt(token):
    """Decode the JWT payload to extract claims."""
    payload = token.split(".")[1]
    padding = "=" * (4 - len(payload) % 4)
    decoded_payload = base64.urlsafe_b64decode(payload + padding)
    return json.loads(decoded_payload)


def get_token_and_username():
    """Fetches a token returns the username and token."""
    # Fetch a new token and extract the username
    token = get_credential().get_token(
        "https://ossrdbms-aad.database.windows.net/.default"
    )
    claims = decode_jwt(token.token)
    username = claims.get("upn")
    if not username:
        raise ValueError("Could not extract username from token. Have you logged in?")

    return username, token.token


def create_postgres_engine():
    db_url = URL.create(
        drivername="postgresql+psycopg",
        username="",  # This will be replaced dynamically
        password="",  # This will be replaced dynamically
        host=os.environ["DBHOST"],
        port=os.environ.get("DBPORT", 5432),
        database=os.environ["DBNAME"],
    )

    # Create a sqlalchemy engine
    engine = create_engine(db_url, echo=True)

    # Listen for the connection event to inject dynamic credentials
    @event.listens_for(engine, "do_connect")
    def provide_dynamic_credentials(dialect, conn_rec, cargs, cparams):
        # Fetch the dynamic username and token
        username, token = get_token_and_username()

        # Override the connection parameters
        cparams["user"] = username
        cparams["password"] = token

    return engine

Kennwortauthentifizierung

Wenn die Entra-Authentifizierung nicht verwendet wird, stellt der get_connection_uri einen Verbindungs-URI bereit, der den Benutzernamen und das Kennwort aus Umgebungsvariablen abruft.

import urllib.parse


def get_connection_uri():
    # Read URI parameters from the environment
    dbhost = os.environ["DBHOST"]
    dbname = os.environ["DBNAME"]
    dbuser = urllib.parse.quote(os.environ["DBUSER"])
    password = os.environ["DBPASSWORD"]
    sslmode = os.environ["SSLMODE"]

    # Construct connection URI
    # Use psycopg 3!
    db_uri = (
        f"postgresql+psycopg://{dbuser}:{password}@{dbhost}/{dbname}?sslmode={sslmode}"
    )
    return db_uri

Erstellen des Vektorspeichers

from langchain_core.documents import Document
from langchain_postgres import PGVector
from langchain_postgres.vectorstores import PGVector

collection_name = "my_docs"

# The connection is either a sqlalchemy engine or a connection URI
connection = create_postgres_engine() if USE_ENTRA_AUTH else get_connection_uri()

vector_store = PGVector(
    embeddings=embeddings,
    collection_name=collection_name,
    connection=connection,
    use_jsonb=True,
)

Verwalten des Vektorspeichers

Hinzufügen von Elementen zum Vektorspeicher

Das Hinzufügen von Dokumenten nach ID überschreibt alle vorhandenen Dokumente, die dieser ID entsprechen.

docs = [
    Document(
        page_content="there are cats in the pond",
        metadata={"id": 1, "location": "pond", "topic": "animals"},
    ),
    Document(
        page_content="ducks are also found in the pond",
        metadata={"id": 2, "location": "pond", "topic": "animals"},
    ),
    Document(
        page_content="fresh apples are available at the market",
        metadata={"id": 3, "location": "market", "topic": "food"},
    ),
    Document(
        page_content="the market also sells fresh oranges",
        metadata={"id": 4, "location": "market", "topic": "food"},
    ),
    Document(
        page_content="the new art exhibit is fascinating",
        metadata={"id": 5, "location": "museum", "topic": "art"},
    ),
    Document(
        page_content="a sculpture exhibit is also at the museum",
        metadata={"id": 6, "location": "museum", "topic": "art"},
    ),
    Document(
        page_content="a new coffee shop opened on Main Street",
        metadata={"id": 7, "location": "Main Street", "topic": "food"},
    ),
    Document(
        page_content="the book club meets at the library",
        metadata={"id": 8, "location": "library", "topic": "reading"},
    ),
    Document(
        page_content="the library hosts a weekly story time for kids",
        metadata={"id": 9, "location": "library", "topic": "reading"},
    ),
    Document(
        page_content="a cooking class for beginners is offered at the community center",
        metadata={"id": 10, "location": "community center", "topic": "classes"},
    ),
]

vector_store.add_documents(docs, ids=[doc.metadata["id"] for doc in docs])

Aktualisieren von Elementen im Vektorspeicher

docs = [
    Document(
        page_content="Updated - cooking class for beginners is offered at the community center",
        metadata={"id": 10, "location": "community center", "topic": "classes"},
    )
]
vector_store.add_documents(docs, ids=[doc.metadata["id"] for doc in docs])

Löschen von Elementen aus dem Vektorspeicher

vector_store.delete(ids=["3"])

Abfragevektorspeicher

Wenn Ihr Vektorspeicher erstellt wurde und die relevanten Dokumente hinzugefügt wurden, können Sie den Vektorspeicher in Ihrer Kette oder Ihrem Agent abfragen.

Filterunterstützung

Der Vektorspeicher unterstützt eine Reihe von Filtern, die für die Metadatenfelder der Dokumente angewendet werden können.

Bediener Bedeutung/Kategorie
$eq Gleichheit (==)
$ne Ungleichheit (!=)
$lt Kleiner als (<)
$lte Kleiner als oder gleich (<=)
$gt Größer als (>)
$gte Größer als oder gleich (>=)
$in Sonderfall (in)
$nin Sonderfall (not in)
$between Spezialfall (between)
$like Text (like)
$ilike Text (Groß-/Kleinschreibung wird nicht beachtet – ilike)
$and Logisch (and)
$or Logisch (oder)

Direkte Abfrage

Die Durchführung einer einfachen Ähnlichkeitssuche kann wie folgt erfolgen:

results = vector_store.similarity_search(
    "kitty", k=10, filter={"id": {"$in": [1, 5, 2, 9]}}
)
for doc in results:
    print(f"* {doc.page_content} [{doc.metadata}]")
    * there are cats in the pond [{'id': 1, 'topic': 'animals', 'location': 'pond'}]
    * ducks are also found in the pond [{'id': 2, 'topic': 'animals', 'location': 'pond'}]
    * the new art exhibit is fascinating [{'id': 5, 'topic': 'art', 'location': 'museum'}]
    * the library hosts a weekly story time for kids [{'id': 9, 'topic': 'reading', 'location': 'library'}]

Wenn Sie ein Wörterbuch mit mehreren Feldern, aber keinen Operatoren bereitstellen, wird die höchste Ebene als logischer AND-Filter interpretiert.

vector_store.similarity_search(
    "ducks",
    k=10,
    filter={"id": {"$in": [1, 5, 2, 9]}, "location": {"$in": ["pond", "market"]}},
)
[Document(id='2', metadata={'id': 2, 'topic': 'animals', 'location': 'pond'}, page_content='ducks are also found in the pond'),
 Document(id='1', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'}, page_content='there are cats in the pond')]
vector_store.similarity_search(
    "ducks",
    k=10,
    filter={
        "$and": [
            {"id": {"$in": [1, 5, 2, 9]}},
            {"location": {"$in": ["pond", "market"]}},
        ]
    },
)
[Document(id='2', metadata={'id': 2, 'topic': 'animals', 'location': 'pond'}, page_content='ducks are also found in the pond'),
 Document(id='1', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'}, page_content='there are cats in the pond')]

Wenn Sie eine Ähnlichkeitssuche ausführen und die entsprechenden Bewertungen erhalten möchten, können Sie folgendes ausführen:

results = vector_store.similarity_search_with_score(query="cats", k=1)
for doc, score in results:
    print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]")
* [SIM=0.528338] there are cats in the pond [{'id': 1, 'topic': 'animals', 'location': 'pond'}]

Eine vollständige Liste der verschiedenen Suchvorgänge, die Sie in einem PGVector Vektorspeicher ausführen können, finden Sie in der API-Referenz.

Abfrage durch Umwandeln in Retriever

Sie können den Vektorspeicher auch in einen Retriever umwandeln, um die Verwendung in Ihren Ketten zu vereinfachen.

retriever = vector_store.as_retriever(search_type="mmr", search_kwargs={"k": 1})
retriever.invoke("kitty")
[Document(id='1', metadata={'id': 1, 'topic': 'animals', 'location': 'pond'}, page_content='there are cats in the pond')]

Aktuelle Einschränkungen

  • langchain_postgres funktioniert nur mit psycopg3. Aktualisieren der Verbindungszeichenfolgen von postgresql+psycopg2://... zu postgresql+psycopg://langchain:langchain@...
  • Das Schema des Einbettungsspeichers und der Sammlung wurde geändert, damit „add_documents“ ordnungsgemäß mit vom Benutzenden angegebenen IDs funktioniert.
  • Man muss jetzt ein explizites Verbindungsobjekt übergeben.

Derzeit gibt es keinen Mechanismus , der einfache Datenmigration bei Schemaänderungen unterstützt. Daher erfordern alle Schemaänderungen im Vektorspeicher, dass der Benutzende die Tabellen neu erstellt und die Dokumente erneut hinzufügt.