構建 LLM 應用:開源聊天機器人(第七部分)

作者:Vipra Singh

編譯:ronghuaiyang

導讀

在這篇博客文章中,我們將使用 LangChain 創建一個基礎的 LLM 應用程序。

介紹

我們之前的博客文章深入探討了大型語言模型(LLMs),覆蓋了它們的發展歷程和廣泛的應用場景。現在,讓我們更近距離地聚焦這一旅程的核心:本地構建 LLM 應用程序。

在這篇博客文章中,我們將使用 LangChain 創建一個基礎的 LLM 應用程序。

之後,我們將繼續在當地開發另外三個開源的 LLM 應用程序。

以下是 RAG(Retrieval-Augmented Generation)應用的一個非常合適的架構:

從頭構建 LLM 的應用

我們將快速構建一個針對項目 GitHub 問題的 RAG(Retrieval Augmented Generation)應用,使用HuggingFaceH4/zephyr-7b-beta 模型和 LangChain。

這裏有一個快速演示:

![img](Building LLM Applications Open-Source Chatbots (Part 7).assets/0uvr3Qq02_CCWfTgS.png)

讓我們通過使用開源 LLM、嵌入模型和 LangChain 來演示構建 RAG 的過程。

首先,讓我們安裝必需的依賴項:

!pip install -q torch transformers accelerate bitsandbytes transformers sentence-transformers faiss-gpu
# If running in Google Colab, you may need to run this cell to make sure you're using UTF-8 locale to install LangChain
import locale
locale.getpreferredencoding = lambda: "UTF-8"
!pip install -q langchain

準備數據

在這個例子中,我們將加載來自 PEFT 庫的倉庫的所有問題(包括開放和已關閉的問題)。

首先,你需要獲取一個 GitHub 個人訪問 token 以訪問 GitHub API。

from getpass import getpass
ACCESS_TOKEN = getpass("YOUR_GITHUB_PERSONAL_TOKEN")

接下來,我們將加載 huggingface/peft 倉庫中的所有問題:

from langchain.document_loaders import GitHubIssuesLoader

loader = GitHubIssuesLoader(repo="huggingface/peft"access_token=ACCESS_TOKEN,
                            include_prs=False, state="all")
docs = loader.load()

單個 GitHub 問題的內容可能比嵌入模型所能接受的輸入長度要長。如果我們想要嵌入所有可用的內容,就需要將文檔分割成適當大小的片段。

最常見的分割方法是定義固定大小的片段以及它們之間是否應該有重疊。在片段間保持一定的重疊可以讓我們在片段之間保留一些語義上下文。對於通用文本,推薦使用的分割器是 RecursiveCharacterTextSplitter,這也是我們將在這裏使用的方法。

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=30)

chunked_docs = splitter.split_documents(docs)

創建嵌入與檢索器

既然文檔都已經分割成合適的大小,我們現在可以創建一個包含它們嵌入的數據庫。

爲了創建文檔片段的嵌入,我們將使用HuggingFaceEmbeddingsBAAI/bge-base-en-v1.5 嵌入模型。在 Hub 上有許多其他的嵌入模型可供選擇,你可以通過查看大規模文本嵌入基準(MTEB)排行榜來關注表現最佳的模型。

爲了創建向量數據庫,我們將使用FAISS,這是一個由 Facebook AI 開發的庫。這個庫提供了高效的相似性搜索和密集向量聚類,正是我們這裏所需要的。FAISS 目前是大規模數據集中近鄰搜索最常用的庫之一。

我們將通過 LangChain API 訪問嵌入模型和 FAISS。

from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings

db = FAISS.from_documents(chunked_docs, HuggingFaceEmbeddings
                         (model_))

我們需要一種方法,根據非結構化查詢返回(檢索)相關的文檔。爲此,我們將使用as_retriever方法,以db作爲基礎:

retriever = db.as_retriever(search_type="similarity"search_kwargs={"k": 4})

向量數據庫和檢索器現在已經設置好了,接下來我們需要設置下一個組件——模型。

加載量化後的模型

在這個例子中,我們選擇了HuggingFaceH4/zephyr-7b-beta,這是一個雖小但功能強大的模型。

鑑於每週都有許多新模型發佈,你可能希望將此模型替換爲最新最好的模型。跟蹤開源 LLM 的最佳方式是查看開源 LLM 排行榜。

爲了加快推理速度,我們將加載模型的量化版本:

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig

model_name = "HuggingFaceH4/zephyr-7b-beta"

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4"bnb_4bit_compute_dtype=torch.bfloat16
)

model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=bnb_config)
tokenizer = AutoTokenizer.from_pretrained(model_name)

設置 LLM 鏈

最後,我們擁有了設置 LLM 鏈所需的所有組件。

首先,使用加載的模型及其分詞器創建一個文本生成 pipeline。

接下來,創建一個提示模板——這應該遵循模型的格式,因此如果你替換了模型 checkpoint,確保使用適當的格式化。

from langchain.llms import HuggingFacePipeline
from langchain.prompts import PromptTemplate
from transformers import pipeline
from langchain_core.output_parsers import StrOutputParser

text_generation_pipeline = pipeline(
    model=model,
    tokenizer=tokenizer,
    task="text-generation",
    temperature=0.2,
    do_sample=True,
    repetition_penalty=1.1,
    return_full_text=True,
    max_new_tokens=400,
)

llm = HuggingFacePipeline(pipeline=text_generation_pipeline)

prompt_template = """
<|system|>
Answer the question based on your knowledge. Use the following context to help:
{context}
</s>
<|user|>
{question}
</s>
<|assistant|>
 """

prompt = PromptTemplate(
    input_variables=["context""question"],
    template=prompt_template,
)

llm_chain = prompt | llm | StrOutputParser()

注意:你也可以使用tokenizer.apply_chat_template將消息列表(作爲字典:{'role': 'user', 'content': '(...)'})轉換爲具有適當聊天格式的字符串。

最後,我們需要將llm_chain與檢索器結合,以創建一個 RAG 鏈。我們將原始問題以及檢索到的上下文文檔一起傳遞到最後的生成步驟:

from langchain_core.runnables import RunnablePassthrough

retriever = db.as_retriever()

rag_chain = {"context": retriever, "question": RunnablePassthrough()} | llm_chain

比較結果

讓我們看看 RAG 在生成針對庫特定問題的答案時所起的作用有何不同。

question = "How do you combine multiple adapters?"

首先,讓我們看看僅使用模型本身,不添加任何上下文,能得到什麼樣的答案:

llm_chain.invoke({"context""""question": question})

輸出:

To combine multiple adapters, you need to ensure that they are compatible with each other and the devices you want to connect. Here's how you can do it:

1. Identify the types of connectors you need: Before combining adapters, determine which connectors you require for both the input and output devices. For example, if you want to connect a USB-C device to an HDMI display, you may need a USB-C to HDMI adapter, and then a separate HDMI to DVI adapter if your display only supports DVI.

2. Check compatibility: Make sure that the adapters you choose are compatible with each other and the devices you want to connect. This is important because some adapters may not work together or may cause signal loss or interference.

3. Connect the adapters: Once you have identified the compatible adapters, connect them in the correct order. Typically, the adapter closest to the device you want to connect goes first, followed by any additional adapters required.

4. Test the connection: After connecting all the adapters, test the connection to ensure that everything is working correctly. If you encounter any issues, try rearranging the order of the adapters or using different adapters altogether.

5. Secure the connections: To prevent accidental disconnections, use cable ties or clips to secure the adapters and cables in place. This will also help to organize your setup and make it easier to manage.

Remember, combining multiple adapters can sometimes result in signal loss or interference, so it's essential to test the connection thoroughly before relying on it for critical tasks. Additionally, be aware of the maximum length of the combined cables to avoid signal degradation.As you can see, the model interpreted the question as one about physical computer adapters, while in the context of PEFT, “adapters” refer to LoRA adapters.

讓我們看看添加來自 GitHub 問題的上下文是否能幫助模型給出更相關、更準確的答案:

rag_chain.invoke(question)

輸出:

 Based on the provided context, here are some potential ways to combine multiple adapters using PEFT (Python Extension for Transformers):

  1. Load each adapter separately and concatenate their outputs:

    ```python
    from peft import Peft
    
    # Load the base model and adapter 1
    base_model = AutoModelForSequenceClassification.from_pretrained("your_base_model")
    adapter1 = Peft("adapter1").requires_grad_(False)
    adapter1(base_model).load_state_dict(torch.load("path/to/adapter1.bin"))
    
    # Load adapter 2
    adapter2 = Peft("adapter2").requires_grad_(False)
    adapter2(base_model).load_state_dict(torch.load("path/to/adapter2.bin"))
    
    # Concatenate the outputs of both adapters
    def forward(self, input_ids, attention_mask):
        x = self.base_model(input_ids, attention_mask)[0]
        x = torch.cat([x, adapter1(x), adapter2(x)]dim=-1)
        return x
    
    # Create a new model class that includes the concatenated outputs
    class MyModel(BaseModel):
        def __init__(self):
            super().__init__()
            self.forward = forward
    
    # Instantiate the new model class and use it for inference
    my_model = MyModel()
 
 2. Freeze multiple adapters and selectively activate them during inference:
     ```python
    from peft import Peft

    # Load the base model and all ad

正如我們所見,添加的上下文確實幫助了同一模型,對庫特定問題給出了更相關、更有見地的回答。

值得注意的是,多個適配器的組合用於推理已經被添加到庫中,人們可以在文檔中找到這些信息,因此對於 RAG 的下一次迭代,包含文檔嵌入可能是值得的。

現在,我們理解了如何從頭開始構建一個 LLM RAG 應用。

Google Colab: https://colab.research.google.com/github/huggingface/cookbook/blob/main/notebooks/en/rag_zephyr_langchain.ipynb

接下來,我們將利用我們的理解來構建另外三個 LLM 應用。

對於下面的應用,我們將使用 Ollama 作爲我們的 LLM 服務器。讓我們先從下面瞭解更多關於 LLM 服務器的信息。

LLM 服務器

這個應用最關鍵的部分是 LLM 服務器。藉助於 Ollama,我們擁有一個強大且可在本地設置的 LLM 服務器。

什麼是 Ollama?

Ollama 可以從 _ollama.ai 網站_進行安裝。

Ollama 不是一個單一的語言模型,而是一個框架,它讓我們能夠在本地機器上運行多個開源 LLM。可以把它想象成一個平臺,用來運行不同的語言模型,如 Llama2、Mistral 等,而不僅僅是一個特定的模型。

此外,我們可以使用 Langchain SDK,這是一個工具,可以讓與 Ollama 的交互更加便捷。

在命令行使用 Ollama 非常簡單。以下是一些我們可以嘗試在計算機上運行 Ollama 的命令:

我們可以在本地機器上下載這些模型,然後通過命令行提示與這些模型進行交互。或者,當我們運行模型時,Ollama 也會運行一個默認位於端口 11434 的推理服務器,我們可以通過 API 和其他庫(如 Langchain)與其進行交互。

截至目前,Ollama 擁有 74 個模型,其中包括嵌入模型等類別。

![img](Building LLM Applications Open-Source Chatbots (Part 7).assets/1xqE17xnt2zQ0e62oiec9ZQ.png)Source : Ollama

聊天機器人應用

接下來我們將構建的三個關鍵聊天機器人應用是:

  1. 使用 LangChain、ChromaDB 和 Streamlit 與多個 PDF 文件聊天

  2. 帶有 Open WebUI 的聊天機器人

  3. 使用 Docker 部署聊天機器人。

通過構建這三個應用,我們將建立起對如何大規模構建和部署工業級應用的直觀認識。

應用 1:與多個 PDF 文件聊天

我們將構建一個類似於 ChatPDF 但更簡單的應用,用戶可以上傳多個 PDF 文檔並通過直觀的界面提問。

我們的技術棧非常簡單,使用 Langchain、Ollama 和 Streamlit。

好的,讓我們開始設置吧。

聊天機器人可以從不同的 PDF 中獲取信息。下面是具體分解:

GitHub 倉庫結構:https://github.com/vsingh9076/Building_LLM_Applications.git

  1. 克隆倉庫到本地:
git clone https://github.com/vsingh9076/Building_LLM_Applications.git
cd 7_Ollama/local-pdf-chatbot
  1. 創建虛擬環境
python3 -m venv myenv
source myenv/bin/activate
  1. 安裝需要的包
pip install -r requirements.txt
  1. 安裝 Ollama 並根據 config.yml 中指定的模型拉取 LLM 模型 [我們已經在上面的部分中覆蓋了 Ollama 的設置]

  2. 使用 Ollama 運行 LLama2 模型

ollama pull llama2
ollama run llama2
  1. 使用 Streamlit CLI 運行 app.py 文件。執行以下命令:
streamlit run app.py

應用 2:帶有 Open WebUI 的聊天機器人

在這個應用中,我們將使用 Open WebUI 而不是 Streamlit、Chainlit 或 Gradio 作爲用戶界面來構建聊天機器人。

以下是步驟:

  1. 安裝 Ollama 並部署 LLM

我們可以在本地機器上直接安裝 Ollama,也可以在本地部署 Ollama 的 Docker 容器。選擇權在我們手中,無論哪種方式都可以適用於 langchain Ollama 接口、Ollama 官方 Python 接口以及 Open WebUI 接口。

以下是在本地系統中直接安裝 Ollama 的說明:

設置和運行 Ollama 很簡單。首先,訪問 ollama.ai,並下載適合我們操作系統的應用程序。

接下來,打開終端並執行以下命令來拉取最新的模型。雖然有衆多其他 LLM 模型可供選擇,我選擇 Mistral-7B 是因爲其緊湊的大小和競爭力的質量。

ollama pull llama2

ollama run llama2

對於所有其他模型,設置程序是相同的。我們需要拉取並運行。

2. 安裝 open-webui(ollama-webui)

Open WebUI 是一個可擴展、功能豐富且用戶友好的自託管 WebUI,設計用於完全離線運行。它支持多種 LLM 運行器,包括 Ollama 和兼容 OpenAI 的 API。

官方 GitHub 倉庫:https://github.com/open-webui/open-webui

運行以下 Docker 命令以在本地機器上部署 open-webui 的 Docker 容器。

docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v ollama-webui:/app/backend/data --name ollama-webui --restart always ghcr.io/ollama-webui/ollama-webui:main

3. 打開瀏覽器

打開瀏覽器並且使用端口 3000 訪問 localhost

http://localhost:3000

要開始使用,我們需要首次註冊。只需點擊 “Sign up” 按鈕來創建我們的賬戶即可

註冊後,我們將訪問到 open-webui 的主頁。

根據我們在本地機器上部署的 LLM,這些選項將會在下拉菜單中顯示供我們選擇。

選擇了之後,就可以進行聊天:

這避免了我們爲了實驗各種開源 LLM、做演示等目的而需要創建 Streamlit 或 Gradio 的用戶界面。

現在,我們可以與任何 PDF 文件進行聊天:

附上一個演示 GIF 文件,展示我們如何使用 open-webui 與圖像進行聊天。

讓我們來看看如何使用 Ollama 與我們定製的模型配合工作。以下是相應的步驟。

如何使用定製模型?

從 GGUF 導入

Ollama 支持在 Modelfile 中導入 GGUF 模型:

  1. 創建一個名爲Modelfile的文件,其中包含一條FROM指令,指向我們想要導入的模型的本地文件路徑。
FROM ./vicuna-33b.Q4_0.gguf
  1. 在 Ollama 中創建模型
ollama create example -f Modelfile
  1. 運行這個模型
ollama run example

從 PyTorch 或 Safetensors 導入

更多關於導入模型的信息,請參閱指南:https://github.com/ollama/ollama/blob/main/docs/import.md。

定製提示

來自 Ollama 庫的模型可以通過提示進行定製。例如,要定製llama2模型:

ollama pull llama2

創建 Modelfile:

FROM llama2

# set the temperature to 1 [higher is more creative, lower is more coherent]
PARAMETER temperature 1
# set the system message
SYSTEM """
You are Mario from Super Mario Bros. Answer as Mario, the assistant, only.
"""

接下來,創建並運行模型:

ollama create mario -f ./Modelfile
ollama run mario
>>> hi
Hello! It's your friend Mario.

應用 3:使用 Docker 部署聊天機器人

讓我們使用 Langshan 構建聊天機器人應用,爲了從 Python 應用中訪問我們的模型,我們將構建一個簡單的 Steamlit 聊天機器人應用。我們將在一個容器中部署這個 Python 應用,並在另一個容器中使用 Ollama。我們將使用 docker-compose 來構建基礎設施。

下圖展示了容器之間的交互架構,以及它們將訪問的端口。

我們構建了兩個容器,

GitHub 倉庫:

這段翻譯詳細描述了兩個容器的設置,包括 Ollama 容器和 Streamlit 聊天機器人應用的端口映射情況,以及指出了 GitHub 倉庫的存在,如果需要進一步的翻譯或有其他問題,隨時告訴我。然而,實際的 GitHub 倉庫鏈接需要由用戶提供,這裏只是描述了其存在。

克隆倉庫到本地:

git clone https://github.com/vsingh9076/Building_LLM_Applications.git
cd 7_Ollama/docker-pdf-chatbot

創建虛擬環境:

python3 -m venv ./ollama-langchain-venv
source ./ollama-langchain-venv/bin/activate

按照所需的包:

pip install -r requirements.txt

Ollama 是一個框架,允許我們將 Ollama 服務器作爲 Docker 鏡像運行。這對於構建使用 Ollama 模型的微服務應用非常有用。我們可以在 Docker 生態系統中輕鬆部署我們的應用,如 OpenShift、Kubernetes 等。要在 Docker 中運行 Ollama,我們必須使用 docker run 命令,如下所示。在此之前,我們應該已經在我們的系統中安裝了 Docker。

docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

下面是輸出 :

然後,我們應該能夠使用docker exec與這個容器進行交互,如下所示,並運行提示。

docker exec -it ollama ollama run phi

在上面的命令中,我們使用 docker 運行 phi

curl http://localhost:11434/api/generate -d '{
  "model": "phi:latest",
  "prompt":"Who are you?",
  "stream":false
}'

下面是結果:

需要注意的是,Docker 容器是短暫的,無論我們拉取什麼模型,當重啓容器時都會消失。在下一篇博客中,我們將解決這個問題,從零開始構建一個分佈式 Streamlit 應用。我們將把容器的卷與宿主機進行映射。

Ollama 是一個強大的工具,它開啓了在雲上創建和運行 LLM 應用的新途徑。它簡化了開發流程,並提供了靈活的部署選項。它還允許輕鬆管理和擴展應用。

現在,讓我們開始 Streamlit 應用的構建吧。

我們正在使用Ollama,並通過 Ollama Langchain 庫(這是langchain_community的一部分)調用模型。

我們在 requirement.txt 中定義依賴項。

現在,讓我們定義一個 Dockerfile 來構建 Streamlit 應用的 Docker 鏡像。

我們使用 Python 的 Docker 鏡像作爲基礎鏡像,並創建一個名爲/app的工作目錄。然後,我們將應用文件複製到該目錄,並運行pip install來安裝所有依賴。接着,我們暴露 8501 端口,並啓動streamlit應用。

可以使用docker build命令來構建 Docker 鏡像,如下所示。

docker build . -t viprasingh/ollama-langchain:0.1

我們應該能夠使用docker images命令檢查 Docker 鏡像是否已經構建,如下所示。

現在,讓我們構建一個 docker-compose 配置文件,來定義 Streamlit 應用和 Ollama 容器之間的網絡,以便它們能夠相互通信。我們還將定義上圖中所示的各種端口配置。對於 Ollama,我們還將映射卷,這樣拉取的任何模型都將被持久化。

我們可以通過運行 docker-compose up 命令來啓動應用,一旦執行docker-compose up,我們會看到兩個容器都開始運行,如下圖所示。

我們可以通過執行docker-compose ps命令,如下所示,來查看容器是否正在運行。

img

我們現在訪問 http://localhost:11434,如下圖所示,來檢查 Ollama 是否正在運行。

現在,讓我們通過使用 docker exec 命令登錄到 docker 容器,如下所示,來下載所需的模型。

docker exec -it docker-pdf-chatbot-ollama-container-1 ollama run phi

因爲我們使用的是 phi 模型,所以我們正在拉取這個模型,並通過運行它來進行測試。我們可以在下面的截圖中看到,phi 模型已經被下載,並將開始運行(因爲我們使用了-it標誌,所以我們應該能夠通過交互和使用示例提示來測試它)

我們可以在本地文件夾./data/ollama中看到下載的模型文件和清單(在容器內部,這個文件夾映射到/root/.ollama,這是 Ollama 查找已下載模型以提供服務的位置)

現在,讓我們通過在瀏覽器中打開 http://localhost:8501 來訪問我們的 Streamlit 應用。以下截圖顯示了應用界面:

讓我們嘗試運行一個提示:“generate a story about dog called bozo”。我們應該能夠在控制檯日誌中看到來自我們 Streamlit 應用的 API 調用,如下所示:

我們可以在下面的截圖中看到,對於我發送的提示,我得到了這樣的響應:

我們可以通過調用docker-compose down來停止部署。

以下截圖顯示了輸出結果:

就這樣完成了。在本次博客中,我們非常愉快地探索瞭如何讓 Ollama 與 Langchain 協同工作,並使用 Docker-Compose 在 Docker 上部署它們。

總結

這篇博客探討了在本地構建大型語言模型(LLM)應用,重點放在了增強檢索生成(RAG)鏈上。它涵蓋了 LLM 服務器(由 Ollama 驅動)、LangChain 框架、用於嵌入的 Chroma 以及用於 Web 應用的 Streamlit 等組件。它詳細介紹瞭如何使用 Ollama、LangChain、ChromaDB 和 Streamlit 創建聊天機器人應用,包括 GitHub 倉庫結構和 Docker 部署。總體而言,它提供了一份高效開發 LLM 應用的實用指南。

英文原文:https://medium.com/@vipra_singh/building-llm-applications-open-source-chatbots-part-7-1ca9c3653175

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