最近在尝试将大语言模型LLM集成到实际业务中时你是否也遇到了这些挑战模型回答不够精准、无法调用外部工具、缺乏长期记忆导致对话割裂或者想微调自己的模型却对复杂的流程望而却步这些问题正是当前AI应用开发从“玩具”走向“生产”的核心障碍。本文将为你带来一套整合了LangChain、LangGraph、LLaMA-Factory、RAG 和 MCP的实战开发教程。这不是简单的概念介绍而是一个从零到一、可落地的完整解决方案。你将学会如何构建一个具备长期记忆、工具调用、知识检索和私有模型微调能力的智能体Agent系统。无论你是想快速入门AI应用开发还是希望将现有项目升级为更智能的Agent这篇文章都能提供清晰的路径和可复现的代码。1. 背景与核心概念为什么需要这套技术栈在深入代码之前我们有必要理解这套“组合拳”中每个组件的定位和它们协同工作的价值。单独使用任何一个工具都可能遇到瓶颈而将它们有机结合才能构建出真正强大的AI应用。1.1 各组件角色解析LangChain应用开发框架LangChain 是一个用于开发由语言模型驱动的应用程序的框架。它抽象了与LLM交互的复杂性提供了链Chains、代理Agents、记忆Memory等核心概念让你能像搭积木一样构建复杂的AI工作流。简单说它是你组织整个AI应用逻辑的“骨架”和“粘合剂”。LangGraph构建有状态、多步骤的智能体如果说LangChain的Agent是单次决策那么LangGraph就是为Agent赋予了“流程图”和“长期记忆”。它允许你明确定义智能体的状态State和节点Nodes并通过边Edges控制执行流程。这使得构建复杂的、多轮交互的、具备持久记忆的智能体如客服机器人、数据分析助手变得异常清晰和可控。LLaMA-Factory低成本、高效率的模型微调工具直接使用通用大模型如ChatGPT、通义千问往往无法满足特定领域如医疗、法律、金融或私有数据的需求。微调Fine-tuning是解决这一问题的关键。LLaMA-Factory 是一个统一、易用的LLM微调框架支持数十种主流开源模型LLaMA, Qwen, Baichuan等并提供了Web UI极大降低了微调的技术门槛和资源消耗。RAG让模型“拥有”你的知识库检索增强生成Retrieval-Augmented Generation是解决模型“幻觉”和知识过时问题的利器。其核心思想是在回答用户问题前先从你的私有知识库向量数据库中检索相关文档片段然后将这些片段和问题一起交给LLM生成答案。这样答案既利用了LLM强大的理解和生成能力又基于你提供的准确事实。MCP智能体的“手”和“眼睛”模型上下文协议Model Context Protocol是一个新兴的开放协议它定义了AI应用客户端与工具、数据源服务器之间通信的标准方式。通过MCP你可以让智能体安全、标准化地访问外部工具如计算器、搜索引擎、数据库和资源如公司文档、API极大地扩展了智能体的能力边界。1.2 技术栈协同工作流想象一个智能数据分析助手的构建过程知识准备使用RAG框架将公司报表、业务文档存入向量数据库如Chroma作为智能体的“知识库”。能力定制使用LLaMA-Factory基于业务对话数据微调一个专属的Qwen模型让它更懂你的业务术语和风格。智能体编排使用LangGraph定义智能体的状态包含用户问题、历史对话、检索到的知识等和流程先检索再分析最后调用工具绘图。工具集成通过MCP让智能体能够调用Python执行环境进行复杂计算或调用数据库API查询最新数据。应用集成用LangChain将以上所有组件封装成一个统一的链或代理并提供给Web如Flask或API使用。这套组合确保了应用的准确性RAG、专业性微调、复杂性LangGraph和扩展性MCP。2. 环境准备与版本说明在开始实战前请确保你的开发环境已就绪。本文将在一个相对干净的环境中进行推荐使用Python 3.10或3.11。2.1 基础环境与依赖安装我们首先创建一个独立的Python虚拟环境并安装核心依赖。# 1. 创建并进入项目目录 mkdir ai-agent-tutorial cd ai-agent-tutorial # 2. 创建虚拟环境以conda为例也可使用venv conda create -n ai-agent python3.10 -y conda activate ai-agent # 3. 安装核心框架 pip install langchain langchain-community langgraph # 安装Ollama用于本地运行开源模型可选但推荐 # 访问 https://ollama.com/ 下载并安装对应系统的Ollama # 安装后运行ollama pull qwen2.5:7b # 拉取一个常用模型 # 4. 安装向量数据库与RAG相关 pip install chromadb sentence-transformers pypdf # 用于文档加载和向量化 # 5. 安装MCP相关以基础工具为例 pip install mcp[cli] # 安装MCP客户端和CLI工具 # 6. 安装Web框架用于最终演示 pip install flask2.2 关键组件版本说明为了保证教程的稳定性以下列出了撰写本文时测试通过的主要版本。如果你的环境存在冲突可以尝试指定这些版本。# 可以创建一个 requirements.txt 文件 langchain0.2.1 langchain-community0.2.1 langgraph0.2.29 chromadb0.4.24 sentence-transformers2.7.0 flask3.0.3 # 其他依赖版本通常兼容性较好无需严格锁定重要提示AI领域库更新极快部分API可能发生变化。如果遇到问题请优先查阅对应项目的官方文档。本文的代码逻辑和架构思路是核心可适应不同版本。3. 核心组件原理与快速上手在构建完整应用前我们先快速体验每个核心组件的“最小可行产品”MVP理解其基本用法。3.1 LangChain与模型对话的起点LangChain的核心是LCELLangChain Expression Language它允许你以声明式的方式组合组件。# 文件demo_langchain_basic.py from langchain_community.llms import Ollama from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # 1. 初始化模型这里使用本地Ollama服务的Qwen模型 llm Ollama(modelqwen2.5:7b) # 2. 定义提示词模板 prompt ChatPromptTemplate.from_messages([ (system, 你是一个乐于助人的助手。), (user, {input}) ]) # 3. 创建链提示词 - 模型 - 输出解析器 chain prompt | llm | StrOutputParser() # 4. 调用链 response chain.invoke({input: LangChain是什么}) print(response)3.2 LangGraph构建一个有状态的对话循环LangGraph通过“图”来管理状态。下面我们构建一个简单的、能记住对话历史的智能体。# 文件demo_langgraph_memory.py from typing import TypedDict, Annotated import operator from langgraph.graph import StateGraph, END from langchain_community.llms import Ollama from langchain_core.messages import HumanMessage, AIMessage # 1. 定义状态结构 class AgentState(TypedDict): messages: Annotated[list, operator.add] # 关键这是一个累加器自动追加消息 user_input: str # 2. 初始化模型 llm Ollama(modelqwen2.5:7b) # 3. 定义节点函数 def call_model(state: AgentState): 调用模型生成回复 messages state[messages] # 将历史消息和最新输入组合 prompt \n.join([f{msg.type}: {msg.content} for msg in messages[-5:]]) # 只保留最近5条 response llm.invoke(prompt f\nuser: {state[user_input]}\nassistant:) # 将AI回复添加到消息列表 new_message AIMessage(contentresponse) return {messages: [new_message]} def human_input_node(state: AgentState): 模拟用户输入节点实际应用中会从外部获取 # 这里我们简化直接从状态中取出预设输入 user_msg HumanMessage(contentstate[user_input]) return {messages: [user_msg]} # 4. 构建图 workflow StateGraph(AgentState) workflow.add_node(human_input, human_input_node) workflow.add_node(call_model, call_model) # 5. 定义边执行顺序 workflow.set_entry_point(human_input) workflow.add_edge(human_input, call_model) workflow.add_edge(call_model, END) # 6. 编译图 app workflow.compile() # 7. 运行图 initial_state AgentState(messages[], user_input你好我是小明。) result app.invoke(initial_state) print(AI回复:, result[messages][-1].content) # 第二次调用状态中包含了历史 new_state AgentState(messagesresult[messages], user_input我刚才说我叫什么名字) result2 app.invoke(new_state) print(AI回复有记忆:, result2[messages][-1].content)3.3 RAG为模型注入知识我们使用ChromaDB和本地嵌入模型来构建一个简单的RAG系统。# 文件demo_rag_simple.py from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OllamaEmbeddings from langchain.chains import RetrievalQA from langchain_community.llms import Ollama # 1. 准备知识文档这里用字符串模拟实际可从文件加载 knowledge_text 项目Alpha的核心技术是量子压缩算法该算法由张三博士于2023年提出。 主要竞争对手是Beta公司其产品“星云计算平台”市场占有率约为30%。 我们的优势在于能耗降低40%但初期部署成本较高。 with open(knowledge.txt, w, encodingutf-8) as f: f.write(knowledge_text) # 2. 加载并分割文档 loader TextLoader(knowledge.txt) documents loader.load() text_splitter RecursiveCharacterTextSplitter(chunk_size200, chunk_overlap50) splits text_splitter.split_documents(documents) # 3. 创建向量存储使用Ollama的嵌入模型 embeddings OllamaEmbeddings(modelnomic-embed-text) vectorstore Chroma.from_documents(documentssplits, embeddingembeddings, persist_directory./chroma_db) vectorstore.persist() # 持久化 # 4. 创建检索器 retriever vectorstore.as_retriever(search_kwargs{k: 2}) # 返回最相关的2个片段 # 5. 创建RAG链 llm Ollama(modelqwen2.5:7b) qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 简单地将检索到的文档“塞”进提示词 retrieverretriever, return_source_documentsTrue ) # 6. 提问 question 项目Alpha的优势是什么竞争对手是谁 result qa_chain.invoke({query: question}) print(问题:, question) print(答案:, result[result]) print(来源文档:, result[source_documents])4. 完整实战构建企业级智能客服助手现在我们将所有组件融合构建一个具备长期记忆、私有知识库查询和工具调用能力的智能客服助手。这个助手可以回答关于公司产品的问题并能在需要时进行简单计算。4.1 项目结构设计ai-customer-service/ ├── app.py # Flask主应用 ├── agent/ # 智能体核心模块 │ ├── __init__.py │ ├── graph_builder.py # 定义LangGraph工作流 │ ├── knowledge_base.py # RAG知识库管理 │ └── tools.py # MCP工具定义 ├── data/ # 知识库文档 │ └── product_manual.pdf ├── storage/ # 向量数据库存储 │ └── chroma_db/ └── requirements.txt4.2 实现知识库模块RAG首先我们创建一个管理知识库的类负责文档的加载、向量化和检索。# 文件agent/knowledge_base.py import os from langchain_community.document_loaders import PyPDFLoader, TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OllamaEmbeddings from typing import List from langchain.schema import Document class KnowledgeBase: def __init__(self, persist_directory: str ./storage/chroma_db): 初始化知识库 self.persist_directory persist_directory self.embeddings OllamaEmbeddings(modelnomic-embed-text) self.text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap100, separators[\n\n, \n, 。, , , , , , ] ) self.vectorstore None self._init_vectorstore() def _init_vectorstore(self): 初始化或加载已有的向量存储 if os.path.exists(self.persist_directory): print(f加载已有向量数据库: {self.persist_directory}) self.vectorstore Chroma( persist_directoryself.persist_directory, embedding_functionself.embeddings ) else: print(f创建新的向量数据库: {self.persist_directory}) # 初始化为空集合 self.vectorstore Chroma.from_documents( documents[Document(page_content初始化)], embeddingself.embeddings, persist_directoryself.persist_directory ) self.vectorstore.delete_collection() # 删除初始化文档 def add_document(self, file_path: str): 向知识库添加单个文档 if file_path.endswith(.pdf): loader PyPDFLoader(file_path) elif file_path.endswith(.txt): loader TextLoader(file_path) else: raise ValueError(f不支持的文件格式: {file_path}) documents loader.load() splits self.text_splitter.split_documents(documents) if self.vectorstore is None: self.vectorstore Chroma.from_documents( documentssplits, embeddingself.embeddings, persist_directoryself.persist_directory ) else: ids [str(i) for i in range(len(splits))] self.vectorstore.add_documents(documentssplits, idsids) self.vectorstore.persist() print(f已添加文档: {file_path}, 分割为 {len(splits)} 个片段) def search(self, query: str, k: int 3) - List[Document]: 在知识库中搜索相关文档 if self.vectorstore is None: return [] return self.vectorstore.similarity_search(query, kk) def get_retriever(self, k: int 3): 获取检索器用于集成到链中 if self.vectorstore is None: return None return self.vectorstore.as_retriever(search_kwargs{k: k}) # 示例初始化并添加文档 if __name__ __main__: kb KnowledgeBase() # 假设有一个产品手册PDF # kb.add_document(./data/product_manual.pdf) print(知识库模块就绪。)4.3 实现工具模块MCP概念模拟由于完整的MCP服务器部署较复杂我们先用LangChain的Tool接口模拟两个工具计算器和当前时间查询。在实际生产中你可以将这些工具部署为独立的MCP服务器。# 文件agent/tools.py from datetime import datetime from langchain.tools import tool import math tool def calculator(expression: str) - str: 执行数学计算。输入一个数学表达式字符串如 3 5 * 2。 try: # 警告在生产环境中使用eval有安全风险这里仅作演示。 # 应使用更安全的表达式解析库如 ast.literal_eval 或 numexpr。 result eval(expression, {__builtins__: {}}, {**math.__dict__}) return f计算结果: {expression} {result} except Exception as e: return f计算错误: {e} tool def get_current_time(timezone: str Asia/Shanghai) - str: 获取指定时区的当前时间。时区默认为Asia/Shanghai。 from pytz import timezone as tz import pytz try: tz_obj pytz.timezone(timezone) current_time datetime.now(tz_obj).strftime(%Y-%m-%d %H:%M:%S %Z%z) return f{timezone} 的当前时间是: {current_time} except pytz.exceptions.UnknownTimeZoneError: return f错误未知时区 {timezone}。请提供有效的时区名称如 UTC, America/New_York。 # 工具列表方便导入 ALL_TOOLS [calculator, get_current_time] if __name__ __main__: print(calculator.invoke(3 5 * 2)) print(get_current_time.invoke({}))4.4 构建智能体工作流LangGraph这是最核心的部分我们将定义一个具备工具调用、知识检索和记忆的智能体图。# 文件agent/graph_builder.py from typing import TypedDict, Annotated, List, Union import operator from langgraph.graph import StateGraph, END from langgraph.prebuilt import ToolExecutor, ToolInvocation from langchain_community.llms import Ollama from langchain_core.messages import HumanMessage, AIMessage, ToolMessage from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from .knowledge_base import KnowledgeBase from .tools import ALL_TOOLS # 1. 定义智能体状态 class AgentState(TypedDict): messages: Annotated[List[Union[HumanMessage, AIMessage, ToolMessage]], operator.add] knowledge_context: str # 从知识库检索到的上下文 user_query: str # 当前用户问题 # 2. 初始化核心组件 llm Ollama(modelqwen2.5:7b, temperature0.1) kb KnowledgeBase() tools ALL_TOOLS tool_executor ToolExecutor(tools) # 3. 定义提示词模板 system_prompt 你是一个专业的企业客服助手拥有公司产品知识库并且可以调用工具。 请遵循以下步骤回答用户问题 1. 如果问题与公司产品、服务、政策相关请优先使用提供的knowledge_context来回答。 2. 如果问题涉及计算、获取时间等请调用合适的工具。 3. 如果knowledge_context中没有相关信息或者问题与公司无关请根据你的通用知识礼貌回答。 4. 保持回答简洁、准确、有帮助。 当前知识上下文 {knowledge_context} prompt ChatPromptTemplate.from_messages([ (system, system_prompt), MessagesPlaceholder(variable_namemessages), # 自动填充历史对话消息 ]) # 4. 定义图节点 def retrieve_knowledge(state: AgentState): 节点从知识库检索相关信息 query state[user_query] relevant_docs kb.search(query, k2) context \n.join([doc.page_content for doc in relevant_docs]) return {knowledge_context: context} def call_model(state: AgentState): 节点调用模型决定下一步行动回复或调用工具 # 绑定工具描述给模型 llm_with_tools llm.bind_tools(tools) # 准备输入消息 messages state[messages] # 构建提示词 formatted_prompt prompt.invoke({ knowledge_context: state[knowledge_context], messages: messages }) # 调用模型 response llm_with_tools.invoke(formatted_prompt.to_messages()) # 将模型的响应添加到消息历史 return {messages: [response]} def execute_tools(state: AgentState): 节点执行模型选择的工具 messages state[messages] last_message messages[-1] tool_calls last_message.tool_calls if hasattr(last_message, tool_calls) else [] if not tool_calls: raise ValueError(没有需要执行的工具调用) results [] for tool_call in tool_calls: # 执行工具 result tool_executor.invoke(tool_call) # 创建工具结果消息 results.append(ToolMessage(contentstr(result), tool_call_idtool_call[id])) return {messages: results} def should_continue(state: AgentState) - str: 路由函数决定下一步是调用工具还是结束 messages state[messages] last_message messages[-1] # 如果模型的最新消息包含工具调用则去执行工具 if hasattr(last_message, tool_calls) and last_message.tool_calls: return execute_tools # 否则结束本轮对话 return END # 5. 构建图 workflow StateGraph(AgentState) # 添加节点 workflow.add_node(retrieve, retrieve_knowledge) workflow.add_node(call_model, call_model) workflow.add_node(execute_tools, execute_tools) # 设置入口点 workflow.set_entry_point(retrieve) # 添加边 workflow.add_edge(retrieve, call_model) workflow.add_conditional_edges( call_model, should_continue, # 根据此函数返回值决定路由 { execute_tools: execute_tools, END: END } ) workflow.add_edge(execute_tools, call_model) # 工具执行后返回模型节点重新思考 # 编译图 agent_graph workflow.compile() # 6. 封装一个易用的调用函数 def run_agent_query(user_input: str, chat_history: list None): 运行智能体处理一次用户查询 if chat_history is None: chat_history [] # 准备初始状态 initial_state: AgentState { messages: chat_history [HumanMessage(contentuser_input)], knowledge_context: , user_query: user_input } # 执行图 final_state agent_graph.invoke(initial_state) # 提取最终的AI回复消息 final_messages final_state[messages] ai_response None for msg in reversed(final_messages): if isinstance(msg, AIMessage) and not hasattr(msg, tool_calls): ai_response msg.content break return ai_response, final_state[messages] if __name__ __main__: # 测试 response, history run_agent_query(你们产品的主要优势是什么) print(测试1 - 知识库问题:) print(问题你们产品的主要优势是什么) print(回答, response) print(- * 50) response2, history run_agent_query(请计算一下(15 27) * 3 等于多少, history) print(测试2 - 工具调用问题:) print(问题请计算一下(15 27) * 3 等于多少) print(回答, response2)4.5 创建Web应用接口Flask最后我们用一个简单的Flask应用将智能体包装成Web API方便前端调用。# 文件app.py from flask import Flask, request, jsonify, render_template_string from agent.graph_builder import run_agent_query import os app Flask(__name__) # 简单的内存存储对话历史生产环境应使用数据库 conversation_sessions {} HTML_TEMPLATE !DOCTYPE html html head title企业智能客服助手/title style body { font-family: Arial; max-width: 800px; margin: 40px auto; } #chatbox { border: 1px solid #ccc; height: 400px; overflow-y: scroll; padding: 10px; margin-bottom: 10px; } .user { color: blue; text-align: right; margin: 5px; } .bot { color: green; margin: 5px; } input { width: 70%; padding: 10px; } button { padding: 10px 20px; } /style /head body h2 企业智能客服助手演示/h2 div idchatbox/div input typetext iduserInput placeholder输入您的问题... onkeypresshandleKeyPress(event) button onclicksendMessage()发送/button button onclickclearChat()清空对话/button script let sessionId demo_session; function addMessage(sender, text) { const chatbox document.getElementById(chatbox); const msgDiv document.createElement(div); msgDiv.className sender; msgDiv.innerHTML strong${sender}:/strong ${text}; chatbox.appendChild(msgDiv); chatbox.scrollTop chatbox.scrollHeight; } function sendMessage() { const input document.getElementById(userInput); const text input.value.trim(); if (!text) return; addMessage(用户, text); input.value ; fetch(/chat, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({session_id: sessionId, message: text}) }) .then(r r.json()) .then(data { addMessage(助手, data.response); }) .catch(err { addMessage(系统, 请求出错: err); }); } function handleKeyPress(event) { if (event.key Enter) sendMessage(); } function clearChat() { document.getElementById(chatbox).innerHTML ; fetch(/clear, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({session_id: sessionId}) }); } // 初始问候 window.onload () addMessage(助手, 您好我是企业客服助手可以回答产品问题、进行计算或查询时间。请问有什么可以帮您); /script /body /html app.route(/) def index(): return render_template_string(HTML_TEMPLATE) app.route(/chat, methods[POST]) def chat(): 处理聊天请求 data request.json session_id data.get(session_id, default) user_message data.get(message, ) if not user_message: return jsonify({error: 消息不能为空}), 400 # 获取或初始化该会话的历史 history conversation_sessions.get(session_id, []) # 调用智能体 response, new_history run_agent_query(user_message, history) # 更新会话历史注意控制长度避免上下文过长 conversation_sessions[session_id] new_history[-10:] # 只保留最近10轮 return jsonify({response: response}) app.route(/clear, methods[POST]) def clear_history(): 清空指定会话的历史 data request.json session_id data.get(session_id, default) if session_id in conversation_sessions: del conversation_sessions[session_id] return jsonify({status: cleared}) if __name__ __main__: # 初始化知识库假设已有文档 # from agent.knowledge_base import KnowledgeBase # kb KnowledgeBase() # kb.add_document(./data/product_manual.pdf) print(启动智能客服助手服务...) app.run(host0.0.0.0, port5000, debugTrue)4.6 运行与验证启动服务cd ai-customer-service python app.py访问http://localhost:5000即可看到Web界面。功能测试知识问答在输入框提问“我们产品的优势是什么”。智能体会先从knowledge_context中检索如果你已添加文档再结合模型生成回答。工具调用提问“计算123乘以456是多少”或“现在纽约时间是几点”。观察日志和回复智能体会调用相应的工具并返回结果。多轮对话连续提问“我叫张三”、“我的名字是什么”测试LangGraph的记忆功能。5. 进阶使用LLaMA-Factory微调专属模型前面的示例使用了通用的Qwen模型。为了让助手更贴合你的业务语气和知识可以使用LLaMA-Factory对开源模型进行微调。5.1 LLaMA-Factory 环境搭建# 1. 克隆仓库 git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory # 2. 安装依赖 pip install -r requirements.txt # 3. 启动Web UI最方便的方式 python src/train_web.py访问http://localhost:7860即可打开微调界面。5.2 准备微调数据微调需要准备一个JSON格式的数据集。例如创建一个data/train.json[ { instruction: 介绍产品Alpha的核心功能。, input: , output: 产品Alpha是一款基于量子压缩算法的企业级数据压缩解决方案核心功能包括实时无损压缩、跨平台部署和超低能耗管理主要优势是可降低40%的存储能耗。 }, { instruction: 客户抱怨部署成本高如何回应, input: , output: 理解您的顾虑。我们的产品虽然初期部署成本相对较高但其带来的长期节能效益约40%能耗降低通常在18个月内即可覆盖初始投资。此外我们提供灵活的分期付款方案和详尽的ROI分析报告。 } // ... 更多对话样本 ]5.3 在Web UI中微调模型选择在“Model”标签页选择或输入一个模型名称如Qwen/Qwen2.5-7B-Instruct。数据配置在“Dataset”标签页上传你的train.json文件并设置对话模板如qwen。训练参数在“Training”标签页选择微调方法如LoRA设置学习率、训练轮数等初学者可先用默认值。开始训练点击“Start”按钮开始训练。根据数据量和硬件可能需要几十分钟到数小时。导出模型训练完成后可以在“Export”标签页将适配器权重合并到原模型或直接导出为GGUF等格式。5.4 在智能体中使用微调后的模型假设你使用Ollama服务可以将微调后的模型导入Ollama# 将LLaMA-Factory输出的模型文件夹例如./saves/qwen2.5-7b-lora转换为Ollama支持的Modelfile格式 # 具体步骤请参考LLaMA-Factory和Ollama的文档通常涉及创建一个Modelfile并build。 # 假设新模型名为 my-tuned-qwen ollama pull my-tuned-qwen然后在graph_builder.py中将模型名称替换即可# 修改这一行 llm Ollama(modelmy-tuned-qwen, temperature0.1)6. 常见问题与排查思路在集成和运行过程中你可能会遇到以下问题问题现象可能原因排查思路与解决方案Ollama服务连接失败Ollama未启动或模型未下载1. 运行ollama serve启动服务。2. 运行ollama list检查模型是否存在。3. 使用curl http://localhost:11434/api/tags测试API连通性。LangGraph图编译或运行报错状态State定义不匹配或节点函数返回值错误1. 检查AgentState的TypedDict定义确保Annotated类型正确。2. 确保每个节点函数返回一个字典其键是状态字段的子集。3. 使用print或日志输出每个节点的输入和输出进行调试。RAG检索结果不相关文档分割策略不佳或嵌入模型不匹配1. 调整RecursiveCharacterTextSplitter的chunk_size和chunk_overlap。2. 尝试不同的嵌入模型如text-embedding-3-small需调用API或BAAI/bge-small-zh。3. 检查检索器search_kwargs中的k值。工具调用失败或模型不调用工具工具描述不清晰或模型能力不足1. 确保工具函数有清晰的文档字符串docstring这是模型理解工具用途的关键。2. 在提示词system_prompt中明确指导模型何时调用工具。3. 尝试使用能力更强的模型如qwen2.5:14b或llama3.2。Flask应用上下文过长错误LangGraph状态中积累的消息过多1. 在app.py的/chat接口中我们已限制历史消息长度new_history[-10:]。2. 可考虑实现更复杂的记忆管理如摘要式记忆。LLaMA-Factory训练失败显存不足、数据格式错误、依赖冲突1. 尝试更小的模型或使用QLoRA等低资源微调方法。2. 严格检查训练数据JSON格式。3. 在虚拟环境中严格按照requirements.txt安装依赖。MCP工具连接问题MCP服务器未运行或协议版本不兼容1. 确保MCP服务器已正确启动并监听指定端口。2. 检查客户端如LangGraph使用的MCP协议版本是否与服务器兼容。3. 查看服务器日志以获取连接错误信息。7. 最佳实践与工程建议将上述演示项目用于生产环境还需要考虑以下方面模型部署与优化生产级服务考虑使用vLLM、TGI(Text Generation Inference) 或OpenAI-compatible API来部署模型以获得更好的吞吐量和并发支持。模型缓存对频繁使用的提示词模板或嵌入结果进行缓存减少重复计算。流式输出对于长文本生成实现流式响应Server-Sent Events以提升用户体验。知识库管理增量更新实现知识库文档的增量更新和去重机制避免全量重建。多源支持扩展KnowledgeBase类以支持更多文档类型Word, Excel, HTML, 数据库。元数据过滤在检索时除了语义相似度还应支持基于来源、日期等元数据的过滤。智能体鲁棒性超时与重试为模型调用和工具调用设置超时和重试机制。异常处理在图中的每个节点添加完善的异常捕获和降级处理逻辑例如工具调用失败时返回友好提示而不是崩溃。输入验证与清理对用户输入进行严格的验证和清理防止提示词注入攻击。可观测性与监控全面日志记录每个用户查询、检索到的文档、调用的工具、模型响应和耗时。链路追踪使用OpenTelemetry等工具对智能体的完整调用链路进行追踪便于排查问题。关键指标监控请求量、响应延迟、Token消耗、工具调用成功率等业务和技术指标。安全与权限工具沙箱对calculator这类执行动态代码的工具必须在严格隔离的沙箱环境中运行。访问控制为不同的工具和知识库文档设置访问权限例如某些内部文档只能被特定部门的员工查询。内容审核对模型的输入和输出添加内容安全过滤层。架构解耦将智能体核心LangGraph、知识库RAG、工具MCP Server、模型服务LLM部署为独立的微服务通过API或消息队列通信。这提高了系统的可维护性、可扩展性和容错能力。通过本教程你不仅学会了如何组合使用 LangChain、LangGraph、RAG、MCP 和 LLaMA-Factory 这些强大的工具更重要的是掌握了构建一个可记忆、可检索、可扩展、可定制的AI智能体的完整方法论。从简单的对话链到复杂的有状态工作流从通用模型到领域专家这套技术栈为你提供了将大模型能力深度集成到业务中的全栈解决方案。下一步你可以尝试接入更真实的业务数据、开发更复杂的MCP工具或者探索多智能体协作的LangGraph高级特性持续迭代你的AI应用。