(LangChain实战17):LangChain通用方式FUNCTION_CALL调用工具
·
LangChain通用方式FUNCTION_CALL调用工具
引言
本文将介绍如何使用LangChain的通用方式通过Function Call模式调用工具,以天气查询为例,展示如何结合Tavily搜索工具和DeepSeek模型构建智能助手。
完整代码实现
import os
from dotenv import load_dotenv
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_community.tools import TavilySearchResults
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
# 加载env环境变量
load_dotenv()
# tavily工具的API_KEY
os.environ['TAVILY_API_KEY'] = os.getenv('TAVILY_API_KEY')
# 创建搜索工具
search_tool = TavilySearchResults(
max_results=3,
name="web_search",
description="用于检索互联网上的实时信息,如天气、新闻等"
)
tools = [search_tool]
# 获取对话模型
chat_model = ChatOpenAI(
model="deepseek-chat",
base_url=os.getenv('OPENAI_BASE_URL'),
api_key=os.getenv('OPENAI_API_KEY'),
temperature=0.7
)
# 创建ChatPromptTemplate
prompt_template = ChatPromptTemplate.from_messages([
("system", """你是一个智能助手,可以使用搜索工具获取实时信息。
当用户询问天气、新闻等需要实时数据的问题时,请使用搜索工具。
回答时请基于搜索结果,用友好的方式总结信息。"""),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"), # 用于记录agent的思考过程
])
# 获取agent
agent = create_tool_calling_agent(
llm=chat_model,
prompt=prompt_template,
tools=tools
)
# AgentExecutor
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True, # 添加错误处理
max_iterations=3 # 限制最大迭代次数
)
# 正确传递参数
result = agent_executor.invoke({"input": "查询北京今天的天气"})
print("\n最终结果:")
print(result["output"])
代码详解
1. 导入必要的库
import os
from dotenv import load_dotenv
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_community.tools import TavilySearchResults
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
os: 操作系统环境变量操作load_dotenv: 从.env文件加载环境变量create_tool_calling_agent: 创建支持工具调用的AgentAgentExecutor: Agent执行器,负责管理Agent的运行TavilySearchResults: Tavily搜索工具ChatPromptTemplate: 聊天提示词模板MessagesPlaceholder: 消息占位符,用于动态插入消息ChatOpenAI: OpenAI格式的聊天模型
2. 环境变量配置
# 加载env环境变量
load_dotenv()
# tavily工具的API_KEY
os.environ['TAVILY_API_KEY'] = os.getenv('TAVILY_API_KEY')
load_dotenv(): 加载项目根目录下的.env文件- 从环境变量中获取Tavily API密钥并设置到系统环境变量中
3. 创建搜索工具
search_tool = TavilySearchResults(
max_results=3,
name="web_search",
description="用于检索互联网上的实时信息,如天气、新闻等"
)
tools = [search_tool]
max_results=3: 最多返回3条搜索结果name="web_search": 工具名称,Agent通过名称调用description: 工具功能描述,帮助Agent理解何时使用该工具- 将工具放入列表中,支持多个工具
4. 初始化对话模型
chat_model = ChatOpenAI(
model="deepseek-chat",
base_url=os.getenv('OPENAI_BASE_URL'),
api_key=os.getenv('OPENAI_API_KEY'),
temperature=0.7
)
model="deepseek-chat": 使用DeepSeek聊天模型base_url: DeepSeek API接口地址api_key: DeepSeek API密钥temperature=0.7: 控制输出随机性,值越大回答越多样
5. 创建提示词模板
prompt_template = ChatPromptTemplate.from_messages([
("system", """你是一个智能助手,可以使用搜索工具获取实时信息。
当用户询问天气、新闻等需要实时数据的问题时,请使用搜索工具。
回答时请基于搜索结果,用友好的方式总结信息。"""),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"), # 用于记录agent的思考过程
])

from_messages(): 从消息列表创建模板("system", ...): 系统消息,定义Agent的行为准则("human", "{input}"): 用户消息,{input}会被实际输入替换MessagesPlaceholder("agent_scratchpad"): 占位符,用于记录Agent的中间思考过程,参考文章最后(MessagesPlaceholder("agent_scratchpad")详细解释)
6. 创建Agent和Executor
agent = create_tool_calling_agent(
llm=chat_model,
prompt=prompt_template,
tools=tools
)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
max_iterations=3
)
create_tool_calling_agent: 创建支持Function Call的AgentAgentExecutor: 创建Agent执行器verbose=True: 显示详细的执行过程handle_parsing_errors=True: 处理解析错误max_iterations=3: 最大迭代次数,防止无限循环
7. 执行查询
result = agent_executor.invoke({"input": "查询北京今天的天气"})
print("\n最终结果:")
print(result["output"])
invoke(): 执行Agent,传入参数字典result["output"]: 获取最终的输出结果
运行输出
E:\pythonWorkSpace\LangChain\(18)Langchain通用方式FUNCTION_CALL调用工具.py:15: LangChainDeprecationWarning: The class `TavilySearchResults` was deprecated in LangChain 0.3.25 and will be removed in 1.0. An updated version of the class exists in the `langchain-tavily package and should be used instead. To use it run `pip install -U `langchain-tavily` and import as `from `langchain_tavily import TavilySearch``.
search_tool = TavilySearchResults(
> Entering new AgentExecutor chain...
Invoking: `web_search` with `{'query': '北京今天天气'}`
responded: 我来帮您查询北京今天的天气情况。
[搜索结果详情...]
根据搜索结果,我来为您总结北京今天的天气情况:
## 北京今天(2月16日)天气概况:
**当前天气状况:**
- **温度:** 3°C(体感温度0°C)
- **天气:** 阴天
- **湿度:** 65%
- **风力:** 东南偏南风,2 km/h
- **能见度:** 24.14 km
- **气压:** 1022 hPa
- **云量:** 100%
**今日温度范围:**
- **最高温度:** 6°C
- **最低温度:** -6°C
**今日详细预报:**
- 白天:晴,西南风微风,最高6°C
- 夜间:晴,西南风微风,最低-6°C
**穿衣建议:**
今天北京天气寒冷,建议您:
- 穿保暖夹克或中等重量的外套
- 穿闭趾鞋或靴子
- 戴围巾保护颈部
- 穿长裤(牛仔裤或其他厚料)
**空气质量:**
空气质量非常不健康,对所有人都有健康风险,可能出现紧急情况。
**温馨提示:**
今天天气寒冷,外出请注意保暖。由于空气质量较差,建议减少户外活动时间,特别是老人、儿童和有呼吸道疾病的人群。
> Finished chain.
最终结果:
根据搜索结果,我来为您总结北京今天的天气情况:
## 北京今天(2月16日)天气概况:
**当前天气状况:**
- **温度:** 3°C(体感温度0°C)
- **天气:** 阴天
- **湿度:** 65%
- **风力:** 东南偏南风,2 km/h
- **能见度:** 24.14 km
- **气压:** 1022 hPa
- **云量:** 100%
**今日温度范围:**
- **最高温度:** 6°C
- **最低温度:** -6°C
**今日详细预报:**
- 白天:晴,西南风微风,最高6°C
- 夜间:晴,西南风微风,最低-6°C
**穿衣建议:**
今天北京天气寒冷,建议您:
- 穿保暖夹克或中等重量的外套
- 穿闭趾鞋或靴子
- 戴围巾保护颈部
- 穿长裤(牛仔裤或其他厚料)
**空气质量:**
空气质量非常不健康,对所有人都有健康风险,可能出现紧急情况。
**温馨提示:**
今天天气寒冷,外出请注意保暖。由于空气质量较差,建议减少户外活动时间,特别是老人、儿童和有呼吸道疾病的人群。
Process finished with exit code 0
代码执行流程分析
1. 初始化阶段
- 加载环境变量,配置API密钥
- 创建Tavily搜索工具实例
- 初始化DeepSeek聊天模型
2. Agent创建阶段
- 定义系统提示词,指导Agent的行为
- 创建提示词模板,包含系统消息、用户输入和思考过程占位符
- 使用
create_tool_calling_agent创建Function Call Agent - 创建
AgentExecutor管理Agent执行
3. 执行阶段
- 用户输入:“查询北京今天的天气”
- Agent判断需要调用搜索工具
- 自动调用
web_search工具,参数为{'query': '北京今天天气'} - Tavily返回搜索结果(3条相关天气信息)
- Agent基于搜索结果生成友好的回答
- 输出格式化的天气信息
4. 输出阶段
- 显示完整的天气信息,包括温度、湿度、风力等
- 提供穿衣建议和温馨提示
- 包含空气质量提醒
Function Call 机制解析
什么是Function Call?
Function Call是OpenAI推出的模型能力,允许模型识别何时需要调用外部函数,并生成结构化的函数调用参数。LangChain将其封装为create_tool_calling_agent。
工作流程
1. 用户输入 → 2. 模型判断是否需要工具 → 3. 需要则生成函数调用
4. 执行工具 → 5. 将结果返回给模型 → 6. 模型生成最终回答
与传统ReAct的区别
- ReAct: 模型自己生成文本格式的Action和Action Input
- Function Call: 模型生成结构化的函数调用,更稳定、更准确
注意事项
1. API密钥配置
在项目根目录创建.env文件:
OPENAI_BASE_URL=https://api.deepseek.com
OPENAI_API_KEY=your_deepseek_api_key_here
TAVILY_API_KEY=your_tavily_api_key_here
2. 依赖安装
pip install langchain langchain-community langchain-openai langchain-core
pip install tavily-python python-dotenv
3. 版本警告说明
代码运行时会显示两个警告:
TavilySearchResults将在LangChain 1.0中被移除,建议迁移到langchain-tavily包- 这是正常的废弃警告,不影响当前功能
4. 错误处理
代码中添加了:
handle_parsing_errors=True: 自动处理解析错误max_iterations=3: 防止Agent陷入无限循环
总结
本文通过一个完整的天气查询示例,展示了如何使用LangChain的通用方式通过Function Call模式调用工具。主要优点包括:
- 结构化调用: Function Call提供结构化的工具调用方式
- 稳定性高: 相比文本解析更稳定可靠
- 易于扩展: 可以方便地添加多个工具
- 实时性强: 能够获取最新的实时信息
MessagesPlaceholder("agent_scratchpad") 详细解释
1. 什么是 agent_scratchpad?
agent_scratchpad 是 LangChain Agent 中的一个特殊占位符,用于记录 Agent 的中间思考过程和工具调用历史。
2. 它的作用
# 这个占位符会被自动填充以下内容:
- Agent的思考过程(Thought)
- 调用了什么工具(Action)
- 工具返回的结果(Observation)
- 多轮对话的完整历史
3. 实际运行中的数据填充
以天气查询为例,agent_scratchpad 会被填充的内容:
# agent_scratchpad 中实际存储的内容
"""
Thought: 用户询问北京天气,我需要使用搜索工具获取实时信息
Action: web_search
Action Input: {"query": "北京今天天气"}
Observation: [{"title": "北京天气", "content": "今天北京晴,温度-2~12℃..."}]
Thought: 我已经获取到天气信息,可以回答用户了
"""
4. 为什么需要 agent_scratchpad?
场景1:多轮工具调用
# 如果没有 agent_scratchpad,Agent 会忘记之前的调用
第一轮:调用工具A → 忘记
第二轮:重新思考 → 效率低
# 有 agent_scratchpad,Agent 能记住上下文
第一轮:调用工具A → 记录到 scratchpad
第二轮:基于之前的结果继续思考
场景2:复杂推理
# Agent 的完整思考链条都保存在 scratchpad 中
{
"agent_scratchpad": [
"Thought: 我需要先查询北京天气",
"Action: web_search",
"Observation: 获取到天气数据",
"Thought: 还需要查询空气质量",
"Action: web_search",
"Observation: 获取到AQI指数",
"Thought: 现在可以完整回答用户了"
]
}
5. 实际代码示例对比
❌ 错误示例:没有 agent_scratchpad
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一个助手"),
("human", "{input}"),
# 缺少 agent_scratchpad
])
# Agent 执行过程(问题):
# 第一轮:调用工具 → 得到结果
# 第二轮:忘记了第一轮的结果 → 重新调用工具
# 导致:重复调用、效率低、可能无限循环
✅ 正确示例:有 agent_scratchpad
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一个助手"),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"), # 记录思考过程
])
# Agent 执行过程(优点):
# 1. 调用工具 web_search("北京天气")
# 2. 将这次调用记录到 scratchpad
# 3. 基于 scratchpad 中的结果继续思考
# 4. 不需要重复调用,直接使用已有结果
6. 可视化工作流程
┌─────────────────────────────────────┐
│ 用户输入 │
│ "查询北京今天的天气" │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ Agent 接收输入 │
│ 提示词模板包含: │
│ - 系统提示 │
│ - 用户输入 │
│ - agent_scratchpad (初始为空) │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ Agent 思考过程 │
│ 写入 scratchpad: │
│ "Thought: 需要查询天气" │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ 调用工具 │
│ 写入 scratchpad: │
│ "Action: web_search" │
│ "Action Input: 北京天气" │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ 工具返回结果 │
│ 写入 scratchpad: │
│ "Observation: 北京今天晴..." │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ Agent 继续思考 │
│ 读取 scratchpad 中的历史: │
│ - 已经调用过工具 │
│ - 已经有搜索结果 │
│ 得出结论:"可以回答用户了" │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ 生成最终回答 │
│ 基于 scratchpad 中的完整信息 │
└─────────────────────────────────────┘
7. 调试时查看 scratchpad 内容
# 在代码中查看 scratchpad 内容
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 设置为True可以看到scratchpad的内容
)
# 输出示例:
"""
> Entering new AgentExecutor chain...
Thought: 我需要查询北京天气
Action: web_search
Action Input: 北京天气
Observation: {"temperature": "3°C", "weather": "阴天"}
Thought: 我已经获取到天气信息
Final Answer: 北京今天天气...
"""
8. 总结
MessagesPlaceholder("agent_scratchpad") 的核心作用:
| 功能 | 说明 |
|---|---|
| 记忆功能 | 记录Agent的思考轨迹和工具调用历史 |
| 上下文保持 | 确保多轮交互中的信息连贯性 |
| 避免重复 | 防止Agent重复调用相同的工具 |
| 调试辅助 | 通过verbose=True可以查看完整推理过程 |
| 错误恢复 | 出错时可以回溯之前的步骤 |
简单理解:
- 就像人类的草稿纸,记录思考过程
- 防止Agent"失忆"
- 让Agent的推理更加连贯和准确
没有这个占位符,Agent就会像一个健忘的人,每步都要重新思考,效率低下且容易出错。
更多推荐



所有评论(0)