天天看點

萬字長文:LLM應用建構全解析

作者:智驅力AI

一、背景介紹

在不斷發展的人工智能領域,語言模型占據了重要位置。随着ChatGPT受到廣泛認可,語言模型,尤其是大語言模型LLM,成為科技領域的重要話題。這些模型在大量的文本資料上進行訓練,使他們能夠掌握複雜的語言模式與語義内容的細微差别。憑借前所未有的語言處理能力,LLM能夠生成高品質内容,完成複雜語言處理任務。

以LLM模型為核心的開發架構的出現為自然語言處理打開了一個充滿可能性的世界,我們可以用其建立各種應用程式,包括聊天機器人和智能問答工具。

本文将重點關注三個方面:

1、LLM應用的技術架構 2、LLM應用開發架構 3、LLM應用建構執行個體

二、LLM應用技術架構

萬字長文:LLM應用建構全解析

LLM 應用開發架構

如上圖,一個典型的LLM應用開發架構主要分為四層,詳情如下。

(1)存儲層:主要為向量資料庫,用于存儲文本、圖像等編碼後的特征向量,支援向量相似度查詢與分析。例如,我們在做文本語義檢索時,通過比較輸入文本的特征向量與底庫文本特征向量的相似性,進而檢索目标文本,即利用了向量資料庫中的相似度查詢(餘弦距離、歐式距離等)。

【代表性資料庫】Pinecone向量資料庫、Qdrant向量資料庫等。

(2)模型層:選擇需要調用的大語言模型,可以是OpenAI的GPT系列模型,Hugging Face中的開源LLM系列等。模型層提供最核心支撐,包括聊天接口、上下文QA問答接口、文本總結接口、文本翻譯接口等。

【代表性模型】OpenAI的GPT-3.5/4,Anthropic的Claude,Google的PaLM,THU的ChatGLM等。

(3)服務層:将各種語言模型或外部資源整合,建構實用的LLM模型。Langchain是一個開源LLM應用架構,概念新穎,将LLM模型、向量資料庫、互動層Prompt、外部知識、外部工具整合到一起,可自由建構LLM應用。

【代表性架構】:LangChain,AutoGPT,BabyAGI,Llama-Index等。

(4)互動層:使用者通過UI與LLM應用互動,如langflow是langchain的GUI,通過拖放元件和聊天架構提供一種輕松的實驗和原型流程方式。例如,我們想實作一個簡單的聊天應用,我們輸入“城市名字”,聊天機器人回複“該城市的天氣情況”。我們隻需要拖動三個元件:PromptTemplate、OpenAI、LLMChain。完成PromptTemplate、OpenAI、LLMChain的界面化簡單配置即可生成應用。生成執行個體如下:

萬字長文:LLM應用建構全解析

LangFlow應用生成

三、LLM應用開發架構

Langchain是一個大語言模型應用開發架構。它緻力于将各種語言模型整合起來,并結合其他知識來源或外部能力,建立一個實用的應用程式。

萬字長文:LLM應用建構全解析

LangChain元件構成

如上圖,LangChain包含六部分元件,分别為:Models、Prompts、Indexes、Memory、Chains、Agents。

(1)Models(模型): 可選擇不同的LLM與Embedding模型

大語言模型是Models的核心,同時也是LangChain的核心,這些模型包括OpenAI的GPT-3.5/4、谷歌的LaMDA/PaLM,Meta AI的LLaMA等。Text Embedding用于文本的向量化表示。例如,可調用OpenAI、Cohere、Hugging Face等Embedding标準接口,對文本向量化。

舉個例子:

import os
from langchain.llms import OpenAI
openai_api_key = 'sk-F9O70vxxxxxlbkFJK55q8YgXb6s5dJ1A4LjA'
os.environ['OPENAI_API_KEY'] = openai_api_key

llm = OpenAI(model_name="gpt-3.5-turbo")

print(llm("講個笑話,很冷的笑話"))           

輸出:

為什麼鳥兒會成為遊泳高手?因為它們有一隻腳比另一隻腳更長,是以遊起泳來不費力!(笑)           

以上是LangChain調用OpenAI的gpt-3.5-turbo大語言模型的簡單示例。

(2)Prompts(提示語): 管理LLM輸入

當使用者與大語言模型對話時,使用者所說的内容即Prompt(提示語)。如果使用者每次輸入的Prompt中包含大量的重複内容,我們可以考慮生成一個Prompt模闆,将通用的部分提取出來,使用者輸入輸入部分作為變量。如下所示:

舉個例子:

from langchain import PromptTemplate

name_template = """
我想讓你成為一個起名字的專家。給我傳回一個名字的名單. 名字寓意美好,簡單易記,朗朗上口.
關于{name_description},好聽的名字有哪些?
"""
# 建立一個prompt模闆
prompt_template = PromptTemplate(input_variables=["name_description"], template=name_template)
description = "男孩名字"
print(prompt_template.format(name_description=description))           

輸出:

我想讓你成為一個起名字的專家。給我傳回一個名字的名單. 名字寓意美好,簡單易記,朗朗上口.關于男孩名字,好聽的名字有哪些?           

以上為調用LangChain的PromptTemplate的簡單示例。Prompt模闆十分有用,比如,我們想利用langchain建構專屬客服助理,并且明确告訴其隻回答知識庫(産品介紹、購買流程等)裡面的知識,其他無關的詢問,隻回答“我還沒有學習到相關知識”。這時,可利用Prompt模闆對llm進行限制。

(3)Indexes(索引):對文檔結構化的方法

索引是指對文檔進行結構化的方法,以便LLM能夠更好的與之互動。該元件主要包括:Document Loaders(文檔加載器)、Text Splitters(文本拆分器)、VectorStores(向量存儲器)以及Retrievers(檢索器)。

萬字長文:LLM應用建構全解析

indexes構成

文字檢索器:将特定格式的資料,轉換為文本。輸入可以是pdf、word、csv、images等。

文本拆分器:将長文本拆分成小的文本塊,便于LLM模型處理。由于模型在處理資料時,對輸入長度有限制,是以需要對長文本進行分塊。不同語言模型對塊的大小定義不同,比如OpenAI的GPT對分塊的長度通過token大小來限制,比如GPT-3.5是4096,即這個分塊所包含的Token數量不能超過4096。一般的分塊方法:首先,對長文本進行斷句,即分成一句一句話。然後,計算每句話包含的token數量,并從第一句話開始往後依次累加,直到達到指定的數量,組成為1個分塊。依次重複上述操作。比如按照字母切分的Character,按照token切分的Tiktoken等。

向量存儲器:存儲提取的文本向量,包括Faiss、Milvus、Pinecone、Chroma等。

向量檢索器:通過使用者輸入的文本,檢索器負責從底庫中檢索出特定相關度的文檔。度量準則包括餘弦距離、歐式距離等。

(4)Chains(鍊條):将LLM與其他元件結合

Chain提供了一種将各種元件統一到應用程式中的方法。例如,可以建立一個Chain,它接受來自使用者的輸入,并通過PromptTemplate将其格式化,然後将格式化的輸出傳入到LLM模型中。通過多個Chain與其他部件結合,可生成複雜的鍊,完成複雜的任務。

萬字長文:LLM應用建構全解析

Chains示意圖

LLM與其他元件結合,建立不同應用,一些例子如下:

  • 将LLM與提示模闆相結合
  • 通過将第一個 LLM 的輸出作為第二個 LLM 的輸入來順序組合多個 LLM
  • LLM與外部資料結合,比如,通過langchain擷取youtube視訊連結,通過LLM視訊問答
  • LLM與長期記憶結合,比如聊天機器人

(5)Agents(智能體):通路其他工具

Agents是LLM與工具之間的接口,Agents用來确定任務與工具。一般的Agents執行任務過程如下:

a. 首先,接收使用者的輸入,并轉化為PromptTemplate

b. 其次,Agents通過調用LLM輸出action,并決定使用哪種工具執行action

c. 最後,Agents調用工具完成action任務

萬字長文:LLM應用建構全解析

Agents示意圖

Agents可以調用那些工具完成任務?

工具 描述
搜尋 調用谷歌浏覽器或其他浏覽器搜尋指定内容
終端 在終端中執行指令,輸入應該是有效的指令,輸出将是運作該指令的任何輸出
Wikipedia 從維基百科生成結果
Wolfram-Alpha WA 搜尋插件——可以回答複雜的數學、實體或任何查詢,将搜尋查詢作為輸入。
Python REPL 用于評估和執行 Python 指令的 Python shell。它以 python 代碼作為輸入并輸出結果。輸入的 python 代碼可以從 LangChain 中的另一個工具生成

舉個例子:

import os
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.llms import OpenAI

openai_api_key = 'sk-F9xxxxxxx55q8YgXb6s5dJ1A4LjA'
os.environ['OPENAI_API_KEY'] = openai_api_key
llm = OpenAI(temperature=0)
tools = load_tools(["wikipedia","llm-math"], llm=llm)
agent = initialize_agent(tools, llm, agent="zero-shot-react-description", verbose=True)
print(agent.run("列舉spaceX星艦在2022年後的發射記錄?"))           

輸出:

萬字長文:LLM應用建構全解析

如上圖,agent通過調用wikipedia工具,對使用者提出的問題回答。盡管gpt-3.5功能強大,但是其知識庫截止到2021年9月,是以,agent調用wikipedia外部知識庫對使用者問題回答。回答過程如下:

a. 根據使用者輸入的問題分析後,采取的Action為通過Wikipedia實作,并給出了Action的輸入

b. 根據分析得到了最相關的兩頁,并進行了總結

c. 對最後的内容進一步提煉,得到最終答案

(6)Memory(記憶):

對于像聊天機器人這樣的應用程式,它們需要記住以前的對話内容。但預設情況下,LLM對曆史内容沒有記憶功能。也就是說LLM的輸出知針對使用者目前的提問内容回答。為解決這個問題,Langchain提供了記憶元件,用來管理與維護曆史對話内容。

萬字長文:LLM應用建構全解析

Memory示意圖

langchain提供了不同的Memory元件完成内容記憶,下面列舉四種:

  • ConversationBufferMemory:記住全部對話内容。這是最簡單的記憶體記憶元件,它的功能是直接将使用者和機器人之間的聊天内容記錄在記憶體中。
萬字長文:LLM應用建構全解析
  • ConversationBufferWindowMemory:記住最近k輪的聊天内容。與之前的ConversationBufferMemory元件的差别是它增加了一個視窗參數,它的作用是可以指定儲存多輪對話的數量。
萬字長文:LLM應用建構全解析

​在該例子中設定了對話輪數k=2,即隻能記住前兩輪的内容,“我的名字”是在前3輪中的Answer中回答的,是以其沒有對其進行記憶,是以無法回答出正确答案。

  • ConversationSummaryMemory:ConversationSummaryMemory它不會将使用者和機器人之前的所有對話都存儲在記憶體中。它隻會存儲一個使用者和機器人之間的聊天内容的摘要,這樣做的目的可能是為了節省記憶體開銷和token的數量。
萬字長文:LLM應用建構全解析

​ConversationSummaryMemory第一輪對話

萬字長文:LLM應用建構全解析

ConversationSummaryMemory第二輪對話

​由上圖可知,在第一輪對話完成後,Memory對第一輪對話的内容進行了總結,放到了摘要中。在第二輪對話中,LLM基于摘要與該輪的問題進行回答。

  • VectorStored-Backed Memory: 它是将所有之前的對話通過向量的方式存儲到VectorDB(向量資料庫)中,在每一輪新的對話中,會根據使用者的輸入資訊,比對向量資料庫中最相似的K組對話。

四、LLM應用建構執行個體

1、本地文檔知識問答助理(chat with pdf)

GPT幾秒内讀完了26頁文檔,并準确回答了我提出的問題。

需求描述:假設您有大量的本地文檔資料。您希望通過問答的方式快速擷取想要的知識或資訊(例如,從公司産品資料中擷取産品參數細節,從海量的公司資産資料中擷取統計資料等),以便提高工作效率。

如果這個工作通過人力完成,需要做的工作:首先從大量資料中找到問題相關支撐材料;其次,對這些相關的支撐材料進行總結。

解決方案:langchain+llms

本地化知識專屬問答助理建構過程可簡單概括如下:

第一步:資料加載&預處理(将資料源轉換為text,并做text split等預處理)

第二步:向量化(将處理完成的資料embedding處理)

第三步:召回(通過向量檢索工具Faiss等對query相關文檔召回)

第四步:閱讀了解,總結答案(将context與query傳給llms,總結答案)

萬字長文:LLM應用建構全解析

方案示意圖

(1)資料加載&預處理

import os

openai_api_key = 'sk-F9Oxxxxxx3BlbkFJK55q8YgXb6s5dJ1A4LjA'
os.environ['OPENAI_API_KEY'] = openai_api_key
from langchain.llms import OpenAI
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader
from langchain import PromptTemplate

llm = OpenAI(model_name="gpt-3.5-turbo", temperature=0, openai_api_key=openai_api_key)

# data loader
loader = PyPDFLoader("data/ZT91.pdf")
doc = loader.load()
# text splitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=3000, chunk_overlap=400)
docs = text_splitter.split_documents(doc)           

加載pdf檔案,對文本進行分塊,這裡每個分塊的最大長度為3000個字元。這個最大長度根據llm的輸入大小确定,比如gpt-3.5-turbo最大輸入是4096個token。相鄰塊之間的重疊部分為400個字元,這樣做的目的是每個片段保留一定的上文資訊,後續處理任務利用重疊資訊更好了解文本。

(2)向量化

embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)           

在這裡調用的是OpenAIEmbeddings接口對文本進行向量化。在實際應用中,可以使用開源模型,如下所示(OpenAIEmbeddings用的是第6個)。同時,中文embedding效果優秀的模型有百度的ERNIE。

萬字長文:LLM應用建構全解析

Embedding模型

​(3)召回

docsearch = FAISS.from_documents(docs, embeddings)           

采用FAISS工具對輸入文檔建構FAISS索引,傳回建構好的FAISS索引對象。建立FAISS索引包含的工作有:為索引配置設定記憶體空間;選擇合适的索引類型與參數(比如Flat IVFFlat等);将文檔向量添加到索引中。

qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=docsearch.as_retriever(search_kwargs={"k": 5}),
                                 chain_type_kwargs={"prompt": PROMPT})           

docsearch.as_retriever(search_kwargs={"k": 5})表示傳回前5個最相關的chunks,預設是4,可以修改

(4)閱讀了解,總結答案

prompt_template = """請注意:請謹慎評估query與提示的Context資訊的相關性,隻根據本段輸入文字資訊的内容進行回答,如果query與提供的材料無關,請回答"對不起,我不知道",另外也不要回答無關答案:
    Context: {context}
    Question: {question}
    Answer:"""
PROMPT = PromptTemplate(template=prompt_template, input_variables=["context", "question"])
query = "實時畫面無法檢視,怎樣解決?"
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=docsearch.as_retriever(search_kwargs={"k": 5}),
                                 chain_type_kwargs={"prompt": PROMPT})
qa.run(query)           

輸入:query+context

輸出:answer

其中context為通過faiss retriever檢索出的相關文檔:retriever=docsearch.as_retriever(search_kwargs={"k": 5}),query為使用者輸入的提問。

特别的,為了讓gpt隻回答文檔中的内容,在prompt_template 增加了限制:“請注意:請謹慎評估query與提示的Context資訊的相關性,隻根據本段輸入文字資訊的内容進行回答,如果query與提供的材料無關,請回答"對不起,我不知道",另外也不要回答無關答案:”。即如果文檔中沒有使用者提問相關内容,需要回答“不知道”,防止“答非所問”誤導使用者。

(5)效果驗證

效果驗證采用本公司的曉知精靈ZT91産品資料,一個26頁的pdf文檔,包含文字、表格、圖檔。驗證中隻對文本文字與表格文字處理。采用以上流程處理後,進行問答。

Question:請介紹如何快速接入裝置?

Answer:

萬字長文:LLM應用建構全解析

回答的答案正确。

Question:實時畫面無法檢視怎樣解決?

Answer:

萬字長文:LLM應用建構全解析

回答完整正确。

Question:告警資料的預設存儲為幾天?

Answer:

萬字長文:LLM應用建構全解析

這個是表格中的資料,回答正确。

Question:曉知精靈的價格是多少?

Answer:

萬字長文:LLM應用建構全解析

對于産品資料中沒有答案的問題,其不會亂說。

【應用】:私域知識問答助理,智能客服,語義檢索總結、輔助教學。

2、視訊知識總結問答助理(chat with video)

GPT十幾秒看完了8分鐘視訊,并準确回答了我提出的問題。

需求描述:youtube等視訊網站上每天都會産生大量視訊,雖然推薦系統按照我們的喜好進行了推薦,但觀看大量有價值的視訊依然面臨挑戰,如果我們能夠快速了解視訊内容,并得到我們關注的資訊,将極大提高資訊擷取效率。

解決方案:langchain+transcript+llms

萬字長文:LLM應用建構全解析

與pdf文檔的處理方式不同,YoutubeLoader庫從youtube視訊連結中加載資料,并轉換為文檔。

# 加載 youtube 頻道
loader = YoutubeLoader.from_youtube_url('https://www.youtube.com/watch?v=_rcnWQ0b2lM')
# 将資料轉成 document
documents = loader.load()           

其文檔的擷取過程是通過youtube-transcript-api擷取的視訊的字幕檔案,這個字幕檔案是youtube生成的。當使用者将視訊上傳至youtube時,youtube會通過内置的語音識别算法将視訊語音轉換為文本。當加載youtube視訊字幕文檔後,接下來的處理工作與第一個例子類似。

原始視訊:Elon Musk Unveils NEW Generation Robots. Tesla Investor Day 2023

萬字長文:LLM應用建構全解析

下面是對視訊内容的提問:

Question: “對視訊中介紹的内容,逐條列舉?”

Answer:

萬字長文:LLM應用建構全解析

GPT對視訊内容進行了總結,并列出了8條視訊内容摘要,通過觀看視訊,給出的内容符合視訊介紹。

【應用】:視訊自動編目、視訊檢索問答。

3、表格知識總結問答助理(chat with csv)

GPT幾分鐘内容分析完了54萬條資料,給出了我正确答案,完成了資料分析師1天的工作。

需求描述:假設您是一家零售商,您手頭有客戶的交易資料。您希望從資料中生成一些基本見解(例如按性别和年齡組劃分的平均支出、每種類型的客戶購買的産品、産品在哪個城市和商店的銷售額最高,等等!),以便識别最佳客戶。

這個問題陳述聽起來像是資料分析師應該做的工作類型,而這正是它的本質。資料分析師将擷取資料,編寫一些 SQL 或 Python 或 R 代碼,生成見解,資料分析師可能需要一天的時間來提供這些結果。

解決方案:langchain+llm+agents

萬字長文:LLM應用建構全解析

方案示意圖

萬字長文:LLM應用建構全解析

表格資料

(1) 按性别顯示客戶數量

我讓gpt幫我顯示表格中使用者的數量,執行情況如下:

舉例:

import os
from langchain.agents import create_csv_agent
from langchain.llms import OpenAI

openai_api_key = 'sk-F9O70vxxxxxxxBlbkFJK55q8YgXb6s5dJ1A4LjA'
os.environ['OPENAI_API_KEY'] = openai_api_key

agent = create_csv_agent(OpenAI(openai_api_key=openai_api_key, temperature=0),
                         'train.csv',
                         verbose=True)
agent.run("請按照性别對使用者數量進行顯示?")           

輸出:

萬字長文:LLM應用建構全解析

​經過驗證,agent給出的答案完全正确。

(2) 年齡與消費金額之間相關性分析

我讓gpt幫我分析下年齡與消費金額之間的相關性,執行情況如下:

舉例:

agent.run("在這份表格中,年齡與消費金額是否存在相關性?")           

輸出:

萬字長文:LLM應用建構全解析

​agent進行了相關性分析,年齡與消費成正相關,但是相關性很弱。

(3) 産品銷售量統計并畫出柱狀圖

我讓gpt幫我統計每種産品的銷售總量,并畫柱狀圖,執行情況如下:

舉例:

agent.run("産品1總量,産品2總量,産品3總量分别是多少,将三個總量通過柱狀圖畫出來并顯示")           

輸出:

萬字長文:LLM應用建構全解析
萬字長文:LLM應用建構全解析

agent了解了我的指令,将産品1,産品2,産品3的總量進行了計算,并畫出了柱狀圖。

【應用】:商業資料分析、市場調研分析、客戶資料精準分析等。

總結:大語言模型的突破加速了人工智能的行業應用,未來不僅prompt engineering,且以llm為核心的application将不斷湧現。在開發llm basic model的同時,利用llm引擎開發高品質的llm應用,不斷釋放應用價值與商業潛力。我們正處于人類與計算機互動方式的一場革命的開端,在短短幾年内,我們使用的每個應用程式都可能将以某種方式由LLM 提供支援。

參考文獻:

【1】https://docs.langchain.com/docs/

【2】Build an LLM-powered application using LangChain

繼續閱讀