LangChain 第五篇 工具调用的两种核心方案:Agent 与 Tool Calling 深度对比

在 LangChain 的生态中,当我们需要让大模型完成查天气、查数据库、调用外部接口这类任务时,核心逻辑其实高度统一:让模型决策 "是否使用工具、使用哪款工具、传入什么参数",但绝不是模型直接执行工具操作。

围绕这一核心,LangChain 提供了两种实现路径 ------ 一种是新手易上手的Agent,另一种是更适配生产环境的Tool Calling。二者看似效果相近、底层原理同源,但在工程可控性、可扩展性与生产可用性上,存在天壤之别。

简单来说,initialize_agent是帮你封装好全流程的高层工具,而bind_tools + 手动调度则是将执行权完全交还给开发者的底层实现。在实际生产落地中,后者往往是更优的选择。

1. LangChain 工具调用的本质逻辑

无论是 OpenAI 的 Function Calling、通义千问的 Tool Calling,还是 Claude、Gemini 的工具调用机制,本质都是一套 "决策 - 执行 - 反馈" 的流程:

以下是一个简单的执行流程

python 复制代码
用户问题
  ↓
LLM 判断是否需要工具
  ↓
返回 tool_call(函数名 + 参数)
  ↓
你在代码里真正调用工具
  ↓
把工具结果再喂回 LLM
  ↓
LLM 给最终自然语言回答

下面两段代码,做的是同一件事,但开发者对流程的控制权截然不同。

2. 方案一:initialize_agent ------ 高层封装,一行跑通

示例代码(OpenAI 风格)

python 复制代码
from langchain.agents import initialize_agent
from langchain.agents.agent_types import AgentType
from langchain_community.chat_models import ChatOpenAI
from langchain_core.tools import tool

@tool
def get_weather(city: str) -> str:
    """获取指定城市天气"""
    return f"{city} 晴,26℃"

llm = ChatOpenAI(model="gpt-4", temperature=0)

# 初始化Agent并绑定工具
agent = initialize_agent(
    tools=[get_weather],
    llm=llm,
    agent=AgentType.OPENAI_FUNCTIONS,
    verbose=True
)

response = agent.run("今天上海天气怎么样?")
print(response)

Agent 帮你包揽的全流程工作

使用initialize_agent时,我们只需要定义工具和模型,剩下的工作全由 Agent 自动完成:

  1. 将@tool装饰的函数,自动转化为符合 OpenAI Function 规范的 Schema
  2. 自动生成引导模型使用工具的 System Prompt
  3. 解析模型返回的function_call指令
  4. 自动触发对应的工具函数执行
  5. 将工具执行结果拼接回对话上下文
  6. 再次调用大模型,生成最终的自然语言回答

整个流程可以简化为:

python 复制代码
用户问题 → agent.run() → 最终答案

适用场景与局限性

适合场景

  • 快速搭建 Demo、开展教学演示
  • 工具数量少、业务逻辑简单的场景
  • 不关注中间执行过程,只需要最终结果的需求

不适合场景

  • 需要精准控制工具调用次数的场景
  • 要自定义工具调用的中间逻辑(如加缓存、做鉴权)
  • 需要记录每一次工具调用的详细 Trace 日志
  • Agent 的核心优势是 "省心省力",但代价是 "交出控制权"。

3.方案二:bind_tools + 手动调度 ------ 可控性拉满的工程级方案

相较于 Agent 的 "黑盒模式",bind_tools + 手动调度的组合,将工具调用的每一步控制权都交还给开发者,更适合复杂的生产环境。

定义模型与工具

python 复制代码
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langchain_community.chat_models import ChatTongyi
from pydantic import SecretStr

# 初始化大模型
llm = ChatTongyi(
    model="qwen-max",
    api_key=SecretStr("sk-xxxx")
)

# 定义工具函数
@tool(response_format="content_and_artifact")
def weather(city: str):
    """查询指定城市的天气情况"""
    return "30℃", {"city": city}

这里的关键亮点是response_format="content_and_artifact":它能让工具的返回值被明确拆分为两个部分

  • content:用于回传给大模型的自然语言文本
  • artifact:留存给程序后续处理的结构化数据

这种分离设计在工程实践中至关重要,既能满足大模型理解需求,又能为后续的数据存储、业务联动提供结构化支撑。

为模型绑定工具

这一步的作用是告诉大模型可以调用哪些工具,但不会触发任何工具的实际执行:

python 复制代码
tools = [weather]
# 为模型绑定工具集
llm_with_tools = llm.bind_tools(tools)

首次调用模型 ------ 获取工具调用决策

这一步的核心目标,是让模型判断是否需要调用工具,并输出具体的调用指令:

python 复制代码
# 构造用户提问的消息体
messages = [HumanMessage(content='烟台天气情况')]
# 调用模型获取决策
response = llm_with_tools.invoke(messages)
# 将模型的决策消息加入上下文
messages.append(response)

此时模型返回的不是最终答案,而是工具调用的决策指令,格式如下:

python 复制代码
AIMessage(
  tool_calls=[{
    name: "weather",
    args: {"city": "烟台"}
  }]
)

手动执行工具调用

这是整个流程中最能体现可控性的环节。我们可以在这里嵌入任何自定义逻辑:

python 复制代码
# 构建工具名到工具函数的映射表
tool_map = {tool.name.lower(): tool for tool in tools}

# 遍历模型输出的工具调用指令
for tool_call in response.tool_calls:
    selected_tool = tool_map[tool_call["name"].lower()]
    # 执行工具函数
    tool_msg = selected_tool.invoke(tool_call)
    # 将工具执行结果加入上下文
    messages.append(tool_msg)

在这个环节,我们可以轻松加入各类工程化逻辑,比如:

  • 记录工具调用的日志与 Trace
  • 对工具调用做限流、鉴权
  • 配置缓存策略,避免重复调用
  • 增加失败重试与兜底方案
  • 实现工具调用的审计与权限管控

二次调用模型 ------ 生成最终回答

将工具执行结果加入上下文后,再次调用模型,即可得到基于工具数据的最终回答:

python 复制代码
# 基于包含工具结果的上下文,生成最终回答
response = llm_with_tools.invoke(messages)
print(response.content)

最终输出结果如下:

python 复制代码
烟台的天气情况是30℃。

4. 两种方案的核心差异对比

对比项 initialize_agent bind_tools + 手动
抽象层级
控制力 ❌ 很弱 ✅ 极强
可观测性 ❌ 黑盒 ✅ 全流程
可扩展性 ❌ 受限 ✅ 自由
生产适用 ⚠️ 一般 ✅ 推荐

5. 方案选型建议

当你满足以下任一条件时,强烈建议选择 Tool Calling 方案:

  1. 系统中集成的工具数量超过 3 个
  2. 工具调用涉及真实业务数据(如数据库查询、外部 API 调用)
  3. 业务需要对工具调用做审计、追责或合规管控
  4. 要求为工具调用配置失败兜底与重试机制
  5. 需要对工具调用做细粒度的权限控制
相关推荐
乌日尼乐5 小时前
【Java基础整理】java数组详解
java·后端
FreeCode5 小时前
智能体设计模式:规划与执行模式(Plan and Execute)
langchain·agent·ai编程
想用offer打牌6 小时前
一站式讲清IO多路复用(轻松愉悦版)
后端·面试·操作系统
嘻哈baby6 小时前
网络延迟与丢包问题排查实战
后端
东百牧码人6 小时前
PostgreSQL 的得力助手:psql.exe 使用指南
后端
东百牧码人6 小时前
C# TimeOfDay TimeOnly如何比较
后端
开心就好20256 小时前
如何保护 iOS IPA 文件中的资源与文件安全
后端
上进小菜猪6 小时前
基于 YOLOv8 的学生课堂行为检测(举手、看书、写作业、玩手机)-完整项目源码
后端
涡能增压发动积7 小时前
英雄联盟证书过期上热搜?吃透 HTTPS 核心:证书、TLS、HTTP3/QUIC 故障复盘全解析
后端
程序员爱钓鱼7 小时前
Node.js 编程实战:Node.js + React Vue Angular 前后端协作实践
前端·后端·node.js