全部產品
Search
文件中心

PolarDB:基於PolarDB PostgreSQL和LLM構建企業專屬Chatbot

更新時間:Jul 06, 2024

隨著ChatGPT的問世,人們開始認識到大語言模型(LLM,Large language model)和產生式人工智慧在多個領域的潛力,如文稿撰寫、映像產生、代碼最佳化和資訊搜尋等。LLM已成為個人和企業的得力助手,並朝著超級應用的方向發展,引領著新的生態系統。本文介紹如何基於PolarDB PostgreSQL版向量資料庫和LLM構建企業專屬Chatbot。

背景資訊

越來越多的企業和個人希望能夠利用LLM和產生式人工智慧來構建專註於其特定領域的具備AI能力的產品。目前,大語言模型在處理通用問題方面表現較好,但由於訓練語料和大模型的產生限制,對於垂直專業領域,則會存在知識深度和時效性不足的問題。在資訊時代,由於企業的知識庫更新頻率越來越高,並且企業所擁有的垂直領域知識庫(例如文檔、映像、音視頻等)往往是未公開或不可公開的。因此,對於企業而言,如果想在大語言模型的基礎上構建屬於特定垂直領域的AI產品,就需要不斷將自身的知識庫輸入到大語言模型中進行訓練。

目前有兩種常見的方法實現:

  • 微調(Fine-tuning):通過提供新的資料集對已有模型的權重進行微調,不斷更新輸入以調整輸出,以達到所需的結果。這適用於資料集規模不大或針對特定類型任務或風格進行訓練,但訓練成本和價格較高。

  • 提示調整(Prompt-tuning):通過調整輸入提示而非修改模型權重,從而實現調整輸出的目的。相較於微調,提示調整具有較低的計算成本,需要的資源和訓練時間也較少,同時更加靈活。

綜上所述,微調的方案投入成本較高,更新頻率較低,並不適合所有企業。提示調整的方案是在向量庫中構建企業的知識資產,通過LLM+向量庫構建垂直領域的深度服務。本質是利用資料庫進行提示工程(Prompt Engineering)將企業知識庫文檔和即時資訊通過向量特徵提取然後儲存到向量資料庫,結合LLM可以讓Chatbot的回答更具專業性和時效性,也更適合中小型企業構建企業專屬Chatbot。

在機器學習領域,為了能夠處理大量的非結構化的資料,通常會使用人工智慧技術提取這些非結構化資料的特徵,並將其轉化為特徵向量,再對這些特徵向量進行分析和檢索以實現對非結構化資料的處理。將這種能儲存、分析和檢索特徵向量的資料庫稱之為向量資料庫。

基於PolarDB PostgreSQL版構建的ChatBot的優勢如下:

  • 藉助PolarDB PostgreSQL版的PGVector外掛程式,可以將即時內容或垂直領域的專業知識和內容轉化為向量化的embedding表示,並儲存在PolarDB PostgreSQL版中,以實現高效的向量化檢索,從而提高私域內容的問答準確性。

  • 作為新一代關係型雲原生資料庫,PolarDB PostgreSQL版既擁有分布式設計的低成本優勢,又具有集中式的易用性。實現了計算節點及儲存節點的分離,提供即時生效的可擴充能力和營運能力。在雲原生分散式資料庫領域整體處於國際領先水平。

  • PGVector外掛程式目前已經在開發人員社區以及基於PostgreSQL的開來源資料庫中得到廣泛應用,同時ChatGPT Retrieval Plugin等工具也及時適配了PostgreSQL。這表明PolarDB PostgreSQL版在向量化檢索領域具有良好的生態支援和廣泛的應用基礎,為使用者提供了豐富的工具和資源。

重要

本文提到的“大型語言模型(LLM)”來自第三方(統稱為“第三方模型”)。阿里雲無法保證第三方模型的合規性和準確性,也不對第三方模型以及您使用第三方模型的行為和結果承擔任何責任。因此,在訪問或使用第三方模型之前請進行評估。另外,我們提醒您,第三方模型附帶有“開源許可”、“許可證”等協議,您應仔細閱讀並嚴格遵守這些協議的規定。

前提條件

  • 已建立PolarDB PostgreSQL版叢集且滿足以下條件:

    PostgreSQL 14(核心小版本14.7.9.0及以上)

    說明

    如需升級核心小版本,請參見版本管理

  • 本文展示的專屬的ChatBot基於PolarDB PostgreSQL版提供的開源外掛程式PGVector,請確保已完全瞭解其相關用法及基本概念。

  • 本文展示的專屬的ChatBot使用了OpenAI的相關能力,請確保您具備Secret API Key,並且您的網路環境可以使用OpenAI,本文展示的程式碼範例均部署在新加坡地區的ECS中。

  • 本文範例程式碼使用了Python語言,請確保已具備Python開發環境,本樣本使用的Python版本為3.11.4,使用的開發工具為PyCharm 2023.1.2

相關概念

嵌入

嵌入(embedding)是指將高維資料對應為低維表示的過程。在機器學習和自然語言處理中,嵌入通常用於將離散的符號或對象表示為連續的向量空間中的點。

在自然語言處理中,詞嵌入(word embedding)是一種常見的技術,它將單詞映射到實數向量,以便電腦可以更好地理解和處理文本。通過詞嵌入,單詞之間的語義和文法關係可以在向量空間中得到反映。

OpenAI提供Embeddings能力。

實現原理

本文展示的專屬ChatBot的實現流程分為兩個階段:

第一階段:資料準備

  1. 知識庫資訊提取和分塊:從領域知識庫中提取相關的文本資訊,並將其分塊處理。這可以包括將長文本拆分為段落或句子,提取關鍵詞或實體等。這樣可以將知識庫的內容更好地組織和管理。

  2. 調用LLM介面產生embedding:利用LLM(如OpenAI)提供的介面,將分塊的文本資訊輸入到模型中,並產生相應的文本embedding。這些embedding將捕捉文本的語義和語境資訊,為後續的搜尋和匹配提供基礎。

  3. 儲存embedding資訊:將產生的文本embedding資訊、文本分塊以及文本關聯的metadata資訊存入PolarDB PostgreSQL版資料庫中。

第二階段:問答

  1. 使用者提問。

  2. 通過OpenAI提供的embedding介面建立該問題的embedding。

  3. 通過PGVector過濾出PolarDB PostgreSQL版資料庫中相似性大於一定閾值的文檔塊,將結果返回。

流程圖如下:

image.png

操作步驟

第一階段:資料準備

本文以2023年PolarDB PostgreSQL版的產品功能動態文檔的常值內容為例,將其拆分並儲存到PolarDB PostgreSQL版資料庫中,您需要準備自己的專屬領域知識庫。

資料準備階段的關鍵在於將專屬領域知識轉化為文本embedding,並有效地儲存和匹配這些資訊。通過利用LLM的強大語義理解能力,您可以獲得與特定領域相關的高品質回答和建議。當前的一些開源架構,可以方便您上傳和解析知識庫檔案,包括URL、Markdown、PDF、Word等格式。例如LangChain和OpenAI開源的ChatGPT Retrieval Plugin。LangChain和ChatGPT Retrieval Plugin均已經支援了基於PGVector擴充的PostgreSQL作為其後端向量資料庫,這使得與PolarDB PostgreSQL版叢集的整合變得更加便捷。通過這樣的整合,您可以方便地完成第一階段領域知識庫的資料準備,並充分利用PGVector提供的向量索引和相似性搜尋功能,實現高效的文本匹配和查詢操作。

  1. 串連PolarDB PostgreSQL版叢集

  2. 建立測試資料庫,以testdb為例。

    CREATE DATABASE testdb;
  3. 進入測試資料庫,並建立PGvector外掛程式。

    CREATE EXTENSION IF NOT EXISTS vector;
  4. 建立測試表(本文以polardb_pg_help_docs為例),用於儲存知識庫內容。

    CREATE TABLE polardb_pg_help_docs (
      id bigserial PRIMARY KEY, 
      title text,			-- 文檔標題
      description text, 		-- 描述
      doc_chunk text, 		-- 文檔分塊
      token_size int, 		-- 文檔分塊字數
      embedding vector(1536));	-- 文本嵌入資訊
  5. 為embedding列建立索引,用於查詢最佳化和加速。

    CREATE INDEX ON polardb_pg_help_docs USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
  6. 在PyCharm中,建立專案,然後開啟Terminal,輸入如下語句,安裝如下依賴庫。

    pip install openai psycopg2 tiktoken requests beautifulsoup4 numpy
    說明

    如果psycopg2有安裝問題,請考慮採用源碼編譯方式。

  7. 建立.py檔案(本文以knowledge_chunk_storage.py為例),拆分知識庫文檔內容並儲存到資料庫中,範例程式碼如下:

    說明

    如下範例程式碼中,自訂的拆分方法僅僅是將知識庫文檔內容按固定字數進行了拆分,您可以使用LangChain和OpenAI開源的ChatGPT Retrieval Plugin等開源架構中提供的方法進行拆分。知識庫中的文檔品質和分塊結果對最終的輸出的結果有較大的影響。

    import openai
    import psycopg2
    import tiktoken
    import requests
    from bs4 import BeautifulSoup
    
    EMBEDDING_MODEL = "text-embedding-ada-002"
    tokenizer = tiktoken.get_encoding("cl100k_base")
    
    # 串連PolarDB-PG資料庫
    conn = psycopg2.connect(database="<資料庫名>",
                            host="<PolarDB PostgreSQL版叢集串連地址>",
                            user="<使用者名稱>",
                            password="<密碼>",
                            port="<資料庫連接埠>")
    conn.autocommit = True
    
    # OpenAI的API Key
    openai.api_key = '<Secret API Key>'
    
    # 自訂拆分方法(僅為樣本)
    def get_text_chunks(text, max_chunk_size):
        chunks_ = []
        soup_ = BeautifulSoup(text, 'html.parser')
    
        content = ''.join(soup_.strings).strip()
    
        length = len(content)
        start = 0
        while start < length:
            end = start + max_chunk_size
            if end >= length:
                end = length
    
            chunk_ = content[start:end]
            chunks_.append(chunk_)
    
            start = end
    
        return chunks_
    
    
    # 指定需要拆分的網頁
    url = 'https://www.alibabacloud.com/help/document_detail/602217.html?spm=a2c4g.468881.0.0.5a2c72c2cnmjaL'
    
    response = requests.get(url)
    if response.status_code == 200:
        # 擷取網頁內容
        web_html_data = response.text
        soup = BeautifulSoup(web_html_data, 'html.parser')
        # 擷取標題(H1標籤)
        title = soup.find('h1').text.strip()
    
        # 擷取描述(class為shortdesc的p標籤內容)
        description = soup.find('p', class_='shortdesc').text.strip()
    
        # 拆分並儲存
        chunks = get_text_chunks(web_html_data, 500)
        for chunk in chunks:
            doc_item = {
                'title': title,
                'description': description,
                'doc_chunk': chunk,
                'token_size': len(tokenizer.encode(chunk))
            }
    
            query_embedding_response = openai.Embedding.create(
                model=EMBEDDING_MODEL,
                input=chunk,
            )
    
            doc_item['embedding'] = query_embedding_response['data'][0]['embedding']
    
            cur = conn.cursor()
            insert_query = '''
            INSERT INTO polardb_pg_help_docs 
                (title, description, doc_chunk, token_size, embedding) VALUES (%s, %s, %s, %s, %s);
            '''
    
            cur.execute(insert_query, (
                doc_item['title'], doc_item['description'], doc_item['doc_chunk'], doc_item['token_size'],
                doc_item['embedding']))
    
            conn.commit()
    
    else:
        print('Failed to fetch web page')
  8. 運行python程式。

  9. 登入資料庫使用如下命令查看是否已將知識庫文檔內容拆分並儲存為向量資料。

    SELECT * FROM polardb_pg_help_docs;

    image.png

第二階段:問答

  1. 在python專案中,建立.py檔案(本文以chatbot.py為例),建立問題並與資料庫中的知識庫內容比較相似性,返回結果。

    # 串連PolarDB PostgreSQL版叢集資料庫
    conn = psycopg2.connect(database="<資料庫名>",
                            host="<PolarDB PostgreSQL版叢集串連地址>",
                            user="<使用者名稱>",
                            password="<密碼>",
                            port="<資料庫連接埠>")
    conn.autocommit = True
    
    
    def answer(prompt_doc, prompt):
        improved_prompt = f"""
        按下面提供的文檔和步驟來回答接下來的問題:
        (1) 首先,分析文檔中的內容,看是否與問題相關
        (2) 其次,只能用文檔中的內容進行回複,越詳細越好,並且以markdown格式輸出
        (3) 最後,如果問題與PolarDB PostgreSQL版不相關,請回複"我對PolarDB PostgreSQL版以外的知識不是很瞭解"
    
        文檔:
        \"\"\"
        {prompt_doc}
        \"\"\"
    
        問題: {prompt}
        """
    
        response = openai.Completion.create(
            model=GPT_COMPLETIONS_MODEL,
            prompt=improved_prompt,
            temperature=0.2,
            max_tokens=MAX_TOKENS
        )
    
        print(f"{response['choices'][0]['text']}\n")
    
    
    similarity_threshold = 0.78
    max_matched_doc_counts = 8
    
    # 通過pgvector過濾出相似性大於一定閾值的文檔塊
    similarity_search_sql = f'''
    SELECT doc_chunk, token_size, 1 - (embedding <=> '{prompt_embedding}') AS similarity 
    FROM polardb_pg_help_docs WHERE 1 - (embedding <=> '{prompt_embedding}') > {similarity_threshold} ORDER BY id LIMIT {max_matched_doc_counts};
    '''
    
    cur = conn.cursor(cursor_factory=DictCursor)
    cur.execute(similarity_search_sql)
    matched_docs = cur.fetchall()
    
    total_tokens = 0
    prompt_doc = ''
    print('Answer: \n')
    for matched_doc in matched_docs:
        if total_tokens + matched_doc['token_size'] <= 1000:
            prompt_doc += f"\n---\n{matched_doc['doc_chunk']}"
            total_tokens += matched_doc['token_size']
            continue
    
        answer(prompt_doc,prompt)
    
        total_tokens = 0
        prompt_doc = ''
    
    answer(prompt_doc,prompt)
  2. 運行Python程式後,您可以在運行視窗看到類似如下的對應答案:

    說明

    您可以對拆分方法以及問題prompt進行最佳化,以獲得更加準確、完善的回答,本文僅為樣本。

    image.png

總結

如果未接入向量資料庫,OpenAI對於問題“列舉2023年PolarDB PostgreSQL 14版本新增功能點”的回答往往與阿里雲不相關,例如:

image.png

在接入儲存在PolarDB PostgreSQL版資料庫中的專屬知識庫後,對於問題“列舉2023年PolarDB PostgreSQL 14版本新增功能點”,我們將會得到只屬於阿里雲PolarDB PostgreSQL版資料庫的相關回答。

image.png

根據上述實踐內容,可以看出PolarDB PostgreSQL版完全具備構建基於LLM的垂直領域知識庫的能力。

相關參考

通過查看GitHub頁面可以瞭解更多資訊:https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases/PolarDB