【动手学大模型开发 18】使用LangChian构建检索问答链(RAG)
🌈 嗨,我是青松,很高兴遇到你!
🌟 希望用我的经验,让每个人的AI学习之路走的更容易些~
🔥 专栏:大模型(LLMs)高频面题全面整理(★2025最新版★)
目录
1. 加载向量数据库
2. 创建一个 LLM
3. 构建检索问答链
4.检索问答链效果测试
4.1 基于召回结果和 query 结合起来构建的 prompt 效果
4.2 大模型自己回答的效果
在之前的章节,我们已经介绍了如何根据自己的本地知识文档,搭建一个向量知识库。 在接下来的内容里,我们将使用搭建好的向量数据库,对 query 查询问题进行召回,并将召回结果和 query 结合起来构建 prompt,输入到大模型中进行问答。
1. 加载向量数据库
首先,我们加载在前一章已经构建的向量数据库。注意,此处你需要使用和构建时相同的 Emedding。
import sys
sys.path.append("../C3 搭建知识库") # 将父目录放入系统路径中# 使用智谱 Embedding API,注意,需要将上一章实现的封装代码下载到本地
from zhipuai_embedding import ZhipuAIEmbeddingsfrom langchain.vectorstores.chroma import Chroma
Copy to clipboardErrorCopied
从环境变量中加载你的 API_KEY
from dotenv import load_dotenv, find_dotenv
import os_ = load_dotenv(find_dotenv()) # read local .env file
zhipuai_api_key = os.environ['ZHIPUAI_API_KEY']
Copy to clipboardErrorCopied
加载向量数据库,其中包含了 ../../data_base/knowledge_db 下多个文档的 Embedding
# 定义 Embeddings
embedding = ZhipuAIEmbeddings()# 向量数据库持久化路径
persist_directory = '../C3 搭建知识库/data_base/vector_db/chroma'# 加载数据库
vectordb = Chroma(persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上embedding_function=embedding
)
Copy to clipboardErrorCopied
print(f"向量库中存储的数量:{vectordb._collection.count()}")
Copy to clipboardErrorCopied
向量库中存储的数量:20
Copy to clipboardErrorCopied
我们可以测试一下加载的向量数据库,使用一个问题 query 进行向量检索。如下代码会在向量数据库中根据相似性进行检索,返回前 k 个最相似的文档。
⚠️使用相似性搜索前,请确保你已安装了 OpenAI 开源的快速分词工具 tiktoken 包:
pip install tiktoken
question = "什么是prompt engineering?"
docs = vectordb.similarity_search(question,k=3)
print(f"检索到的内容数:{len(docs)}")
Copy to clipboardErrorCopied
检索到的内容数:3
Copy to clipboardErrorCopied
打印一下检索到的内容
for i, doc in enumerate(docs):print(f"检索到的第{i}个内容: \n {doc.page_content}", end="\n-----------------------------------------------------\n")
Copy to clipboardErrorCopied
检索到的第0个内容: 相反,我们应通过 Prompt 指引语言模型进行深入思考。可以要求其先列出对问题的各种看法,说明推理依据,然后再得出最终结论。在 Prompt 中添加逐步推理的要求,能让语言模型投入更多时间逻辑思维,输出结果也将更可靠准确。综上所述,给予语言模型充足的推理时间,是 Prompt Engineering 中一个非常重要的设计原则。这将大大提高语言模型处理复杂问题的效果,也是构建高质量 Prompt 的关键之处。开发者应注意给模型留出思考空间,以发挥语言模型的最大潜力。2.1 指定完成任务所需的步骤接下来我们将通过给定一个复杂任务,给出完成该任务的一系列步骤,来展示这一策略的效果。首先我们描述了杰克和吉尔的故事,并给出提示词执行以下操作:首先,用一句话概括三个反引号限定的文本。第二,将摘要翻译成英语。第三,在英语摘要中列出每个名称。第四,输出包含以下键的 JSON 对象:英语摘要和人名个数。要求输出以换行符分隔。
-----------------------------------------------------
检索到的第1个内容: 第二章 提示原则如何去使用 Prompt,以充分发挥 LLM 的性能?首先我们需要知道设计 Prompt 的原则,它们是每一个开发者设计 Prompt 所必须知道的基础概念。本章讨论了设计高效 Prompt 的两个关键原则:编写清晰、具体的指令和给予模型充足思考时间。掌握这两点,对创建可靠的语言模型交互尤为重要。首先,Prompt 需要清晰明确地表达需求,提供充足上下文,使语言模型准确理解我们的意图,就像向一个外星人详细解释人类世界一样。过于简略的 Prompt 往往使模型难以把握所要完成的具体任务。其次,让语言模型有充足时间推理也极为关键。就像人类解题一样,匆忙得出的结论多有失误。因此 Prompt 应加入逐步推理的要求,给模型留出充分思考时间,这样生成的结果才更准确可靠。如果 Prompt 在这两点上都作了优化,语言模型就能够尽可能发挥潜力,完成复杂的推理和生成任务。掌握这些 Prompt 设计原则,是开发者取得语言模型应用成功的重要一步。一、原则一 编写清晰、具体的指令
-----------------------------------------------------
检索到的第2个内容: 一、原则一 编写清晰、具体的指令亲爱的读者,在与语言模型交互时,您需要牢记一点:以清晰、具体的方式表达您的需求。假设您面前坐着一位来自外星球的新朋友,其对人类语言和常识都一无所知。在这种情况下,您需要把想表达的意图讲得非常明确,不要有任何歧义。同样的,在提供 Prompt 的时候,也要以足够详细和容易理解的方式,把您的需求与上下文说清楚。并不是说 Prompt 就必须非常短小简洁。事实上,在许多情况下,更长、更复杂的 Prompt 反而会让语言模型更容易抓住关键点,给出符合预期的回复。原因在于,复杂的 Prompt 提供了更丰富的上下文和细节,让模型可以更准确地把握所需的操作和响应方式。所以,记住用清晰、详尽的语言表达 Prompt,就像在给外星人讲解人类世界一样,“Adding more context helps the model understand you better.”。从该原则出发,我们提供几个设计 Prompt 的技巧。1.1 使用分隔符清晰地表示输入的不同部分
-----------------------------------------------------
Copy to clipboardErrorCopied
2. 创建一个 LLM
在这里,我们调用 OpenAI 的 API 创建一个 LLM,当然你也可以使用其他 LLM 的 API 进行创建
import os
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
Copy to clipboardErrorCopied
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name = "gpt-3.5-turbo", temperature = 0)llm.invoke("请你自我介绍一下自己!")
Copy to clipboardErrorCopied
AIMessage(content='你好,我是一个智能助手,专门为用户提供各种服务和帮助。我可以回答问题、提供信息、解决问题等等。如果您有任何需要,请随时告诉我,我会尽力帮助您的。感谢您的使用!', response_metadata={'token_usage': {'completion_tokens': 81, 'prompt_tokens': 20, 'total_tokens': 101}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_3bc1b5746c', 'finish_reason': 'stop', 'logprobs': None})
Copy to clipboardErrorCopied
3. 构建检索问答链
from langchain.prompts import PromptTemplatetemplate = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。
{context}
问题: {question}
"""QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template)
Copy to clipboardErrorCopied
再创建一个基于模板的检索链:
from langchain.chains import RetrievalQAqa_chain = RetrievalQA.from_chain_type(llm,retriever=vectordb.as_retriever(),return_source_documents=True,chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})
Copy to clipboardErrorCopied
创建检索 QA 链的方法 RetrievalQA.from_chain_type() 有如下参数:
- llm:指定使用的 LLM
- 指定 chain type : RetrievalQA.from_chain_type(chain_type="map_reduce"),也可以利用load_qa_chain()方法指定chain type。
- 自定义 prompt :通过在RetrievalQA.from_chain_type()方法中,指定chain_type_kwargs参数,而该参数:chain_type_kwargs = {"prompt": PROMPT}
- 返回源文档:通过RetrievalQA.from_chain_type()方法中指定:return_source_documents=True参数;也可以使用RetrievalQAWithSourceChain()方法,返回源文档的引用(坐标或者叫主键、索引)
4.检索问答链效果测试
question_1 = "什么是南瓜书?"
question_2 = "王阳明是谁?"
Copy to clipboardErrorCopied
4.1 基于召回结果和 query 结合起来构建的 prompt 效果
result = qa_chain({"query": question_1})
print("大模型+知识库后回答 question_1 的结果:")
print(result["result"])
Copy to clipboardErrorCopied
d:\Miniconda\miniconda3\envs\llm2\lib\site-packages\langchain_core\_api\deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.warn_deprecated(大模型+知识库后回答 question_1 的结果:
抱歉,我不知道南瓜书是什么。谢谢你的提问!
Copy to clipboardErrorCopied
result = qa_chain({"query": question_2})
print("大模型+知识库后回答 question_2 的结果:")
print(result["result"])
Copy to clipboardErrorCopied
大模型+知识库后回答 question_2 的结果:
我不知道王阳明是谁。谢谢你的提问!
Copy to clipboardErrorCopied
4.2 大模型自己回答的效果
prompt_template = """请回答下列问题:{}""".format(question_1)### 基于大模型的问答
llm.predict(prompt_template)
Copy to clipboardErrorCopied
d:\Miniconda\miniconda3\envs\llm2\lib\site-packages\langchain_core\_api\deprecation.py:117: LangChainDeprecationWarning: The function `predict` was deprecated in LangChain 0.1.7 and will be removed in 0.2.0. Use invoke instead.warn_deprecated('南瓜书是指一种关于南瓜的书籍,通常是指介绍南瓜的种植、养护、烹饪等方面知识的书籍。南瓜书也可以指一种以南瓜为主题的文学作品。'
Copy to clipboardErrorCopied
prompt_template = """请回答下列问题:{}""".format(question_2)### 基于大模型的问答
llm.predict(prompt_template)
Copy to clipboardErrorCopied
'王阳明(1472年-1529年),字宪,号阳明,浙江绍兴人,明代著名的哲学家、军事家、教育家、政治家。他提出了“致良知”、“格物致知”等重要思想,强调人的内心本具良知,只要发挥良知,就能认识道德真理。他的思想对后世影响深远,被称为“阳明心学”。'
Copy to clipboardErrorCopied
⭐ 通过以上两个问题,我们发现 LLM 对于一些近几年的知识以及非常识性的专业问题,回答的并不是很好。而加上我们的本地知识,就可以帮助 LLM 做出更好的回答。另外,也有助于缓解大模型的“幻觉”问题。