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: 创建支持工具调用的Agent
  • AgentExecutor: 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的Agent
  • AgentExecutor: 创建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模式调用工具。主要优点包括:

  1. 结构化调用: Function Call提供结构化的工具调用方式
  2. 稳定性高: 相比文本解析更稳定可靠
  3. 易于扩展: 可以方便地添加多个工具
  4. 实时性强: 能够获取最新的实时信息

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就会像一个健忘的人,每步都要重新思考,效率低下且容易出错。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐