Entwickeln von KI-Apps mit LangChain in Azure HorizonDB (Vorschau)

LangChain ist ein LLM-Orchestrierungsframework (Large Language Model), das das Erstellen von generativen KI-Anwendungen vereinfacht. Azure HorizonDB ist in LangChain als vector store integriert, sodass Sie Dokumente speichern, Indizes erstellen und Ähnlichkeitssuche (Kosinus, L2, inneres Produkt) direkt in PostgreSQL durchführen können.

Azure HorizonDB unterstützt Vektoreinbettungen über pgvector mit DiskANN-Indizierung für eine leistungsstarke Suche im großen Maßstab. Ausführliche Informationen finden Sie unter Retrieval Foundation: vector, full-text, and hybrid search in Azure HorizonDB (Preview).

Authentifizierung

Azure HorizonDB unterstützt kennwortbasierte und Microsoft Entra Authentifizierung.

Mit der Microsoft Entra-Authentifizierung können Sie Microsoft Entra ID verwenden, um sich bei Ihrem PostgreSQL-Server zu authentifizieren. Microsoft Entra ID beseitigt die Notwendigkeit, separate Benutzernamen und Kennwörter für Ihre Datenbankbenutzer zu verwalten. Sie können dieselben Sicherheitsmechanismen verwenden, die Sie für andere Azure-Dienste verwenden.

In diesem Artikel können Sie eine der beiden Authentifizierungsmethoden verwenden.

Konfiguration

Azure HorizonDB verwendet die Open Source-LangChain Postgres-Unterstützung zum Herstellen einer Verbindung mit Azure HorizonDB. Laden Sie zuerst das Partnerpaket herunter:

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

Aktivieren von pgvector auf Azure HorizonDB

Siehe Implement-Vektorsuche in Azure HorizonDB mithilfe der pgvector-Erweiterung (Vorschau).

Einrichten der Anmeldeinformationen

Sie müssen Ihre Azure HorizonDB Connection-Details abrufen und sie als Umgebungsvariablen hinzufügen.

Setzen Sie die USE_ENTRA_AUTH Flagge auf True, wenn Sie die Microsoft Entra-Authentifizierung verwenden möchten. Wenn Sie die Microsoft Entra-Authentifizierung verwenden, müssen Sie nur die 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 you're using a username and password, supply them here
    os.environ["DBUSER"] = "<username>"
    os.environ["DBPASSWORD"] = getpass.getpass("Database Password:")

Einrichten von Azure OpenAI-Einbettungen

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

Verwenden Sie die Microsoft Entra-Authentifizierung

In den folgenden Abschnitten wird veranschaulicht, wie LangChain für die Verwendung der Microsoft Entra-Authentifizierung eingerichtet wird. Die Klasse AzurePGConnectionPool im LangChain-Azure Postgres-Paket ruft Token für den Azure HorizonDB-Dienst mithilfe von DefaultAzureCredential aus der Bibliothek azure.identity ab.

Sie können die Verbindung an den connection Parameter des AzurePGVectorStore LangChain-Vektorspeichers übergeben.

Anmelden bei Azure

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

az login

Nachdem Sie sich angemeldet haben, ruft der folgende Code das Token ab:

from langchain_azure_postgresql.common import (
    BasicAuth,
    AzurePGConnectionPool,
    ConnectionInfo,
)
from langchain_azure_postgresql.langchain import AzurePGVectorStore
entra_connection_pool = AzurePGConnectionPool(
        azure_conn_info=ConnectionInfo(
            host=os.environ["DBHOST"],
            dbname=os.environ["DBNAME"]
        )
    )

Kennwortauthentifizierung verwenden

Wenn Sie die Microsoft Entra-Authentifizierung nicht verwenden, ermöglicht die Klasse BasicAuth die Verwendung von Benutzername und Kennwort:

basic_auth_connection_pool = AzurePGConnectionPool(
    azure_conn_info=ConnectionInfo(
        host=os.environ["DBHOST"],
        dbname=os.environ["DBNAME"],
        credentials=BasicAuth(
            username=os.environ["DBUSER"],
            password=os.environ["DBPASSWORD"],
        )
    )
)

Erstellen des Vektorspeichers

from langchain_core.documents import Document
from langchain_azure_postgresql.langchain import AzurePGVectorStore

collection_name = "my_docs"

# The connection is either using Entra ID or Basic Auth
connection = entra_connection_pool if USE_ENTRA_AUTH else basic_auth_connection_pool

vector_store = AzurePGVectorStore(
    embeddings=embeddings,
    table_name=collection_name,
    connection=connection,
)

Verwalten des Vektorspeichers

Hinzufügen von Elementen zum Vektorspeicher

Wenn Sie Dokumente nach ID hinzufügen, überschreiben Sie alle vorhandenen Dokumente mit derselben ID.

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

uuids = vector_store.add_documents(docs)
uuids

Aktualisieren von Elementen im Vektorspeicher

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

Anzeigen von Elementen aus dem Vektorspeicher

vector_store.get_by_ids([str(uuids[-1])])

Löschen von Elementen aus dem Vektorspeicher

vector_store.delete(ids=[uuids[-1]])

Abfragen des Vektorspeichers

Nachdem Sie Ihren Vektorspeicher erstellt und die relevanten Dokumente hinzugefügt haben, können Sie den Vektorspeicher in Ihrer Kette oder Ihrem Agent abfragen.

Filterunterstützung

Der Vektorspeicher unterstützt eine Reihe von Filtern, die Sie auf die Metadatenfelder der Dokumente anwenden können, indem Sie das FilterConditionOrFilterAndFilterLangChain-Azure PostgreSQL-Paket verwenden:

Operator Bedeutung/Kategorie
= Gleichheit (==)
!= Ungleichheit (!=)
< Kleiner als (<)
<= Kleiner als oder gleich (<=)
> Größer als (>)
>= Größer als oder gleich (>=)
in Sonderfall (in)
not in Sonderfall (not in)
is null Sonderfall (is null)
is not null Als Sonderfall behandelt (ist nicht null)
between Spezialfall (between)
not between Sonderfall (not between)
like Text (like)
ilike Text (Groß-/Kleinschreibung wird nicht beachtet – ilike)
AND Logisch (and)
OR Logisch (oder)

Direktabfrage

Sie können eine direkte Ähnlichkeitssuche wie folgt ausführen:

from langchain_azure_postgresql import FilterCondition, AndFilter

results = vector_store.similarity_search(
    "kitty",
    k=10,
    filter=FilterCondition(
        column="(metadata->>'doc_id')::int",
        operator="in",
        value=[1, 5, 2, 9],
    ),
)

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

Wenn Sie ein Wörterbuch mit mehreren Feldern, aber ohne Operatoren bereitstellen, wird die oberste Ebene als logischer AND-Filter interpretiert:

results = vector_store.similarity_search(
    "ducks",
    k=10,
    filter=AndFilter(
        AND=[
            FilterCondition(
                column="(metadata->>'doc_id')::int",
                operator="in",
                value=[1, 5, 2, 9],
            ),
            FilterCondition(
                column="metadata->>'location'",
                operator="in",
                value=["pond", "market"],
            ),
        ]
    ),
)

for doc in results:
    print(f"* {doc.page_content} [{doc.metadata}]")
    * ducks are also found in the pond [{'topic': 'animals', 'doc_id': 2, 'location': 'pond'}]
    * there are cats in the pond [{'topic': 'animals', 'doc_id': 1, 'location': 'pond'}]

Wenn Sie eine Ähnlichkeitssuche durchführen und die entsprechenden Ergebnisse 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 [{'doc_id': 1, 'topic': 'animals', 'location': 'pond'}]

Wenn Sie die maximal marginale Relevanzsuche in Ihrem Vektorspeicher verwenden möchten:

results = vector_store.max_marginal_relevance_search(
    "query about cats",
    k=10,
    lambda_mult=0.5,
    filter=FilterCondition(
        column="(metadata->>'doc_id')::int",
        operator="in",
        value=[1, 2, 5, 9],
    ),
)

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

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

Transformation in einen 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='00000000-0000-0000-0000-000000000000', metadata={'doc_id': 1, 'topic': 'animals', 'location': 'pond'}, page_content='there are cats in the pond')]