一、大模型開(kāi)發(fā)整理流程
1.1、什么是大模型開(kāi)發(fā)
我們將開(kāi)發(fā)以大語(yǔ)言模型為功能核心、通過(guò)大語(yǔ)言模型的強(qiáng)大理解能力和生成能力、結(jié)合特殊的數(shù)據(jù)或業(yè)務(wù)邏輯來(lái)提供獨(dú)特功能的應(yīng)用稱(chēng)為大模型開(kāi)發(fā)。
開(kāi)發(fā)大模型相關(guān)應(yīng)用,其技術(shù)核心點(diǎn)雖然在大語(yǔ)言模型上,但一般通過(guò)調(diào)用 API 或開(kāi)源模型來(lái)實(shí)現(xiàn)核心的理解與生成,通過(guò) Prompt Enginnering 來(lái)實(shí)現(xiàn)大語(yǔ)言模型的控制,因此,雖然大模型是深度學(xué)習(xí)領(lǐng)域的集大成之作,大模型開(kāi)發(fā)卻更多是一個(gè)工程問(wèn)題。
在大模型開(kāi)發(fā)中,我們一般不會(huì)去大幅度改動(dòng)模型,而是將大模型作為一個(gè)調(diào)用工具,通過(guò) Prompt Engineering、數(shù)據(jù)工程、業(yè)務(wù)邏輯分解等手段來(lái)充分發(fā)揮大模型能力,適配應(yīng)用任務(wù),而不會(huì)將精力聚焦在優(yōu)化模型本身上。

大模型開(kāi)發(fā)與傳統(tǒng)的AI 開(kāi)發(fā)在整體思路上有著較大的不同。
- 傳統(tǒng)AI 開(kāi)發(fā):首先需要將復(fù)雜的業(yè)務(wù)邏輯依次拆解,對(duì)于每個(gè)子業(yè)務(wù)構(gòu)造訓(xùn)練數(shù)據(jù)與驗(yàn)證數(shù)據(jù),對(duì)于每個(gè)子業(yè)務(wù)訓(xùn)練優(yōu)化模型,最后形成完整的模型鏈路來(lái)解決整個(gè)業(yè)務(wù)邏輯。
- 大模型開(kāi)發(fā):用 Prompt Engineering 來(lái)替代子模型的訓(xùn)練調(diào)優(yōu),通過(guò) Prompt 鏈路組合來(lái)實(shí)現(xiàn)業(yè)務(wù)邏輯,用一個(gè)通用大模型 + 若干業(yè)務(wù) Prompt 來(lái)解決任務(wù),從而將傳統(tǒng)的模型訓(xùn)練調(diào)優(yōu)轉(zhuǎn)變成了更簡(jiǎn)單、輕松、低成本的 Prompt 設(shè)計(jì)調(diào)優(yōu)。
大模型開(kāi)發(fā)與傳統(tǒng) AI 開(kāi)發(fā)在評(píng)估思路上也有了質(zhì)的差異。
- 傳統(tǒng) AI 開(kāi)發(fā):構(gòu)造訓(xùn)練集、測(cè)試集、驗(yàn)證集,通過(guò)在訓(xùn)練集上訓(xùn)練模型、在測(cè)試集上調(diào)優(yōu)模型、在驗(yàn)證集上最終驗(yàn)證模型效果來(lái)實(shí)現(xiàn)性能的評(píng)估。
- 大模型開(kāi)發(fā):更敏捷、靈活,不會(huì)在初期顯式地確定訓(xùn)練集、驗(yàn)證集,而是直接從實(shí)際業(yè)務(wù)需求出發(fā)構(gòu)造小批量驗(yàn)證集,設(shè)計(jì)合理 Prompt 來(lái)滿(mǎn)足驗(yàn)證集效果。然后,不斷從業(yè)務(wù)邏輯中收集當(dāng)下 Prompt 的 Bad Case,并將 Bad Case 加入到驗(yàn)證集中,針對(duì)性?xún)?yōu)化 Prompt,最后實(shí)現(xiàn)較好的泛化效果。


1.2、大模型開(kāi)發(fā)整體流程

1.2.1、設(shè)計(jì)
包括確定目標(biāo),設(shè)計(jì)功能。
確定目標(biāo):在進(jìn)行開(kāi)發(fā)前,需要確定開(kāi)發(fā)的目標(biāo),即要開(kāi)發(fā)的應(yīng)用的應(yīng)用場(chǎng)景、目標(biāo)人群、核心價(jià)值。一般應(yīng)先設(shè)定最小化目標(biāo),從構(gòu)建一個(gè) mvp(最小可行性產(chǎn)品)開(kāi)始,逐步進(jìn)行完善和優(yōu)化。
設(shè)計(jì)功能:確定開(kāi)發(fā)目標(biāo)后,設(shè)計(jì)本應(yīng)用所要提供的功能,首先確定應(yīng)用的核心功能,然后延展設(shè)計(jì)核心功能的上下游功能;例如,想打造一款個(gè)人知識(shí)庫(kù)助手,核心功能就是結(jié)合個(gè)人知識(shí)庫(kù)內(nèi)容進(jìn)行問(wèn)題的回答,上游功能——用戶(hù)上傳知識(shí)庫(kù)、下游功能——用戶(hù)手動(dòng)糾正模型回答,就是子功能。
1.2.2、架構(gòu)搭建
搭建整體架構(gòu):搭建項(xiàng)目的整體架構(gòu),實(shí)現(xiàn)從用戶(hù)輸入到應(yīng)用輸出的全流程貫通。包括搭建整體架構(gòu)和搭建數(shù)據(jù)庫(kù)。
目前,絕大部分大模型應(yīng)用都是采用的特定數(shù)據(jù)庫(kù)+ Prompt + 通用大模型的架構(gòu)。
推薦基于 LangChain 框架進(jìn)行開(kāi)發(fā)。LangChain 提供了 Chain、Tool 等架構(gòu)的實(shí)現(xiàn),可以基于 LangChain 進(jìn)行個(gè)性化定制,實(shí)現(xiàn)從用戶(hù)輸入到數(shù)據(jù)庫(kù)再到大模型最后輸出的整體架構(gòu)連接。
搭建數(shù)據(jù)庫(kù): 大模型應(yīng)用需要進(jìn)行向量語(yǔ)義檢索,一般使用諸如 chroma 的向量數(shù)據(jù)庫(kù)。搭建數(shù)據(jù)庫(kù)需要收集數(shù)據(jù)并進(jìn)行預(yù)處理,再向量化存儲(chǔ)到數(shù)據(jù)庫(kù)中。數(shù)據(jù)預(yù)處理一般包括從多種格式向純文本的轉(zhuǎn)化,例如 pdf、markdown、html、音視頻等,以及對(duì)錯(cuò)誤數(shù)據(jù)、異常數(shù)據(jù)、臟數(shù)據(jù)進(jìn)行清洗。完成預(yù)處理后,需要進(jìn)行切片、向量化構(gòu)建出個(gè)性化數(shù)據(jù)庫(kù)。
1.2.3、Prompt Engineering
明確 Prompt 設(shè)計(jì)的一般原則及技巧,構(gòu)建出一個(gè)來(lái)源于實(shí)際業(yè)務(wù)的小型驗(yàn)證集,基于小型驗(yàn)證集設(shè)計(jì)滿(mǎn)足基本要求、具備基本能力的 Prompt。
優(yōu)質(zhì)的 Prompt 對(duì)大模型能力具有極大影響,需要逐步迭代構(gòu)建優(yōu)質(zhì)的 Prompt Engineering 來(lái)提升應(yīng)用性能。
1.2.4、驗(yàn)證迭代
驗(yàn)證迭代在大模型開(kāi)發(fā)中是極其重要的一步,指通過(guò)不斷發(fā)現(xiàn) Bad Case 并針對(duì)性改進(jìn) Prompt Engineering 來(lái)提升系統(tǒng)效果、應(yīng)對(duì)邊界情況。在完成上一步的初始化 Prompt 設(shè)計(jì)后,應(yīng)該進(jìn)行實(shí)際業(yè)務(wù)測(cè)試,探討邊界情況,找到 Bad Case,并針對(duì)性分析 Prompt 存在的問(wèn)題,從而不斷迭代優(yōu)化,直到達(dá)到一個(gè)較為穩(wěn)定、可以基本實(shí)現(xiàn)目標(biāo)的 Prompt 版本。
1.2.5、前后端搭建
完成 Prompt Engineering 及其迭代優(yōu)化之后,就完成了應(yīng)用的核心功能,可以充分發(fā)揮大語(yǔ)言模型的強(qiáng)大能力。接下來(lái)搭建前后端,設(shè)計(jì)產(chǎn)品頁(yè)面,讓?xiě)?yīng)用上線(xiàn)成為產(chǎn)品。
兩種快速開(kāi)發(fā) Demo 的框架:Gradio 和 Streamlit,可以幫助個(gè)體開(kāi)發(fā)者迅速搭建可視化頁(yè)面實(shí)現(xiàn) Demo 上線(xiàn)。
在完成前后端搭建之后,應(yīng)用就可以上線(xiàn)體驗(yàn)了。接下來(lái)就需要進(jìn)行長(zhǎng)期的用戶(hù)體驗(yàn)跟蹤,記錄 Bad Case 與用戶(hù)負(fù)反饋,再針對(duì)性進(jìn)行優(yōu)化即可。
二、項(xiàng)目流程簡(jiǎn)析
基于個(gè)人知識(shí)庫(kù)的問(wèn)答助手介紹項(xiàng)目流程。
項(xiàng)目原理:項(xiàng)目原理如下圖所示,過(guò)程包括加載本地文檔 -> 讀取文本 -> 文本分割 -> 文本向量化 -> question向量化 -> 在文本向量中匹配出與問(wèn)句向量最相似的 top k個(gè) -> 匹配出的文本作為上下文和問(wèn)題一起添加到 prompt中 -> 提交給 LLM生成回答。

2.1、項(xiàng)目規(guī)劃與需求分析
1、項(xiàng)目目標(biāo):基于個(gè)人知識(shí)庫(kù)的問(wèn)答助手
2、核心功能:
1、上傳文檔、創(chuàng)建知識(shí)庫(kù);
2、選擇知識(shí)庫(kù),檢索用戶(hù)提問(wèn)的知識(shí)片段;
3、提供知識(shí)片段與提問(wèn),獲取大模型回答;
4、流式回復(fù);
5、歷史對(duì)話(huà)記錄
3、確定技術(shù)架構(gòu)和工具:
1、LangChain框架
2、Chroma知識(shí)庫(kù)
3、大模型使用 GPT、科大訊飛的星火大模型、文心一言、GLM 等
4、前后端使用 Gradio 和 Streamlit。
2.2、數(shù)據(jù)準(zhǔn)備與向量知識(shí)庫(kù)構(gòu)建
2.2.1、收集和整理用戶(hù)提供的文檔
用戶(hù)常用文檔格式有 pdf、txt、doc 等,首先使用工具讀取文本,通常使用 langchain 的文檔加載器模塊,也可以使用 python 比較成熟的包進(jìn)行讀取。
由于目前大模型使用 token 的限制,需要對(duì)讀取的文本進(jìn)行切分,將較長(zhǎng)的文本切分為較小的文本,這時(shí)一段文本就是一個(gè)單位的知識(shí)。
2.2.2、將文檔詞向量化
使用文本嵌入(Embeddings)對(duì)分割后的文檔進(jìn)行向量化,使語(yǔ)義相似的文本片段具有接近的向量表示。然后,存入向量數(shù)據(jù)庫(kù),這個(gè)流程正是創(chuàng)建 索引(index) 的過(guò)程。
向量數(shù)據(jù)庫(kù)對(duì)各文檔片段進(jìn)行索引,支持快速檢索。這樣,當(dāng)用戶(hù)提出問(wèn)題時(shí),可以先將問(wèn)題轉(zhuǎn)換為向量,在數(shù)據(jù)庫(kù)中快速找到語(yǔ)義最相關(guān)的文檔片段。然后將這些文檔片段與問(wèn)題一起傳遞給語(yǔ)言模型,生成回答。
2.2.3、將向量化后的文檔導(dǎo)入Chroma知識(shí)庫(kù),建立知識(shí)庫(kù)索引
Chroma 向量庫(kù)輕量級(jí)且數(shù)據(jù)存儲(chǔ)在內(nèi)存中,非常容易啟動(dòng)和開(kāi)始使用。
用戶(hù)知識(shí)庫(kù)內(nèi)容經(jīng)過(guò) embedding 存入向量知識(shí)庫(kù),然后用戶(hù)每一次提問(wèn)也會(huì)經(jīng)過(guò) embedding,利用向量相關(guān)性算法(例如余弦算法)找到最匹配的幾個(gè)知識(shí)庫(kù)片段,將這些知識(shí)庫(kù)片段作為上下文,與用戶(hù)問(wèn)題一起作為 prompt 提交給 LLM 回答。
2.3、大模型集成與API連接
- 集成GPT、星火、文心、GLM 等大模型,配置 API 連接。
- 編寫(xiě)代碼,實(shí)現(xiàn)與大模型 API 的交互,以便獲取問(wèn)題答案。
2.4、核心功能實(shí)現(xiàn)
- 構(gòu)建 Prompt Engineering,實(shí)現(xiàn)大模型回答功能,根據(jù)用戶(hù)提問(wèn)和知識(shí)庫(kù)內(nèi)容生成回答。
- 實(shí)現(xiàn)流式回復(fù),允許用戶(hù)進(jìn)行多輪對(duì)話(huà)。
- 添加歷史對(duì)話(huà)記錄功能,保存用戶(hù)與助手的交互歷史。
2.5、核心功能迭代優(yōu)化
- 進(jìn)行驗(yàn)證評(píng)估,收集 Bad Case。
- 根據(jù) Bad Case 迭代優(yōu)化核心功能實(shí)現(xiàn)。
2.6、前端與用戶(hù)交互界面開(kāi)發(fā)
- 使用 Gradio 和 Streamlit 搭建前端界面。
- 實(shí)現(xiàn)用戶(hù)上傳文檔、創(chuàng)建知識(shí)庫(kù)的功能。
- 設(shè)計(jì)用戶(hù)界面,包括問(wèn)題輸入、知識(shí)庫(kù)選擇、歷史記錄展示等。
2.7、部署測(cè)試與上線(xiàn)
- 部署問(wèn)答助手到服務(wù)器或云平臺(tái),確保可在互聯(lián)網(wǎng)上訪(fǎng)問(wèn)。
- 進(jìn)行生產(chǎn)環(huán)境測(cè)試,確保系統(tǒng)穩(wěn)定。
- 上線(xiàn)并向用戶(hù)發(fā)布。
2.8、維護(hù)與持續(xù)改進(jìn)
- 監(jiān)測(cè)系統(tǒng)性能和用戶(hù)反饋,及時(shí)處理問(wèn)題。
- 定期更新知識(shí)庫(kù),添加新的文檔和信息。
- 收集用戶(hù)需求,進(jìn)行系統(tǒng)改進(jìn)和功能擴(kuò)展。
三、項(xiàng)目架構(gòu)簡(jiǎn)析
3.1、整體架構(gòu)
搭建一個(gè)基于大模型的個(gè)人知識(shí)庫(kù)助手,基于 LangChain 框架搭建,核心技術(shù)包括 LLM API 調(diào)用、向量數(shù)據(jù)庫(kù)、檢索問(wèn)答鏈等。項(xiàng)目整體架構(gòu)如下:

項(xiàng)目從底向上依次分為 LLM 層、數(shù)據(jù)層、數(shù)據(jù)庫(kù)層、應(yīng)用層與服務(wù)層:
3.1.1、LLM 層
基于四種流行 LLM API (OpenAI-ChatGPT、百度文心、訊飛星火、智譜GLM)進(jìn)行了 LLM 調(diào)用封裝,支持用戶(hù)以統(tǒng)一的入口、方式來(lái)訪(fǎng)問(wèn)不同的模型,支持隨時(shí)進(jìn)行模型的切換;
在 LLM 層,構(gòu)建了一個(gè) Self_LLM 基類(lèi),基類(lèi)定義了所有 API 的一些共同參數(shù)(如 API_Key,temperature 等);在該基類(lèi)基礎(chǔ)上繼承實(shí)現(xiàn)了上述四種 LLM API 的自定義 LLM。四種 LLM 的原生 API 封裝在了統(tǒng)一的 get_completion 函數(shù)中。
3.1.2、數(shù)據(jù)層
包括個(gè)人知識(shí)庫(kù)的源數(shù)據(jù)(包括 pdf、txt、md 等)以及 Embedding API,源數(shù)據(jù)經(jīng)過(guò) Embedding 處理可以被向量數(shù)據(jù)庫(kù)使用;
3.1.3、數(shù)據(jù)庫(kù)層
基于個(gè)人知識(shí)庫(kù)源數(shù)據(jù)搭建的向量數(shù)據(jù)庫(kù),本項(xiàng)目中選擇了 Chroma。在該層實(shí)現(xiàn)了源數(shù)據(jù)處理、創(chuàng)建向量數(shù)據(jù)庫(kù)的方法;
3.1.4、應(yīng)用層
應(yīng)用層封裝了整個(gè)項(xiàng)目的全部核心功能。基于 LangChain 提供的檢索問(wèn)答鏈基類(lèi)進(jìn)行了進(jìn)一步封裝,支持通過(guò) model 參數(shù)進(jìn)行不同模型切換以及便捷實(shí)現(xiàn)基于數(shù)據(jù)庫(kù)的檢索問(wèn)答。
實(shí)現(xiàn)了兩個(gè)檢索問(wèn)答鏈,分別是有歷史記錄的 Chat_QA_Chain 和沒(méi)有歷史記錄的 QA_Chain;
3.1.5、服務(wù)層
實(shí)現(xiàn)了 Gradio 搭建 Demo 與 FastAPI 組建 API 兩種方式來(lái)支持本項(xiàng)目的服務(wù)訪(fǎng)問(wèn)。
3.2、代碼結(jié)構(gòu)
-project
-readme.md 項(xiàng)目說(shuō)明
-requirements.txt 使用依賴(lài)包的版本
-llm LLM調(diào)用封裝
-self_llm.py 自定義 LLM 基類(lèi)
-wenxin_llm.py 自定義百度文心 LLM
-spark_llm.py 自定義訊飛星火 LLM
-zhipuai_llm.py 自定義智譜AI LLM
-call_llm.py 將各個(gè) LLM 的原生接口封裝在一起
-test.ipynb 使用示例
-embedding embedding調(diào)用封裝
-zhipuai_embedding.py 自定義智譜AI embedding
-call_embedding.py 調(diào)用 embedding 模型
-data 源數(shù)據(jù)路徑
-database 數(shù)據(jù)庫(kù)層封裝
-create_db.py 處理源數(shù)據(jù)及初始化數(shù)據(jù)庫(kù)封裝
-qa_chain 應(yīng)用層封裝
-qa_chain.py 封裝檢索問(wèn)答鏈,返回一個(gè)檢索問(wèn)答鏈對(duì)象
-chat_qa_chian.py:封裝對(duì)話(huà)檢索鏈,返回一個(gè)帶有歷史記錄的對(duì)話(huà)檢索鏈對(duì)象
-get_vectordb.py 返回向量數(shù)據(jù)庫(kù)對(duì)象
-model_to_llm.py 調(diào)用模型
-test.ipynb 使用示例
-serve 服務(wù)層封裝
-run_gradio.py 啟動(dòng) Gradio 界面
-api.py 封裝 FastAPI
-run_api.sh 啟動(dòng) API
-test.ipynb 使用示例
3.3、項(xiàng)目邏輯
1、用戶(hù):可以通過(guò) run_gradio 或者 run_api 啟動(dòng)整個(gè)服務(wù);
2、服務(wù)層調(diào)用 qa_chain.py 或 chat_qa_chain 實(shí)例化對(duì)話(huà)檢索鏈對(duì)象,實(shí)現(xiàn)全部核心功能;
3、服務(wù)層和應(yīng)用層都可以調(diào)用、切換 prompt_template.py 中的 prompt 模板來(lái)實(shí)現(xiàn) prompt 的迭代;
4、也可以直接調(diào)用 call_llm 中的 get_completion 函數(shù)來(lái)實(shí)現(xiàn)不使用數(shù)據(jù)庫(kù)的 LLM;
5、應(yīng)用層調(diào)用已存在的數(shù)據(jù)庫(kù)和 llm 中的自定義 LLM 來(lái)構(gòu)建檢索鏈;
6、如果數(shù)據(jù)庫(kù)不存在,應(yīng)用層調(diào)用 create_db.py 創(chuàng)建數(shù)據(jù)庫(kù),該腳本可以使用 openai embedding 也可以使用 embedding.py 中的自定義 embedding