用 Azure Database for PostgreSQL 和 Python 建立 RAG 應用程式

已完成

現在資料庫已經準備好嵌入和向量索引,是時候將檢索變成一個可運作的應用程式了。 目標很簡單:從用戶提問,從 PostgreSQL 中擷取最相關的區塊,並透過 Azure OpenAI 透過 LangChain 生成基於這些區塊的答案。

在這個單元中,你會學習用 Python 建立 RAG 應用程式的基礎。 應用程式連接資料庫,執行相似度搜尋,並將搜尋結果傳給模型,並附有明確指示以避免產生幻覺。 由於模型會根據資料庫資料獲得明確的上下文,因此能根據該上下文產生更準確且相關的答案。

使用 PostgreSQL 和 LangChain 的檢索增強生成技术

記得我們之前單元中提到的 RAG 流程是一組結合檢索與生成的步驟。 讓我們將其拆解:

  1. 使用者提出問題。
  2. 資料庫會為問題計算嵌入,並利用向量索引尋找資料庫中最接近的匹配(頂尖區塊)。
  3. 頂端區塊會傳給模型,並提示:「 只能從此上下文回答。不知道就說吧。」
  4. 模型會根據所提供的情境回傳自然語言的答案。

LangChain 提供了一個用語言模型來建構應用程式的框架。 它簡化了連接各種資料來源、管理提示及處理回應的流程。 結合 Azure Database for PostgreSQLPythonLangChain,你可以打造強大的 RAG 應用程式,擷取相關資訊並產生準確回應。 雖然您的RAG應用可能會演變,但這些核心原則將引導其開發。

在這個應用程式中,你會使用 LangChain AzureChatOpenAI 包裝器來與 Azure OpenAI 服務互動。

以下 Python 範例中,你使用以下表格結構:

CREATE TABLE company_policies (
    id SERIAL PRIMARY KEY,
    title TEXT,
    policy_text TEXT,
    embedding VECTOR(1536)
);

在這個表格中,你假設在欄位上建立 embedding 向量索引,以便有效率地進行相似度搜尋。

讓我們來複習每個步驟的 Python 程式碼片段。 大多數RAG申請遵循類似的結構。

連結至資料庫

保持連結短暫且安全。 在此範例中,秘密存在環境變數中並傳遞給連線。

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

這個腳本會為資料庫連線建立情境管理器,確保使用後連結會被正確關閉。 該進入實際資料檢索階段了。

取回相關區塊

既然使用者在問問題,你需要一個函式來查詢資料庫並提出該問題。 該函式應該會根據問題回傳相關區塊。 這個範例假設向量索引是以 餘弦距離排序,因此查詢時會使用相應的運算子類別(<=>)。

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]

因此,這個函式會根據使用者的問題從資料庫中取得相關區塊。 這些區塊接著用來在後續步驟產生具情境感知的答案。

用 LangChain 產生答案

現在你有一個函式可以取得相關區塊,你需要用這些區塊來產生答案。 模型應僅使用檢索的上下文並引用政策標題。 是時候建立答案產生函數了。

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

備註

使用 AzureChatOpenAI

  • 將訊息傳遞為包含(角色, 內容)元組的清單(或訊息物件)。
  • 透過環境變數和建構器提供 Azure 設定:AZURE_OPENAI_API_KEYAZURE_OPENAI_ENDPOINTazure_deploymentapi_version
  • 請保留 temperature=0 以顯示事實性的答案。 較大的數值能提升創意,但可能會降低準確度。

此函式是 RAG 應用程式的核心,負責與語言模型的互動。 注意檢索區塊的格式化與包含在上下文中的呈現方式。 此外,系統提示音設計確保模型符合所提供的情境,並減少可能錯誤的 AI 生成回應。 最後,語言模型會處理訊息,並利用 LangChain調用 方法產生回應。 invoke該方法以格式化訊息被呼叫,模型的回應以自然語言文字回傳。

把它綁在一起

你最不需要的是一個簡單的函式來執行完整流程:

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

在這個例子中,你從資料庫中取得相關區塊,並用它們來產生一個情境感知的答案。 這個函式呼叫展示了從問題到答案產生的完整流程。 首先它會呼叫 retrieve_chunks 函式來取得相關的上下文(基本上是從資料庫回傳的列)。 這個函式接著將該上下文傳遞給 generate_answer 與語言模型互動的函數,以自然語言回應的方式產生最終答案(利用列作為上下文的一部分)。 完整流程確保答案基於檢索資料,提供更準確且可靠的回應。

重點摘要

實務上的 RAG 應用程式會接收使用者問題,建立 SQL 嵌入,使用向量索引擷取資料庫中最接近的段落,並只將該上下文交給模型。 為了降低幻覺,模型也會被指示僅在提供的內容範圍內作答,並在缺少資訊時坦承說明。 保持連線短暫,參數化查詢,並將秘密傳遞給環境變數。 對於事實性回答,請使用低溫度設定,並加入輕量化引用 (標題或 ID),以便回應可追溯。

你現在應該已經很清楚如何用 Azure Database for PostgreSQLPython 建立檢索增強生成(RAG)應用程式。 雖然你的RAG應用在實際情境中可能更為複雜,但核心原則依然相同。