【Agent】 LangChain Agents 实战:从工具调用到智能决策
本篇会详细介绍 Agent 的工作原理、传统方式和新方式的使用、如何给 Agent 加上记忆,以及实战案例。看完这篇,你就能让大模型真正动起来。
LangChain Agents 实战:从工具调用到智能决策
前言
这篇是 LangChain 系列的学习笔记,这次整理的是第六章 Agents(智能体)模块。
说到 Agent,这可是整个 LangChain 框架最核心的部分了。前面我们学了 Tools(工具),知道了怎么让大模型"分析"要调用哪个工具。但你有没有发现,每次都要我们手动去检查 function_call、手动调用工具、手动把结果返回给大模型……这也太麻烦了吧?
Agent 就是来解决这个问题的。它能自动完成这整个流程:分析问题 → 选择工具 → 执行工具 → 分析结果 → 决定下一步。而且这个过程可以循环进行,Agent 会一直思考、一直调用工具,直到找到最终答案。
本篇会详细介绍 Agent 的工作原理、传统方式和新方式的使用、如何给 Agent 加上记忆,以及实战案例。看完这篇,你就能让大模型真正"动起来"了。
🏠个人主页:山沐与山
文章目录
- 一、Agent是什么?
- 二、传统方式:initialize_agent
- 三、新方式:create_tool_calling_agent和create_react_agent
- 四、Agent加上记忆
- 五、实战案例
- 六、Agent的工作原理
- 七、常见问题
- 八、传统vs新方式对比
- 九、总结
- 热门专栏推荐
一、Agent 是什么?
1.1 一个生动的例子
假设你问 Agent:“美团股价是多少?比 2024 年下跌了多少?”
没有 Agent 的情况(上一篇文章的做法):
- 你:调用大模型分析问题
- 大模型:返回 function_call,说要调用搜索工具
- 你:手动调用搜索工具查美团股价
- 你:把结果传回给大模型
- 大模型:返回 function_call,说要调用计算工具
- 你:手动调用计算工具
- 你:把结果传回给大模型
- 大模型:生成最终答案
累不累?每一步都要你手动操作。
有 Agent 的情况:
result = agent_executor.invoke("美团股价是多少?比2024年下跌了多少?")
print(result)
一行代码,Agent 自动完成上面的所有步骤。这就是 Agent 的威力。
1.2 Agent 的核心组件
一个完整的 Agent 由三部分组成:
Agent = LLM(大脑)+ Tools(工具箱)+ Memory(记忆)
- LLM:负责思考和决策
- Tools:负责执行具体操作(搜索、计算、文件操作等)
- Memory(可选):负责记住对话历史
二、传统方式:initialize_agent
2.1 最简单的例子
from langchain.agents import initialize_agent, AgentType
from langchain.tools import Tool
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_openai import ChatOpenAI
import os
import dotenv
# 加载环境变量
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY1")
os.environ["TAVILY_API_KEY"] = os.getenv("TAVILY_API_KEY")
# 初始化大模型
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 创建搜索工具
search = TavilySearchResults()
search_tool = Tool(
name="Tavily搜索",
func=search.run,
description="用于检索互联网上的信息,特别是获取实时的新闻、天气等时效性信息。输入应该是一个明确的搜索查询。"
)
# 初始化 Agent
agent_executor = initialize_agent(
tools=[search_tool],
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True # 打印详细过程
)
# 开始提问
result = agent_executor.invoke("查询长沙天气")
print(result)
运行这段代码,你会看到类似这样的输出:
> Entering new AgentExecutor chain...
我需要查找长沙的天气信息。
Action: Tavily搜索
Action Input: 长沙天气
Observation: [根据搜索结果,长沙今天晴,温度15-25度...]
Thought: 我现在知道答案了。
Final Answer: 长沙今天天气晴朗,温度在15-25度之间...
> Finished chain.
看到没?Agent 自己完成了整个思考过程:
- Thought(思考):我需要查天气
- Action(行动):使用搜索工具
- Action Input(输入):长沙天气
- Observation(观察):得到搜索结果
- Final Answer(最终答案):整理成人类可读的回答
这就是 ReAct 模式(Reasoning + Acting)的核心思想。
2.2 AgentType 类型
initialize_agent 支持好几种 Agent 类型:
| Agent 类型 | 特点 | 适用场景 |
|---|---|---|
| ZERO_SHOT_REACT_DESCRIPTION | 零样本学习,显示思考过程 | 单次任务,需要看清楚 Agent 的思考 |
| OPENAI_FUNCTIONS | 基于 OpenAI 的 Function Call | 工具名称必须是英文 |
| CONVERSATIONAL_REACT_DESCRIPTION | 支持会话历史 | 多轮对话场景 |
最常用的是 ZERO_SHOT_REACT_DESCRIPTION,因为它的思考过程清晰,方便调试。
2.3 多工具协作
Agent 真正厉害的地方在于可以同时使用多个工具:
from langchain.tools import Tool
# 定义计算器工具
def simple_calculator(expression: str) -> float:
"""执行简单的数学计算"""
return eval(expression)
calc_tool = Tool(
name="Calculator",
func=simple_calculator,
description="用于执行数学计算。输入应该是一个数学表达式,例如 '100 * 0.8'"
)
# 创建包含多个工具的 Agent
agent_executor = initialize_agent(
tools=[search_tool, calc_tool], # 注意:同时传入两个工具
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# 提出需要多步骤的问题
result = agent_executor.invoke("美团股价是多少?比2024年下跌了多少?")
Agent 会自动规划:
- 先用搜索工具查询美团股价
- 再用搜索工具查询 2024 年的股价
- 最后用计算器工具算出跌幅
你只需要提问,Agent 会自己决定用哪个工具、用几次。
2.4 一个小坑:工具名称
如果你用 OPENAI_FUNCTIONS 类型,工具名称必须是英文,不能用中文!
# ❌ 错误写法
search_tool = Tool(
name="Tavily搜索", # 中文名称在 OPENAI_FUNCTIONS 模式下会报错
func=search.run,
description="..."
)
# ✅ 正确写法
search_tool = Tool(
name="tavily_search", # 英文名称
func=search.run,
description="..."
)
为什么?因为 OpenAI 的 Function Call 规范要求函数名必须匹配 ^[a-zA-Z0-9_-]+$ 这个正则表达式。
三、新方式:create_tool_calling_agent 和 create_react_agent
3.1 为什么要换新 API?
上面的 initialize_agent 已经被 LangChain 标记为"弃用"(deprecated)了。官方推荐使用新的 API,原因是:
- 新 API 更灵活,可以自定义提示词模板
- 更容易集成到复杂的工作流中
- 为未来迁移到 LangGraph 做准备
所以如果你是新项目,直接用新 API 就好。
3.2 create_tool_calling_agent(Function Call 模式)
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
# 定义提示词模板
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一个有帮助的助手,可以使用工具来回答问题。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}") # Agent 的工作空间
])
# 创建 Agent
agent = create_tool_calling_agent(
llm=llm,
tools=[search_tool],
prompt=prompt_template
)
# 创建执行器
agent_executor = AgentExecutor(
agent=agent,
tools=[search_tool],
verbose=True
)
# 提问
result = agent_executor.invoke({"input": "查询2025年AI技术趋势"})
print(result)
这种方式的特点是:
- 使用 ChatPromptTemplate(聊天式模板)
- 必须包含
{input}和{agent_scratchpad}占位符 - 工具名称必须是英文
- 思考过程是隐式的(不会打印 Thought、Action 这些)
3.3 create_react_agent(ReAct 模式)
如果你喜欢看到 Agent 的思考过程,可以用 ReAct 模式:
from langchain.agents import create_react_agent
from langchain import hub
# 使用 LangChain Hub 上的官方模板
prompt_template = hub.pull("hwchase17/react")
# 创建 Agent
agent = create_react_agent(
llm=llm,
tools=[search_tool],
prompt=prompt_template
)
# 创建执行器
agent_executor = AgentExecutor(
agent=agent,
tools=[search_tool],
verbose=True
)
result = agent_executor.invoke({"input": "查询新零售行业发展前景"})
这种方式会显示完整的 Thought → Action → Observation 过程,跟传统方式的 ZERO_SHOT_REACT_DESCRIPTION 效果一样。
3.4 自定义 ReAct 提示词
如果你不想用官方模板,也可以自己写:
from langchain_core.prompts import PromptTemplate
template = """你是一个有帮助的助手。你可以使用以下工具:
{tools}
使用这个格式:
问题:你需要回答的输入问题
思考:你应该思考要做什么
行动:要采取的行动,应该是 [{tool_names}] 之一
行动输入:行动的输入
观察:行动的结果
... (这个 思考/行动/行动输入/观察 可以重复N次)
思考:我现在知道最终答案了
最终答案:对原始输入问题的最终答案
开始!
问题:{input}
思考:{agent_scratchpad}"""
prompt = PromptTemplate.from_template(template)
agent = create_react_agent(
llm=llm,
tools=[search_tool],
prompt=prompt
)
自定义模板的好处是你可以控制 Agent 的"人格"和"行为方式"。比如你可以加上"如果不确定就说不确定"、"回答要简洁"这样的约束。
3.5 两种新方式的对比
| 特性 | create_tool_calling_agent | create_react_agent |
|---|---|---|
| 提示词类型 | ChatPromptTemplate | PromptTemplate |
| 思考过程 | 隐式(看不到) | 显式(能看到 Thought/Action) |
| 工具名称 | 必须英文 | 支持中文 |
| 适用场景 | 生产环境,不需要调试 | 开发调试,需要看清楚思考过程 |
| 性能 | 稍快(少一些输出) | 稍慢(多一些输出) |
我个人建议:开发的时候用 create_react_agent,方便调试;上线的时候用 create_tool_calling_agent,更简洁高效。
四、Agent 加上记忆
到目前为止,我们的 Agent 都是"金鱼记忆"——每次对话都忘记之前说过什么。这在多轮对话场景下就不够用了。
4.1 传统方式 + 记忆
from langchain.memory import ConversationBufferMemory
# 创建记忆对象
memory = ConversationBufferMemory(
return_messages=True, # 返回消息对象而不是字符串
memory_key="chat_history" # 在提示词中的变量名
)
# 初始化 Agent(注意 agent 类型变了)
agent_executor = initialize_agent(
tools=[search_tool],
llm=llm,
agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, # 支持会话的类型
memory=memory,
verbose=True
)
# 第一轮对话
agent_executor.invoke({"input": "查询长沙天气"})
# 第二轮对话
agent_executor.invoke({"input": "上海呢?"}) # Agent 知道"呢"指的是查天气
看到没?第二次提问只说了"上海呢?",Agent 能理解这是延续上一个问题,要查上海的天气。
这里的关键是:
- 使用 ConversationBufferMemory 存储历史
- Agent 类型要用 CONVERSATIONAL_REACT_DESCRIPTION(带 CONVERSATIONAL 前缀)
- 每次调用 Agent 时,它会自动从 memory 里读取历史
4.2 新方式 + 记忆(Function Call 模式)
from langchain.memory import ConversationBufferMemory
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
# 创建记忆
memory = ConversationBufferMemory(
return_messages=True,
memory_key="chat_history"
)
# 定义提示词(注意增加了 chat_history)
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一个有用的助手,可以使用工具回答问题。"),
("placeholder", "{chat_history}"), # 对话历史
("human", "{input}"),
("placeholder", "{agent_scratchpad}")
])
# 创建 Agent
agent = create_tool_calling_agent(
llm=llm,
tools=[search_tool],
prompt=prompt_template
)
# 创建执行器(传入 memory)
agent_executor = AgentExecutor(
agent=agent,
tools=[search_tool],
memory=memory,
verbose=True
)
# 多轮对话
agent_executor.invoke({"input": "查询自动化行业发展前景"})
agent_executor.invoke({"input": "新零售呢?"})
这里的关键是在 ChatPromptTemplate 里加了一个 ("placeholder", "{chat_history}"),用来插入对话历史。
4.3 新方式 + 记忆(ReAct 模式)
ReAct 模式也支持记忆,可以用官方模板:
from langchain import hub
# 使用支持会话历史的 ReAct 模板
prompt_template = hub.pull("hwchase17/react-chat")
agent = create_react_agent(
llm=llm,
tools=[search_tool],
prompt=prompt_template
)
agent_executor = AgentExecutor(
agent=agent,
tools=[search_tool],
memory=memory,
verbose=True,
handle_parsing_errors=True # 处理解析错误
)
agent_executor.invoke({"input": "查询AI行业趋势"})
agent_executor.invoke({"input": "那机器学习呢?"})
注意这里加了 handle_parsing_errors=True,因为 ReAct 模式需要解析 Thought/Action 这些结构化输出,有时候大模型会输出格式不对,加上这个参数可以优雅地处理错误。
4.4 记忆的选择
LangChain 提供了多种记忆类型:
| 记忆类型 | 特点 | 适用场景 |
|---|---|---|
| ConversationBufferMemory | 存储完整对话历史 | 短对话 |
| ConversationBufferWindowMemory | 只保留最近 K 轮 | 长对话,控制成本 |
| ConversationSummaryMemory | 用 LLM 总结历史 | 长对话,需要完整上下文 |
在 Agent 场景下,我一般用 ConversationBufferMemory 或 ConversationBufferWindowMemory。
五、实战案例
来几个真实的例子感受一下 Agent 的威力。
5.1 股票分析助手
# 准备两个工具:搜索 + 计算
search_tool = Tool(
name="search",
func=search.run,
description="搜索互联网信息,特别是股票价格、新闻等"
)
calc_tool = Tool(
name="calculator",
func=simple_calculator,
description="执行数学计算,输入应该是数学表达式"
)
# 创建 Agent
agent_executor = initialize_agent(
tools=[search_tool, calc_tool],
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# 复杂的分析任务
result = agent_executor.invoke(
"比亚迪和美团的股价各是多少?哪个比2024年涨幅更大?"
)
Agent 会自动:
- 搜索比亚迪股价(当前和 2024 年)
- 搜索美团股价(当前和 2024 年)
- 计算比亚迪涨幅
- 计算美团涨幅
- 比较两者,给出答案
你不需要告诉它怎么做,它自己会规划。
5.2 多轮咨询机器人
memory = ConversationBufferMemory(
return_messages=True,
memory_key="chat_history"
)
agent_executor = initialize_agent(
tools=[search_tool],
llm=llm,
agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
memory=memory,
verbose=True
)
# 第一轮:查询天气
agent_executor.invoke({"input": "长沙天气怎么样?"})
# 第二轮:追问细节
agent_executor.invoke({"input": "那适合出门吗?"})
# 第三轮:换个城市
agent_executor.invoke({"input": "上海呢?"})
Agent 能理解:
- “那适合出门吗?” → 基于长沙天气判断
- “上海呢?” → 查询上海天气
这就是有记忆的威力。
5.3 新闻搜索助手
result = agent_executor.invoke({
"input": "查询长沙和芷江最近有什么新闻"
})
Agent 会:
- 先搜索长沙的新闻
- 再搜索芷江的新闻
- 把两个结果整合成一段回答
它知道要分两次搜索,而不是一次搜索"长沙和芷江"。
六、Agent 的工作原理
让我用一个完整的执行过程来说明 Agent 是怎么运行的。
6.1 完整的执行流程
假设用户问:“美团股价比去年跌了多少?”
[1] 用户输入
↓
[2] Agent 接收任务
↓
[3] LLM 思考:
"我需要知道美团现在的股价和去年的股价,然后计算差值"
↓
[4] LLM 决策:
"首先要搜索美团现在的股价"
↓
[5] 选择工具:search_tool
↓
[6] 执行工具:search.run("美团股价")
↓
[7] 观察结果:
"美团股价为 180 元"
↓
[8] LLM 再次思考:
"现在知道了当前股价,还需要去年的股价"
↓
[9] 选择工具:search_tool
↓
[10] 执行工具:search.run("美团2024年股价")
↓
[11] 观察结果:
"2024年美团股价为 200 元"
↓
[12] LLM 再次思考:
"现在两个数据都有了,需要计算差值"
↓
[13] 选择工具:calculator_tool
↓
[14] 执行工具:calculator.run("180 - 200")
↓
[15] 观察结果:
"-20"
↓
[16] LLM 最终思考:
"我现在有足够信息生成答案了"
↓
[17] 生成最终答案:
"美团股价比去年下跌了20元,跌幅为10%"
↓
[18] 返回给用户
整个过程完全自动,Agent 会不断循环"思考 → 行动 → 观察",直到找到答案。
6.2 关键参数
verbose=True:打印详细过程,方便调试
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True # 开启详细日志
)
handle_parsing_errors=True:处理解析错误
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
handle_parsing_errors=True # 优雅处理错误
)
有时候 LLM 输出的格式不规范,这个参数可以让 Agent 重试而不是直接报错。
max_iterations:限制最大循环次数
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=10 # 最多循环10次
)
防止 Agent 陷入死循环,消耗太多 Token。
early_stopping_method:提前停止策略
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
early_stopping_method="generate" # 达到 max_iterations 时生成答案
)
到达最大循环次数时,让 Agent 生成一个"目前能给出的答案",而不是报错。
七、常见问题
学 Agent 过程中,这几个问题经常被问到:
7.1 Agent 一直调用同一个工具,陷入循环怎么办?
问题原因:工具的 description 写得不清楚,LLM 不知道工具是干什么的。
解决方案:写清楚工具的用途、输入格式、输出格式。
# ❌ 不好的写法
Tool(
name="search",
func=search.run,
description="搜索" # 太简单了
)
# ✅ 好的写法
Tool(
name="search",
func=search.run,
description="用于在互联网上搜索信息。输入应该是一个明确的搜索查询字符串,例如'北京天气'、'比亚迪股价'。返回相关的搜索结果摘要。"
)
7.2 两个工具名字一样,Agent 该调用哪个?
问题原因:工具名称冲突,Agent 无法区分。
解决方案:确保每个工具的 name 是唯一的。
# ❌ 错误
tools = [
Tool(name="search", func=search1.run, description="..."),
Tool(name="search", func=search2.run, description="...") # 名字重复了!
]
# ✅ 正确
tools = [
Tool(name="web_search", func=search1.run, description="..."),
Tool(name="image_search", func=search2.run, description="...")
]
7.3 Agent 执行一次任务消耗太多 Token 怎么办?
问题原因:
- 使用了 ConversationBufferMemory,历史对话越来越长
- Agent 循环次数太多
解决方案:
- 换记忆类型:改用 ConversationBufferWindowMemory,只保留最近几轮对话
- 限制循环次数:设置 max_iterations 参数限制最大循环次数
- 换更便宜的模型:比如用 gpt-4o-mini 替代 gpt-4
7.4 看到 API 弃用警告怎么办?
警告内容:
DeprecationWarning: initialize_agent is deprecated.
Use create_tool_calling_agent or create_react_agent instead.
不用慌:代码还能正常运行,只是官方推荐使用新的 API。
建议:
- 如果是老项目,可以继续使用,暂不影响
- 如果是新项目,直接用 create_tool_calling_agent 或 create_react_agent
- 如果要迁移,按照第三章的新方式重写即可
八、传统 vs 新方式对比
最后总结一下传统方式和新方式的区别:
| 特性 | 传统方式 | 新方式 |
|---|---|---|
| 初始化函数 | initialize_agent() | create_tool_calling_agent() / create_react_agent() |
| Agent 类型 | AgentType.XXX 枚举 | 通过不同的 create 函数区分 |
| 提示词 | 内置,无法自定义 | 完全自定义,使用 PromptTemplate |
| 状态 | 已弃用 | 官方推荐 |
| 灵活性 | 低 | 高 |
| 学习曲线 | 简单 | 稍复杂 |
| 适用场景 | 快速原型、学习 | 生产环境、复杂需求 |
我的建议:
- 如果你是刚接触 LangChain,先用传统方式理解概念
- 如果你要做正式项目,直接用新方式
- 如果你要迁移老代码,可以慢慢从传统方式改成新方式
九、总结
这篇文章介绍了 LangChain 的 Agents 模块,核心内容包括:
-
Agent 的本质:Agent = LLM(思考)+ Tools(执行)+ Memory(记忆),本质上就是一个自动循环:分析问题 → 选择工具 → 执行工具 → 分析结果 → 决定下一步
-
两套 API:传统方式(initialize_agent)已弃用但简单易学;新方式(create_tool_calling_agent / create_react_agent)是官方推荐
-
两种模式:Function Call 模式(隐式思考,适合生产环境)和 ReAct 模式(显式思考过程,适合开发调试)
-
记忆管理:给 Agent 加上 Memory,就能进行多轮对话
-
实战技巧:写清楚工具描述、合理设置 max_iterations、打开 verbose 调试、处理解析错误
关键要点总结表:
| 概念 | 说明 | 适用场景 | 注意事项 |
|---|---|---|---|
| initialize_agent | 传统方式,简单直接 | 快速原型、学习 | 已弃用,但仍可用 |
| create_tool_calling_agent | 新方式 Function Call 模式 | 生产环境 | 工具名必须英文 |
| create_react_agent | 新方式 ReAct 模式 | 开发调试 | 可以看到思考过程 |
| ConversationBufferMemory | 存储完整对话历史 | 短对话 | 会消耗较多 Token |
| verbose=True | 打印详细过程 | 开发调试 | 生产环境建议关闭 |
下一步:到这里,LangChain 的核心模块我们就都学完了:Model I/O、Chains、Memory、Tools、Agents。接下来的内容会更高级,比如 LangGraph(Agent 的升级版)、向量数据库、RAG 应用等等。
热门专栏推荐
- Agent小册
- 服务器部署
- Java基础合集
- Python基础合集
- Go基础合集
- 大数据合集
- 前端小册
- 数据库合集
- Redis 合集
- Spring 全家桶
- 微服务全家桶
- 数据结构与算法合集
- 设计模式小册
- 消息队列合集
等等等还有许多优秀的合集在主页等着大家的光顾,感谢大家的支持
文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😊
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🙏
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🌟
更多推荐


所有评论(0)