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

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

快速體驗

阿里雲提供雲速搭CADT平臺模板,該方案模板已預部署了ECS以及PolarDB PostgreSQL版數據庫,並且預安裝了前置安裝包,能夠幫助您快速體驗專屬ChatBot,您可以前往雲速搭CADT控制檯,參考大模型結合POLARDB PG數據庫構建企業級專屬Chatbot進行體驗。

前提條件

  • 已創建PolarDB PostgreSQL版集羣且滿足以下條件:
  • PostgreSQL 14(內核小版本14.7.9.0及以上)
說明
如需升級內核小版本,請參見 版本管理
  • 本文展示的專屬的ChatBot基於PolarDB PostgreSQL版提供的開源插件PGVector,請確保已完全瞭解其相關用法及基本概念,更多信息,請參見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版數據庫中相似度大於一定閾值的文檔塊,將結果返回。

流程圖如下:

操作步驟

第一階段:數據準備

本文以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);
說明
向量列創建索引的更多說明,請參見 PGVector

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://help.aliyun.com/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;

第二階段:問答

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進行優化,以獲得更加準確、完善的回答,本文僅爲示例。

總結

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

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

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

相關參考

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

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章