ARTICLE DETAIL

资讯详情

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

LangChain实战指南:从Chain到Agent与RAG应用

LangChain实战指南:从Chain到Agent与RAG应用 1. 项目概述LangChain到底是什么先说结论LangChain是目前市面上把大模型能力落成实际应用最顺手的一套工具框架。没接触过的朋友可能会被它的名字唬住感觉是某种高深莫测的技术其实拆开看就两个词——Lang语言 Chain链翻译过来就是“语言模型链”。它做的事情本质上是把大模型当成一个“会思考但记性差、没有手”的实习生LangChain负责给这个实习生配上笔记本、工具箱和一套标准工作流程让它能查资料、能调用工具、能按步骤干活。这个项目的定位是“04-LangChain快速上手”属于系列教程的第四篇所以我默认读者已经有Python基础知道大模型的基础调用方式比如ChatGPT API或者开源模型怎么跑起来但还没系统接触过LangChain。这篇博文我会从安装配置讲起一直聊到能跑通的RAG本地知识库、Agent实战、LangGraph的区别对比以及我在实际项目中踩过的坑。适合想快速把大模型落到业务场景里的开发者也适合准备LangChain面试、需要系统梳理知识体系的同学。我自己从LangChain 0.1时代就开始用到现在经历了API频繁重构、LCELLangChain Expression Language的普及、以及LangGraph的独立演进算是把这个框架的“脾气”摸得比较透了。这篇文章不会照着官方文档翻译我会按“为什么这么设计→实际怎么用→有什么坑”的逻辑来讲尽量让零基础的读者也能跟着操作同时让有经验的开发者也能从避坑部分拿到有价值的东西。2. 环境准备安装没那么简单2.1 依赖选择与版本锁定很多人一上来就打开终端执行pip install langchain这确实是最快的方式但我建议你在动手之前先想清楚要用哪些模块。LangChain从0.1版本之后做了一次大规模模块拆分核心包变成langchain-core而langchain本身变成了一个聚合包你实际用到的可能还包括langchain-community社区贡献的模型和工具集成、langchain-openai、langchain-chroma、langchain-text-splitters等等。我第一次上手时图省事直接pip install langchain结果装了一堆用不到的依赖还因为版本冲突跟pydantic干架。后来学乖了改用按需安装的方式# 基础环境建议用conda创建独立环境避免污染全局Python conda create -n langchain-env python3.11 -y conda activate langchain-env # 按需安装核心模块 pip install langchain-core pip install langchain-community pip install langchain-openai # 如果要接OpenAI兼容接口 pip install langchain-chroma # 向量数据库 pip install langchain-text-splitters # 文本分割器这里有个实用建议把langchain全家桶装全没有错但生产环境最好pip freeze requirements.txt锁住版本。LangChain的版本迭代速度太快了0.1.x到0.2.x之间某些API直接改名字或者换了传参方式锁版本能让你在半年后还能跑通当时的代码。2.2 模型接入的兼容性问题LangChain的模型接入层抽象得不错但它有个隐藏依赖——你选的是哪家模型。以最流行的OpenAI为例langchain-openai包不仅支持OpenAI官方接口还兼容所有OpenAI格式的本地服务比如Ollama、LM Studio、vLLM启动的服务。我实际测试下来这个兼容性做得相当好只需改两个参数base_url和api_key。举个例子用Ollama跑本地模型时from langchain_openai import ChatOpenAI llm ChatOpenAI( base_urlhttp://localhost:11434/v1, api_keyollama, # Ollama的API Key任意填 modelqwen2.5:7b ) response llm.invoke(你好用一句话介绍LangChain) print(response.content)这段代码很多人卡住的地方是api_keyollama——以为必须填真实的Key其实Ollama的/v1接口不校验Key随便填什么字符串都行。类似的还有国内一些模型厂商的服务他们做成了OpenAI兼容格式直接用LangChain的OpenAI类就能对接省了不少事。2.3 环境变量的坑另一个常见的“新手劝退点”是API Key的配置方式。LangChain官方示例里喜欢用环境变量配Key比如export OPENAI_API_KEYsk-xxxx但很多初学者会把Key直接硬编码在代码里然后推到GitHub上——这是非常危险的操作。我的建议是使用.env文件配合python-dotenv管理密钥同时把.env加入.gitignorepip install python-dotenvfrom dotenv import load_dotenv load_dotenv() # 自动加载 .env 文件中的环境变量这种方式的好处不仅是安全还方便切换不同的模型服务商。你可以在.env里同时配置OpenAI、Anthropic、本地Ollama等多套Key代码完全不用改。3. 核心概念解析从Chain到Agent3.1 五大模块的底层逻辑LangChain的“五大模块”几乎出现在每篇教程里但大多数教程只罗列名词没有把它们串起来讲。我从实际应用的角度重新梳理一遍Models模型是大脑负责理解和生成。Prompts提示词是给大脑的指令模板决定模型输出的风格和格式。Chains链把“调模型”这个动作编成流水线比如“先翻译再总结”就是一条两步链。Agents代理是链的升级版——模型自己决定下一步要调用什么工具、按什么顺序执行。Memory记忆负责保存上下文让多轮对话不“失忆”。这五个模块的关系我用一句话概括Models和Prompts是基础组件Chains是固定的流程Agents是动态的流程Memory是贯穿所有环节的共享数据。3.2 Chain与Agent的本质区别在面试里经常被问到“Chain和Agent有什么区别”我的回答是Chain的路径是预先写死的Agent的路径是模型自己选的。举例来说你写一条Chain做“摘要生成”流程固定为“读入文本→调用模型→输出摘要”中间不会多出一步。而Agent相当于你给了它几个工具搜索、计算、读取文件告诉它“帮我写一篇关于XX的报告”它自己会判断要不要先搜索资料、要不要算数据甚至中途发现缺信息时回头再搜一轮。实际开发中只要业务流程是确定的优先用Chain——稳定、可控、好排查。只有业务路径不确定、需要模型自主规划时才上Agent。很多人一上来就追求Agent结果发现跑出来的结果不可控这是方向性错误。3.3 Memory选择的经验之谈我见过不少人在Memory这个环节翻车。LangChain提供了好几种记忆实现最简单的ConversationBufferMemory存全量对话、ConversationBufferWindowMemory只保留最近N轮、ConversationSummaryMemory对历史做摘要。它们的核心差异是“存多少”和“怎么存”。实际项目中我的经验是简单的FAQ问答机器人用ConversationBufferWindowMemory就够了保留最近3-5轮省Token且效果不错复杂业务咨询场景用ConversationSummaryMemory把早期对话压缩成摘要既能省Token又不会完全丢失早期信息千万别在正式项目里用全量BufferMemory对话轮数一多随便一次请求就能把上下文窗口塞满报错还难排查还有一个容易被忽略的点Memory本质上也要传给模型才算数所以你在设计Prompt时得留出记忆的插入位。LangChain封装好了但你要理解背后是“拼接字符串”的逻辑这样排查问题时才能一眼看出对话历史是怎么混进Prompt的。4. 实战入门5分钟跑通第一个Chain4.1 最小化代码示例光讲概念不过瘾咱们直接上代码。一个最小的LangChain应用需要三样东西模型对象、Prompt模板、Chain组装。下面这段代码是我平时教新人入门用的“第一个程序”from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # 1. 初始化模型 llm ChatOpenAI( modelgpt-4o-mini, temperature0.7 ) # 2. 定义Prompt模板 prompt ChatPromptTemplate.from_messages([ (system, 你是一位资深的技术博主擅长用通俗的语言解释复杂概念。), (human, 请用500字以内的篇幅解释{topic}) ]) # 3. 组装ChainLCEL语法用管道符连接 chain prompt | llm | StrOutputParser() # 4. 调用 result chain.invoke({topic: 什么是RAG}) print(result)这段代码就是LangChain最基本的使用范式。注意第3步用的是LCELLangChain Expression Language语法用|管道符把组件串联起来这种方式可读性好、方便调试也容易做流式输出。StrOutputParser做了一件事把模型的输出对象转成纯文本字符串。如果不加这个你拿到的会是一个带各种元数据的AIMessage对象打印出来一大坨不好处理。4.2 LCEL语法的核心思路LCELLangChain Expression Language是LangChain 0.1之后的主推写法。它借鉴了Unix管道设计思想——每个组件只做一件事输出传给下一个组件的输入。我刚开始从旧版Chain类迁移到LCEL时很不习惯用多了才发现LCEL的几个好处延迟执行用管道符定义Chain时并不会真正调用模型直到.invoke()才触发。多者容易测试和复用并行如果Chain里有两条独立分支可以用RunnableParallel并行执行节省时间流式输出LCEL天然支持.stream()做大模型打字机效果时非常方便下面是一个并行调用的例子用在“同时做总结和关键词提取”的场景from langchain_core.runnables import RunnableParallel # 定义两条独立分支 summary_chain prompt_summary | llm | StrOutputParser() keywords_chain prompt_keywords | llm | StrOutputParser() # 并行执行 parallel_chain RunnableParallel( summarysummary_chain, keywordskeywords_chain ) result parallel_chain.invoke({content: long_text}) print(result[summary]) print(result[keywords])4.3 Prompt模板的设计技巧Prompt模板是很多人忽略的重点。我做了这么多项目后总结出一个教训模型的差距可以用Prompt弥补但Prompt再精妙也补不了模型本身的能力上限所以要把精力花在“明确指令控制输出格式”上不要指望模型自动领会你的意图。在LangChain中ChatPromptTemplate支持定义多轮消息。我最常用的做法是先写System消息设定角色和行为准则再用Human消息挖空占位符。如果输出需要结构化可以在Prompt里明确要求“必须输出JSON格式键名为xxx”然后配合JsonOutputParser解析from langchain_core.output_parsers import JsonOutputParser parser JsonOutputParser() prompt ChatPromptTemplate.from_messages([ (system, 你是一个信息抽取助手。请从用户输入中抽取指定字段只输出JSON不要输出其他内容。), (human, 输入{input}\n抽取字段{fields}) ]) chain prompt | llm | parser result chain.invoke({ input: 张三在2024年3月15日购买了苹果电脑花费12999元。, fields: [姓名, 日期, 购买物品, 金额] }) print(result) # 输出示例{姓名: 张三, 日期: 2024-03-15, ...}用JsonOutputParser的好处是自动清洗模型返回的JSON格式文本。不过要留意模型偶尔会输出不合法JSON实际项目中我会在外面套一层try-except搭配简单的重试逻辑保证稳定性。5. RAG流程实战本地知识库搭建5.1 Ollama LangChain Chroma的整体架构RAGRetrieval-Augmented Generation检索增强生成是大模型落地最火的方案。说白了就是先把你自己的文档切片存进向量数据库用户提问时先把相关内容检索出来再把这些内容连同问题一起交给大模型回答。这么做的好处是让模型基于你的私有知识回答而不是凭空瞎编。架构图省了我用文字描述链路文档加载 → 文本分割 → 向量化 → 存入Chroma → 用户提问 → 相似度检索 → 组建Prompt → 大模型生成。本地化部署推荐组合是Ollama LangChain Chroma。Ollama负责跑开源模型如Qwen2.5、Llama3.1LangChain负责编排Chroma负责向量存储。这套组合完全离线可用不依赖外部API数据隐私安全性好。5.2 RAG全流程代码实现下面是一份可以直接跑通的本地知识库搭建代码我用的是Qwen2.5 Chromafrom langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser # 1. 加载文档 loader TextLoader(./knowledge_base.txt, encodingutf-8) documents loader.load() # 2. 文本分割关键参数详解见下一节 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , , , ] ) docs text_splitter.split_documents(documents) # 3. 向量化 存入Chroma embeddings OllamaEmbeddings(modelnomic-embed-text) vectorstore Chroma.from_documents( documentsdocs, embeddingembeddings, persist_directory./chroma_db ) # 4. 创建检索器 retriever vectorstore.as_retriever( search_typesimilarity, search_kwargs{k: 4} ) # 5. 定义RAG Prompt prompt ChatPromptTemplate.from_messages([ (system, 你是一个基于本地知识库的问答助手。请根据提供的资料回答问题不要编造知识。如果资料中没有相关信息请直接说不知道。), (human, 相关资料\n{context}\n\n用户问题{question}) ]) # 6. 组装RAG Chain llm ChatOpenAI( base_urlhttp://localhost:11434/v1, api_keyollama, modelqwen2.5:7b ) rag_chain ( {context: retriever, question: RunnablePassthrough()} | prompt | llm | StrOutputParser() ) # 7. 查询 result rag_chain.invoke(我们公司的产品有哪些) print(result)这份代码里面的每个环节我都测过能直接跑通。有几个点需要强调OllamaEmbeddings用的是nomic-embed-text这个模型第一次运行时Ollama会自动下载速度看网速RunnablePassthrough()的作用是把用户输入原样传递给下一个组件在这里就是把问题传给Promptretriever会返回4条相关文档片段Prompt里用{context}接收5.3 文本分割的参数选择与调优文本分割是整个RAG流程里最影响效果、也最容易被忽略的环节。我见过不少人调了半天Embedding模型、换了各种向量库结果问题出在chunk_size设置不合理的。先说原理chunk_size决定每个切片的字符数chunk_overlap决定相邻切片之间的重叠字符数。切片太小会丢失上下文切片太大会让单条内容太长、向量化精度下降。选500-800字符是一个经验值但要根据你的文档类型调整知乎/博客文章500-800字符比较合适产品手册、操作文档300-500字符因为这类文档语义密度高学术论文800-1000字符需要保留段落完整性chunk_overlap一般设置chunk_size的10%-20%。重叠的目的是避免信息被从中间截断比如“李明”在第一条切片末尾、“是一个销售”在第二条切片开头如果不重叠检索时可能两条都搜不到完整的信息。调参建议先用20-30条有代表性的测试问题跑一遍看检索回来的片段是否符合直觉再针对性调整。不要一上来就用几百MB的大文档测试浪费时间还看不出问题。5.4 Chroma的持久化与复用代码里persist_directory./chroma_db表示向量库会保存到本地磁盘。第二次运行你就可以直接加载不需要重新切割文档和计算向量vectorstore Chroma( persist_directory./chroma_db, embeddingembeddings ) retriever vectorstore.as_retriever()这里有个坑要提醒Chroma的persist_directory目录不要放在Git仓库里向量库文件动辄几十上百MB而且二进制格式不适合版本管理。另外如果你的Embedding模型换了之前存的向量全部作废必须重新构建索引——不同模型的向量维度不对齐无法混用。6. MultiVectorRetriever的进阶用法6.1 多向量检索器解决的痛点如果你的原始文档很长比如整本PDF、几十页的合同用一个Embedding向量表示一整篇文档效果必然不好。LangChain 0.1之后提供了MultiVectorRetriever专门解决“长文档检索”的问题。它的核心思路是一对多映射——为每一大段文档生成多个“摘要块”检索时匹配摘要块但返回给模型的是对应原始整个大段文档。这样既匹配了语义摘要可能更精准地捕获主题又不丢失细节返回的是完整原始片段不是被截短的摘要。用生活化的比方这就像图书馆里的分类卡片卡片上写着一本书的简介和主题词查阅时先翻卡片找对书架号再抽出整本书来看。直接检索全文相当于挨页翻每本书效率低且容易漏。6.2 代码实现摘要原始文档的组合检索下面是利用大模型生成摘要再与原始文档一起构建索引的完整示例from langchain.retrievers.multi_vector import MultiVectorRetriever from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OllamaEmbeddings from langchain.storage import InMemoryStore from langchain_core.documents import Document from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate # 1. 准备一个大文档 big_docs [ Document(page_content这里是一份很长的产品说明书包含..., metadata{source: product.md}) ] # 2. 用模型为每个大文档生成摘要 summarize_prompt ChatPromptTemplate.from_messages([ (system, 你是一个文档摘要助手请用200字以内概括用户提供文档的核心内容。), (human, {doc}) ]) llm ChatOpenAI(base_urlhttp://localhost:11434/v1, api_keyollama, modelqwen2.5:7b) summarize_chain summarize_prompt | llm # 3. 生成摘要文档 from langchain_core.output_parsers import StrOutputParser summary_docs [] for doc in big_docs: summary (summarize_prompt | llm | StrOutputParser()).invoke({doc: doc.page_content}) summary_docs.append(Document(page_contentsummary, metadata{original_idx: len(summary_docs)})) # 4. 构建多向量检索器 vectorstore Chroma(embedding_functionOllamaEmbeddings(modelnomic-embed-text)) store InMemoryStore() # 原始文档存内存也可以换成其他存储 retriever MultiVectorRetriever( vectorstorevectorstore, docstorestore, id_keydoc_id ) # 5. 给文档分配ID并写入 for i, (summary_doc, original_doc) in enumerate(zip(summary_docs, big_docs)): doc_id fdoc_{i} summary_doc.metadata[doc_id] doc_id original_doc.metadata[doc_id] doc_id vectorstore.add_documents([summary_doc]) store.mset([(doc_id, original_doc.page_content)]) # 6. 检索命中摘要返回完整原始文档 results retriever.invoke(产品的保修期是多久) for r in results: print(r.page_content)注意第4步的id_keydoc_id是把摘要向量和原始文档关联起来的关键。vectorstore存摘要的向量docstore存原始文档全文检索时先把摘要查出来再通过doc_id去docstore取原始内容。这个方案的代价是构建索引时需要多一步大模型摘要耗时和成本有所增加但检索质量提升非常明显。如果你的需求是“从100页的PDF里精准找答案”这个方案值得试试。7. LangChain与LangGraph的区别解析7.1 设计理念的演进脉络LangChain和LangGraph的关系简单说LangChain是框架LangGraph是框架里长出来的新人专攻复杂状态流图场景。LangChain的Chain是线性或简单并行的本质上不适合“循环”和“分支内再分支”这样的复杂流程。比如你希望Agent先思考、再调用工具、根据工具结果判断是否再调用其他工具这个流程如果全用Chain硬写代码会非常别扭。LangGraph引入了“图”的概念节点Node就是执行单元边Edge就是状态转移条件。你可以像搭积木一样定义“从A节点出来后如果满足条件X就去B节点否则去C节点”并且支持循环允许回到之前的节点重新执行。这种设计让它天然适合Agent应用。7.2 面试常见考点速查现在LangChain和LangGraph的对比已经是面试高频题我整理了面试官最常问的几个点对比维度LangChainLangGraph核心抽象Chain/AgentGraph节点边状态管理需手动传递内置StateSchema自动维护循环控制不擅长原生支持条件分支不灵活原生支持适用场景固定流程、简单RAG复杂多步推理、Agent调度调试体验简单直观可以用LangSmith可视化追踪面试时如果能现场画一下“同一个多步工具调用流程在LangChain和LangGraph里实现方式的差异”基本就能过关。个人经验是不要背对比表要讲场景——拿一个具体例子说明比如“如果工具A返回异常需要再试一次工具B”这个需求在LangChain里怎么别扭、在LangGraph里怎么自然比背诵十个差异点更有说服力。7.3 实际开发中的选型建议我自己的选型原则很简单流程里没有循环需求用LangChain有循环、分支复杂、需要精细化状态控制用LangGraph。举个例子我之前做一个客户支持Agent需求是“先分析用户问题→判断是否需要查订单系统→查询→结果不满意则追问澄清→再决定下一步”。这个流程天然是带循环和条件分支的用LangChain写到最后自己都晕了移到LangGraph之后代码清晰多了。反过来一个“读取文档→生成摘要→格式化输出”的固定流程用LangGraph就是杀鸡用牛刀。如果你是新项目我建议直接学LangGraph。LangChain的老API在0.2里仍然可用但新功能基本都往LangGraph倾斜了。8. Agent实战能力盘点与对比8.1 DeepAgents在LangChain生态中的定位LangChain推出的DeepAgents对标的是“用LangGraph实现的深度代理框架”。它的核心改进是把Agent的工作流拆得更细规划器Planner负责拆解任务执行器Executor负责调用工具校验器Validator负责检查结果是否达标。三者通过LangGraph的图结构串起来形成了“计划→执行→检查→再计划”的循环机制。我用下来最直观的感受是DeepAgents在多工具、多步骤任务上的成功率比早期ReAct Agent明显更高。早期Agent经常出现“还没查够资料就开始回答”的情况DeepAgents的Validator环节能拦住这种半成品输出督促模型再检索一轮。8.2 与Claude Agent能力的差距分析热词里有人问“LangChain的DeepAgents现在的能力咋样与Claude比差距在哪”这个问题很有意思。ClaudeAnthropic出的模型本身在工具调用和Agent行为上经过大量强化训练它的Agent能力更像“模型原生自带的天赋”DeepAgents则是“用工程手段把Agent能力做扎实”。两者差距主要在三处指令遵循能力Claude对复杂指令的拆分和理解更稳定遇到歧义时倾向主动澄清而不是乱猜。LangChain的DeepAgents依赖底层模型如果你用的是开源模型比如Qwen2.5指令遵循会弱一些工具选择的准确性当你有十几个工具时Claude选出正确工具的准确率明显更高而开源模型在工具选择上容易“选错工具硬执行”错误恢复能力Claude在工具调用出错或返回意外格式时能自行调整策略DeepAgents虽然有Validator但遇到比较离谱的返回还是容易卡死但这不意味着DeepAgents没用。如果你用Claude作为底层模型DeepAgents的“规划-执行-验证”框架依然能带来流程上的收益——它的强项在架构严密性和可操控性Claude的强项在单点智能。两者不是对立的关系而是可以组合的。8.3 用LangGraph手写一个简易Agent为了不空谈我用LangGraph写一个带“搜索计算汇总”能力的简易Agent核心代码展示最简状态流转from langgraph.graph import StateGraph, END from typing import TypedDict, Annotated class AgentState(TypedDict): question: str intermediate_result: str final_answer: str def plan_node(state: AgentState): # 根据问题类型决定调用哪个工具实际项目中这里会调用LLM做意图识别 return {intermediate_result: 需要先搜索背景资料} def search_node(state: AgentState): # 调用搜索工具简化为直接返回固定字符串 return {intermediate_result: 找到相关资料LangGraph是专为复杂Agent设计的图框架} def final_node(state: AgentState): # 根据检索结果生成最终回答 return {final_answer: LangGraph适合构建复杂Agent因为它支持循环和条件分支} # 构建状态图 builder StateGraph(AgentState) builder.add_node(plan, plan_node) builder.add_node(search, search_node) builder.add_node(final, final_node) builder.set_entry_point(plan) builder.add_edge(plan, search) builder.add_edge(search, final) builder.add_edge(final, END) graph builder.compile() # 调用 result graph.invoke({question: LangGraph和LangChain的区别}) print(result[final_answer])这段代码把LangGraph的核心概念都体现了状态Schema、节点函数、边的连接、图的编译与执行。真实的Agent项目会在这个框架上增加条件边add_conditional_edges、工具注册表、对话历史管理等但骨架就是长这样。9. 常见问题与排查技巧实录9.1 问题速查表我在实践过程中积累了一份LangChain常见问题排查表这里直接分享出来症状可能原因解决方案ImportError: cannot import name xxx版本过旧或依赖缺失升级到最新版本或按报错提示安装对应子包OpenAIError: Connection errorbase_url配错或服务没启动检查Ollama/本地服务是否在运行确认端口号检索结果全是无关内容chunk_size不合理或Embedding模型不合适调小chunk_size换更强的Embedding模型Chroma数据不更新persist_directory已有旧数据不会自动覆盖删除旧目录重新构建或手动调用delete_collectionAgent调用工具后死循环工具返回格式不符合预期给工具增加异常处理在Agent Prompt里明确输出格式中文回答乱码或变英文模型没有指定中文指令在System Prompt中强调“请用中文回答”LangGraph节点数据未更新返回字典里缺少对应字段检查每个节点的返回值是否包含所有需要更新的键流式输出隔很久才一次性吐出模型服务端未开启流式支持确认模型API支持stream参数或检查网络代理9.2 版本升级踩坑实录LangChain 0.1到0.2的升级我踩了不少坑整理几个影响最大的PromptTemplate.from_template变为ChatPromptTemplate.from_messages前者只能处理字符串后者支持多轮消息定义LLMChain类虽然还在但官方主推LCEL写法——用|管道符组合组件TextSplitter的split_text方法返回值类型变了直接索引字符串会报错需要用split_documentsvectorstore.from_documents和as_retriever的参数有小幅变动旧代码不一定兼容最稳妥的方法是升级前先看官方MIGRATION.md或release notes别直接pip install -U langchain。9.3 关于RRF实现去重缺陷的分析搜索热词里有一条“LangChain和LangChain4J的默认RRF实现去重逻辑存在缺陷”这是一个比较细的研究点。RRFReciprocal Rank Fusion是一种融合多个检索结果排名的方法原理是对每个文档在多个结果列表中的排名倒数求和越靠前的文档分越高。LangChain和LangChain4JJava版的LangChain对RRF的实现我实测下来在“两个检索源返回相同文档但metadata不同”的场景下会出现同一文档被重复展示的情况。原因在于它们的融合逻辑只关注page_content文本没有把metadata里的唯一标识字段纳入去重判断。如果两条来源内容一样、但metadata里标注了不同来源路径融合时就会当成两条记录保留。解决方案很简单在构建检索器之前把每个Document的metadata里加上统一的doc_id或者自己在融合后按page_content去重。如果你在做多路召回比如同时用关键词检索向量检索再融合建议提前处理这个坑不然用户看到同一个答案出现两遍体验会很奇怪。10. 个人实践经验与收尾分享写到这我回忆了一下自己从零上手LangChain的经历最想说的一点是别被框架演进的速度吓到核心思想三五年内不会变。LangChain从0.1到0.2再到拆分LangGraph变的都是API形态和工程封装“文档切片—向量化—检索—组装Prompt—生成”这条主线一直没变过。你把这套思想吃透换个框架也就两三天的事。给大家几点我认为最有价值的建议第一按需学习不要试图一上来掌握所有组件。先用ChatPromptTemplate ChatOpenAI StrOutputParser搭出能跑的最小应用再逐步引入向量库、Agent、LangGraph。每次只学一个概念配合一个实际场景练手。第二一定要会看LangSmith的追踪日志。LangChain生态里最有价值的一块能用图形化方式展示Prompt最终组装成了什么、模型返回了什么、检索到了哪些片段。排查线上问题时这个工具能让排查时间缩短一半。第三RAG项目优先调分段参数再调模型和Prompt。很多人做知识库效果不好就去换Embedding模型或者换大模型其实80%的问题都出在文本分割和检索环节。你把chunk_size、chunk_overlap、k值调对效果立刻不一样。第四多写代码多实测少看“最佳实践”。LangChain的Context Window、模型能力、业务场景各不相同参数没有银弹。我的习惯是写一组测试问题集每次改动都跑一遍用表格记录效果用数据说话而不是凭直觉调参。这篇文章提到的代码都可以直接拿去改从最小跑通的Chain到完整的RAG方案再到LangGraph的Agent骨架覆盖了初学者最短的上手路径。我自己在几次迭代里也重新确认了一件事LangChain的最大价值不是“帮你调模型”而是“帮你搭了一套工程化的架子”很多潜在的技术问题上下文管理、状态流转、工具编排都被框架抽象了。剩下的就是业务视角的思考——你要解决什么问题数据长什么样怎么切怎么喂——这些还是得靠人来做。工具越顺手越要清楚自己要往哪走。
返回列表