ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

llm-universe 如何用 RunnableBranch 为检索问答链添加聊天记录并压缩指代不清的 query

llm-universe 如何用 RunnableBranch 为检索问答链添加聊天记录并压缩指代不清的 query llm-universe 如何用 RunnableBranch 为检索问答链添加聊天记录并压缩指代不清的 query【免费下载链接】llm-universe本项目是一个面向小白开发者的大模型应用开发教程在线阅读地址https://datawhalechina.github.io/llm-universe/项目地址: https://gitcode.com/GitHub_Trending/ll/llm-universe在 llm-universe 教程的第四章中我们已经用 LangChain 搭出了一条单轮的检索问答链把向量知识库的召回结果拼进 prompt再交给大模型回答。但它有一个明显缺陷——模型不记得之前的对话。当用户接着上一轮追问“南瓜书跟它有什么关系”时query 本身语义不完整直接用它去查向量库很难召回相关内容。本文的任务就是在这条已有的检索问答链上扩展出多轮对话能力用ChatPromptTemplate把chat_history传给模型并用 LangChain 的RunnableBranch在检索前先用 LLM 根据聊天记录压缩、补全指代不清的最新 query再用补全后的 query 去检索。全部代码和验证方式均来自 docs/C4/C4.md 的 4.2.5 与 4.2.6 节可在 C4.ipynb 中对照执行。准备条件按文档要求开始之前需要满足三个前提已建好的向量数据库。文档要求此处必须使用和构建时相同的 Embedding仓库中示例使用智谱 Embedding加载路径为data_base/vector_db/chromaChroma 持久化目录。封装好的 Embedding 代码。文档要求将 zhipuai_embedding.py 放到当前 notebook 的同级目录下教程 notebook 中通过sys.path.append(../C3 搭建知识库)引入。环境变量与依赖。在.env中配置OPENAI_API_KEY用于ChatOpenAI和ZHIPUAI_API_KEY用于ZhipuAIEmbeddings并用load_dotenv(find_dotenv())加载。依赖版本以 requirements.txt 为准langchain0.3.0、langchain-core0.3.0、langchain-openai0.2.0、langchain-chroma0.1.4、python-dotenv1.0.1、zhipuai2.1.5.20250106等。加载向量数据库并构建检索器这一步沿用文档 4.2.1 节的写法用与建库时相同的 Embedding 加载 Chroma 库再用as_retriever构造检索器。import sys sys.path.append(../C3 搭建知识库) # 将 C3 目录放入系统路径中 from dotenv import load_dotenv, find_dotenv import os _ load_dotenv(find_dotenv()) # 读取本地 .env 文件 from zhipuai_embedding import ZhipuAIEmbeddings from langchain.vectorstores.chroma import Chroma # 定义 Embeddings必须与建库时相同 embedding ZhipuAIEmbeddings() # 向量数据库持久化路径notebook 内的相对路径以你的工作目录为准 persist_directory ../../data_base/vector_db/chroma # 加载数据库 vectordb Chroma( persist_directorypersist_directory, embedding_functionembedding )加载成功后可以用一个简单问题验证检索器可用question 什么是prompt engineering? retriever vectordb.as_retriever(search_kwargs{k: 3}) docs retriever.invoke(question) print(f检索到的内容数{len(docs)})文档示例中k3时输出“检索到的内容数3”文档示例具体召回数量取决于你的知识库。这一步确认了后文RunnableBranch两条分支最终汇入的retriever是可用的。构造带 chat_history 的问答 prompt文档 4.2.5 节的思路是聊天模型的接口基于消息而非纯文本ChatPromptTemplate可以接收聊天消息历史记录与当前问题一起传给模型从而把历史加入上下文。问答链的系统 prompt 和模板如下from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnableBranch, RunnablePassthrough from langchain_openai import ChatOpenAI # 4.2.3 节定义的 LLM llm ChatOpenAI(model_namegpt-4o, temperature0) # 问答链的系统 prompt system_prompt ( 你是一个问答任务的助手。 请使用检索到的上下文片段回答这个问题。 如果你不知道答案就说不知道。 请使用简洁的话语回答用户。 \n\n {context} ) # 制定 prompt template qa_prompt ChatPromptTemplate( [ (system, system_prompt), (placeholder, {chat_history}), (human, {input}), ] )其中(placeholder, {chat_history})是关键历史消息会被展开为完整的消息列表插在 system 与当前用户输入之间。可以先单独验证这一点不带历史记录和有历史记录各调用一次qa_prompt.invoke# 有历史记录 messages qa_prompt.invoke( { input: 你可以介绍一下他吗, chat_history: [ (human, 西瓜书是什么), (ai, 西瓜书是指周志华老师的《机器学习》一书是机器学习领域的经典入门教材之一。), ], context: } ) for message in messages.messages: print(message.content)文档示例输出依次打印出系统提示、“西瓜书是什么”其 AI 回复以及最新的“你可以介绍一下他吗”说明历史消息已按顺序进入消息列表文档示例。到这里回答环节已经具备多轮对话能力但检索环节还没有。用 RunnableBranch 压缩指代不清的 query问题出在检索这一环像“你可以介绍一下他吗”这样语义不全的最新对话直接查向量库几乎召回不到有效内容——它其实想问的是“你可以介绍下周志华老师吗”。文档 4.2.6 节的解法是信息压缩先让 LLM 根据历史记录完善用户的问题再用完善后的 query 检索。分支选择用RunnableBranch完成# 压缩问题的系统 prompt condense_question_system_template ( 请根据聊天记录完善用户最新的问题 如果用户最新的问题不需要完善则返回用户的问题。 ) # 构造压缩问题的 prompt template condense_question_prompt ChatPromptTemplate([ (system, condense_question_system_template), (placeholder, {chat_history}), (human, {input}), ]) # 构造检索文档的链 # RunnableBranch 会根据条件选择要运行的分支 retrieve_docs RunnableBranch( # 分支 1: 若聊天记录中没有 chat_history 则直接使用用户问题查询向量数据库 (lambda x: not x.get(chat_history, False), (lambda x: x[input]) | retriever, ), # 分支 2: 若聊天记录中有 chat_history 则先让 llm 根据聊天记录完善问题再查询向量数据库 condense_question_prompt | llm | StrOutputParser() | retriever, )两个分支的执行路径要分清分支 1输入字典里没有chat_history或其值为假如空列表时取x[input]原样交给retriever不产生额外的 LLM 调用分支 2存在聊天记录时先用condense_question_prompt | llm | StrOutputParser()把 query 补全为独立成句的问题再交给retriever检索。组装支持聊天记录的检索问答链有了retrieve_docs接下来把它接入问答链。注意这里combine_docs需要相对单轮版本做一处改动检索结果现在被RunnablePassthrough.assign存到了键context下所以整合函数要改成从docs[context]取值# 重新定义 combine_docs def combine_docs(docs): return \n\n.join(doc.page_content for doc in docs[context]) # 将 docs 改为 docs[context] # 定义问答链 qa_chain ( RunnablePassthrough.assign(contextcombine_docs) # 使用 combine_docs 函数整合 qa_prompt 中的 context | qa_prompt # 问答模板 | llm | StrOutputParser() # 规定输出的格式为 str ) # 定义带有历史记录的问答链 qa_history_chain RunnablePassthrough.assign( context (lambda x: x) | retrieve_docs # 将查询结果存为 context ).assign(answerqa_chain) # 将最终结果存为 answer整条链的运行顺序是输入{input: ..., chat_history: [...]}→RunnableBranch按有无历史选择压缩或直查得到文档列表并存为context→qa_chain用combine_docs拼出上下文、带上chat_history交给 LLM得到answer。验证链是否生效文档给出了两组对照测试。第一组不带聊天记录走的是分支 1# 不带聊天记录 qa_history_chain.invoke({ input: 西瓜书是什么, chat_history: [] })文档示例返回的字典中context是从南瓜书 PDF 召回的若干Documentanswer为“西瓜书是指周志华老师的《机器学习》一书它是机器学习领域的经典入门教材之一。”文档示例第二组带聊天记录验证压缩后的 query 能否检索到正确内容、LLM 能否正确理解指代# 带聊天记录 qa_history_chain.invoke({ input: 南瓜书跟它有什么关系, chat_history: [ (human, 西瓜书是什么), (ai, 西瓜书是指周志华老师的《机器学习》一书是机器学习领域的经典入门教材之一。), ] })文档示例中context召回了南瓜书中把西瓜书作为前置知识的段落answer为“南瓜书是对西瓜书中较难理解的公式进行解析和推导细节补充的书籍。它以西瓜书的内容为前置知识帮助读者更好地理解和学习西瓜书中的内容。”文档示例文档对成功现象的判断是LLM 准确地判断了“它”是什么说明历史信息被成功传递召回的内容中包含问题的答案证明信息压缩策略也起到了作用。限制说明与下一步Embedding 必须一致加载向量库时必须使用与建库时相同的 Embedding这是文档 4.2.1 节明确要求的前提否则召回结果不可用。两处文档措辞不同4.2.6 节压缩 prompt 用“请根据聊天记录完善用户最新的问题如果用户最新的问题不需要完善则返回用户的问题”而 4.3.2 节 Streamlit 应用中用的是“请根据聊天记录总结用户最近的问题如果没有多余的聊天记录则返回用户的问题”。本文主路径按 4.2.6 节执行两种措辞在文档中均给出实际效果以你自己的知识库测试为准。Chroma 弃用警告文档示例中出现The class Chroma was deprecated in LangChain 0.2.9 ... import as from langchain_chroma import Chroma的提示requirements.txt 已包含langchain-chroma0.1.4属于警告信息不影响示例链的运行。文档 4.3.2 节给出了这条链的后续用法把同样的RunnableBranch逻辑封装进get_qa_history_chain函数再用 Streamlit 以st.session_state保存多轮messages、以chain.stream流式输出本地运行命令为streamlit run notebook/C4 构建 RAG 应用/streamlit_app.py。对应源码可直接查看 streamlit_app.py细节以 docs/C4/C4.md 的 4.3 节为准。【免费下载链接】llm-universe本项目是一个面向小白开发者的大模型应用开发教程在线阅读地址https://datawhalechina.github.io/llm-universe/项目地址: https://gitcode.com/GitHub_Trending/ll/llm-universe创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表