LangChain 1.3实战:从零构建具备工具调用与记忆的智能体
2026/8/2 17:55:06 网站建设 项目流程

在实际项目中集成大语言模型时,开发者常常面临一个核心矛盾:一方面,我们希望利用LLM强大的理解和生成能力;另一方面,我们又需要它能够稳定、可靠地调用外部工具、访问私有数据并执行复杂逻辑。直接调用模型API往往只能完成简单的对话,而要实现一个能自主规划、使用工具、处理数据的智能体,则需要大量的胶水代码来处理提示词工程、工具调用、状态管理和错误处理。

LangChain正是为了解决这一工程化难题而生的框架。它不是一个单一的库,而是一套用于构建由语言模型驱动的应用程序的完整工具链和抽象层。对于开发者而言,LangChain的核心价值在于将与大模型交互的常见模式(如问答、摘要、数据提取)和复杂流程(如多步推理、工具调用、记忆管理)标准化、模块化,从而让开发者能够专注于业务逻辑,而非底层通信细节。

本文将以LangChain 1.3版本为基础,从零开始,带你完成一个智能体(Agent)从模型初始化到构建完整工作流的全过程。我们将不仅关注如何让代码跑起来,更会深入解释每一步的设计意图、关键参数的影响以及生产环境中可能遇到的坑。无论你是希望快速上手LangChain进行应用开发,还是为相关技术面试做准备,这篇文章都将提供一条清晰的实践路径。

1. 理解LangChain的核心抽象:组件与链

在开始写代码之前,必须理解LangChain的几个核心抽象。这些抽象是框架的骨架,混淆它们会导致后续配置和调试异常困难。

1.1 模型I/O:与LLM对话的标准化接口

模型I/O层是LangChain与各种大模型交互的桥梁。它主要包含三个部分:模型(Models)提示词(Prompts)输出解析器(Output Parsers)

  • 模型(LLMs/ChatModels):这是对底层大模型API的封装。LLM模型接收字符串并返回字符串(如GPT-3的text-davinci-003),而ChatModel则接收消息列表并返回消息(如GPT-4、Claude等)。LangChain通过统一的接口调用它们,屏蔽了不同供应商API的差异。
  • 提示词(Prompts):直接拼接字符串构建提示词既脆弱又难以维护。LangChain的PromptTemplate允许你创建带有变量的模板,例如"请总结关于{topic}的内容:"。更高级的ChatPromptTemplate则用于构建结构化的消息序列(如System、Human、AI消息)。
  • 输出解析器(Output Parsers):LLM的输出是文本,但程序需要结构化的数据。OutputParser负责将模型的文本输出解析成我们需要的格式,比如JSON对象、Python列表,或者一个自定义的Pydantic模型。

这三者共同构成了一个可预测的输入输出管道:提示词模板 + 变量 -> 填充后的提示词 -> 发送给模型 -> 原始文本输出 -> 解析为结构化数据

1.2 记忆(Memory):让对话拥有上下文

普通的API调用是无状态的。Memory组件赋予了链或智能体记住历史交互的能力。常见的Memory类型包括:

  • ConversationBufferMemory: 简单地将所有对话历史保存在一个缓冲区中。
  • ConversationBufferWindowMemory: 只保留最近K轮对话。
  • ConversationSummaryMemory: 对历史对话进行摘要,以节省Token并保留长期上下文。
  • VectorStoreRetrieverMemory: 将历史信息存入向量数据库,需要时通过检索召回。

选择哪种Memory取决于你的应用场景。对于长文档问答,BufferWindow可能就够了;对于多轮、复杂的对话,SummaryMemoryVectorStoreMemory更为合适。

1.3 链(Chains):将组件组合成可执行序列

Chain是LangChain中最核心的编排概念。它代表了对一个组件的调用或对多个组件的序列化调用。最简单的链是LLMChain,它组合了一个PromptTemplate、一个LLM和一个可选的OutputParser

# 伪代码示例:一个简单的LLMChain from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from langchain_openai import ChatOpenAI prompt = PromptTemplate.from_template("请用一句话介绍{product}。") llm = ChatOpenAI(model="gpt-3.5-turbo") chain = LLMChain(llm=llm, prompt=prompt) # 运行链 result = chain.invoke({"product": "LangChain"}) print(result["text"])

但链的真正威力在于组合。你可以通过SimpleSequentialChainSequentialChain将多个链串联起来,前一个链的输出作为后一个链的输入。例如,可以先有一个链总结文档,再用另一个链根据总结回答问题。

1.4 智能体(Agents)与工具(Tools):让LLM学会使用外部能力

智能体是LangChain的“杀手级”特性。一个智能体由以下几部分组成:

  1. LLM:作为智能体的“大脑”,负责规划和决策。
  2. 工具(Tools):智能体可以调用的函数。一个工具通常是一个Python函数,它执行特定的任务,如搜索网络、查询数据库、执行计算或调用API。
  3. 工具包(Toolkits):一组相关工具的集合。
  4. 智能体执行器(Agent Executor):这是运行智能体的运行时环境。它负责调用LLM,解析其关于使用哪个工具的决策,执行工具,将结果反馈给LLM,并循环此过程,直到LLM给出最终答案。

智能体的工作流可以概括为:问题 -> LLM思考(决定使用哪个工具及输入)-> 执行工具 -> 观察结果 -> 再次思考 -> ... -> 给出最终答案。这使LLM的能力突破了其训练数据的限制,能够处理实时信息、私有数据和复杂计算。

2. 环境准备与项目初始化

在开始编码前,我们需要一个干净、可复现的Python环境。强烈建议使用虚拟环境来管理依赖。

2.1 创建虚拟环境与安装依赖

首先,创建一个新的项目目录并进入。

mkdir langchain-agent-tutorial && cd langchain-agent-tutorial

使用venv创建虚拟环境(Python 3.8+)。

python -m venv venv

激活虚拟环境:

  • Linux/macOS:source venv/bin/activate
  • Windows:venv\Scripts\activate

激活后,命令行提示符前通常会显示(venv)

接下来,安装核心依赖。LangChain 1.x 版本后,许多集成(如与OpenAI、向量数据库的集成)被拆分到了独立的langchain-*包中。我们需要安装核心包和可能用到的社区包。

# 安装LangChain核心包 pip install langchain # 安装LangChain社区包(包含许多第三方工具和集成) pip install langchain-community # 安装OpenAI集成(如果你使用OpenAI的模型) pip install langchain-openai # 安装用于解析HTML等网络内容的包 pip install beautifulsoup4 # 安装用于发起HTTP请求的包(许多工具需要) pip install requests # 可选:安装用于环境变量管理的包 pip install python-dotenv

注意:langchain-community包包含了大量由社区维护的工具、LLM集成和向量存储集成。对于生产环境,建议只安装你确切需要的特定集成包(如langchain-openai),以减少依赖冲突和安全风险。这里为了演示方便,安装了社区包。

2.2 配置API密钥

大多数LLM服务(如OpenAI、Anthropic)都需要API密钥。永远不要将密钥硬编码在代码中。推荐使用环境变量管理。

在项目根目录创建一个名为.env的文件:

# .env 文件 OPENAI_API_KEY=sk-your-openai-api-key-here # 其他API密钥,如SERPAPI_KEY、TAVILY_API_KEY等,可按需添加

然后在你的Python代码开头,使用dotenv加载这些变量:

from dotenv import load_dotenv import os load_dotenv() # 从 .env 文件加载环境变量 openai_api_key = os.getenv("OPENAI_API_KEY")

2.3 初始化LangSmith(可选但强烈推荐)

LangSmith是LangChain官方提供的调试、测试和监控平台。它能可视化地追踪每次链或智能体的调用步骤、输入输出、Token消耗和延迟,是开发和排查问题的利器。

  1. 访问 LangSmith官网 并注册。
  2. 在设置中创建API密钥。
  3. .env文件中添加:
    LANGCHAIN_TRACING_V2=true LANGCHAIN_ENDPOINT=https://api.smith.langchain.com LANGCHAIN_API_KEY=ls-your-langsmith-api-key-here LANGCHAIN_PROJECT=your-project-name # 设置项目名,便于归类

配置完成后,当你运行代码时,调用轨迹会自动上传到LangSmith,你可以在网页上查看详细的执行过程。

3. 从零构建你的第一个智能体

我们将构建一个能够回答实时问题的智能体,它可以使用网络搜索工具来获取最新信息。

3.1 步骤一:初始化语言模型

我们以OpenAI的Chat模型为例。确保你的.env文件中已正确设置OPENAI_API_KEY

# agent_demo.py from langchain_openai import ChatOpenAI # 初始化Chat模型 # temperature控制创造性,0.0更确定,1.0更随机。对于工具调用,通常设低一些。 # model_name指定模型版本。 llm = ChatOpenAI( model="gpt-3.5-turbo", temperature=0, openai_api_key=os.getenv("OPENAI_API_KEY") )

关键参数解释:

  • model: 指定使用的模型。对于工具调用,gpt-3.5-turbogpt-4系列是常见选择,因为它们对函数调用(Function Calling)有良好支持。
  • temperature: 生成文本的随机性。在需要稳定、可重复工具调用的Agent场景,通常设置为0或一个较低的值(如0.1)。
  • openai_api_key: 从环境变量传入,避免密钥泄露。
  • max_tokens: 限制模型单次响应的最大Token数,防止响应过长。
  • streaming: 设置为True可以启用流式输出,适合需要实时显示的场景。

3.2 步骤二:定义工具(Tools)

工具是智能体的“手”和“脚”。LangChain社区提供了大量预定义工具,我们也可以自定义工具。

首先,我们使用一个预定义的网络搜索工具。这里以TavilySearchResults为例,它需要一个 Tavily 的API密钥(免费注册有额度)。你也可以使用SerpAPIDuckDuckGoSearchRun等。

# 先安装Tavily集成包 # pip install langchain-tavily-search from langchain_community.tools.tavily_search import TavilySearchResults # 初始化搜索工具 # Tavily是一个专为AI优化的搜索引擎API search_tool = TavilySearchResults( tavily_api_key=os.getenv("TAVILY_API_KEY"), # 需要在.env中配置 max_results=3 # 每次搜索返回的结果数 )

自定义工具示例:假设我们还需要一个计算器工具。

from langchain.tools import tool from math import sqrt, log10, sin, cos, tan, radians @tool def calculator(expression: str) -> str: """执行数学计算。输入应为一个可被Python `eval()`安全执行的数学表达式字符串,仅支持基本算术、math模块中的sqrt, log10, sin, cos, tan(角度需先转弧度)。例如:‘3 + 5 * 2’, ‘sqrt(16)’, ‘sin(radians(30))’。""" # 安全警告:在生产环境中,直接使用eval是危险的,可能造成代码注入。 # 这里仅为演示。实际应用应使用安全的表达式解析库(如`asteval`)或严格限制字符集。 allowed_names = {"sqrt": sqrt, "log10": log10, "sin": sin, "cos": cos, "tan": tan, "radians": radians} try: # 使用一个限制性的环境来执行表达式 result = eval(expression, {"__builtins__": {}}, allowed_names) return str(result) except Exception as e: return f"计算错误:{e}"

@tool装饰器会自动将函数转换为LangChain能识别的Tool对象。docstring非常重要,LLM会根据它来决定何时以及如何使用这个工具。

3.3 步骤三:创建智能体执行器

有了模型和工具,我们需要将它们组装起来。LangChain提供了多种智能体类型(如ZERO_SHOT_REACT_DESCRIPTION,OPENAI_FUNCTIONS,STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION),它们使用不同的提示词模板和推理逻辑。

对于支持函数调用的模型(如OpenAI的gpt-3.5/4),OPENAI_FUNCTIONSSTRUCTURED_CHAT是高效且可靠的选择。

from langchain.agents import create_openai_functions_agent, AgentExecutor from langchain import hub # 从LangChain Hub拉取一个适合OpenAI函数调用的提示词 # 这是一个预定义的、优化过的系统提示词,指导LLM如何使用工具。 prompt = hub.pull("hwchase17/openai-functions-agent") # 定义工具列表 tools = [search_tool, calculator] # 创建智能体 agent = create_openai_functions_agent(llm, tools, prompt) # 创建智能体执行器,它是实际运行智能体的循环控制器 agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, # 开启详细日志,会在控制台打印每一步的思考过程 handle_parsing_errors=True, # 当LLM输出无法解析为工具调用时,尝试处理错误 max_iterations=10, # 限制最大迭代次数,防止无限循环 early_stopping_method="generate", # 当LLM生成最终答案时停止 )

关键参数解释:

  • verbose=True: 开发调试时必开,可以清晰看到Agent的“思考-行动-观察”循环。
  • handle_parsing_errors=True: 当LLM的输出不符合工具调用格式时,尝试让LLM重新生成。这是一个重要的容错机制。
  • max_iterations:必须设置。防止Agent陷入死循环,不断调用工具却无法得出最终答案。
  • early_stopping_method: 通常设为"generate",表示当LLM的响应是一个直接给用户的最终答案(而非工具调用)时,停止循环。

3.4 步骤四:运行与测试

现在,让我们运行这个智能体,问它一个需要结合实时信息和计算的问题。

# 运行智能体 question = "截至今天,苹果公司(AAPL)的股价是多少美元?如果我用5000美元购买,大概能买多少股?(忽略交易费用)" try: result = agent_executor.invoke({"input": question}) print("\n=== 最终答案 ===") print(result["output"]) except Exception as e: print(f"执行过程中出错:{e}")

verbose=True时,你会在控制台看到类似下面的输出,这清晰地展示了Agent的工作流:

> Entering new AgentExecutor chain... 我需要找到苹果公司当前的股价,然后计算5000美元能买多少股。 Action: tavily_search_results_json Action Input: {"query": "Apple Inc AAPL stock price today"} Observation: [{'title': 'Apple Inc. (AAPL) Stock Price Today, Quote & News - Google Finance', 'url': 'https://www.google.com/finance/quote/AAPL:NASDAQ', 'content': 'Apple Inc. (AAPL) stock price today is $182.63 ...'}, ...] Thought: 根据搜索结果,苹果股价大约是182.63美元。现在计算5000美元能买多少股。 Action: calculator Action Input: 5000 / 182.63 Observation: 27.37 Thought: 我得到了计算结果。现在可以给出最终答案。 Final Answer: 截至今天(根据网络信息),苹果公司(AAPL)的股价大约为182.63美元。用5000美元购买,在不考虑交易费用的情况下,大约可以购买27股。 > Finished chain. === 最终答案 === 截至今天(根据网络信息),苹果公司(AAPL)的股价大约为182.63美元。用5000美元购买,在不考虑交易费用的情况下,大约可以购买27股。

4. 深入解析:工具调用、记忆与复杂工作流

4.1 工具调用的底层机制与性能影响

LangChain工具调用与LLM原生Function Call的区别?本质上,它们是一回事。当使用create_openai_functions_agent时,LangChain在后台利用的是OpenAI模型原生的“函数调用(Function Calling)”能力。LLM接收工具(函数)的schema(名称、描述、参数),并在认为需要时,输出一个符合该schema的JSON对象,而不是普通文本。LangChain的Agent Executor捕获这个JSON,找到对应的工具函数并执行。

工具调用的速度受什么影响?

  1. LLM响应延迟:模型生成包含工具调用的响应需要时间,与模型本身和temperature参数有关。
  2. 工具执行时间:如果工具是慢速的(如调用一个慢速API、执行复杂查询),这会成为瓶颈。
  3. 网络往返次数:每次工具调用都意味着一次LLM API调用。一个需要N步工具调用的任务,至少需要N+1次LLM调用(N次思考+1次最终生成),这会显著增加总耗时和成本。
  4. 上下文长度:随着对话历史(记忆)和工具结果的累积,提示词会变长,可能影响LLM的处理速度和Token消耗。

优化建议:

  • 为工具编写清晰、精确的description,帮助LLM准确判断何时使用。
  • 对于耗时工具,考虑异步执行或设置超时。
  • 使用max_iterations严格限制循环次数。
  • 对于复杂但固定的流程,考虑使用Chain而非Agent,因为Chain的执行路径是确定的,更高效。

4.2 为智能体添加记忆(Memory)

要让智能体在多轮对话中记住上下文,需要将Memory集成到AgentExecutor中。

from langchain.memory import ConversationBufferMemory # 创建一个对话记忆缓冲区 memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) # 在创建AgentExecutor时传入memory agent_executor_with_memory = AgentExecutor( agent=agent, tools=tools, memory=memory, verbose=True, handle_parsing_errors=True, max_iterations=10, ) # 现在进行多轮对话 result1 = agent_executor_with_memory.invoke({"input": "你好,我叫小明。"}) print(result1["output"]) # 可能回复:你好小明! result2 = agent_executor_with_memory.invoke({"input": "你还记得我的名字吗?"}) # 因为memory中保存了上一轮对话,LLM能回答出“你叫小明”。 print(result2["output"])

记忆的内容会被自动添加到发送给LLM的提示词中,使其具备上下文感知能力。

4.3 构建顺序工作流:LangChain与LangGraph的选择

LangChain vs. LangGraph vs. LangSmith

  • LangChain:核心框架,提供构建LLM应用的基础模块(模型、提示词、链、智能体、记忆)。
  • LangGraph:基于LangChain,专注于构建有状态、多参与者、可循环的复杂工作流。它用图(Graph)来定义节点(Node)和边(Edge),非常适合需要严格流程控制、分支、循环、并行处理的应用。如果你的智能体需要更复杂的决策循环(比如一个模拟游戏、一个有多步审批的流程),LangGraph是更好的选择。
  • LangSmith:开发运维平台,用于调试、测试、监控和部署LangChain/LangGraph应用。

何时用Chain,何时用Agent,何时用LangGraph?

  • 确定性流程用Chain:如果任务的步骤和顺序是固定的、可预测的(例如:获取数据 -> 清洗数据 -> 总结数据),使用SequentialChain。它更高效、稳定。
  • 不确定性决策用Agent:如果任务需要根据输入内容动态决定下一步做什么、使用哪个工具(例如:回答一个可能涉及搜索、计算、查数据库的开放性问题),使用Agent
  • 复杂状态与循环用LangGraph:如果工作流包含复杂的状态管理、多个“参与者”(不同的LLM或工具)、显式的循环或条件分支(例如:一个客服对话系统,需要根据用户意图在不同子流程间跳转),使用LangGraph

5. 生产环境注意事项与常见问题排查

将LangChain应用部署到生产环境,需要考虑更多因素。

5.1 配置管理

切勿将API密钥、数据库连接字符串等敏感信息硬编码。使用环境变量或专业的配置管理服务(如HashiCorp Vault、AWS Secrets Manager)。在代码中通过os.getenv()读取。

# 生产环境配置示例 import os from langchain_openai import ChatOpenAI from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OpenAIEmbeddings def get_llm(): api_key = os.environ.get("OPENAI_API_KEY") if not api_key: raise ValueError("OPENAI_API_KEY 环境变量未设置") return ChatOpenAI( model=os.environ.get("OPENAI_MODEL", "gpt-4"), temperature=float(os.environ.get("LLM_TEMPERATURE", "0.1")), openai_api_key=api_key, max_tokens=int(os.environ.get("MAX_TOKENS", "2000")), timeout=30, # 设置请求超时 max_retries=2, # 设置重试次数 )

5.2 错误处理与鲁棒性

智能体可能因为多种原因失败:工具调用异常、LLM输出无法解析、达到最大迭代次数等。必须进行完善的错误处理。

from langchain.schema import AgentFinish, OutputParserException try: result = agent_executor.invoke( {"input": question}, config={"callbacks": [your_callback_handler]} # 可配置回调 ) except OutputParserException as e: # 处理LLM输出解析失败 logging.error(f"解析Agent输出失败: {e}") # 可以尝试让Agent重试,或返回一个友好的用户消息 result = {"output": "抱歉,我处理您的请求时遇到了理解上的困难,请尝试换一种方式提问。"} except Exception as e: # 处理其他未知错误 logging.exception(f"Agent执行发生未知错误: {e}") result = {"output": "系统暂时开小差了,请稍后再试。"}

AgentExecutor中,合理设置max_iterationshandle_parsing_errorsmax_execution_time是防止失控的重要手段。

5.3 性能与成本监控

  • Token消耗:监控每次调用的输入/输出Token数。使用OpenAICallbackHandler等回调可以自动收集这些数据。
  • 延迟:记录每个步骤(LLM调用、工具执行)的耗时。
  • 成本估算:根据Token使用量和模型定价估算成本。对于高频应用,这至关重要。
  • LangSmith:将这些监控任务交给LangSmith是最省心的方式,它提供了完整的可视化追踪。

5.4 常见问题排查清单

当你的智能体表现不如预期时,可以按照以下清单排查:

问题现象可能原因检查点解决方案
Agent不调用任何工具,直接回答1. 工具描述不清晰。
2. LLM的temperature过高,导致输出不稳定。
3. 提示词(Prompt)未正确引导使用工具。
1. 检查工具的description是否准确描述了功能和使用场景。
2. 将temperature设为0再测试。
3. 查看LangSmith轨迹,检查发送给LLM的完整提示词。
1. 重写工具描述,使其更精确。
2. 使用更低的temperature
3. 尝试不同的Agent类型或自定义提示词。
Agent陷入无限循环,不断调用同一个工具1. 工具返回的结果无法让LLM得出最终结论。
2.max_iterations设置过高或未设置。
3. 工具功能有缺陷,返回错误或无关信息。
1. 查看每次工具调用的Observation内容。
2. 检查AgentExecutormax_iterations参数。
3. 单独测试工具函数,确保其返回正确结果。
1. 优化工具,使其返回更结构化、信息量更足的结果。
2. **务必设置合理的max_iterations(如10)。
3. 修复工具逻辑,增加错误处理。
报错KeyError: ‘xxx‘或解析错误1. LLM输出的工具调用参数格式错误,与预期schema不匹配。
2. 自定义工具的args_schema定义有误。
1. 开启verbose=True查看LLM输出的原始Action Input
2. 检查自定义工具的参数定义(Pydantic模型)。
1. 确保handle_parsing_errors=True以增加容错。
2. 简化工具参数,或为LLM提供更清晰的示例。
执行速度非常慢1. 工具本身是慢速操作(如网络请求)。
2. 上下文过长,导致LLM处理慢。
3. 网络延迟高。
1. 为工具调用添加超时设置。
2. 检查memory是否积累了过多内容。
3. 使用本地模型或更近的API端点。
1. 异步执行工具,或使用缓存。
2. 使用ConversationSummaryMemoryConversationBufferWindowMemory限制历史长度。
3. 考虑对LLM调用进行批处理或优化提示词。
在LangSmith中看不到轨迹1. 环境变量LANGCHAIN_TRACING_V2未设置或为false
2. API密钥或端点配置错误。
3. 代码中未正确初始化回调。
1. 确认.env文件已加载,且变量值正确。
2. 检查LangSmith网站上的API密钥和项目设置。
1. 确保在代码最开头加载dotenv并设置环境变量。
2. 可以尝试在代码中显式设置os.environ[“LANGCHAIN_TRACING_V2”] = “true”

6. 扩展方向与学习建议

掌握了基础智能体的构建后,你可以向以下几个方向深入:

  1. 集成更多数据源:学习使用Document Loaders加载PDF、Word、网页等文档,结合Text SplittersEmbeddingsVectorstores构建RAG(检索增强生成)系统,让智能体能够基于你的私有数据回答问题。
  2. 探索复杂工作流:学习使用LangGraph来构建具有复杂状态和分支的工作流,例如模拟一个多角色对话系统或一个自动化业务流程。
  3. 自定义与优化:深入研究Custom AgentsCustom Tools,创建完全符合你业务逻辑的组件。优化提示词工程,提升任务执行的准确率和效率。
  4. 部署与运维:研究如何将LangChain应用打包为API服务(使用FastAPI、Flask),并部署到云服务器或容器平台。建立完整的监控、日志和告警体系。
  5. 探索多模态:结合视觉模型(如GPT-4V)和多模态工具,构建能理解和处理图像、音频的智能体。

学习路径建议:从官方文档和教程开始,然后通过克隆和修改示例项目来实践。遇到问题时,优先查看LangSmith的调用轨迹,它能帮你精准定位问题发生在哪个环节(是提示词问题、工具问题还是LLM输出问题)。记住,构建可靠的AI应用是一个迭代过程,需要不断地测试、观察和调整。

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

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

立即咨询