LangChain RAG 构建知识库

149 阅读7分钟

概览

构建企业级 RAG 系统的高级指南 [译] | 宝玉的分享 (baoyu.io)

原文: Mastering RAG: How To Architect An Enterprise RAG System - Galileo AI

[2401.05856] Seven Failure Points When Engineering a Retrieval Augmented Generation System

LangGraph: Multi-Agent Workflows (langchain.dev)

Building a fully local LLM voice assistant to control my smart home | John's Website (johnthenerd.com)

使用客户端技术构建基于LLM的Web应用程序 · Ollama 博客 - Ollama 框架

本文介绍基于 RAG 查询增强生成搭建知识问答机器人的一种方案。

RAG 是一种增加 LLM 之外的数据,从而增强 LLM 知识能力的技术。解决 LLM 由于训练数据的局限,缺少相关领域知识,而无法生成领域回答的缺点。

LangChain

LangChain 提供搭建问答应用的相关组件,以及 RAG 组件。 安装 langchain 0.1.11

pip install langchain==0.1.11

检查是否安装成功,通过加载模型执行简单生成任务,遇到报错

AttributeError: module 'sqlalchemy' has no attribute 'Select'

langchain 0.1.11 依赖安装的 sqlalchemy 版本为 1.4,将 sqlalchemy 版本升级到 2.0.0 可解决错误。 AttributeError: module 'sqlalchemy' has no attribute 'Select' when importing AmazonTextractPDFLoader · Issue #18528 · langchain-ai/langchain · GitHub

pip install sqlalchemy==2.0.0

3. LCEL 方式

LangChain 推荐使用方式,简单易用。

  1. AgentInput 是 KV 结构的 map 类型,其中 intermediate_steps 是必要 key 。
  2. Prompt 至少包含两个参数:input 和 agent_scratchped。input 是用户输入,agent_scratchped 则是前一个 AgentAction 的输出。
  3. Tools 需要转换成 LLM 语言模型能理解的格式
from langchain.agents.format_scratchpad.openai_tools import (
    format_to_openai_tool_messages,
)
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "You are very powerful assistant, but don't know current events",
        ),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

llm_with_tools = llm.bind_tools(tools)

agent = (
    {
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(
            x["intermediate_steps"]
        ),
    }
    | prompt
    | llm_with_tools
    | OpenAIToolsAgentOutputParser()
)

结构化输出

信息提取

Quickstart | 🦜️🔗 Langchain

Tools

langchain-community

LCEL

The input type and output type varies by component:

ComponentInput TypeOutput Type
PromptDictionaryPromptValue
ChatModelSingle string, list of chat messages or a PromptValueChatMessage
LLMSingle string, list of chat messages or a PromptValueString
OutputParserThe output of an LLM or ChatModelDepends on the parser
RetrieverSingle stringList of Documents
ToolSingle string or dictionary, depending on the toolDepends on the tool

基于 RAG 的架构

RAG 应用具有几个主要组件:

  1. Indexing;
  2. Retrieval

Indexing: 索引

1. 加载文档

文档的类型是多种多样的,不同类型的文档需要不同的加载类,常见的有:

  • 纯文本文档
  • PDF 文档
  • HTML 文档
  • MarkDown 文档
  • JSON 文档
  • CSV 文档

2. 分割

3. Embedding

Embedding 是将文本或者图像信息转换到向量空间,使用数值表示,方便计算文本或者图像之间的相关性。

基于 embedding 的检索,使用词向量进行查询,相比基于关键词的查询,可以发现信息之间的语义联系。也提高对于拼写错误,同义词和近义词的容错性。

如何利用大语言模型(LLM)打造定制化的Embedding模型:一种低成本、高效率的解决方案 - 知乎 (zhihu.com)

GitHub - shibing624/text2vec: text2vec, text to vector. 文本向量表征工具,把文本转化为向量矩阵,实现了Word2Vec、RankBM25、Sentence-BERT、CoSENT等文本表征、文本相似度计算模型,开箱即用。

模型

Embedding 进行词嵌入,同样是使用语言模型进行转换。下面列部分 embedding 模型:

  • FlagEmbedding: FlagEmbedding专注于检索增强llm领域,包含多个项目,其中 BGE Embedding是一个通用向量模型

FlagEmbedding/README_zh.md at master · FlagOpen/FlagEmbedding · GitHub

bge-large-zh: Mirror of https://huggingface.co/BAAI/bge-large-zh (gitee.com)

BGE论文解读:如何炼成中文世界的通用Embedding Model - 知乎 (zhihu.com)

  • text2vec-base-chinese:型,是用CoSENT方法训练,基于hfl/chinese-macbert-base在中文STS-B数据训练得到,并在中文STS-B测试集评估达到较好效果

shibing624/text2vec-base-chinese · HF Mirror (hf-mirror.com)

GitHub - shibing624/text2vec: text2vec, text to vector. 文本向量表征工具,把文本转化为向量矩阵,实现了Word2Vec、RankBM25、Sentence-BERT、CoSENT等文本表征、文本相似度计算模型,开箱即用。

使用方式

Embedding 模型的加载使用,需要使用到一些开源库,有如下几种使用方式:

  • SentenceTransformers 一个可以用于句子、文本和图像嵌入的Python库。 可以为 100 多种语言计算文本的嵌入并且可以轻松地将它们用于语义文本相似性、语义搜索和同义词挖掘等常见任务。

sentence-transformers 使用 BERT 等模型进行多语句,段落和图像 embedding, 这些模型基于 Transformers ,如 BERT, RoBERTa, XLM-RoBERTa 等。文本在向量空间中 embedding ,可以使用余弦相似度进行查询。

from sentence_transformers import SentenceTransformer
sentences_1 = ["样例数据-1", "样例数据-2"]
sentences_2 = ["样例数据-3", "样例数据-4"]
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
embeddings_1 = model.encode(sentences_1, normalize_embeddings=True)
embeddings_2 = model.encode(sentences_2, normalize_embeddings=True)
similarity = embeddings_1 @ embeddings_2.T
print(similarity)
  • LangChain: SentenceTransformers embeddings are called using the HuggingFaceEmbeddings integration. We have also added an alias for SentenceTransformerEmbeddings for users who are more familiar with directly using that package.

Sentence Transformers Embeddings | 🦜️🔗 Langchain

from langchain.embeddings import HuggingFaceBgeEmbeddings
model_name = "BAAI/bge-large-en-v1.5"
model_kwargs = {'device': 'cuda'}
encode_kwargs = {'normalize_embeddings': True} # set True to compute cosine similarity
model = HuggingFaceBgeEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs,
    query_instruction="为这个句子生成表示以用于检索相关文章:"
)
model.query_instruction = "为这个句子生成表示以用于检索相关文章:"
  • HuggingFace Transformers: With the transformers package, you can use the model like this: First, you pass your input through the transformer model, then you select the last hidden state of the first token (i.e., [CLS]) as the sentence embedding.
from transformers import AutoTokenizer, AutoModel
import torch
# Sentences we want sentence embeddings for
sentences = ["样例数据-1", "样例数据-2"]

# Load model from HuggingFace Hub
tokenizer = AutoTokenizer.from_pretrained('BAAI/bge-large-zh-v1.5')
model = AutoModel.from_pretrained('BAAI/bge-large-zh-v1.5')
model.eval()

# Tokenize sentences
encoded_input = tokenizer(sentences, padding=True, truncation=True, return_tensors='pt')
# for s2p(short query to long passage) retrieval task, add an instruction to query (not add instruction for passages)
# encoded_input = tokenizer([instruction + q for q in queries], padding=True, truncation=True, return_tensors='pt')

# Compute token embeddings
with torch.no_grad():
    model_output = model(**encoded_input)
    # Perform pooling. In this case, cls pooling.
    sentence_embeddings = model_output[0][:, 0]
# normalize embeddings
sentence_embeddings = torch.nn.functional.normalize(sentence_embeddings, p=2, dim=1)
print("Sentence embeddings:", sentence_embeddings)

Retrieval

实际开发应用时,例如依据用户提问,需要生成文档相关回答,首先需要使用 embedding 嵌入向量查询文档相关内容,再使用 LLM 大语言模型生成回答。

直接 embedding 计算相关性

使用 setencetransformer 库的 econde 方法,计算 query prompt 的 embedding 嵌入向量。同时遍历分割后的文档数据,同样使用 setencetransformer 库计算文档的 embedding 嵌入向量。然后进行相关性计算,常见的相关性计算有余旋值计算。

model = SentenceTransformer('paraphrase-MiniLM-L6-v2')

# The sentences we'd like to compute similarity about
sentences = ['Python is an interpreted high-level general-purpose programming language.',
    'Python is dynamically-typed and garbage-collected.',
    'The quick brown fox jumps over the lazy dog.']

# Get embeddings of sentences
embedding_vector = model.encode(sentences)

# Compute similarities
sim = util.cos_sim(embeddings[0], embeddings[1])
print("{0:.4f}".format(sim.tolist()[0][0])) # 0.6445
sim = util.cos_sim(embeddings[0], embeddings[2])
print("{0:.4f}".format(sim.tolist()[0][0])) # 0.0365

向量数据库查询

减少文档的 embedding 嵌入向量重复计算,将文档的 embedding 嵌入向量持久化存放在向量数据库中。检索余 query prompt 相关性最大的文档时,直接在向量数据库中进行查询。向量数据库支持文本查询,向量查询。

  • 文本查询:
query = "What did the president say about Ketanji Brown Jackson"
docs = db.similarity_search(query)
print(docs[0].page_content)
  • 向量查询:
model = SentenceTransformer('paraphrase-MiniLM-L6-v2')
embedding_vector = model.encode(query)
docs = db.similarity_search_by_vector(embedding_vector)
print(docs[0].page_content)
model = SentenceTransformerEmbeddings('paraphrase-MiniLM-L6-v2')
embedding_vector = model.embed_query(query)
docs = db.similarity_search_by_vector(embedding_vector)
doc_result = embeddings.embed_documents([text, "This is not a test document."])
print(docs[0].page_content)

LangChain Retriever

LangChain 定义了 Retriever 接口,包装了 embedding 嵌入向量查询策略,并返回 Document 对象结果。 VectorStoreRetriever 是其中最通用的一种 Retriever,利用 VectorStore 的相关性查询能力。任意 vectorstore 都有 vectorStore.as_retriever() 方法返回一个 retriever.

retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 6})
retrieved_docs = retriever.invoke("What are the approaches to Task Decomposition?")
print(retrieved_docs[0].page_content)

QAbot

将 RAG 与 LLM 组合在一起构成 Q&A bot 知识问答应用,可以将领域知识通过 RAG 查询到的问题相关性大的领域知识与问题一起组成 prompt ,LLM 依据新 prompt 生成领域答案。

question = "领域问题"
documents = retrieval.search_documents(question)
prompt = self.create_prompt(question=question, documents=documents)
resp = chatGLM.generate_resp(prompt=prompt)
return resp

Chatbot

ChatModel 是 LLM 大语言模型的变种,与 LLM 不同的其输入与输出类型不是纯文本,而是 Message,LangChain 提供了 AIMessage, HumanMessage, SystemMessage, ChatMessage, FunctionMessage.

ChatModel 支持自定义 ChatModel

Custom Chat Model | 🦜️🔗 Langchain