
简介这是黑马程序员出品的LangChain大模型实战教程聚焦RAG检索增强生成与Agent智能体构建从提示词设计一路推进到项目落地覆盖知识检索、外部知识引用和自主任务执行等关键环节适合已掌握Python基础、想进入AI应用开发的中高级技术人员。压缩包共235个文件整体仅22.96MB其中121个py文件构成主体源码txt/yml/json承载配置与提示词规则6个sqlite3提供可直接使用的演示数据库16个log保留运行日志pdf与pptx是配套讲解课件目录分层清晰适合按源码、文档与数据的顺序系统研习。目前已有377人学习浏览。教程结合中文开发场景整理了多类AI编程提示词并演示如何把Cursor、Devin、VSCode Agent等工具嵌入工程流程案例覆盖RAG外部知识引用和Agent自主任务执行给出可复用的项目骨架、数据库样例以及常见排错思路可直接用于技术预研、二次开发或教学参考。1. 一套教程为什么值得按提示词到Agent的顺序啃RAG应用的技术栈与学习路径很多人第一次做大模型应用最先被卡住的往往不是代码而是“同一个模型换个问法效果天差地别”。提示词刚调顺又发现模型回答业务问题时爱编造于是被推着去做RAG知识库能跑了业务方又问“能不能让它自己查一下内部系统”这又绕到Agent。这条从提示词到RAG再到Agent的链路恰好就是“黑马程序员大模型RAG与Agent智能体项目实战教程”的骨架以LangChain为编排框架把提示词工程、RAG、Agent三个环节串成一条从入门到实战的路径。标题里“从大模型提示词到实战项目”这句话本质上就是一条应用落地的进阶路线先把模型调明白再把知识接进来最后让模型具备调用工具的能力。适合正在做企业知识库问答、内部智能助手或者从“会调API”迈向“能交付AI应用”的开发者。如果你已经在做模型微调或底层训练这条链路帮助不大它的重心在应用编排而不是算法。2. 提示词工程与LangChain入门先学会和模型对话再谈框架编排2.1 提示词不是玄学而是三件事指令、上下文、输出约束提示词工程被说得神乎其神实际拆开就是三个信息块模型扮演什么角色、基于什么材料回答、输出满足什么格式。绝大多数“模型好笨”的抱怨都是这三块里缺了某一块。比如“帮我分析一下数据”这句话模型只能猜改成“你是一名数据分析师根据下面这张销售表找出连续两个月下滑的产品线输出为Markdown表格不要写过程”输出稳定性立刻上一个台阶。AI编程提示词、客服提示词套路都一样只是把场景换掉。这里有个常见误区提示词不是越长越好有效信息密度比长度重要。很多人把角色设定写成小作文反而把关键指令淹没在形容词里。我的习惯是先用一句话定义角色再用三到五句写任务要求最后用一句硬性约束收尾比如“资料里没有答案就明说不知道”。给模型留退路是减少幻觉最便宜的方案。调提示词也有先后顺序。先改任务指令再补上下文最后才动temperature这类生成参数。三条里指令的优先级最高很多“不听话”其实是没把任务说清楚。把这三件事想明白再去套LangChain才不至于在模板里反复横跳。2.2 用ChatPromptTemplate把提示词沉淀成可复用代码LangChain入门的第一个停靠点就是把上面这套结构化提示词写成模板。不要直接在调用里拼字符串真实项目里提示词要反复改版本模板化之后才能做批量测试和版本管理。from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate prompt ChatPromptTemplate.from_messages([ (system, 你是{role}。回答问题时严格依据提供的{context} 不要编造。如果资料里没有答案直接说资料中未找到。), (human, 问题{question}) ]) llm ChatOpenAI(modelgpt-4o-mini, temperature0.2) chain prompt | llm resp chain.invoke({ role: 企业制度问答助手, context: 年假规则入职满1年可休5天满3年可休7天。, question: 我入职两年能休几天 }) print(resp.content)这段代码的作用是把“system指令 用户问题”封装成一条可复用链。ChatPromptTemplate.from_messages里的{role}、{context}、{question}是占位符chain.invoke()时一次性传入。prompt | llm的竖线是LangChain的链式调用语法左边模板、右边模型后文加RAG和工具时也是在这个链上继续挂节点。两个参数值得注意。temperature0.2问答类任务压随机性检索增强场景下通常控制在0到0.3之间避免模型“自由发挥”。modelgpt-4o-mini示例按常见的OpenAI兼容接口写实际换成任意兼容接口的模型都可以开发阶段用免费或便宜的API调通链路完全够用。这里有一个版本坑需要注意新版LangChain把模型实现拆分到了langchain-openai、langchain-community等独立包里老教材里的from langchain.llms import OpenAI在新环境会直接报错。装包时别只装一个langchainlangchain-core加上对应供应商的包是一起装的教程代码跑不通时先查这个。2.3 用一个评测小脚本替代肉眼调参提示词改一次就丢到业务群里测一次是效率最低的调法。更稳的做法是建一个迷你评测集让几组提示词批量跑一遍用关键词命中率做初筛。# 评测用例expected 里放“答案里必须出现的关键词” cases [ {role: 制度问答, context: 年假规则入职满1年休5天满3年休7天。, question: 入职两年能休几天, expected: [5]}, {role: 制度问答, context: , question: 年假规则是什么, expected: [不知道, 未找到]}, ] templates { v1: 请回答{question}, v2: 你是{role}。资料{context}\n请依据资料回答资料中没有就说不知道。\n问题{question} } def build_chain(template: str): t ChatPromptTemplate.from_template(template) return t | llm for name, tpl in templates.items(): passed 0 for c in cases: out build_chain(tpl).invoke(c).content if any(k in out for k in c[expected]): passed 1 print(f{name}: {passed}/{len(cases)})这个脚本的思路是把“期望答案里必须出现的关键词”写进expected用any(k in out)判断是否命中。它很粗糙但足以筛掉明显不好的模板。项目关键路径上再把粗筛通过的模板交给人工标注或大模型打分跑更细的语义评测。为什么要先做这一步因为提示词的改动影响是全局的。同一个模板可能在A场景表现好、在B场景翻车单条调试只能证明“这条过了”。评测集至少覆盖正常问法、缺上下文问法、模糊问法三类每个提示词版本留档。这条线做扎实了后面RAG也好、Agent也好排查问题才分得清是“提示词问题”还是“检索问题”。3. RAG检索增强生成全流程从文档加载到答案生成的六个环节RAG被当成解决幻觉的标配但它不是“把文档丢进向量库”这么简单。真正做起来瓶颈会依次出现在切分、向量化、检索和生成这四个环节上。下面的顺序就是一条完整的RAG流水线加载、切分、向量化、入库、检索、生成。3.1 RAG的价值边界为什么说它解决幻觉但替代不了微调一句话原理RAG不训练模型而是在回答前先检索外部资料把资料拼进上下文让模型“开卷回答”。这也是它能缓解幻觉的根本原因——回答有了依据而不是凭记忆硬编。同时它的知识可以随时更新替换知识库里的文档就行不需要重新训练。对大模型LLM应用来说这是接入企业私有知识最直接的手段。但RAG也有它的瓶颈。资料本身必须是文本或能被转成文本检索结果如果和问题对不上给的依据就是错的模型反而会照着错误资料一本正经地胡说。另一个常见问题是“RAG知识库能存储图片吗”——症状是文档里有一堆截图表格检索却总是漏掉。图片里的信息要先进OCR转成文本或者用多模态模型把图片内容描述出来再入库纯向量检索本身不“看”图。它与微调的定位也不同微调改变模型本身的行为和输出风格适合固定话术、专业术语转写RAG提供事实和时效信息适合制度问答、产品手册、售后知识库这类内容频繁更新的场景。我一般先问需求侧你要模型“更听话”还是“更懂行”前者倾向微调后者先上RAG。预算有限时优先做RAG它的迭代成本比微调低一个数量级。3.2 文档加载与切分chunk_size和overlap两个必调参数RAG的原料是文档文档进检索前先要切成块。切块这事看着简单实际是RAG里最需要反复试的步骤。from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter loader TextLoader(company_policy.md, encodingutf-8) docs loader.load() splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , , ] ) chunks splitter.split_documents(docs) print(f共切出 {len(chunks)} 个块)RecursiveCharacterTextSplitter会按separators里的顺序尝试切分先用段落、再用换行、再用句号、再退到逗号和空格。这里把中文标点。、、加进separators是照着中文文档习惯改的默认的英文标点对中文文本不友好这是很多中文RAG项目漏召回的第一个隐性原因。chunk_size和chunk_overlap是两个必调参数。块越小检索粒度越细但单个块携带的上下文越少模型容易“只见树木不见森林”块越大上下文越完整但容易把多个主题混在一起检索命中率反而下降。overlap让相邻块有重叠避免一个完整知识点恰好被切分线拦腰截断。给两组经验起点制度条款、操作手册这类条目清晰的文档chunk_size用400到600技术报告、产品介绍这类长叙述文档用800到1200。最终值取决于业务里的真实问题我习惯先拿20个真实问题跑一遍看漏召回的场景集中在什么文档、什么位置再回头调。怎么判断切分有问题把检索命中的chunk打印出来看它能否完整回答一个业务问题。如果答案总是差半句去原文里找那个断点多半就卡在切分线上。这个排查路径比反复调阈值快得多。3.3 向量化与向量库选型Embedding模型与存储方案的取舍切好的块要变成向量才能做相似度检索。Embedding模型选型直接影响检索质量这里没有“参数最大就最好”的说法只有“适配你的文档和语言才最好”。from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import FAISS # 首次运行会自动下载中文Embedding模型权重 embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-small-zh-v1.5 ) vectorstore FAISS.from_documents(chunks, embeddings) vectorstore.save_local(kb_index)Embedding模型决定两件事文本被映射成什么向量、向量空间里的远近是否和语义一致。对中文场景通用的英文Embedding模型效果明显偏差换成中文预训练模型是第一步优化。生产环境如果要求全私有化Embedding模型也必须本地部署不能只把LLM换成内部的、Embedding还打外网接口。向量库选型按场景来向量库适合阶段特点FAISS单机开发、Demo部署简单文件持久化适合千万元素以内Chroma轻量应用自带客户端接口简单适合小团队Milvus生产、海量数据分布式、支持过滤和标量字段运维成本高开发机上看不出差距先上FAISS把链路跑通安装时pip install faiss-cpu即可等文档量过百万、需要多用户并发时再迁Milvus。FAISS本地索引的“后悔药”也够用——索引文件删了重建就行但记得保存原始chunk的元数据比如来源文件名和页码。3.4 检索与合成相似度TopK、分数阈值与Rerank重排检索不是简单把TopK拼给模型。这里有几个容易忽略的点相似度分数阈值、召回范围、重排。retriever vectorstore.as_retriever( search_typesimilarity_score_threshold, search_kwargs{score_threshold: 0.35, k: 8} ) question 入职两年能休几天年假 docs retriever.invoke(question) context \n.join([d.page_content for d in docs])similarity_score_threshold是先按相似度阈值过滤再取前k个。阈值的坑在于不同Embedding模型算出的分数区间完全不同网上抄来的0.5不一定适合你的模型。我会拿几十条确认相关的问答统计它们的相似度分数分布把下限设在“已知相关样本的最低分再降一点”的位置这个标定过程各项目之间不能通用。只取TopK还不够稳。长文档场景下真正包含答案的片段可能排在第七第八位。常见做法是加一层Rerank重排先用向量检索召回20到50个候选再用交叉编码器逐条打精排分取前3到5个进上下文。from sentence_transformers import CrossEncoder reranker CrossEncoder(BAAI/bge-reranker-base) pairs [(question, d.page_content) for d in docs] scores reranker.predict(pairs) ranked [d for _, d in sorted(zip(scores, docs), keylambda x: x[0], reverseTrue)]向量检索看“语义相近”重排看“和问题是否直接相关”两者互补。加了重排k可以适当放大让粗召回多捞一些候选把精确筛选交给重排。RAG的瓶颈很多时候不在模型而在召回质量重排是投入产出比很高的一步。4. Agent智能体设计与工具调用让模型从“会说话”到“会干活”RAG让模型有了“知识”但还停留在“查资料、写答案”的阶段。业务方下一句往往是“能不能让它自己去计算、去查库存、去发审批”。这就是Agent上场的地方。4.1 Agent和普通任务链的区别ReAct循环与工具调用普通的LangChain链是“输入→模板→模型→输出”一次走完。Agent多了一个循环模型先根据用户问题判断当前信息够不够不够就选一个工具、生成工具参数、调工具、把工具返回结果放回上下文再判断要不要继续调下一个工具直到它认为可以回答了才输出最终答案。这个“思考→行动→观察”的循环来自ReAct模式也是Agent开发的基础。因此Agent真正的门槛不在“能调工具”而在“什么时候调、调哪个”。工具一多模型选错工具、参数传错的概率会显著上升。一个止血的经验是给Agent配的工具不要贪多3到5个精调的比十几个大杂烩稳定得多。有人问“LangChain、Dify、CrewAI这几个Agent框架哪个好”。我的看法是Dify上手最快适合业务团队快速搭MVPCrewAI的多角色编排适合做偏文案、偏流程复盘的场景LangChain生态最全、自由度最高能从Chain平滑过渡到RAG和Agent。框架只是壳核心还是对工具边界和模型能力的理解。还有一种不适合上Agent的场景工具只有一两个且调用逻辑固定。这时用普通的工具调用直接让模型输出结构化参数比套Agent循环更省、更不容易翻车。Agent的价值在“动态决策”不在“接线”。4.2 用tool定义工具并绑定到AgentExecutor把内部接口变成Agent能调用的工具LangChain里最直接的方式是tool装饰器加类型注解。工具描述是Agent能不能选对工具的胜负手模型不会读你的代码它只读函数名和docstring。from langchain_core.tools import tool tool def get_leave_balance(employee_id: str) - dict: 查询指定员工的剩余年假天数。 参数employee_id是员工工号格式如E0001。 # 这里替换成真实的HR系统接口调用 balance {E0001: {annual: 5.0, sick: 3.0}} return balance.get(employee_id, {error: not found}) tools [get_leave_balance]描述里要写清楚“这个工具做什么、参数是什么格式”泛泛的“一个查询函数”等于没有。注意employee_id: str类型注解和docstring里的格式示例这是模型生成参数时的参考锚点比什么都不写强得多。绑定并执行from langchain.agents import create_react_agent, AgentExecutor from langchain_core.prompts import PromptTemplate agent_prompt PromptTemplate.from_template( 你是一个企业助手。可用工具如下{tools}。 优先用工具回答事实类问题用不了工具时如实说明。 工具名{tool_names}\n 历史对话{chat_history}\n 用户输入{input}\n 思考{agent_scratchpad} ) agent create_react_agent(llm, tools, agent_prompt) executor AgentExecutor( agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue, max_iterations5 ) resp executor.invoke({input: 员工E0001还剩几天年假, chat_history: }) print(resp[output])max_iterations5是硬性保险Agent陷入循环时能及时退出。handle_parsing_errorsTrue的作用是模型输出格式不合法时不至于整个崩溃而是把错误信息返回给模型让它修正。生产环境里这两个参数必须设。Agent的安全边界比普通接口更重要——模型可能按用户的自然语言指示去调用工具一句“帮我清空测试数据”就可能触发危险操作。我的习惯是工具函数内部做权限校验和参数白名单高危操作不做成Agent可调用工具最多做成“生成审批请求”而不是“直接执行”。Agent安全不只是防外人还要防模型自己“理解偏了”。4.3 Memory多轮对话的记忆与检索结果的协同Agent不带记忆时每轮都是“失忆式”回答。要让多轮对话连贯需要挂记忆组件。from langchain.memory import ConversationBufferMemory memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue) executor AgentExecutor( agentagent, toolstools, memorymemory, verboseTrue ) resp1 executor.invoke({input: 员工E0001还剩几天年假}) resp2 executor.invoke({input: 那他的病假呢}) print(resp2[output])注意memory_keychat_history必须和prompt模板里的{chat_history}占位符同名否则拼接不上。但ConversationBufferMemory会把所有历史原样堆进上下文几轮之后Prompt就变长API成本和延迟都上来。长对话场景我会换成ConversationBufferWindowMemory只留最近几轮或ConversationSummaryMemory把老对话压缩成摘要。还有一个RAG与Agent协同的细节别把上一轮检索出来的文档块原样塞进memory。旧文档可能已经过时下一轮提问时应该重新检索。这也是很多Agent应用“第二轮开始胡说”的根源。RAG加Agent组合时比较顺的结构是分层Agent保留“判断是否需要工具”的决策但知识库检索不走Agent循环而是作为链上的一个固定节点先执行再把结果送进上下文。简单问题走RAG链复杂问题再升级到Agent多数场景都能这样拆。5. 避坑/常见问题排查RAG和Agent翻车现场的五类高频故障下面五条是从真实项目中反复出现的故障里筛出来的每条按现象、原因、解决来写。RAG和Agent的排查本质上就是先分清是哪一层出了问题——提示词、切分、检索、还是工具调用不要一上来就怀疑模型。5.1 检索到的内容与问题不相关模型照着错误资料硬答现象回答引用了知识库内容但内容和问题沾边不贴边甚至南辕北辙。模型一本正经把无关资料当成依据。原因查询词太短和文档的表述不在一个语义空间或者Embedding模型本身对领域术语不敏感也可能是TopK取多了把低分垃圾块也拼进了上下文。解决先打印检索命中的文档块确认是不是召回错了。是查询词太口语化把“年假怎么算”改写成“年假计算规则”再查是嵌入不识别术语换领域Embedding模型是低分混入加分数阈值或接Rerank。排查顺序不要乱先看检索结果再看生成结果。5.2 chunk切分把一个完整条款拦腰截断现象问到某个条款的下半句检索结果只拼出上半句回答凭空补全了下半句补得还是错的。原因切分时把一条完整规则从中间切断两个chunk各守一半而TopK只命中了前一半。中文文档里尤其常见——默认切分器按英文句号切中文的句号、分号都不认。解决把中文标点加进separators条款型文档按编号切分而不是按字符硬切适当提高chunk_overlap。更稳的做法是自定义切分逻辑按“第X条”正则切。这属于RAG里投入产出比很高的一步优化。5.3 中文知识库的相似度阈值“不工作”现象按教程配了score_threshold0.3结果该召回的召回不了不相干的又都通过了。原因相似度阈值不是通用常量。不同Embedding模型输出的分数范围差异很大有的模型相似度分数整体偏高有的整体偏低。直接抄别的项目的阈值等于拿一个尺子去量另一个物体的长度。解决用自己知识库里50到100条已知相关和不相关的问题跑一遍得到分数分布再定阈值。BGE系列和OpenAI系列各自标定项目换Embedding模型时必须重新标。这个标定过程要留脚本写进项目的README里不然三个月后你自己也会忘。5.4 Agent工具参数解析失败或乱传参现象Agent选择了正确的工具但生成的参数格式不对工具调用直接报错或者把employee_id填成了“张三”而不是工号E0001。原因模型从自然语言里抽参数是生成式行为不是查表遇到工具期望严格枚举值时特别容易翻车。这与模型基础能力有关提示词能缓解但不能根治。解决工具参数用类型注解和枚举约束把可选范围写进参数描述handle_parsing_errorsTrue开起来让错误反哺给模型重试如果抽参频繁出错把“从问题里抽参”这一步从Agent里拆出来单独做一个带结构化输出约束的抽取链再把结果传给工具。大多数Agent项目最后都会走上这条路。5.5 多轮对话后上下文塞爆甚至遗忘前文现象对话进行到七八轮响应变慢、报上下文超限或者模型把前面的结论忘得干干净净。原因ConversationBufferMemory把每轮内容都堆进Prompt窗口消耗快超出上下文后系统截断早轮的关键信息被挤掉。解决短对话用窗口记忆长对话用摘要记忆把“历史结论”显式整理成结构化状态比如一个字典存用户的已确认信息而不是依赖自然语言历史。RAG会话里每轮都重新检索不要复用第一轮的检索结果。6. 从教程到可交付项目端到端验证的最后一步6.1 最小验收脚本检索-拼接-生成三步验证教程看到最后判断自己是否真的学会了不是“看懂了”而是能把前面所有环节串成一个能回答真实业务问题的脚本。这个脚本只要三步检索、拼上下文、生成。def ask(question: str, retriever, llm) - str: docs retriever.invoke(question) context \n.join(d.page_content for d in docs) resp llm.invoke( f请根据资料回答\n{context}\n\n问题{question}\n 资料不足时直接说不知道。 ) return resp.content, [d.metadata.get(source, ) for d in docs] answer, sources ask(入职两年能休几天年假, retriever, llm) print(answer) print(依据, sources)这个脚本同时暴露两个最关键的信号答案质量、依据来源。返回的sources能告诉你回答是哪段资料支撑的用来快速定位“答错了是检索错还是生成错”。这一步跑不通不要去碰Agent先把基础链路钉死。6.2 从Demo到可用评测集先行、私有化部署走到这里整套链路已经可以跑通。再往下有三件事决定项目能不能从Demo变成可用。第一是评测集早建比晚建好收集50条业务真实问题标注好答案和依据文档段后续每个改动都跑一遍对比避免“修好A场景弄坏B场景”。第二是本地化部署企业场景往往不允许数据出境常见做法是用Ollama这类工具部署开源模型LLM和Embedding都放内网再配合前面的FAISS或Milvus做企业大模型私有化部署。第三是上线后监控记录每个问题的检索召回情况和用户反馈反哺切分和阈值调整。我自己在知识库项目里做得最晚的一件事就是评测集。前一个月肉眼看着回答都还行结果业务方拿边角问法一测就露馅从此养成“先建评测集、再动模型”的习惯。这趟从提示词到RAG再到Agent的链路每一步都有可调的空间但顺序别乱先让提示词稳再让检索准最后才谈Agent的工具编排。希望帮到你。本文还有配套的精品资源点击获取