RAG-toepassingen bouwen met Azure Database for PostgreSQL en Python
Nu de database klaar is met insluitingen en vectorindexen, is het tijd om het ophalen om te zetten in een werkende toepassing. Het doel is eenvoudig: neem een gebruikersvraag, haal de meest relevante segmenten op uit PostgreSQL en genereer een antwoord dat is geaard in deze segmenten met behulp van Azure OpenAI via LangChain.
In deze les leert u de basisprincipes van het bouwen van een RAG-toepassing in Python. De toepassing maakt verbinding met de database, voert een overeenkomstenzoekactie uit op de database en geeft de zoekresultaten door aan een model met duidelijke instructies om halluinaties te voorkomen. Omdat het model een goed gedefinieerde context ontvangt op basis van de databasegegevens, kan het nauwkeurigere en relevante antwoorden genereren op basis van die context.
Retrieval-Augmented Generatie met PostgreSQL en LangChain
Vergeet niet dat de RAG-stroom uit onze vorige lessen bestaat uit een reeks stappen die het ophalen en genereren combineren. Laten we het opsplitsen:
- Gebruiker stelt een vraag.
- Database berekent een insluiting voor de vraag en gebruikt de vectorindex om de dichtstbijzijnde overeenkomsten in de database te vinden (bovenste segmenten).
- De bovenste segmenten worden doorgegeven aan het model met een prompt met de tekst: 'Antwoord alleen vanuit deze context. Als je het niet weet, zeg het dan."
- Model retourneert een antwoord in natuurlijke taal op basis van de gegeven context.
LangChain biedt een framework voor het bouwen van toepassingen met taalmodellen. Het vereenvoudigt het proces van het maken van verbinding met verschillende gegevensbronnen, het beheren van prompts en het verwerken van antwoorden. Door Azure Database for PostgreSQL te combineren met Python en LangChain, kunt u een krachtige RAG-toepassing maken die relevante informatie ophaalt en nauwkeurige antwoorden genereert. Hoewel uw RAG-toepassing zich kan ontwikkelen, vormen deze kernprincipes een leidraad voor de ontwikkeling ervan.
In deze toepassing gebruikt u de Wrapper LangChain AzureChatOpenAI om te communiceren met de Azure OpenAI-service .
Voor de volgende Python-voorbeelden gebruikt u de volgende tabelstructuur:
CREATE TABLE company_policies (
id SERIAL PRIMARY KEY,
title TEXT,
policy_text TEXT,
embedding VECTOR(1536)
);
In deze tabel gaat u ervan uit dat er een vectorindex wordt gemaakt op de embedding kolom om efficiënte overeenkomstzoekopdrachten mogelijk te maken.
Laten we de Python-codefragmenten voor elke stap bekijken. De meeste RAG-toepassingen volgen een vergelijkbare structuur.
Verbinding maken met de database
Houd verbindingen met een korte levensduur en veilig. In dit voorbeeld bevinden geheimen zich in omgevingsvariabelen en worden ze doorgegeven aan de verbinding.
import os, psycopg2
from contextlib import contextmanager
@contextmanager
def get_conn():
conn = psycopg2.connect(
host=os.getenv("PGHOST"),
user=os.getenv("PGUSER"),
password=os.getenv("PGPASSWORD"),
dbname=os.getenv("PGDATABASE"),
connect_timeout=10
)
try:
yield conn
finally:
conn.close()
Met dit script wordt een contextbeheer ingesteld voor databaseverbindingen, zodat deze na gebruik correct worden gesloten. Tijd om naar het daadwerkelijke ophalen te gaan.
De relevante segmenten ophalen
Omdat de gebruiker een vraag stelt, hebt u een functie nodig om een query uit te voeren op de database met die vraag. Deze functie moet de relevante segmenten retourneren op basis van de vraag. In dit voorbeeld wordt ervan uitgegaan dat de vectorindex wordt gerangschikt op basis van cosinusafstand, dus u gebruikt de respectieve operatorklasse (<=>) voor het uitvoeren van query's.
def retrieve_chunks(question, top_k=5):
sql = """
WITH q AS (
SELECT azure_openai.create_embeddings(%s, %s)::vector AS qvec
)
SELECT id, title, policy_text
FROM company_policies, q
ORDER BY embedding <=> q.qvec
LIMIT %s;
"""
params = (os.getenv("OPENAI_EMBED_DEPLOYMENT"), question, top_k)
with get_conn() as conn, conn.cursor() as cur:
cur.execute(sql, params)
rows = cur.fetchall()
return [{"id": r[0], "title": r[1], "text": r[2]} for r in rows]
Deze functie haalt dus relevante segmenten op uit de database op basis van de vraag van de gebruiker. Deze segmenten worden vervolgens gebruikt om in een volgende stap een contextbewust antwoord te genereren.
Een antwoord genereren met LangChain
Nu u een functie hebt om relevante segmenten op te halen, moet u een antwoord genereren met behulp van deze segmenten. Het model mag alleen de opgehaalde context gebruiken en beleidstitels citeren. Tijd om de functie voor het genereren van antwoorden te maken.
from langchain_openai import AzureChatOpenAI
SYSTEM_PROMPT = """
You are a helpful assistant. Answer using ONLY the provided context.
If the answer is not in the context, say you don’t have enough information.
Cite policy titles in square brackets, e.g., [Vacation policy].
"""
def format_context(chunks):
return "\n\n".join([f"[{c['title']}] {c['text']}" for c in chunks])
def generate_answer(question, chunks):
llm = AzureChatOpenAI(
azure_deployment=os.getenv("OPENAI_CHAT_DEPLOYMENT"),
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
temperature=0
)
context = format_context(chunks)
messages = [
("system", SYSTEM_PROMPT),
("human", f"Question: {question}\nContext:\n{context}")
]
return llm.invoke(messages).content
Opmerking
AzureChatOpenAI gebruiken:
- Berichten doorgeven als een lijst met tuples (rol, inhoud) (of berichtobjecten).
- Geef Azure-instellingen op via env vars en constructor:
AZURE_OPENAI_API_KEY,AZURE_OPENAI_ENDPOINT, enazure_deployment.api_version - Behoud
temperature=0voor feitelijke antwoorden. Grotere waarden vergroten creativiteit, maar kunnen de nauwkeurigheid verminderen.
Deze functie is de kern van de RAG-toepassing en verwerkt de interactie met het taalmodel. U ziet hoe opgehaalde segmenten zijn opgemaakt en opgenomen in de context. Bovendien is de systeemprompt ontworpen om ervoor te zorgen dat het model voldoet aan de gegeven context en vermindert ai gegenereerde antwoorden die mogelijk onjuist zijn. Ten slotte worden de berichten verwerkt door het taalmodel om een antwoord te genereren met behulp van de aanroepmethodeLangChain. De invoke methode wordt aangeroepen met de opgemaakte berichten en het antwoord van het model wordt geretourneerd als tekst in natuurlijke taal.
Het aan elkaar koppelen
Het laatste wat je nodig hebt, is een eenvoudige functie om de volledige workflow uit te voeren.
def answer_question(question):
chunks = retrieve_chunks(question)
if not chunks:
return "I couldn’t find relevant content in the policy store."
return generate_answer(question, chunks)
# Quick test
print(answer_question("How many vacation days do employees get?"))
In dit voorbeeld haalt u de relevante segmenten op uit de database en gebruikt u deze om een contextbewust antwoord te genereren. Deze functie-aanroep demonstreert de volledige stroom van vraag tot het genereren van antwoorden. Eerst wordt de retrieve_chunks functie aangeroepen om de relevante context op te halen (in principe de rijen die worden geretourneerd uit de database). Met deze functie wordt die context vervolgens doorgegeven aan de generate_answer functie die communiceert met het taalmodel om het uiteindelijke antwoord te produceren (met behulp van de rijen als onderdeel van de context) als antwoord in natuurlijke taal. De volledige stroom zorgt ervoor dat het antwoord wordt geaard in de opgehaalde gegevens, waardoor een nauwkeuriger en betrouwbaarder antwoord wordt geboden.
Belangrijke punten
Een praktische RAG-toepassing neemt een gebruikersvraag, maakt een insluiting in SQL, gebruikt een vectorindex om de dichtstbijzijnde passages in de database op te halen en geeft alleen die context aan het model. Om hallucinatie te verminderen, wordt het model ook geïnstrueerd om binnen de opgegeven context te blijven en toe te geven wanneer er informatie ontbreekt. Bewaar de verbindingen met korte levensduur, parameteriseer query's en geef geheimen door via omgevingsvariabelen. Gebruik een lage temperatuur voor feitelijke antwoorden en neem lichtgewicht bronvermeldingen (titels of id's) op, zodat reacties kunnen worden herleid.
U moet nu een solide kennis hebben van het bouwen van een RAG-toepassing (Retrieval Augmented Generation) met behulp van Azure Database for PostgreSQL en Python. Hoewel uw RAG-toepassing in een praktijkscenario veel complexer kan zijn, blijven de belangrijkste principes hetzelfde.