在 LangChain 中,使用聊天模型调用工具是一种强大的机制,允许语言模型(LLM)通过动态选择和执行外部工具来完成复杂任务。工具调用(Tool Calling)结合了聊天模型的自然语言理解能力和外部工具(如搜索、计算、API 调用等)的功能,使模型能够处理需要外部数据或特定操作的任务。这在代理(Agents)、问答系统或自动化工作流中尤为重要。
以下是对 LangChain 中使用聊天模型调用工具的详细介绍,涵盖其定义、工作原理、实现方式、应用场景、代码示例、优化建议以及与生态系统的结合。
1. 什么是聊天模型调用工具?
聊天模型调用工具是指利用支持工具调用功能的语言模型(如 OpenAI 的 GPT-4、Anthropic 的 Claude)动态选择并执行外部工具,以完成用户指定的任务。工具调用通常涉及:
- 工具定义:指定工具的名称、描述和输入输出格式。
- 模型推理:模型根据用户输入和工具描述决定是否调用工具、调用哪个工具以及传递什么参数。
- 工具执行:模型生成工具调用指令,LangChain 执行工具并返回结果。
- 结果整合:模型将工具结果整合到最终响应中。
工具调用的核心优势:
- 动态性:模型根据任务上下文选择合适的工具。
- 结构化交互:工具输入和输出通常为结构化格式(如 JSON),便于解析。
- 扩展能力:通过工具弥补模型在实时数据、计算或领域知识上的不足。
2. 工具调用的工作原理
工具调用的工作流程如下:
- 定义工具:
- 使用
Tool
类、@tool
装饰器或StructuredTool
定义工具,包括名称、描述和输入模式(通常基于 Pydantic 或 JSON Schema)。
- 使用
- 绑定工具到模型:
- 使用
bind_tools
方法将工具注册到聊天模型,模型理解工具的功能。
- 使用
- 用户输入:
- 用户提供查询,模型分析任务需求。
- 推理与工具选择:
- 模型根据提示和工具描述决定是否调用工具以及调用哪个工具。
- 生成工具调用指令(通常为 JSON 格式,包含工具名称和参数)。
- 工具执行:
- LangChain 执行工具,传递模型提供的参数,获取结果。
- 结果处理:
- 工具结果反馈给模型,模型可能继续推理或直接生成最终输出。
- 输出返回:
- 模型整合工具结果,生成用户期望的响应。
关键组件:
- 聊天模型:支持工具调用的模型(如 OpenAI、Anthropic)。
- 工具:定义外部功能(如搜索、计算)。
- 绑定机制:
bind_tools
或代理框架。 - 解析器:处理工具调用和结果(如
JsonOutputParser
)。
3. LangChain 中工具调用的实现方式
LangChain 提供了多种方式实现聊天模型调用工具,以下是主要方法的详细说明:
(1) 使用 bind_tools
- 功能:直接将工具绑定到聊天模型,模型生成工具调用指令。
- 特点:
- 适合简单工具调用场景。
- 模型直接输出结构化工具调用(如 JSON)。
- 需要手动处理工具执行和结果反馈。
- 适用场景:
- 单次工具调用。
- 结构化输出任务。
- 示例:
from langchain_openai import ChatOpenAI from langchain_core.tools import tool from pydantic import BaseModel # 定义工具 class CalcInput(BaseModel): a: float b: float @tool def add_numbers(input: CalcInput) -> float: """将两个数字相加""" return input.a + input.b # 初始化模型并绑定工具 llm = ChatOpenAI(api_key="your-openai-key").bind_tools([add_numbers]) # 调用模型 response = llm.invoke("请将 5 和 3 相加") if response.tool_calls: tool_call = response.tool_calls[0] tool_name = tool_call["name"] tool_args = tool_call["args"] result = add_numbers.invoke(tool_args) print(f"工具 {tool_name} 结果:{result}")
- 输出:
工具 add_numbers 结果:8.0
- 注意:
- 模型需要支持工具调用(如
gpt-4-1106-preview
或更高版本)。 - 需手动执行工具并处理结果。
- 模型需要支持工具调用(如
(2) 使用 AgentExecutor
- 功能:通过代理框架自动管理工具调用和推理循环。
- 特点:
- 适合多步骤任务,代理自动选择和执行工具。
- 支持 ReAct 或 OpenAI Functions 推理框架。
- 自动处理工具结果和模型交互。
- 适用场景:
- 复杂任务需要多次工具调用。
- 动态决策场景。
- 示例:
from langchain_openai import ChatOpenAI from langchain.agents import initialize_agent, AgentType from langchain_core.tools import Tool from langchain_community.tools import DuckDuckGoSearchRun # 定义计算工具 def calculator(expression: str) -> str: return str(eval(expression)) calc_tool = Tool( name="Calculator", func=calculator, description="执行数学计算,输入为数学表达(如 '2 + 2')。" ) # 初始化搜索工具 search_tool = DuckDuckGoSearchRun() # 工具列表 tools = [calc_tool, search_tool] # 初始化代理 llm = ChatOpenAI(api_key="your-openai-key") agent = initialize_agent( tools=tools, llm=llm, agent_type=AgentType.OPENAI_FUNCTIONS, verbose=True ) # 执行任务 result = agent.invoke("计算 5 + 3 并搜索量子计算的最新进展") print(result["output"])
- 输出:
计算结果为 8,量子计算的最新进展包括...
- 注意:
- 代理自动管理推理循环,适合复杂任务。
- 需优化工具描述以提高选择准确性。
(3) 使用 StructuredTool
- 功能:定义结构化工具,指定输入和输出格式。
- 特点:
- 使用 Pydantic 模型定义输入,确保参数验证。
- 适合需要严格输入格式的工具。
- 支持代理和直接调用。
- 适用场景:
- 复杂工具调用。
- 需要结构化输入输出的场景。
- 示例:
from langchain_openai import ChatOpenAI from langchain_core.tools import StructuredTool from pydantic import BaseModel # 定义输入和输出结构 class CalcInput(BaseModel): a: float b: float def add_numbers(input: CalcInput) -> float: return input.a + input.b calc_tool = StructuredTool.from_function( func=add_numbers, name="Adder", description="将两个数字相加" ) # 初始化模型并绑定工具 llm = ChatOpenAI(api_key="your-openai-key").bind_tools([calc_tool]) # 调用 response = llm.invoke("请将 5 和 3 相加") if response.tool_calls: tool_call = response.tool_calls[0] result = calc_tool.invoke(tool_call["args"]) print(f"结果:{result}")
- 输出:
结果:8.0
(4) 使用 LangGraph 自定义工具调用
- 功能:通过 LangGraph 构建自定义工具调用工作流。
- 特点:
- 高度灵活,适合复杂逻辑。
- 支持状态管理和多步骤推理。
- 需要更多开发工作。
- 适用场景:
- 自定义代理逻辑。
- 复杂多工具交互。
- 示例:
from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langgraph.graph import StateGraph, END from typing import TypedDict, Annotated # 定义状态 class AgentState(TypedDict): messages: list # 定义工具 @tool def calculator(expression: str) -> str: """执行数学计算""" return str(eval(expression)) # 初始化模型 llm = ChatOpenAI(api_key="your-openai-key").bind_tools([calculator]) # 定义节点 def call_model(state: AgentState): response = llm.invoke(state["messages"]) return {"messages": state["messages"] + [response]} def call_tool(state: AgentState): last_message = state["messages"][-1] if last_message.tool_calls: tool_call = last_message.tool_calls[0] result = calculator.invoke(tool_call["args"]) return {"messages": state["messages"] + [{"content": str(result)}]} return {"messages": state["messages"]} # 构建图 workflow = StateGraph(AgentState) workflow.add_node("model", call_model) workflow.add_node("tool", call_tool) workflow.set_entry_point("model") workflow.add_conditional_edges( "model", lambda state: "tool" if state["messages"][-1].tool_calls else END ) workflow.add_edge("tool", END) graph = workflow.compile() # 调用 result = graph.invoke({"messages": [{"role": "user", "content": "计算 2 + 2"}]}) print(result["messages"][-1]["content"])
- 输出:
4
4. 工具调用的应用场景
工具调用在以下场景中广泛应用:
- 实时信息检索:
- 使用搜索工具(如
DuckDuckGoSearchRun
)获取最新数据。 - 示例:回答“今天的新闻”。
- 使用搜索工具(如
- 数学与计算:
- 使用计算工具(如
PythonREPLTool
)解决数学问题。 - 示例:计算复杂公式。
- 使用计算工具(如
- 数据库查询:
- 使用数据库工具查询结构化数据。
- 示例:从 SQL 数据库提取销售数据。
- API 集成:
- 调用外部 API 执行操作。
- 示例:查询天气 API。
- 自动化工作流:
- 触发外部动作(如发送邮件)。
- 示例:结合
ZapierToolkit
。
- 结构化数据处理:
- 生成或解析 JSON 数据。
- 示例:提取用户输入中的实体。
5. 工具调用的优化建议
(1) 提高工具选择准确性
- 清晰的工具描述:
- 描述应明确工具功能和输入格式。
- 示例:
description="执行数学计算,输入为字符串表达式,如 '2 + 2'。"
。
- 结构化输入:
- 使用 Pydantic 模型定义输入格式。
class SearchInput(BaseModel): query: str
- 限制工具数量:
- 通常少于 10 个工具,避免模型选择困难。
(2) 提高性能
- 缓存工具结果:
- 使用 LangChain 的缓存机制。
from langchain.globals import set_llm_cache from langchain.cache import SQLiteCache set_llm_cache(SQLiteCache(database_path="cache.db"))
- 异步工具:
- 实现异步工具以支持高并发。
@tool async def async_search(query: str) -> str: import aiohttp async with aiohttp.ClientSession() as session: async with session.get(f"https://api.example.com?q={query}") as resp: return await resp.text()
- 批量处理:
- 批量调用工具,减少开销。
results = agent.batch([{"input": "q1"}, {"input": "q2"}])
(3) 错误处理
- 回退机制:
- 为工具配置回退。
calc_tool_with_fallback = calc_tool.with_fallbacks([backup_calc_tool])
- 异常捕获:
- 在工具中处理异常。
@tool def safe_calculator(expression: str) -> str: try: return str(eval(expression)) except Exception as e: return f"计算错误:{e}"
- 验证输入:
- 使用 Pydantic 验证工具输入。
class CalcInput(BaseModel): a: float b: float
(4) 监控与调试
- 回调:
- 使用回调记录工具调用。
from langchain_core.callbacks import BaseCallbackHandler class ToolCallback(BaseCallbackHandler): def on_tool_start(self, serialized, input_str, **kwargs): print(f"工具 {serialized['name']} 开始,输入:{input_str}")
- LangSmith:
- 分析工具调用性能和准确性。
from langsmith import Client agent.invoke(input, config={"callbacks": [Client(api_key="your-langsmith-key")]})
(5) 工具输出处理
- 结构化输出:
- 确保工具返回结构化数据。
@tool def search(query: str) -> dict: return {"query": query, "results": ["result1", "result2"]}
- 后处理:
- 使用解析器规范化工具输出。
from langchain_core.output_parsers import JsonOutputParser parser = JsonOutputParser() chain = llm | parser
6. 注意事项
- 模型支持:
- 工具调用需要模型支持(如 OpenAI 的
gpt-4
或 Anthropic 的 Claude)。 - 检查模型文档是否支持
tool_calls
。
- 工具调用需要模型支持(如 OpenAI 的
- 工具描述:
- 不清晰的描述可能导致模型误选工具。
- 使用具体、简洁的描述。
- 性能:
- 多次工具调用可能增加延迟,需优化工具执行。
- 设置
max_iterations
限制代理循环。
agent = initialize_agent(tools, llm, max_iterations=5)
- 安全性:
- 避免执行不安全的工具(如未沙箱化的
eval
)。 - 验证工具输入和输出,防止注入攻击。
- 避免执行不安全的工具(如未沙箱化的
- 成本:
- 工具调用和模型推理可能产生费用。
- 使用缓存和低成本工具优化。
7. 与其他模块的结合
- 代理(Agents):
- 工具调用是代理的核心,
AgentExecutor
自动管理。
agent = initialize_agent(tools, llm, agent_type=AgentType.OPENAI_FUNCTIONS)
- 工具调用是代理的核心,
- 结构化输出:
- 工具调用通常生成结构化输出。
llm.with_structured_output(schema=Answer)
- 回调(Callbacks):
- 监控工具调用过程。
agent.invoke(input, config={"callbacks": [ToolCallback()]})
- 缓存(Cache):
- 缓存工具结果,减少重复调用。
set_llm_cache(SQLiteCache(database_path="cache.db"))
- 检索器(Retrievers):
- 结合检索器为工具提供上下文。
from langchain.chains import ConversationalRetrievalChain qa_chain = ConversationalRetrievalChain.from_llm(llm, retriever, tools=tools)
- LangSmith:
- 分析工具调用性能和准确性。
8. 综合示例:多工具代理
以下是一个结合搜索和计算工具的代理示例:
from langchain_openai import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
from langchain_core.tools import Tool, StructuredTool
from langchain_community.tools import DuckDuckGoSearchRun
from pydantic import BaseModel
from langchain_core.callbacks import StdOutCallbackHandler
# 定义计算工具
class CalcInput(BaseModel):
a: float
b: float
def add_numbers(input: CalcInput) -> float:
return input.a + input.b
calc_tool = StructuredTool.from_function(
func=add_numbers,
name="Adder",
description="将两个数字相加"
)
# 初始化搜索工具
search_tool = DuckDuckGoSearchRun()
# 工具列表
tools = [calc_tool, search_tool]
# 初始化代理
llm = ChatOpenAI(api_key="your-openai-key")
agent = initialize_agent(
tools=tools,
llm=llm,
agent_type=AgentType.OPENAI_FUNCTIONS,
verbose=True,
callbacks=[StdOutCallbackHandler()]
)
# 执行任务
result = agent.invoke("计算 5 + 3 并搜索量子计算的最新进展")
print(result["output"])
输出:
[AgentExecutor] 正在执行...
[Tool: Adder] 输入:{'a': 5, 'b': 3}
[Tool Output] 8.0
[Tool: DuckDuckGoSearch] 输入:量子计算的最新进展
[Tool Output] 量子计算的最新研究...
[Final Answer] 计算结果为 8,量子计算的最新进展包括...
9. 学习资源
- 官方文档:https://python.langchain.com/docs/modules/tools
- GitHub 示例:https://github.com/langchain-ai/langchain
- LangSmith:用于调试工具调用(https://smith.langchain.com)。
- 社区教程:LangChain 官方博客、YouTube 视频。
10. 总结
- 定义:聊天模型调用工具通过动态选择和执行外部工具扩展模型能力。
- 实现方式:
bind_tools
:直接绑定工具。AgentExecutor
:自动管理工具调用。StructuredTool
:定义结构化工具。LangGraph
:自定义工作流。
- 工作原理:定义工具 → 绑定模型 → 推理选择 → 执行工具 → 整合结果。
- 应用场景:信息检索、计算、数据库查询、API 集成、自动化、数据处理。
- 优化点:工具选择、性能、错误处理、监控、输出处理。
- 注意事项:模型支持、工具描述、性能、安全性、成本。