免费的个人主页网站,昵称小写 wordpress,查企业免费查询,wordpress 收费 主题文章目录 准备创建矢量数据库对象创建 LangGraph 链将检索步骤转化为工具定义节点构建图 见证效果qwen2.5llama3.1MFDoom/deepseek-r1-tool-calling:7b 总结代码参考 上一篇文章我们演练了一个 用 langgraph 实现的 RAG(Retrieval Augmented Generation,检索增强生成) 系统。本… 文章目录 准备创建矢量数据库对象创建 LangGraph 链将检索步骤转化为工具定义节点构建图 见证效果qwen2.5llama3.1MFDoom/deepseek-r1-tool-calling:7b 总结代码参考 上一篇文章我们演练了一个 用 langgraph 实现的 RAG(Retrieval Augmented Generation,检索增强生成) 系统。本文将要创建的系统将更加智能如果在知识库中未找到靠谱的文档则用 LLM(大语言模型) 自身的能力弥补。 另外检索和生成部分增加了更多的细节控制。本次构建的 LangGraph 链如下图
如上图query_or_respond 是一个条件节点它通过能否根据用户的问题生成 工具调用tool_calls 来判断是否需要检索矢量知识库如果 工具调用 为空则直接由大语言模型处理否则通过 工具调用 调用 tools 进行检索。 使用 qwen2.5 、 deepseek 以及 llama3.1 做实验用 shaw/dmeta-embedding-zh 做中文嵌入和检索。 准备
在正式开始撸代码之前需要准备一下编程环境。 计算机 本文涉及的所有代码可以在没有显存的环境中执行。 我使用的机器配置为 CPU: Intel i5-8400 2.80GHz内存: 16GB Visual Studio Code 和 venv 这是很受欢迎的开发工具相关文章的代码可以在 Visual Studio Code 中开发和调试。 我们用 python 的 venv 创建虚拟环境, 详见 在Visual Studio Code中配置venv。 Ollama 在 Ollama 平台上部署本地大模型非常方便基于此平台我们可以让 langchain 使用 llama3.1、qwen2.5、deepseek 等各种本地大模型。详见 在langchian中使用本地部署的llama3.1大模型 。
创建矢量数据库对象
我们直接使用之前使用 chroma 创建好的本地嵌入数据库它的数据源是一个 csv 文件每一行包含了一种动物的信息例如
名称,学名,特点,作用
狗,Canis lupus familiaris,忠诚、聪明、社交性强,看家护院、导盲、搜救、警务、情感陪伴
猫,Felis catus,独立、高冷、善于捕鼠,消灭害鼠、陪伴、缓解压力详细的创建过程可参见本地大模型编程实战(14)初探智能体Agent(1) embed_model_name shaw/dmeta-embedding-zh
vector_store Chroma(persist_directoryget_persist_directory(embed_model_name),embedding_functionOllamaEmbeddings(modelembed_model_name))创建 LangGraph 链
在上一篇文章 RAG(Retrieval Augmented Generation,检索增强生成)(2) 中我们将用户输入、检索到的上下文和生成的答案表示为状态对象中的单独键。 除了来自用户和AI的消息之外还可以通过工具消息将检索到的文档内容合并到消息序列中这样我们可以使用一系列消息来表示 RAG 应用程序的状态。具体来说我们将:
把用户输入作为 HumanMessage把向量存储查询作为带有工具调用的 AIMessage把检索到的文档作为 ToolMessage最终响应作为 AIMessage。 这种状态模型非常通用因此 LangGraph 提供了一个内置版本以方便使用
from langgraph.graph import MessagesState, StateGraph
graph_builder StateGraph(MessagesState)将检索步骤转化为工具
利用工具调用与检索步骤交互还有另一个好处即检索查询由我们的大语言模型生成。这在对话环境中尤其重要因为用户查询可能需要基于聊天记录进行情境化。例如考虑以下交流
Human“什么是任务分解”
AI“任务分解涉及将复杂任务分解为更小、更简单的步骤以使代理或模型更易于管理。”
Human“常见的做法是什么”在这种情况下模型可以生成查询例如任务分解的常见方法。 由于大语言模型在生成工具调用时会智能的生成查询那么它将会明显提升查询的准确性。它还可以生成不涉及检索步骤的直接响应例如响应用户的一般问候也可以自动进行指代消解根据上下文自动修改问题把问题中的代词替换成上下文中的内容。
下面我们基于 检索 生成一个 工具
tool(response_formatcontent_and_artifact,parse_docstringTrue) # docstring的内容对agent自动推理影响比较大
def retrieve(query: str):检索与 query参数内容 相关的信息Args:query: 要搜索的字符串。 print(fstart retrieve:{query})# 定义相似度阈值。因为这种相似性检索并不考虑相似性大小如果不限制可能会返回相似性不大的文档 可能会影响问答效果。similarity_threshold 0.8retrieved_docs vector_store.similarity_search_with_score(query, k3)# 根据相似度分数过滤结果filtered_docs [doc for doc, score in retrieved_docs if score similarity_threshold]serialized \n\n.join((fSource: {doc.metadata}\n fContent: {doc.page_content})for doc in filtered_docs)if not serialized:return 抱歉我找不到任何相关信息。, Noneelse:return serialized, filtered_docs上述检索工具完善了以下几个细节
定义了相似度阈值过滤掉相似度太低的文档防止差的结果产生负面影响如果未找到靠谱的文档返回:“抱歉我找不到任何相关信息。”这个结果将会在 生成 阶段被添加到提示词中有助于大语言模型自行推理更合理的回答。
定义节点
我们的链将由三个节点组成
一个处理用户输入的节点要么为检索器生成查询要么直接响应一个用于执行检索步骤的检索器工具的节点一个使用检索到的上下文生成最终响应的节点。 我们在下面构建它们。 请注意我们利用另一个预先构建的 LangGraph 组件 ToolNode它执行该工具并将结果作为 ToolMessage 添加到状态(state)。
# 1: 生成可能包含工具调用tool_call的 AIMessage。
def query_or_respond(state: MessagesState):生成用于检索或响应的工具调用。llm_with_tools llm.bind_tools([retrieve])response llm_with_tools.invoke(state[messages])这里会自动进行指代消解根据上下文自动修改问题把问题中的代词替换成上下文中的内容# MessagesState 将消息附加到 state 而不是覆盖return {messages: [response]}# 2: 执行检索
tools ToolNode([retrieve])# 3: 使用检索到的内容生成响应。
def generate(state: MessagesState):生成回答。# 获取生成的 ToolMessagesrecent_tool_messages []for message in reversed(state[messages]):if message.type tool:recent_tool_messages.append(message)else:breaktool_messages recent_tool_messages[::-1]# 获取 ToolMessages 的内容并格式化为提示词docs_content \n\n.join(doc.content for doc in tool_messages)system_message_content (你是一个负责答疑任务的助手。 使用以下检索到的上下文来回答问题。 如果你不知道答案就说你不知道。 最多使用三句话并保持答案简洁。 \n\nf{docs_content})conversation_messages [messagefor message in state[messages]if message.type in (human, system)or (message.type ai and not message.tool_calls)]prompt [SystemMessage(system_message_content)] conversation_messages# 执行response llm.invoke(prompt)# MessagesState 将消息附加到 state 而不是覆盖return {messages: [response]}构建图
现在我们将节点连接起来将应用程序编译成单个 LangGraph 对象。 第一个节点 query_or_respond 步骤可以“短路”并直接响应用户如果它不生成工具调用。这使我们的应用程序能够支持对话体验。 例如响应可能不需要检索步骤的通用问候语。
def build_graph(llm_model_name):构建 langgraph 链llm ChatOllama(modelllm_model_name,temperature0, verboseTrue)# 串联节点和边构建图graph_builder StateGraph(MessagesState)graph_builder.add_node(query_or_respond)graph_builder.add_node(tools)graph_builder.add_node(generate)graph_builder.set_entry_point(query_or_respond)graph_builder.add_conditional_edges(query_or_respond,tools_condition,{END: END, tools: tools},)graph_builder.add_edge(tools, generate)graph_builder.add_edge(generate, END)graph graph_builder.compile()return graph
见证效果
现在可以定义测试方法
def ask(llm_model_name,question):提问graph build_graph(llm_model_name)for step in graph.stream({messages: [{role: user, content: question}]},stream_modevalues,):step[messages][-1].pretty_print()我们准备两个问题
query1 马的学名是什么它有什么用途
query2 中国有多少个省份现在用各大模型试一下。
qwen2.5
query1 “马的学名是什么它有什么用途” Human Message 马的学名是什么它有什么用途Ai Message
Tool Calls:retrieve (729acad9-b8ae-4ed3-aa12-f48dd431d8ed)Call ID: 729acad9-b8ae-4ed3-aa12-f48dd431d8edArgs:query: 马 学名
start retrieve:马 学名Tool Message
Name: retrieveSource: {row: 2, source: D:\\project\\programming-with-local-large-language-model\\server\\services\\practice\\assert/animals.csv}
Content: 名称: 马
学名: Equus ferus caballus
特点: 速度快、耐力强、与人类有深厚的合作关系
作用: 交通、战马、农业、体育竞技Ai Message 马的学名是Equus ferus caballus。马的用途包括交通、作为战马、用于农业劳动以及参与体育竞技。在生成工具调用(Tool Calls)调用工具检索步骤都很稳妥最终结果也没问题。
query2 “中国有多少个省份” Human Message 中国有多少个省份Ai Message 中国的行政区划中通常指的是34个省级行政区包括23个省、5个自治区、4个直辖市以及香港特别行政区和澳门特别行政区。如果您需要更详细的信息或者有其他问题请告诉我这次生成的工具调用(Tool Calls)为空所以调过了检索和生成步骤直接由大语言模型返回结果了。 值得一提的是虽然大语言模型 在 query_or_respond 函数中生成的 response 对象的工具调用(tool_calls)为空但是 content 是有正确内容的这说明它还可以生成不涉及检索步骤的直接响应。 很棒
llama3.1
query1 “马的学名是什么它有什么用途” Human Message 马的学名是什么它有什么用途Ai Message
Tool Calls:retrieve (c811874c-528f-4f17-878b-774452af14d8)Call ID: c811874c-528f-4f17-878b-774452af14d8Args:query: 王的学名我是常用给为为
start retrieve:王的学名我是常用给为为Tool Message
Name: retrieve抱歉我找不到任何相关信息。Ai Message 马的学名是Equus caballus。马主要用于运输、耕作和骑乘等方面。显然它生成的工具调用(tool_calls)是错误的 retrieve 返回错误信息“抱歉我找不到任何相关信息。”,大模型最终还是利用自己的能力回答了问题。
query2 “中国有多少个省份” Human Message 中国有多少个省份Ai Message
Tool Calls:retrieve (95b0f1ca-9292-4df6-b670-64a267ef1a60)Call ID: 95b0f1ca-9292-4df6-b670-64a267ef1a60Args:query: 中国有有二市综名
start retrieve:中国有有二市综名Tool Message
Name: retrieve抱歉我找不到任何相关信息。Ai Message 中国共有34个省份。按照常理来讲这次不应该工具调用(tool_calls)了结果 llama3.1 还是生成了一个不靠谱的 工具调用(tool_calls)。 看起来 llama3.1 生成中文的工具调用(tool_calls)有问题。
MFDoom/deepseek-r1-tool-calling:7b
query1 “马的学名是什么它有什么用途” Human Message 马的学名是什么它有什么用途Ai Message
Tool Calls:retrieve (9fcc67a2-e78e-489c-81d3-ed50688c4422)Call ID: 9fcc67a2-e78e-489c-81d3-ed50688c4422Args:query: horses scientific name
start retrieve: horses scientific nameTool Message
Name: retrieveSource: {row: 2, source: D:\\project\\programming-with-local-large-language-model\\server\\services\\practice\\assert/animals.csv}
Content: 名称: 马
学名: Equus ferus caballus
特点: 速度快、耐力强、与人类有深厚的合作关系
作用: 交通、战马、农业、体育竞技Ai Message think
好的我现在需要回答用户的问题“马的学名是什么它有什么用途”首先我查看提供的上下文内容。内容中提到“名称: 马”和“学名: Equus ferus caballus”。所以马的学名应该是Equus ferus caballus。接下来关于用途部分内容里列出了几点交通、战马、农业、体育竞技。这些都是马被广泛使用的领域。因此我需要将这些信息整合成一个简洁的回答。最后确保回答不超过三句话并且保持简洁明了。这样用户就能清楚地得到学名和用途的信息。
/think马的学名是Equus ferus caballus。它主要用作交通工具、军事马、农业动物以及体育竞技中的赛马。不知为什么生成的 工具调用(Tool Calls)中的查询是英文当然最终结果不错。
query2 “中国有多少个省份” Human Message 中国有多少个省份Ai Message
Tool Calls:retrieve (3ce0bbec-2612-4e79-85b8-d7f9cbe0d422)Call ID: 3ce0bbec-2612-4e79-85b8-d7f9cbe0d422Args:query: 中国有多少个省份
start retrieve:中国有多少个省份Tool Message
Name: retrieve抱歉我找不到任何相关信息。Ai Message think
嗯用户问的是中国有多少个省份。我记得中国有34个省份对吧不过有时候也会加上特别行政区和台湾地区这样就是36个地方了。但通常在正式统计中不包括香港和澳门所以应该是34个省份。我应该确认一下这个数字是否正确 确保回答准确。
/think中国有34个省份。显然针对这个问题大语言模型不应该生成工具调用。最终检索工具未检索出靠谱的文档大语言模型凭借自己的能力回答了问题。
总结
这次我们实验了一个 通过 是否可以生成工具调用(tool_calls)作为条件来判断是否检索知识库的 RAG(Retrieval Augmented Generation,检索增强生成) 系统。 经过3个大模型的对比实验我们发现在处理中文工具生成这方面qwen2.5 非常稳健。 如果您想自己实现一个包含前端和后端的 RAG 系统从零搭建langchain本地大模型本地矢量数据库的RAG系统 可能对您入门有帮助。 代码
本文涉及的所有代码以及相关资源都已经共享参见
githubgitee 为便于找到代码程序文件名称最前面的编号与本系列文章的文档编号相同。 参考
Build a Retrieval Augmented Generation (RAG) App
感谢您观看祝好运