隨著ChatGPT的問世,人們開始認識到大語言模型(LLM,Large language model)和生成式人工智能在多個領域的潛力,如文稿撰寫、圖像生成、代碼優化和信息搜索等。LLM已成為個人和企業的得力助手,并朝著超級應用的方向發展,引領著新的生態系統。本文介紹如何基于RDS PostgreSQL構建專屬ChatBot。
背景
越來越多的企業和個人希望能夠利用LLM和生成式人工智能來構建專注于其特定領域的具備AI能力的產品。目前,大語言模型在處理通用問題方面表現較好,但由于訓練語料和大模型的生成限制,對于專業知識和時效性方面存在一些局限。在信息時代,企業的知識庫更新頻率越來越高,而企業所擁有的垂直領域知識庫(如文檔、圖像、音視頻等)可能是未公開或不可公開的。因此,對于企業而言,如果想在大語言模型的基礎上構建屬于特定垂直領域的AI產品,就需要不斷將自身的知識庫輸入到大語言模型中進行訓練。
目前有兩種常見的方法實現:
微調(Fine-tuning):通過提供新的數據集對已有模型的權重進行微調,不斷更新輸入以調整輸出,以達到所需的結果。這適用于數據集規模不大或針對特定類型任務或風格進行訓練,但訓練成本和價格較高。
提示調整(Prompt-tuning):通過調整輸入提示而非修改模型權重,從而實現調整輸出的目的。相較于微調,提示調整具有較低的計算成本,需要的資源和訓練時間也較少,同時更加靈活。
基于RDS PostgreSQL構建ChatBot的優勢如下:
借助RDS PostgreSQL的pgvector插件,可以將實時內容或垂直領域的專業知識和內容轉化為向量化的embedding表示,并存儲在RDS PostgreSQL中,以實現高效的向量化檢索,從而提高私域內容的問答準確性。
作為先進的開源OLTP引擎,RDS PostgreSQL能夠同時完成在線用戶數據交互和數據存儲的任務,例如,它可以用于處理對話的交互記錄、歷史記錄、對話時間等功能。RDS PostgreSQL一專多長的特性使得私域業務的構建更加簡單,架構也更加輕便。
pgvector插件目前已經在開發者社區以及基于PostgreSQL的開源數據庫中得到廣泛應用,同時ChatGPT Retrieval Plugin等工具也及時適配了PostgreSQL。這表明RDS PostgreSQL在向量化檢索領域具有良好的生態支持和廣泛的應用基礎,為用戶提供了豐富的工具和資源。
本文將以RDS PostgreSQL提供的開源向量索引插件(pgvector)和OpenAI提供的embedding能力為例,展示如何構建專屬的ChatBot。
快速體驗
阿里云提供云速搭CADT平臺模板,該方案模板已預部署了ECS以及RDS PostgreSQL數據庫,并且預安裝了前置安裝包,能夠幫助您快速體驗專屬ChatBot,您可以前往云速搭CADT控制臺,參考大模型結合RDS PostgreSQL數據庫構建企業級專屬Chatbot進行體驗。
前提條件
已創建RDS PostgreSQL實例且滿足以下條件:
實例大版本為PostgreSQL 14或以上。
實例內核小版本為20230430或以上。
本文展示的專屬的ChatBot基于RDS 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的實現流程分為兩個階段:
第一階段:數據準備
知識庫信息提取和分塊:從領域知識庫中提取相關的文本信息,并將其分塊處理。這可以包括將長文本拆分為段落或句子,提取關鍵詞或實體等。這樣可以將知識庫的內容更好地組織和管理。
調用LLM接口生成embedding:利用LLM(如OpenAI)提供的接口,將分塊的文本信息輸入到模型中,并生成相應的文本embedding。這些embedding將捕捉文本的語義和語境信息,為后續的搜索和匹配提供基礎。
存儲embedding信息:將生成的文本embedding信息、文本分塊以及文本關聯的metadata信息存入RDS PostgreSQL數據庫中。
第二階段:問答
用戶提問。
通過OpenAI提供的embedding接口創建該問題的embedding。
通過pgvector過濾出RDS PostgreSQL數據庫中相似度大于一定閾值的文檔塊,將結果返回。
流程圖如下:
操作步驟
第一階段:數據準備
本文以創建RDS PostgreSQL實例文檔的文本內容為例,將其拆分并存儲到RDS PostgreSQL數據庫中,您需要準備自己的專屬領域知識庫。
數據準備階段的關鍵在于將專屬領域知識轉化為文本embedding,并有效地存儲和匹配這些信息。通過利用LLM的強大語義理解能力,您可以獲得與特定領域相關的高質量回答和建議。當前的一些開源框架,可以方便您上傳和解析知識庫文件,包括URL、Markdown、PDF、Word等格式。例如LangChain和OpenAI開源的ChatGPT Retrieval Plugin。LangChain和ChatGPT Retrieval Plugin均已經支持了基于pgvector擴展的PostgreSQL作為其后端向量數據庫,這使得與RDS PostgreSQL實例的集成變得更加便捷。通過這樣的集成,您可以方便地完成第一階段領域知識庫的數據準備,并充分利用pgvector提供的向量索引和相似度搜索功能,實現高效的文本匹配和查詢操作。
創建測試數據庫,以
rds_pgvector_test
為例。CREATE DATABASE rds_pgvector_test;
進入測試數據庫,并創建pgvector插件。
CREATE EXTENSION IF NOT EXISTS vector;
創建測試表(本文以
rds_pg_help_docs
為例),用于存儲知識庫內容。CREATE TABLE rds_pg_help_docs ( id bigserial PRIMARY KEY, title text, -- 文檔標題 description text, -- 描述 doc_chunk text, -- 文檔分塊 token_size int, -- 文檔分塊字數 embedding vector(1536)); -- 文本嵌入信息
為embedding列創建索引,用于查詢優化和加速。
CREATE INDEX ON rds_pg_help_docs USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
說明向量列創建索引的更多說明,請參見高維向量相似度搜索(pgvector)。
在PyCharm中,創建項目,然后打開Terminal,輸入如下語句,安裝如下依賴庫。
pip install openai psycopg2 tiktoken requests beautifulsoup4 numpy
創建
.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") # 連接RDS PostgreSQL數據庫 conn = psycopg2.connect(database="<數據庫名>", host="<RDS實例連接地址>", 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 = 'http://m.bestwisewords.com/document_detail/148038.html' 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 rds_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')
運行python程序。
登錄數據庫使用如下命令查看是否已將知識庫文檔內容拆分并存儲為向量數據。
SELECT * FROM rds_pg_help_docs;
第二階段:問答
在python項目中,創建
.py
文件(本文以chatbot.py
為例),創建問題并與數據庫中的知識庫內容比較相似度,返回結果。import openai import psycopg2 from psycopg2.extras import DictCursor GPT_MODEL = "gpt-3.5-turbo" EMBEDDING_MODEL = "text-embedding-ada-002" GPT_COMPLETIONS_MODEL = "text-davinci-003" MAX_TOKENS = 1024 # OpenAI的API Key openai.api_key = '<Secret API Key>' prompt = '如何創建一個RDS PostgreSQL實例' prompt_response = openai.Embedding.create( model=EMBEDDING_MODEL, input=prompt, ) prompt_embedding = prompt_response['data'][0]['embedding'] # 連接RDS PostgreSQL數據庫 conn = psycopg2.connect(database="<數據庫名>", host="<RDS實例連接地址>", user="<用戶名>", password="<密碼>", port="<數據庫端口>") conn.autocommit = True def answer(prompt_doc, prompt): improved_prompt = f""" 按下面提供的文檔和步驟來回答接下來的問題: (1) 首先,分析文檔中的內容,看是否與問題相關 (2) 其次,只能用文檔中的內容進行回復,越詳細越好,并且以markdown格式輸出 (3) 最后,如果問題與RDS PostgreSQL不相關,請回復"我對RDS 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 rds_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)
運行Python程序后,您可以在運行窗口看到類似如下的對應答案:
說明您可以對拆分方法以及問題prompt進行優化,以獲得更加準確、完善的回答,本文僅為示例。
總結
如果未接入專屬知識庫,OpenAI對于問題“如何創建一個RDS PostgreSQL實例”的回答往往與阿里云不相關,例如:
在接入存儲在RDS PostgreSQL數據庫中的專屬知識庫后,對于問題“如何創建一個RDS PostgreSQL實例”,我們將會得到只屬于阿里云RDS PostgreSQL數據庫的相關回答。
根據上述實踐內容,可以看出RDS PostgreSQL完全具備構建基于LLM的垂直領域知識庫的能力。