ARTICLE DETAIL

资讯详情

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

LangChain 1.x 实战指南:从零构建智能代理与 RAG 问答系统

LangChain 1.x 实战指南:从零构建智能代理与 RAG 问答系统

1. 项目概述:为什么是 LangChain 1.x?

如果你最近在折腾大语言模型应用,大概率听过 LangChain 这个名字。它就像一个为 LLM 应用开发准备的“瑞士军刀”,把调用模型、处理数据、管理对话流程这些繁琐的活儿都封装好了。但你可能也发现了,LangChain 的版本迭代有点快,社区里关于 0.x 和 1.x 的讨论也让人有点迷糊。今天咱们就抛开那些复杂的概念,直接上手最新的 LangChain 1.x,看看它到底怎么用,以及为什么说现在从 1.x 开始学是更明智的选择。

简单来说,LangChain 1.x 不是一个简单的版本号升级,它代表了一次重大的 API 重构和设计理念的进化。0.x 版本虽然功能强大,但 API 设计上存在一些历史包袱,模块间的耦合度较高,对于新手来说学习曲线陡峭。而 1.x 版本的核心目标就是“简化”和“模块化”,它提供了更清晰、更一致的接口,让开发者能像搭积木一样构建应用。举个例子,以前你可能需要写一堆胶水代码来连接不同的组件,现在很多功能通过声明式的LCEL就能轻松搞定。所以,无论你是刚接触 LLM 开发的新手,还是从 0.x 迁移过来的老手,直接切入 1.x 都是性价比最高的选择。它能让你更快地构建出稳定、可维护的应用,而不是把时间浪费在理解和适配旧的、即将被淘汰的 API 上。

2. 环境准备与核心概念扫盲

2.1 搭建你的 Python 开发环境

工欲善其事,必先利其器。在开始写代码之前,一个干净、隔离的 Python 环境是必须的。我强烈推荐使用condavenv来管理你的项目依赖,这能避免不同项目间的包版本冲突,是专业开发的基本素养。

对于大多数开发者,我建议直接使用venv,因为它是 Python 3.3 之后内置的,无需额外安装。打开你的终端或命令行,跟着以下步骤操作:

# 1. 为你的 LangChain 项目创建一个新目录并进入 mkdir my-langchain-project && cd my-langchain-project # 2. 创建虚拟环境,环境名通常叫 `venv` 或 `.venv` python -m venv venv # 3. 激活虚拟环境 # 在 Windows 上: venv\Scripts\activate # 在 macOS/Linux 上: source venv/bin/activate

激活后,你的命令行提示符前通常会显示(venv),这表明你已经在这个隔离的环境中工作了。接下来安装 LangChain:

pip install langchain

但请注意,仅仅安装langchain是不够的。LangChain 本身是一个框架,它需要与具体的大模型“连接”才能工作。因此,你通常还需要安装对应模型供应商的 SDK。例如,如果你要使用 OpenAI 的模型,就需要额外安装openai库:

pip install openai

注意langchain包是一个“元包”,它包含了许多核心接口和工具,但一些特定的集成(如与向量数据库、特定工具链的深度集成)可能需要安装额外的子包,如langchain-communitylangchain-openai等。在 1.x 版本中,这种模块化设计更加清晰。对于快速上手,先安装langchain和对应的模型 SDK(如openai)就足够了。

2.2 理解 LangChain 1.x 的核心构建块

安装好环境后,我们先不急着写代码,花几分钟理解一下 LangChain 1.x 最核心的几个抽象概念。这能让你后续的代码编写事半功倍。1.x 版本的设计非常清晰,主要围绕以下几个核心组件展开:

  1. 模型 I/O (Model I/O):这是与 LLM 交互的基础层。主要包括:

    • Prompt 模板:用于构建和格式化发送给模型的指令。1.x 的模板更加强大和灵活。
    • 语言模型:大模型本身,如 OpenAI 的 GPT、Anthropic 的 Claude 等。在 LangChain 中,它们被抽象成统一的BaseLanguageModel接口。
    • 输出解析器:用于将模型返回的非结构化文本(字符串)解析成你程序里需要的结构化数据(如 Python 对象、JSON 等)。
  2. 检索 (Retrieval):当你的问题需要基于特定知识库(如公司文档、产品手册)来回答时,就需要用到检索。这通常涉及将文档“切割”成片段,转换成“向量”存入数据库,提问时再找出最相关的片段送给模型。这是构建 RAG 应用的核心。

  3. 链 (Chains):链是将多个组件(模型、提示词、工具等)按特定顺序组合起来,完成一个复杂任务的“配方”。例如,“总结网页内容”这个任务,可能就是一个“获取网页文本 -> 提取关键信息 -> 用模型总结”的链。在 1.x 中,LCEL成为了构建链的首选和推荐方式,它让链的创建和组合变得异常简单和直观。

  4. 代理 (Agents):代理是 LangChain 中最具想象力的部分。一个代理内置了一个“大脑”(通常是 LLM)和一套“工具”(如搜索网络、查询数据库、执行代码)。大脑根据用户的目标,自主决定调用哪个工具、按什么顺序调用,直到完成任务。这实现了真正的“自主”AI 应用。我们后面会重点体验如何使用create_agent来快速构建一个代理。

  5. 记忆 (Memory):为了让对话或交互具有连续性,记忆组件负责存储和管理历史对话信息,并在新的交互中将其提供给模型。

理解这些组件后,你会发现 LangChain 1.x 的应用开发,本质上就是选择合适的“积木”(组件),并用“胶水”(LCEL 或链)把它们按照业务逻辑粘合起来的过程。

3. 从零开始:你的第一个 LangChain 应用

理论说得再多,不如动手跑一行代码。让我们从最简单的“模型调用”开始,逐步增加复杂度。

3.1 基础模型调用与对话

首先,你需要一个 LLM 的 API 密钥。这里我们以 OpenAI 为例(其他模型如 Anthropic、智谱 AI 等操作类似)。确保你已经在环境变量中设置了OPENAI_API_KEY

import os from langchain_openai import ChatOpenAI # 1. 初始化聊天模型 # 推荐使用 `ChatOpenAI` 而非旧版的 `OpenAI`,它专为对话优化。 # `model_name` 指定模型,如最新的 “gpt-4o”, `temperature` 控制创造性(0-1,越高越随机)。 llm = ChatOpenAI(model_name="gpt-4o", temperature=0.7) # 2. 发起一次简单的对话 response = llm.invoke("请用一句话介绍 LangChain。") print(response.content) # 输出可能类似于:“LangChain 是一个用于开发由语言模型驱动的应用程序的框架。”

这段代码完成了最核心的交互:问模型一个问题,得到回答。llm.invoke是 1.x 中同步调用的标准方法。你可能会问,为什么不直接用 OpenAI 的官方 SDK?LangChain 的价值在于,当你明天想换用 Claude 或国产大模型时,只需要将ChatOpenAI替换成ChatAnthropicChatZhipuAI,后面的代码几乎不用改。这就是抽象层带来的可移植性优势。

3.2 使用 Prompt 模板提升交互质量

直接传递字符串给模型在简单场景下可行,但在复杂应用中,我们需要结构化、可复用的提示词。这就是 Prompt 模板的用武之地。

from langchain_core.prompts import ChatPromptTemplate # 1. 定义一个提示词模板 # 使用 `ChatPromptTemplate.from_messages`,这是 1.x 推荐的方式,支持多角色对话。 prompt_template = ChatPromptTemplate.from_messages([ ("system", "你是一位专业的{domain}专家,回答问题时需严谨且易于理解。"), ("human", "请解释一下什么是{concept}?") ]) # 2. 格式化模板,传入变量 formatted_prompt = prompt_template.invoke({ "domain": "软件开发", "concept": "面向对象编程" }) print(formatted_prompt.to_string()) # 输出: # System: 你是一位专业的软件开发专家,回答问题时需严谨且易于理解。 # Human: 请解释一下什么是面向对象编程? # 3. 将格式化后的提示词传给模型 llm = ChatOpenAI(model="gpt-4o", temperature=0.5) response = llm.invoke(formatted_prompt) print(response.content)

实操心得:在 1.x 中,ChatPromptTemplate比旧的PromptTemplate更强大,因为它天然支持 System、Human、AI 等多种消息角色,这对于构建复杂的多轮对话应用至关重要。invoke方法返回的是一个PromptValue对象,可以直接传递给模型的invoke方法,这种一致性让代码非常优雅。

3.3 使用 LCEL 构建你的第一个链

前面我们把模型和提示词分开操作,现在用LCEL把它们“链”起来。LCEL 的语法非常直观,使用管道符|

from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from langchain_core.output_parsers import StrOutputParser # 1. 定义组件 prompt = ChatPromptTemplate.from_template("请将以下文本翻译成{language}:{text}") llm = ChatOpenAI(model="gpt-4o") output_parser = StrOutputParser() # 一个简单的将模型输出解析为字符串的解析器 # 2. 使用 LCEL 构建链 # 语法: prompt | llm | output_parser # 读作:将输入传给 prompt 格式化,再给 llm 处理,最后用 output_parser 解析。 translation_chain = prompt | llm | output_parser # 3. 调用链 result = translation_chain.invoke({ "language": "法语", "text": "你好,世界!" }) print(result) # 输出: Bonjour le monde!

看,我们只用一行代码prompt | llm | output_parser就创建了一个功能完整的翻译链。LCEL 的魅力在于它的可组合性。如果你想在翻译前先总结一下原文,只需要再组合一个总结链即可。这种声明式的编程风格,让复杂的 AI 应用逻辑变得清晰易懂。

4. 核心实战:打造你的第一个智能代理

代理是 LangChain 的“杀手级”功能。想象一下,你告诉 AI “帮我查一下北京明天天气,然后根据天气推荐一件合适的穿搭”,AI 能够自己决定先去调用天气查询工具,拿到结果后再调用一个穿衣推荐工具,最后把整合的结果给你。这就是代理。

在 1.x 中,创建代理的标准方式是使用create_react_agentReAct是“推理+行动”的框架,是当前最主流的代理范式之一。

4.1 为代理准备工具

代理自己不会搜索网络或计算,它需要“工具”。我们先定义两个简单的工具:

from langchain.agents import tool import datetime # 使用 `@tool` 装饰器可以轻松地将一个函数转化为 LangChain 可识别的工具。 # `description` 至关重要!代理的大脑(LLM)就是根据这个描述来决定何时使用这个工具。 @tool def get_current_time(placeholder: str) -> str: """当用户询问当前时间、日期或今天星期几时,调用此工具。参数 placeholder 无实际用处,仅为满足工具格式要求。""" now = datetime.datetime.now() return f"当前时间是:{now.strftime('%Y-%m-%d %H:%M:%S')},今天是星期{['一','二','三','四','五','六','日'][now.weekday()]}。" @tool def calculate_length(text: str) -> str: """当用户询问一段文本的长度、字符数或字数时,调用此工具。""" char_count = len(text) word_count = len(text.split()) return f"文本 '{text[:20]}...' 的字符数为 {char_count},单词数(按空格分割)约为 {word_count}。"

4.2 创建并运行代理

有了工具,我们就可以创建代理了。这里会用到create_react_agent函数。

from langchain.agents import create_react_agent from langchain_openai import ChatOpenAI from langchain.agents import AgentExecutor # 1. 准备模型和工具列表 llm = ChatOpenAI(model="gpt-4o", temperature=0) tools = [get_current_time, calculate_length] # 2. 创建 ReAct 代理 # 需要提供一个 `prompt` 参数,LangChain 提供了预制的、针对 ReAct 框架优化的提示词模板。 from langchain.agents import load_tools # 注意:`create_react_agent` 期望一个 `BasePromptTemplate`,我们可以使用内置的助手提示。 from langchain_core.prompts import PromptTemplate # 这是一个简化的 ReAct 提示模板。在实际复杂应用中,建议使用 LangChain Hub 上更完善的版本。 react_prompt = PromptTemplate.from_template(""" 你是一个乐于助人的助手,可以访问以下工具: {tools} 请严格遵循以下格式回答问题: 问题:用户提出的问题 思考:你需要思考如何一步步解决问题。你可以使用工具。 行动:要使用的工具名称,必须是[{tool_names}]中的一个。 行动输入:工具的输入,必须是一个简单的字符串。 观察:工具返回的结果 ... (这个“思考/行动/行动输入/观察”的循环可以重复多次) 思考:我现在知道了最终答案 最终答案:对用户问题的最终、完整的回答 开始! 问题:{input} 思考:{agent_scratchpad} """) agent = create_react_agent(llm, tools, react_prompt) # 3. 创建代理执行器 # AgentExecutor 是驱动代理运行的核心,它负责处理代理的思考循环、工具调用和错误处理。 agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True) # 4. 运行代理! result = agent_executor.invoke({ "input": "请问现在几点了?另外,'Hello, LangChain!' 这句话有多长?" }) print(result["output"])

当你运行这段代码并将verbose=True时,你会在控制台看到代理完整的思考过程:

> 进入新的 AgentExecutor 链... 思考:用户问了两个问题:当前时间和文本长度。我有两个工具:`get_current_time` 和 `calculate_length`。我应该先回答时间,再计算文本长度。 行动:get_current_time 行动输入:现在 观察:当前时间是:2024-05-20 14:30:15,今天是星期二。 思考:我已经得到了当前时间。现在需要计算文本长度。 行动:calculate_length 行动输入:Hello, LangChain! 观察:文本 'Hello, LangChain!...' 的字符数为 16,单词数(按空格分割)约为 2。 思考:我现在知道了最终答案 最终答案:现在是 2024年5月20日 星期二 下午2点30分15秒。文本 “Hello, LangChain!” 的字符数是16,包含大约2个单词。 > 链结束。

这个过程清晰地展示了代理的“推理-行动”循环。它自己规划了步骤,选择了正确的工具,并整合了结果。

4.3 代理开发中的关键技巧与避坑指南

在实际开发中,打造一个稳定可靠的代理需要注意以下几点:

  1. 工具描述是灵魂:LLM 完全依赖工具的description来决定是否调用它。描述必须清晰、准确,说明工具的用途、适用场景和输入格式。模糊的描述会导致代理错误调用或拒绝调用。
  2. 处理复杂输入/输出:工具的参数和返回值应尽量简单(字符串、数字、字典)。如果返回复杂对象,代理可能无法理解。必要时,在工具内部将复杂结果格式化成清晰的文本描述。
  3. 控制成本与超时:代理可能会陷入无效的思考循环。务必在AgentExecutor中设置max_iterations(最大迭代次数,默认15)和max_execution_time(最大执行时间)来防止无限循环和意外的高额 API 费用。
  4. 善用verbose模式:在开发调试阶段,一定要开启verbose=True。这是你洞察代理“内心想法”的唯一窗口,能帮你快速定位是提示词问题、工具描述问题还是逻辑问题。
  5. 错误处理:工具调用可能失败(网络错误、API限制)。设置handle_parsing_errors=True可以让执行器在代理输出格式错误时尝试修复,而不是直接崩溃。

5. 进阶整合:构建一个简单的 RAG 问答系统

代理让 AI 有了“手”和“脚”,而 RAG 则给了 AI 一个“外部大脑”。我们结合之前学的,快速构建一个基于本地文档的问答系统。这里我们用Chroma作为向量数据库,OpenAIEmbeddings来生成向量。

# 安装必要的包: pip install langchain-chroma langchain-openai tiktoken from langchain_chroma import Chroma from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.document_loaders import TextLoader # 步骤 1: 加载并处理文档 loader = TextLoader("./my_document.txt") # 假设你有一个文本文件 documents = loader.load() # 将长文档切分成适合嵌入的小块 text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) chunks = text_splitter.split_documents(documents) # 步骤 2: 创建向量数据库 embeddings = OpenAIEmbeddings(model="text-embedding-3-small") # 使用小尺寸嵌入模型以节省成本 vectorstore = Chroma.from_documents(documents=chunks, embedding=embeddings, persist_directory="./chroma_db") # `persist_directory` 会将向量数据库保存到本地,下次无需重新生成 # 步骤 3: 创建检索器 retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) # 检索最相关的3个片段 # 步骤 4: 定义提示词模板,用于将检索到的上下文和问题组合起来 template = """你是一个知识渊博的助手。请仅根据以下提供的上下文信息来回答问题。 如果你在上下文中找不到答案,就诚实地回答你不知道。不要编造信息。 上下文: {context} 问题:{question} 请根据上下文给出答案:""" prompt = ChatPromptTemplate.from_template(template) # 步骤 5: 定义 LLM 和输出解析器 llm = ChatOpenAI(model="gpt-4o", temperature=0) output_parser = StrOutputParser() # 步骤 6: 使用 LCEL 组装 RAG 链 # 这个链的流程:输入问题 -> 用检索器获取上下文 -> 格式化提示词 -> 调用 LLM -> 解析输出 rag_chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | output_parser ) # 步骤 7: 提问 question = "根据文档,LangChain 的主要优势是什么?" answer = rag_chain.invoke(question) print(f"问题:{question}") print(f"答案:{answer}")

这个流程是 RAG 应用的标准范式:加载 -> 分割 -> 嵌入 -> 存储 -> 检索 -> 生成。通过 LCEL,我们用几行清晰的代码就把这个复杂流程串联了起来。RunnablePassthrough()是一个特殊的组件,它负责将输入(这里是question)原封不动地传递到下游。

6. 常见问题与实战调试技巧

在实际使用 LangChain 1.x 的过程中,你肯定会遇到各种问题。这里我总结了一些最常见的“坑”和解决方法。

6.1 版本兼容性与导入错误

问题:代码报错ImportError: cannot import name '...' from 'langchain'或看到LangChainDeprecationWarning

原因:LangChain 1.x 进行了大幅度的模块重构。许多在 0.x 版本中直接从langchain主包导入的类,现在移到了子包中。

解决方案

  • 查阅官方迁移指南:这是最重要的步骤。LangChain 官方提供了详细的迁移说明。
  • 使用正确的导入路径
    • from langchain_openai import ChatOpenAI, OpenAIEmbeddings(替代from langchain.llms import OpenAI)
    • from langchain_community.chat_models import ChatAnthropic(社区维护的集成)
    • from langchain_core.prompts import ChatPromptTemplate(核心提示词模板)
    • 当你不知道从哪导入时,直接去 LangChain API 参考 搜索类名是最快的方法。
  • 安装正确的包:确保你安装了所需的集成包,例如pip install langchain-openai langchain-chroma langchain-community

6.2 代理陷入循环或行为异常

问题:代理不停地调用同一个工具,或者给出与问题无关的奇怪回答。

排查思路

  1. 检查工具描述:这是最常见的原因。确保@tool装饰器里的description字段清晰、无歧义,准确说明了工具的功能和输入格式。用verbose=True模式观察代理的思考过程,看它是否误解了工具描述。
  2. 简化提示词:一开始可以使用官方提供的、经过验证的提示词模板(如从 LangChain Hub 加载)。不要一开始就自定义复杂的提示词。
  3. 调整模型温度:将temperature设为 0 或一个较低的值(如 0.1),可以减少模型的随机性,使代理行为更稳定、可预测。
  4. 限制迭代次数:在AgentExecutor中设置max_iterations=5来强制退出可能出现的死循环。

6.3 RAG 检索效果不佳

问题:问答系统给出的答案不准确,或者经常回答“我不知道”,即使文档里有相关内容。

优化方向

  1. 文本分割策略RecursiveCharacterTextSplitterchunk_sizechunk_overlap是关键参数。块太大,会包含无关信息干扰模型;块太小,可能丢失关键上下文。通常从 500-1000 字符的chunk_size和 50-100 字符的overlap开始尝试。
  2. 检索数量retriever.search_kwargs={“k”: 3}表示返回前 3 个相关片段。对于复杂问题,可以尝试增加到 4 或 5,给模型更多上下文。
  3. 嵌入模型:不同的嵌入模型效果差异很大。OpenAI 的text-embedding-3-small在成本和效果上取得了很好的平衡。对于中文场景,可能需要考虑专门优化的双语或中文嵌入模型。
  4. 提示词工程:在 RAG 提示词中明确指令“仅根据上下文回答”,并设计好上下文和问题的拼接格式,有助于模型更好地利用检索到的信息。

6.4 性能与成本优化

问题:应用响应慢,或者 API 调用费用过高。

实战技巧

  • 缓存:对频繁重复的查询(如相同的用户问题)使用缓存。LangChain 内置了InMemoryCacheSQLiteCache
    from langchain.globals import set_llm_cache from langchain.cache import InMemoryCache set_llm_cache(InMemoryCache())
  • 批处理:如果有大量文档需要嵌入,使用嵌入模型的批处理接口,而不是循环调用单条。
  • 选择合适模型:在原型阶段或简单任务上,使用gpt-3.5-turbo而非gpt-4可以大幅降低成本。对于嵌入,text-embedding-3-smallada-002更便宜且性能更好。
  • 异步调用:对于高并发应用,使用ainvokeabatch等异步方法可以显著提升吞吐量。

走到这里,你已经完成了从环境搭建、基础调用到构建代理和 RAG 系统的完整旅程。LangChain 1.x 的核心思想就是“组合”:用清晰、一致的接口(LCEL)将各种功能模块(模型、提示词、工具、检索器)像管道一样连接起来。我个人的体会是,初期不要追求构建大而全的系统,而是从一个具体的小功能点切入,比如“用一个工具查询天气”,把它跑通,理解数据流。然后逐步添加新的工具、引入记忆、或者换成更复杂的代理逻辑。多利用verbose=True来观察内部过程,这是最好的调试和学习方式。最后,保持关注官方文档和更新,这个生态正在快速发展,但 1.x 的稳定 API 设计已经为你打下了坚实的基础。

返回列表