如何用大語言模型構建一個知識問答系統

作者:simon

傳統搜索系統基於關鍵字匹配,在面向:遊戲攻略、技術圖譜、知識庫等業務場景時,缺少對用戶問題理解和答案二次處理能力。本文探索使用大語言模型(Large Language Model, LLM),通過其對自然語言理解和生成的能力,揣摩用戶意圖,並對原始知識點進行彙總、整合,生成更貼切的答案。關於基本思路,驗證效果和擴展方向,可以參考正文的介紹。

需求描述

打造 特定領域知識 (Domain-specific Knowledge) 問答 系統,具體需求有:

從大語言模型(Large Language Model, LLM)[2] 角度而言,上面的需求是在兩階段訓練模式下,面向下游場景進行適配的問題。基礎模型 (Foundation Model[3]),面向特定領域不能直接應用,因爲領域知識不在預訓練的數據集中,比如:

方案分析

基於 LLM 搭建問答系統的解決方案有以下幾種:

Fine-Tuning

使用下游特定領域的知識對基礎模型進行微調,改變神經網絡中參數的權重。業界已經不少 chatgpt 的平替方案都支持微調,比如:

微調方式比較適合特化的任務或風格,但也存在一些問題:

基於 Prompt

將特定領域的知識作爲輸入消息提供給模型。類似於短期記憶,容量有限但是清晰。舉個例子給 ChatGPT 發送請求,將特定的知識放在請求中,讓 ChatGPT 對消息中蘊含的知識進行分析,並返回處理結果。

XKjdMP

與搜索結合

Fine-Tuning 和基於 Prompt 方式均存在缺陷,比如效率低下、數據不夠精確、不能支持大規模數據量等問題。這裏提出第三種方法,嘗試克服這些困難,基本思想是:

整個方案設計如下圖所示由兩部分組成:

問答系統框架概要

  1. 玩家諮詢倚天劍的屬性

  2. 玩家諮詢屠龍刀的屬性

  3. 玩家要求比較倚天劍和屠龍刀。這裏 LLM 已經獲取兩件兵器的屬性,使用既有的推理能力進行對比。在同一個會話過程中,可以讓 LLM 從會話歷史中提取信息並分析。

方案實現

本文的實現,參考了 OpenAI 提供的樣例,主要理由是 ChatGPT 對外提供了良好的 API 以及中英文支持。但從框架角度而言,不會綁死在 OpenAI 上,每一個具體實現都可以由業務結合自己的需求進行替換。上節提到的方案落地到 ChatGPT 上特化爲:

基於 OpenAI 的一種實現

說明如下:

  1. 使用 OpenAI 的 Embedding 接口將專業領域知識轉化爲向量,連同原始材料一併保存在 Redis 中。

  2. 用戶提問的搜索處理:

  3. 使用 OpenAI API 對用戶的問題進行 Embedding,獲得向量。

  4. 使用問題向量在 Redis 中搜索,找到與之最匹配的若干記錄。將這些記錄的原始材料返回。

  5. 使用 OpenAI 的 Completion API 對這些原始材料進行加工完善,並將最終結果返回。

下面對上述過程展開描述。

領域知識入庫

該過程的主要目的是:將原始知識庫分拆爲若干知識點,並生成與之對應的字典:

該字典的作用是用戶提問時,通過 Embedding 之後的向量比對,實現問題和答案的匹配。具體過程涉及以下幾點,如圖所示:

領域知識入庫流程示意圖

  1. 數據源可能來自於網絡(遊戲已經對外的攻略)、本地文本文件(技術文檔、設計稿)或者數據庫(業務自己維護的 UGC,比如用戶帖子、評論等)。採用合適的方式收集這些數據並整理爲純文本的格式。這裏提供一個 python 庫 textract[12],支持從多種類型文件中提取文字信息,普通文本文件自不必說,其它各種常用格式文件也都支持,比如:Microsoft 全家桶 docx, xlsx;圖像 gif, jpg 等;音頻文件 mp3, ogg 等。

  2. 生成分詞器 tokenizer,將文本分成一個個詞元,保證各個詞元擁有相對完整和獨立的語義,以供後續任務比如 Embedding 使用。tiktoken[13] 是一種 Byte Pair Encoding(BPE)[14] 分詞器,有多種編碼方法可選,如:r50k_base, p50k_base, cl100k_base 等。面向 OpenAI 的 gpt-4, gpt-3.5-turbo 和 text-embedding-ada-002 模型通常使用 cl100k_base 編碼方法。

  3. 分片。將原始知識庫拆分爲若干個獨立、較短的知識點。每個知識點會作爲問答的最小記錄,與問題進行匹配。在實際使用過程中有以下幾點建議:

  1. 詞嵌入 (Embedding)。使用 OpenAI API 對每個分片後的每個知識點進行處理,獲得向量化的結果。這裏需要調用 openai.Embedding.create 接口。

  2. 存儲。將 Embeddings 生成的向量連同原始分片(知識點),以 kv 形式存儲,便於後續快速匹配索引。專業的解決方案是 vector database[15],但實際上很多傳統的數據庫或存儲中間件也已經提供了支持,比如:

搜索

搜索的核心流程包含兩步:

  1. 將用戶的問題通過 OpenAI API openai.Embedding.create進行 embedding 得到向量。

  2. 向 redis 發起查詢獲得與之最匹配(距離最近、相似度最高)的若干答案。

除此外,也可以利用 LLM 對用戶的問題進行預處理,常見的方式有:

問題預處理的 Prompt 交互示意圖

對於 ChatGPT 而言,上述這種預設對話行爲來引導用戶的方式稱之爲 ChatCompletion,可以 openai.ChatCompletion.create api,將多輪會話的上下文整合起來,對提問和回答過程提供更加強大、靈活的定製能力。比如:

交互式會話中, 提供了三種不同的角色 (role):user,system,assistant。

具體可以參考 ChatGPT API Transition Guide | OpenAI Help Center[19]

結果整合

結果整合的主要作用是將本地搜索系統返回的結果進行二次加工,比如發揮 LLM 的:

實現的方法還是基於 ChatCompletion ,方式很多,業務完全可以結合場景自由發揮。這裏提供一個在 NBA2K Online2 中實現的方式:

完整問答交互示意圖

  1. 本地 search result 有效,可能有多條最貼近的知識點,則整理總結作爲最終結果。

  2. 本地 search result 返回特殊標記,比如:本地知識庫找不到,則基於 conversation history 分析;如果還是找不到則提示找不到。

  3. 基於用戶的請求 user_query 觸發本地搜索,獲得答案 search result

  4. 將答案以 system role 的身份插入 conversation history 中,要求對於用戶發出的請求 user_query 使用 search result 回覆

  5. 將包含本地搜索答案的 system 指令和用戶問題依次推入 conversation history

  6. 交給 chatgpt 的 ChatCompletion 處理:

應用效果

上述方案在測試過程中,以 NBA2K Online2 官網的攻略信息 [20] 爲基礎進行嘗試,基於 OpenAI API,搭建簡單的 CLI 的應用。效果如下所示(爲了簡化過程,一律省略多輪交互問答的過程)。

基礎能力

對問題在本地進行搜索,找到多條匹配語料,然後自動整合使用無序列表的形式返回。

基於會話歷史的問答

知識庫中僅保存了:奧拉朱旺、科比、詹姆斯三人各自的打法信息,並沒有直接提供三者的比較。所以如果僅通過一個問題要求比較三人的打法差異,是無法在 Redis 中直接匹配命中的。但是可以使用會話歷史,當本地無法命中時,讓 ChatGPT 基於過往的信息自動進行整合,如下所示:

基於會話上下文的交互示意

總結

本文針對特定領域知識問答系統的問題,進行方案比較和選型。不難發現:傳統的搜索模式、LLM 的 Fine-Tuning、Prompt Engineer 等方式均存在不同程度的缺陷。經過分析比較後,決定探索 LLM + 搜索 的方式進行處理,並在 NBA2K Online2 攻略應用場景進行驗證。該方法:

在實踐過程中,選擇 ChatGPT 作爲 LLM 的經典實現,使用 RediSearch 提供的 Vector Similarity 作爲問題答案的匹配索引框架。但 LLM + 搜索的方式在框架上是非常通用的,不侷限於上述選擇,業務完全可以基於自身場景使用其他基礎模型和搜索方案。另外業務在和 LLM 交互過程中也可以定製更加靈活、智能的提示詞來引導交互過程。本文的細節實踐僅供參考,希望可以起到拋磚引玉的效果。

參考資料

[1] 似是而非或無意義: https://www.entrepreneur.com/growth-strategies/the-advantages-and-disadvantages-of-chatgpt/450268

[2] 大語言模型(Large Language Model, LLM): https://research.aimultiple.com/large-language-models/

[3] Foundation Model: https://en.wikipedia.org/wiki/Foundation_models

[4] Prompt Engineering: https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/

[5] ChatGLM: https://github.com/THUDM/ChatGLM-6B

[6] Alpaca: https://github.com/tatsu-lab/stanford_alpaca

[7] 擴充中文詞表的開源方案: https://link.juejin.cn/?target=https://arxiv.org/pdf/2304.08177v1.pdf

[8] Dataset Engineering for LLM finetuning: https://www.flowrite.com/blog/dataset-engineering-llm-finetuning

[9] Chatgpt 的限制: https://github.com/openai/openai-cookbook/blob/main/examples/Question_answering_using_embeddings.ipynb?spm=wolai.workspace.0.0.357e11a2XSQ9wO&file=Question_answering_using_embeddings.ipynb

[10] Search: Query Matching via Lexical, Graph, and Embedding Methods: https://eugeneyan.com/writing/search-query-matching/

[11] 該文: https://medium.com/intelligentmachines/word-embedding-and-one-hot-encoding-ad17b4bbe111

[12] textract: https://textract.readthedocs.io/en/stable/

[13] tiktoken: https://github.com/openai/tiktoken

[14] Byte Pair Encoding(BPE): https://en.wikipedia.org/wiki/Byte_pair_encoding

[15] vector database: https://learn.microsoft.com/en-us/semantic-kernel/concepts-ai/vectordb

[16] Vector Similarity: https://redis.io/docs/stack/search/reference/vectors/

[17] pgvector: https://github.com/pgvector/pgvector

[18] ChatGPT 系列教程—提問篇:Prompt 的高級概念: https://zhuanlan.zhihu.com/p/623395924

[19] ChatGPT API Transition Guide | OpenAI Help Center: https://www.wolai.com/9fBmz1E4WZWbHJZQYKHTji.md

[20] 攻略信息: https://nba2k2.qq.com/act/a20200520apph5/app/index.html#/ol2/news/6366

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/movaNCWjJGBaes6KxhpYpg