☰
RAG与Agent开发实战:用LangChain和LangGraph构建AI应用
2026/9/26 18:58:17 网站建设 项目流程

做 AI Agent 相关的开发,最容易出现的一种现象是:教程收藏了几十个,概念名词背得滚瓜烂熟,但一打开 IDE 就不知道从哪里写起。RAG、Agent、LangChain、LangGraph、Embedding、向量库、工具调用……每个字都认识,连起来却不知道它们之间到底是什么关系。

这篇文章想解决的,就是这个断层。

我会从零开始,把 RAG(检索增强生成)和智能代理(Agent)这两条主流技术路线拆开讲清楚,然后用 LangChain 和 LangGraph 各自写一个最小可运行示例。你不需要有很深的机器学习背景,只要会 Python,能装依赖,就能跟着跑通。读完这篇文章,你应该能回答这几个问题:RAG 的完整链路是什么?Agent 和普通 API 调用有什么区别?LangGraph 和 LangChain 到底是什么关系?以及最关键的一条——在真实项目里,你究竟该用 RAG、Agent 还是 Agentic RAG。

先说一个我的判断:LangChain 的 API 变化很快,但它的核心抽象——把 LLM、提示词、文档、检索、外部工具组合成一条可编排的链路——并没有过时。真正值钱的不是记住某个类名,而是理解链路本身。

1. 这篇文章真正要解决的问题

过去一年里,每次有同学问我"AI Agent 该怎么入门",我的回答都不是甩资料链接,而是反问一句:你是想做一个能回答私有知识问题的机器人,还是想让 AI 自主调用工具完成多步任务?

这两个目标,对应的是两条不同的技术路线。

如果要做知识问答,比如"帮我总结这份 30 页合同里的风险条款",那么核心是 RAG:把文档拆碎、向量化、存进向量库,用户提问时先检索相关片段,再交给大模型生成回答。这条路线解决的是大模型"不知道你的私有数据"和"容易编造事实"这两个问题。

如果要做任务执行,比如"把这家店铺过去 30 天的订单按品类汇总,生成一张图表发给我",那么核心是 Agent:大模型先理解意图,再把任务拆成多个步骤,每一步都可能调用一个外部工具,然后把工具结果带回来继续推理。这条路线解决的是大模型"只能聊天、不能行动"的边界问题。

而 LangChain 在整个故事里的位置,是一个中间层框架。它把大模型、向量库、文档加载器、提示词模板、工具调用这些散落的零件,统一成一套 Python 对象和编排方式。这样你就不用给每家 API 单独写胶水代码。

这篇文章会先走通 RAG 的最小链路,再基于 RAG 扩展出 Agent 工具调用,最后用 LangGraph 把 Agent 的流程改造成显式状态图。这样安排是有意的:RAG 是你最容易在真实业务里落地的第一站,Agent 是第二步,LangGraph 则是让复杂 Agent 可维护的工程化选择。

2. RAG、Agent、LangChain 与 LangGraph 的核心概念

2.1 RAG:给大模型配一个"外接知识库"

RAG 的全称是 Retrieval-Augmented Generation,检索增强生成。它不是什么高深算法,而是一种工程架构思路。

传统的 LLM 问答,是你把问题直接丢给模型,模型靠训练时记住的知识来回答。问题有两个:一是知识有截止日期,二是模型对细粒度、私有化的内容几乎一无所知。你问它"我们公司内部系统的登录流程是什么",它只能编一个看起来合理的答案给你。

RAG 的解决方式是:在模型回答之前,先从一个外部知识库中检索出与问题最相关的若干片段,把这些片段和原始问题一起塞进提示词,让模型"参考着资料回答"。

它的核心链路可以拆成五个环节:

  • 文档加载:把 PDF、Word、Markdown、HTML 等原始文件读成纯文本。
  • 文本分块:把长文本切成长度合适的片段。切得太长,检索不精准,还容易超过模型上下文限制;切得太短,语义不完整,模型没法理解。
  • 向量化:用 Embedding 模型把每个文本片段变成一个高维向量,向量空间里语义相近的文本距离更近。
  • 向量检索:用户提问时,把问题也向量化,然后在向量库里搜索最相似的 K 个片段。
  • 融合生成:把检索到的片段作为上下文,拼进提示词,交给大模型生成答案。

你关心的"RAG 知识库指标",本质上就是逐环节评估:检索环节看召回率和准确率,生成环节看忠实度和答案相关性,整体效果可以用端到端的问答评测集来衡量。这部分我在第 7 节展开。

2.2 Agent:让大模型从"回答问题"变成"完成动作"

Agent 和 RAG 解决的完全不是同一个问题。

RAG 的最终产物是一段文字答案;Agent 的最终产物是一个结果,这个结果可能是调用了某个 API、写入了数据库、发送了一封邮件,也可能只是经过多步推理后确定"不需要调用任何工具,直接回答"。

实现 Agent 的主流方式,是利用大模型的工具调用能力(Function Calling / Tool Calling)。你可以定义一组工具,每个工具有名称、描述、参数结构。模型收到用户请求后,会先判断"这个问题需不需要调用工具",如果需要,就返回一个结构化的调用请求:工具名+参数。你的程序负责真正执行这个工具,把执行结果返回给模型,模型继续决定下一步做什么。

所以 Agent 的本质,是一个"循环":

用户输入 -> 模型推理 -> (需要工具?)-> 执行工具 -> 把结果喂回模型 -> 再次推理 -> …… -> 给出最终回复

注意这个循环中的每一步都是模型自主决策的。这意味着,你的程序可能走了一个没法预判的路径。这也是 Agent 比普通程序更难调试的原因——你需要有日志、有追溯能力,最好还有超时和中断机制。

2.3 LangChain 与 LangGraph:框架和编排器

LangChain 是一个开发框架,它提供了一套统一的接口来操作大模型、提示词、文档、向量库和工具。它的价值在于"标准化":你换一个向量库、换一个大模型供应商,不需要重写整条链路。

LangGraph 是 LangChain 生态里的一个图编排库,它解决的问题是 Agent 流程的可控性。

老的 LangChain Agent 用的是AgentExecutor,内部是一个黑盒循环:它会一直循环调用工具,直到模型认为任务完成,或者达到最大迭代次数。这个抽象很方便,但问题是不透明——你不知道它现在执行到哪一步了,想在中间插入一个人工审核、想加一个"步骤超时自动终止",都比较困难。

LangGraph 把 Agent 的流程建模成一张有向图。节点是"做什么",边是"下一步去哪里",状态是节点之间传递的数据。你可以显式写出每个分支,也可以在任何两个节点之间插入新的处理逻辑。所以"LangGraph 和 LangChain 的区别"严格来说不是互相替代,而是 LangGraph 继承了 LangChain 的消息、模型、工具抽象,但在流程控制层面走得更工程化。

我用一个类比解释:LangChain 是标准零件库,LangGraph 是图纸,AgentExecutor 是图纸里的一种固定流水线。你的业务场景越复杂,越需要自己画图纸,而不是照搬固定流水线。

3. 环境准备与前置条件

在开始写代码之前,先把环境准备好。本文的示例基于 Python,版本请以实际项目为准,但建议使用 Python 3.10 或更高版本,避免依赖冲突。

需要说明的是,LangChain 以及相关组件版本更新非常快,接口变动也频繁。本文给出的代码是当前常见写法的演示,如果你的环境版本不同,优先以官方文档和实际报错为准。更重要的是理解链路,而不是死记 API。

3.1 创建虚拟环境

推荐用 venv 或 conda 创建独立虚拟环境,避免污染系统 Python。

python -m venv .venv source .venv/bin/activate # Windows 下使用 .venv\Scripts\activate

3.2 安装依赖

本文会用到以下几个包:

  • langchain:核心框架。
  • langchain-openai:OpenAI 兼容接口的封装。
  • langchain-community:社区贡献的组件,比如文档加载器。
  • langchain-text-splitters:文本分块器。
  • langchain-core:核心抽象,比如 Prompt、OutputParser。
  • chromadb:本地向量数据库。
  • langgraph:LangChain 官方图编排库。

安装命令如下:

pip install langchain langchain-openai langchain-community langchain-text-splitters langchain-core chromadb langgraph

如果你使用的是国产大模型或其他提供 OpenAI 兼容接口的服务,也可以只调整模型服务地址和 API Key,整体代码结构不变。

3.3 配置模型 API

本文示例使用 OpenAI 兼容接口,通过环境变量读取密钥,不要把密钥写死在代码里。

export OPENAI_API_KEY="你的API密钥" export OPENAI_BASE_URL="https://api.openai.com/v1" # 如果使用兼容服务,改成对应地址

如果你希望完全本地运行,也可以使用 Ollama 加载本地模型,然后通过langchain_ollama的ChatOllama和OllamaEmbeddings接入。思路完全相同,只差模型初始化这一段。

4. 核心流程拆解:RAG 五步链路

在写完整代码之前,我先把 RAG 每一步的关键决策讲清楚,因为大部分运行效果不好,问题都出在这些细节上,而不是代码本身。

4.1 文档加载:干净文本是第一优先级

文档加载看起来很基础,其实是整个 RAG 里最容易被低估的环节。PDF 扫描件、有复杂排版的 Word、带大量广告的网页,加载出来往往是一堆乱码或无意义字符。你要做的第一件事是尽量拿到"干净的纯文本",而不是把注意力全放在后面的向量检索上。

常见工具:

  • TextLoader:读取 txt 和 Markdown。
  • PyPDFLoader或PyMuPDFLoader:读取 PDF。
  • BSHTMLLoader:读取 HTML 网页。
  • 自研解析服务:针对复杂 PDF 或扫描件,可能需要 OCR 能力。

判断加载是否成功,不要看文件数量,要看加载后的文本内容。抽出前 500 个字符扫一眼,确认结构完整、编码正常,再继续。

4.2 文本分块:大小和重叠度要一起调

分块策略是 RAG 检索质量最敏感的杠杆之一。理论上,chunk 越短,检索越精准,但单块携带的上下文越少;chunk 越长,单块上下文越完整,但可能混入无关信息,导致召回结果跑偏。

RecursiveCharacterTextSplitter是多数场景下的默认选择。它的逻辑是:按照一组分隔符(比如换行符、句号、空格)递归切分,优先在语义边界处切断,尽量不让一句话被拦腰截断。

通常建议先设置chunk_size=500, chunk_overlap=50作为起点,然后根据实际问答效果调整。chunk 大小没有绝对标准,和你文档的语言、格式、上下文密度都有关系。中文文档建议多关注分隔符配置,默认分隔符对中文的语义边界识别效果一般。

4.3 向量化与向量库:模型和距离度量要匹配

Embedding 模型负责把文本变成向量。选模型时要注意两个点:一是模型对中文的支持程度,二是向量维度是否满足后续应用的兼容要求。

向量库方面,Chroma 是本地开发的轻量选择,不需要额外启动服务,能快速验证链路。生产环境通常考虑 Milvus、Weaviate、Elasticsearch 等更成熟的方案,但原理一致。

距离度量通常用余弦相似度。需要注意:不同向量库的默认相似度度量可能不同,切换向量库时,要先确认你的查询和文档向量是否在使用同一种度量方式,否则会出现"相似度很高但语义完全不相关"的异常现象。

4.4 检索:召回和重排是两件事

基础检索是"先向量相似度取 TopK"。TopK 太小,可能漏掉关键文本;太大,噪声信息会把模型带偏。一般先设 4 到 6。

检索精准度不够时,不要急着调大 TopK,先做这两件事:

  • 查询改写:用户的问题往往很口语化,和文档里的书面表达不完全匹配。可以先用一个小模型把问题改写成更适合检索的关键词组合,再去做向量检索。
  • 召回后重排(ReRank):先召回 20 条,再用一个专门的重排模型(Cross-Encoder)对召回结果重新打分,把真正相关的排到前面。这是目前提升 RAG 效果最立竿见影的手段之一。

4.5 融合生成:提示词要约束引用

最后一步是把检索到的文本和用户问题一起交给大模型。这里最容易出现的问题是:模型仍然依据自己的内部知识自由发挥,忽略了你给的资料。

所以提示词里一定要写清楚"仅根据提供的上下文回答,不要使用内部知识;如果上下文中没有答案,直接回答不知道"。

此外,强烈建议要求模型在回答中标注引用了哪段上下文。这样不仅方便用户核查,也方便你定位"为什么答错了"——是检索没召回相关内容,还是模型没正确引用。

5. 完整示例代码:实现一个最小可用的 RAG 问答链路

现在来看代码。这是一个可以直接运行的最小 RAG 原型,文件结构如下:

rag_demo/ ├── knowledge.txt # 你的知识文档 ├── rag_chain.py # RAG 主程序 └── .env # API 密钥配置(不要提交到 Git)

先准备一份测试文档knowledge.txt,内容随意,但要尽量包含一些"模型内部知识不太可能知道"的信息,这样才能看出 RAG 的效果。示例内容可以是你自己项目的说明文档、内部流程规范等。

接下来是rag_chain.py:

# 文件路径:rag_demo/rag_chain.py from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain_community.vectorstores import Chroma from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough # 1. 加载文档 loader = TextLoader("knowledge.txt", encoding="utf-8") documents = loader.load() # 2. 文本分块 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", " ", ""], ) chunks = text_splitter.split_documents(documents) print(f"文档已切分为 {len(chunks)} 个片段") # 3. 向量化并写入向量库 embedding_model = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = Chroma.from_documents( documents=chunks, embedding=embedding_model, collection_name="demo_collection", persist_directory="./chroma_db", ) # 4. 构建检索器 retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) # 5. 构建提示词 prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个严谨的问答助手。请仅根据以下上下文回答用户问题," "不要使用你的内部知识。如果上下文中没有足够信息,请直接回答" "“根据提供的资料无法回答”。\n\n上下文:\n{context}"), ("human", "问题:{question}"), ]) # 6. 构建 LLM llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) # 7. 组装 RAG 链路 def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) # 8. 执行问答 if __name__ == "__main__": question = "根据提供的资料,该项目的部署步骤是什么?" result = rag_chain.invoke(question) print("问题:", question) print("答案:", result)

代码说明:

  • OpenAIEmbeddings(model="text-embedding-3-small")使用的是 OpenAI 的向量模型,如果你接入的是兼容服务,只需调整模型名和 API 地址。
  • Retriever默认按向量相似度返回 TopK=4 的文本片段。format_docs负责把这些片段拼成提示词里的上下文。
  • 使用 LCEL(LangChain Expression Language)组合链路,可读性比传统 Chain 好,也便于后续插入日志和中间步骤。
  • 整个链路是一个 Runnable,LCEL 会自动把上一步的输出传给下一步。

运行方式:

python rag_chain.py

如果一切正常,你会先看到切分出的片段数量,然后看到基于文档内容生成的答案。把knowledge.txt换成你自己的内部文档,这个最小问答机器人就基本可用了。

6. 从 RAG 到 Agent:构建会调用工具的智能代理

RAG 跑通之后,下一步就是 Agent。

请你先想一个场景:用户问"今天是几号?",你希望模型能调用系统时间工具,而不是猜测一个日期;用户问"12 的 17 次方等于多少?",你希望模型调用计算器,而不是硬答。

这时候就需要给模型挂上工具。下面是一个使用 LangChain 工具调用接口实现的最小 Agent。

# 文件路径:agent_demo/basic_agent.py from datetime import datetime from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langchain_core.prompts import ChatPromptTemplate from langchain.agents import create_tool_calling_agent, AgentExecutor # 工具 1:获取当前时间 @tool def get_current_time() -> str: """获取当前系统的日期和时间。当你被问到日期、时间、今天是几号时,使用这个工具。""" return datetime.now().strftime("%Y-%m-%d %H:%M:%S") # 工具 2:计算数学表达式 @tool def calculate(expression: str) -> str: """计算数学表达式的值。输入应该是一个数学表达式,例如 '12 ** 3'。 注意:这个工具仅用于演示,生产环境不要直接使用 eval。""" return str(eval(expression, {"__builtins__": {}}, {})) # 初始化模型 llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) # 构造 Agent 提示词 prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个智能助手,需要根据用户问题判断是否调用工具。" "如果问题需要外部信息才能回答,你必须先调用工具。"), ("human", "{input}"), ("placeholder", "{agent_scratchpad}"), ]) # 创建 Agent tools = [get_current_time, calculate] agent = create_tool_calling_agent(llm, tools, prompt) executor = AgentExecutor(agent=agent, tools=tools, verbose=True) if __name__ == "__main__": result = executor.invoke({"input": "今天是几号?请使用工具确认后回答"}) print("最终回答:", result["output"])

运行这段代码,你会看到 Agent 的思考过程:它先调用get_current_time,拿到时间字符串,再把它组织成最终回答。verbose=True会把这一过程打印出来,这是学习 Agent 调试最重要的入口。

这里有个容易踩的坑:工具描述写得不好,模型就不调用工具。get_current_time的 docstring 里明确写了"当你被问到日期、时间、今天是几号时,使用这个工具",这样模型才能准确匹配意图。

calculate示例中使用了eval,仅用于演示。真实项目中,如果要执行用户表达式,一定要使用安全沙箱或专门的计算库,否则会有严重的安全风险。

7. 使用 LangGraph 编排 Agent:状态图让流程可控

理解了基础 Agent 之后,你应该问一个问题:如果我的 Agent 有 10 个工具,有分支逻辑,有中途需要人来确认的步骤,AgentExecutor这种黑盒循环还够用吗?

答案是:不太够。这时候需要用 LangGraph 把流程显式画出来。

我用一个最小示例说明 LangGraph 的核心用法。这个示例没有复杂的循环,只演示"节点 + 状态 + 边"的基本模型。真正的 Agent 通常是一个带循环的图:模型节点 -> 工具节点 -> 如果还需要工具,回到模型节点;否则结束。

# 文件路径:agent_demo/langgraph_basic.py from typing import TypedDict from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, START, END llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) # 定义节点之间传递的 State class State(TypedDict): question: str answer: str # 定义一个生成答案的节点 def generate_answer(state: State): response = llm.invoke(f"请用一句话回答:{state['question']}") return {"answer": response.content} # 建立状态图 graph = StateGraph(State) # 添加节点 graph.add_node("generate", generate_answer) # 添加边 graph.add_edge(START, "generate") graph.add_edge("generate", END) # 编译图 app = graph.compile() # 执行 if __name__ == "__main__": result = app.invoke({"question": "LangGraph 是什么?"}) print("答案:", result["answer"])

LangGraph 的编程心智模型和 LangChain 完全不同。LangChain 的 Chain 是线性的"管道",LangGraph 是"图"——你需要在脑子里面先画清楚:

  • 状态(State):每个节点共享的数据结构。
  • 节点(Node):一个普通 Python 函数,输入整个 State,返回要更新的部分。
  • 边(Edge):从哪个节点到哪个节点。
  • 条件边(Conditional Edge):根据当前状态决定下一步去哪个节点。

这个例子里还没有"循环",但你可以明显感觉到,如果想加一个"先检索、再生成、生成质量不合格就重新生成"的逻辑,LangGraph 的表达会比AgentExecutor清晰得多。

真实工程项目中,我建议的路线是:先用AgentExecutor快速验证"工具调用"这件事能不能跑通,等工具数量变多、流程分支变复杂、需要插入人工审核或超时控制时,再迁移到 LangGraph。

8. 运行结果与效果验证:不要只看"答对了"

很多新手验证 RAG 效果,就是问一两个问题,看到答案通顺就认为做好了。这是最大的误区。

一个可维护的 RAG 知识库,需要从多个维度分别评估。你不能用一条问题代表所有场景,也不能因为答案文字通顺就判断检索没问题。

8.1 检索质量指标

  • Recall@K:前 K 条检索结果里,有多少条是真正相关的。
  • Precision@K:前 K 条结果里,有多少比例是相关的。
  • MRR(Mean Reciprocal Rank):第一个正确答案排在第几,反应"最相关的那条有没有排在最前面"。
  • NDCG:考虑位置因素的排序质量指标,越靠前的相关结果权重越高。

这些指标的作用,是把"检索不好"从"生成不好"中剥离出来。如果 Recall 低,说明你的分块或向量检索有问题,优化生成提示词没用;如果 Recall 高但最终答案仍然不对,问题大概率出在生成环节或提示词约束不够。

8.2 生成质量指标

  • Faithfulness / 忠实度:答案内容是否严格基于给定的上下文,有没有编造。
  • Answer Relevance / 答案相关性:答案是否真正回答了用户的问题。
  • Context Relevance / 上下文相关性:给模型的上下文片段是否与问题相关。

当你发现答案"看起来通顺但没回答用户的问题"时,优先怀疑上下文相关性。当你发现答案"回答得很流畅但细节是编的"时,优先怀疑忠实度。

8.3 手工验证建议

在你的开发初期,强烈建议准备一组固定的测试问题,称为评测集。每个问题标注标准答案或"应有信息来自文档哪一段"。每次改动分块策略、Embedding 模型、检索 TopK、提示词之后,用同一组问题跑一遍,对比答对数量。没有评测集的 RAG 优化,基本等于盲人摸象。

9. 常见问题与排查方法

我用表格整理一下 LangChain + RAG + Agent 开发中最常遇到的问题和排查思路。

问题现象可能原因排查方式解决方案
启动时报 OpenAI API Key 错误环境变量未正确加载或密钥无效打印os.environ.get("OPENAI_API_KEY")确认在启动脚本或.env中正确配置环境变量
向量库写入时报维度不匹配Embedding 模型切换后向量维度不一致检查当前 Embedding 模型输出维度,检查向量库 Collection 创建时的维度清空旧 Collection,重新写入向量
分块后文档内容丢失或乱码文档加载阶段编码处理错误打印documents的前 500 个字符针对文件格式换用专用 Loader,检查编码参数
检索结果与问题完全不相关分块策略不合理、TopK 太小、Embedding 模型对中文支持弱打印检索器返回的片段,人工判断片段与问题是否相关调整 chunk_size/chunk_overlap,增大或减小 TopK,换 Embedding 模型,引入重排
模型不使用工具而是直接回答工具描述不清晰,或模型不支持工具调用查看 Agent verbose 输出,确认模型是否收到了工具列表重写工具描述,加入触发条件和示例,换用支持工具调用的模型
回答内容仍是编造的,不引用上下文提示词约束不足,或检索片段质量差在提示词中禁用内部知识,要求逐条引用设置"仅根据上下文回答",必要时用更高温度改为 0
Agent 循环次数过多,迟迟不结束工具返回结果格式不明确,模型无法判断已完成增大max_iterations前先查看每次工具返回内容设计清晰、结构化的工具返回格式,增加终止条件
LangGraph 运行报状态类型错误节点函数返回的 key 不在 State 定义中检查节点返回字典和TypedDict字段是否一致保持 State 字段和节点返回值严格一致

10. 最佳实践与工程建议

10.1 RAG 场景:内容质量先于技术选型

如果你的知识文档本身是混乱的、过时的、互相矛盾的,换什么向量库、调什么分块参数都没用。建议在建立知识库之前,先做一轮文档清理、版本确认、格式统一。RAG 的上限由你的知识库质量决定,而不是由技术栈决定。

10.2 分块策略:用元数据给检索留后路

分块时不要只保存文本内容,还要把来源文件名、章节标题、页码等元数据一并存入向量库。这个习惯非常有用:当检索结果不对劲时,你可以快速定位是哪个文档、哪个章节被召回了,从而判断是分块问题还是文档本身的问题。

10.3 安全与权限:Agent 的工具要最小授权

Agent 工具调用比普通 API 更危险的地方在于,模型会自主决定调用哪个工具以及传什么参数。如果工具内部没有权限校验,一个"帮我删掉项目里的缓存文件"的请求就可能真的执行了删除操作。

建议遵循几个原则:

  • 每个工具使用最小权限,只暴露必要的操作。
  • 对高风险操作(删除、写入、支付、发送消息)强制加入人工确认。
  • 工具日志要完整记录"谁在什么时候调用了什么工具、参数是什么、结果是什么"。
  • API Key 只放在服务端环境变量中,绝不打包进前端或客户端。

10.4 版本锁定:LangChain 系列依赖要固定版本

LangChain、LangGraph、langchain-openai 这些包更新速度快,接口变动频繁。今天能跑的代码,过三个月就可能因为某个包的 breaking change 跑不起来。

建议在项目内使用requirements.txt或pyproject.toml锁定版本号,并且在升级任何依赖后,先跑一遍你的评测集。不要盲目追求最新版本。

10.5 评测与可观测性:从 Demo 到生产的两个台阶

Demo 阶段的 RAG 只需要"能跑通";生产阶段你需要三样东西:

  • 评测集:一组成熟的问题和期望答案,每次改动后自动跑一遍。
  • 可观测性:记录每次查询的检索片段、Token 消耗、响应延迟、模型输出。
  • 告警:当检索召回率下降到阈值以下,或模型连续输出"根据资料无法回答"时,需要收到通知。

很多项目死在"demo 效果不错但没人知道线上到底怎么样",可观测性就是解决办法。

10.6 性能优化:检索和生成分开扩缩容

真实业务里,Embedding 模型的向量化常常是性能瓶颈,尤其是大批量文档入库。检索服务和生成服务(LLM 调用)的负载特征完全不同,一个偏读密集,一个偏计算密集。如果条件允许,把文档入库、向量检索、LLM 生成拆成独立服务,单独扩缩容,性价比更高。

11. 总结与后续学习方向

到这里,你已经走通了三条关键路径:RAG 五步链路、Agent 工具调用、LangGraph 状态图编排。这三条路径是当前 AI 应用开发最核心的技能组合。

接下来你可以朝这几个方向深入:

  • 如果你想精进 RAG,去研究查询改写、混合检索(向量检索 + 关键词检索)、重排模型、多路召回。这能解决"检索不精准"这一核心痛点。
  • 如果你想做复杂的 Agent 应用,去研究 LangGraph 的条件边、记忆机制、多 Agent 协作、人工介入审核节点。
  • 如果你关心知识库效果可衡量,去搭建一个评测集,上线一遍检索指标和生成指标,用数据驱动优化。

建议你先别急着把技术栈全部铺开。把本文的 RAG 最小示例换成你自己的文档,运行三天,记录它回答不了的 10 个问题,然后针对这 10 个问题倒推是分块、检索还是提示词的问题。这个过程比读 30 篇教程都管用。等你把最小链路跑通之后,再回头看那些收藏夹里的资料,会发现真正拦住你的不是知识量,而是缺少一条从文档到答案的完整链路。先跑通它,再谈优化。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询