Sviluppare app di intelligenza artificiale con LangChain in Azure HorizonDB (anteprima)

LangChain è un framework di orchestrazione LLM (Large Language Model) che semplifica la creazione di applicazioni di intelligenza artificiale generative. Azure HorizonDB si integra con LangChain come archivio vector, consentendo di archiviare documenti, creare indici ed eseguire ricerche di somiglianza (coseno, L2, prodotto interno) direttamente in PostgreSQL.

Azure HorizonDB supporta incorporamenti vettoriali tramite pgvector con indicizzazione DiskANN per la ricerca a prestazioni elevate su larga scala. Per ulteriori informazioni, vedere Fondamenti del recupero: ricerca vettoriale, full-text e ibrida in Azure HorizonDB (Preview).

Authentication

Azure HorizonDB supporta l'autenticazione basata su password e Microsoft Entra.

L'autenticazione Di Microsoft Entra consente di usare l'ID Entra di Microsoft per eseguire l'autenticazione al server PostgreSQL. Microsoft Entra ID elimina la necessità di gestire nomi utente e password separati per gli utenti del database. Consente di usare gli stessi meccanismi di sicurezza usati per altri servizi di Azure.

In questo articolo è possibile usare uno dei due metodi di autenticazione.

Installazione

Azure HorizonDB usa il supporto open source LangChain Postgres per connettersi a Azure HorizonDB. Prima di tutto, scaricare il pacchetto partner:

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

Abilitare pgvector in Azure HorizonDB

Vedere Implementare la ricerca vettoriale in Azure HorizonDB usando l'estensione pgvector (Anteprima).

Configurare le credenziali

È necessario ottenere i dettagli di Azure HorizonDB connessione e aggiungerli come variabili di ambiente.

Impostare il USE_ENTRA_AUTH flag su True se si vuole usare l'autenticazione di Microsoft Entra. Se si usa l'autenticazione Microsoft Entra, è necessario specificare gli unici nomi host e di database. Se si usa l'autenticazione con password, è necessario impostare anche il nome utente e la password.

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:")

Configurare gli incorporamenti di Azure OpenAI

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",
)

Inizializzazione

Usare l'autenticazione di Microsoft Entra

Le sezioni seguenti illustrano come configurare LangChain per l'uso dell'autenticazione di Microsoft Entra. La classe AzurePGConnectionPool nel pacchetto LangChain Azure Postgres recupera i token per il servizio Azure HorizonDB usando DefaultAzureCredential dalla libreria azure.identity.

È possibile passare la connessione nel parametro connection dell'archivio vettoriale AzurePGVectorStore di LangChain.

Accedere ad Azure

Per accedere ad Azure, assicurarsi di avere installato l'interfaccia della riga di comando di Azure. Eseguire il comando seguente nel terminale:

az login

Dopo l'accesso, il codice seguente recupera il token:

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"]
        )
    )

Usare l'autenticazione con password

Se non si usa l'autenticazione Microsoft Entra, la classe BasicAuth consente l'uso di nome utente e password:

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"],
        )
    )
)

Creare l'archivio vettoriale

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,
)

Gestione dell'archivio vettoriale

Aggiungere elementi all'archivio vettoriale

Quando si aggiungono documenti in base all'ID, si sovrascrive qualsiasi documento esistente con lo stesso 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

Aggiornare gli elementi nell'archivio vettoriale

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)

Visualizzare gli elementi dall'archivio vettoriale

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

Eliminare elementi dall'archivio vettoriale

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

Interrogazioni sull'archivio vettoriale

Dopo aver creato l'archivio vettoriale e aggiunto i documenti pertinenti, è possibile eseguire una query sull'archivio vettoriale nella catena o nell'agente.

Supporto dei filtri

L'archivio vettoriale supporta un set di filtri che è possibile applicare ai campi di metadati dei documenti usando FilterCondition, OrFiltere AndFilter nel pacchetto LangChain Azure PostgreSQL:

Operatore Significato/Categoria
= Uguaglianza (==)
!= Disuguaglianza (!=)
< Minore di (<)
<= Minore o uguale (<=)
> Maggiore di (>)
>= Maggiore o uguale a (>=)
in Caso speciale (in)
not in Caso speciale (non incluso)
is null Combinazione di maiuscole/minuscole speciale (è null)
is not null combinazione di maiuscole/minuscole speciale (non è null)
between Caso speciale (tra)
not between Combinazione di maiuscole/minuscole speciale (non compreso tra)
like Testo (ad esempio)
ilike Testo (senza distinzione tra maiuscole e minuscole)
AND Logico (e)
OR Logico (o)

Direct Query

È possibile eseguire una ricerca di somiglianza diretta come indicato di seguito:

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'}]

Se si specifica un dizionario con più campi ma senza operatori, il livello principale viene interpretato come filtro logico AND :

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'}]

Se si vuole eseguire una ricerca di somiglianza e ricevere i punteggi corrispondenti, è possibile eseguire:

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'}]

Se si vuole usare la ricerca di pertinenza marginale massima nell'archivio vettoriale:

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'}]

Per un elenco completo delle ricerche che è possibile eseguire in un AzurePGVectorStore archivio vettoriale, fare riferimento all'API di riferimento.

Trasformazione in una funzione di recupero

È anche possibile trasformare l'archivio vettoriale in un retriever per semplificare l'utilizzo nelle catene:

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')]