langchain (十):Agent 调用外部工具(Tools)
摘要:LangChain框架中的Tools(工具)让大语言模型能够调用外部函数执行特定任务。本文详细介绍了如何通过Agent机制实现模型自动决策调用工具,以通义千问+Tavily搜索为例,展示了完整实现流程。核心内容包括:工具调用原理、环境配置、代码实现(模型初始化、工具绑定、Agent创建)、以及扩展优化建议。通过自定义@tool装饰器和Pydantic参数控制,开发者可以灵活创建各类工具,使大
在 LangChain 中,Tools(工具)是指可以被大模型调用的函数或接口。
假设你问 ChatGPT:“帮我把桌面上的 report.pdf 移动到 Documents 文件夹”。ChatGPT 会回答:“我无法直接操作您的文件系统,但您可以手动进行以下操作……”
为什么它做不到?因为它只是个语言模型,没有操作系统权限。
但在 LangChain 里,我们可以给大模型定义一个"移动文件"的工具。当用户发出同样的请求时,大模型会:
分析用户意图:“哦,用户想移动文件”
选择对应的工具:“我有一个 MoveFileTool”
提取参数:“源路径是 Desktop/report.pdf,目标路径是 Documents/”
调用工具,真正执行移动操作
这就是 Tools 的作用。
在大语言模型应用开发中,单纯的模型对话能力往往不足以解决需要实时信息、特定计算或外部数据的问题。LangChain 框架提供的 Agent(智能体)机制,能够让模型根据问题自动判断是否需要调用外部工具(Tools),并利用工具返回的结果生成最终答案。本文将结合调用通义千问 + Tavily 网络搜索的实战代码,详解 Agent Tools 的核心使用方法。
一、核心原理
Agent 本质是具备 “决策能力” 的大模型封装,其核心逻辑分为三步:
- 解析用户问题,判断是否需要调用外部工具;
- 若需要,选择合适的工具并传入正确的参数执行;
- 获取工具返回结果,结合自身知识生成最终回答。
本文示例中,我们将实现:让通义千问模型(qwen-plus)在回答 “2023 年诺贝尔物理学奖得主” 这类需要实时信息的问题时,自动调用 Tavily 网络搜索工具获取准确答案。
二、完整实战代码
1. 环境准备
首先安装所需依赖:
pip install langchain langchain-openai langchain-tavily python-dotenv pydantic
2. 完整代码(含详细注释)
# 1. 加载环境变量(保护API密钥,避免硬编码)
from dotenv import load_dotenv
load_dotenv() # 自动读取项目根目录的.env文件
import os
from pydantic import SecretStr # 安全存储敏感信息
from langchain_openai import ChatOpenAI # 兼容OpenAI接口的LLM封装
from langchain.agents import create_agent # Agent创建工具
from langchain_tavily import TavilySearch # Tavily网络搜索工具
# 2. 初始化大语言模型(通义千问,兼容OpenAI接口)
llm = ChatOpenAI(
# 阿里云通义千问的兼容接口地址
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
model="qwen-plus", # 通义千问增强版模型
# 从环境变量读取API密钥,用SecretStr加密存储
api_key=SecretStr(os.environ["DASHSCOPE_API_KEY"]),
)
# 3. 初始化外部工具:Tavily网络搜索
web_search = TavilySearch(
api_key=os.environ["TAVILY_API_KEY"], # 从环境变量读取Tavily密钥
max_results=2, # 限制搜索结果数量,减少冗余
search_depth="basic" # 基础搜索模式(速度快,适合通用问题)
)
# 4. 创建Agent(绑定模型+工具+系统提示)
agent = create_agent(
model=llm, # 核心大模型
tools=[web_search], # 可调用的工具列表(支持多个工具)
# 系统提示:定义Agent的角色和行为准则
system_prompt="你是一名多才多艺的智能助手,擅长分析问题并调用合适的工具解决问题。"
)
# 5. 运行Agent,处理用户问题
result = agent.invoke(
{
"messages": [
{"role": "user", "content": "请帮我查询2023年诺贝尔物理学奖得主是谁?"}
]
}
)
# 6. 提取并输出最终答案
final_answer = result['messages'][-1].content
print("Agent最终回答:")
print(final_answer)
3. 环境变量配置(.env 文件)
在项目根目录创建.env文件,填入你的 API 密钥(需提前申请):
# 阿里云通义千问API密钥(从灵积平台获取)
DASHSCOPE_API_KEY=your_dashscope_api_key
# Tavily搜索API密钥(从Tavily官网获取)
TAVILY_API_KEY=your_tavily_api_key
接入内置工具
LangChain 提供了丰富的内置工具库(搜索、数据库、Slack、Jira、文件系统等),可通过 langchain_community 快速接入。
LangChain内置工具列表:https://python.langchain.com/docs/integrations/tools/
以其中内置的TavilySearchResults网络搜索工具为例,借助Tavily进行网络搜索和信息爬取。来介绍如何将内置工具接入create_agent中。
需要先在tavily官网注册并获得API-KEY(每月有免费额度):https://www.tavily.com/

三、关键代码解析
1. 模型初始化:兼容 OpenAI 接口的 LLM
llm = ChatOpenAI(
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
model="qwen-plus",
api_key=SecretStr(os.environ["DASHSCOPE_API_KEY"]),
)
base_url:指定通义千问的 OpenAI 兼容接口,让 LangChain 的 ChatOpenAI 类可以无缝调用阿里云模型;SecretStr:Pydantic 提供的敏感信息加密存储类,避免 API 密钥明文暴露;os.environ[]:从环境变量读取密钥,是生产环境的最佳实践(避免硬编码密钥)。
2. 工具初始化:TavilySearch
web_search = TavilySearch(
api_key=os.environ["TAVILY_API_KEY"],
max_results=2,
search_depth="basic"
)
max_results:控制搜索结果数量,减少模型处理的冗余信息;search_depth:可选basic(基础)或advanced(高级),高级模式搜索更全面但速度稍慢;- Tavily 是专为 LLM 设计的轻量级搜索工具,返回结果格式简洁,适合模型直接解析。
3. Agent 创建:核心绑定逻辑
agent = create_agent(
model=llm,
tools=[web_search],
system_prompt="你是一名多才多艺的智能助手,可以调用工具帮助用户解决问题。"
)
tools参数接收工具列表,支持同时绑定多个工具(如搜索、计算器、数据库查询等);system_prompt用于引导 Agent 的行为,明确告知其 “可以调用工具解决问题”,避免模型仅凭自身知识回答。
4. 执行 Agent 并提取结果
result = agent.invoke({"messages": [{"role": "user", "content": "问题内容"}]})
final_answer = result['messages'][-1].content
invoke()方法接收符合 Chat Message 格式的输入,返回包含完整交互过程的结果;result['messages']是按顺序排列的消息列表,最后一条即为 Agent 的最终回答。
四、扩展与优化建议
- 多工具集成:在
tools列表中添加更多工具(如Calculator、WikipediaQueryRun),让 Agent 具备多场景处理能力; - 工具调用规则优化:通过更精准的
system_prompt限定工具调用条件(如 “仅当问题涉及 2020 年后的实时信息时调用搜索工具”); - 错误处理:添加 try-except 捕获工具调用失败(如 API 密钥错误、网络问题),提升代码健壮性;
- 结果格式化:对 Agent 返回的回答进行结构化处理(如 JSON、markdown),适配不同展示场景。
五、运行效果示例
输入问题:请帮我查询2023年诺贝尔物理学奖得主是谁?Agent 会自动调用 Tavily 搜索工具,获取准确信息后返回:
plaintext
2023年诺贝尔物理学奖授予了皮埃尔·阿戈斯蒂尼(Pierre Agostini)、费伦茨·克劳斯(Ferenc Krausz)和安妮·吕利耶(Anne L'Huillier),以表彰他们在产生阿秒光脉冲方面的实验方法的发明,这一成果为研究物质的电子动力学提供了全新的工具。
六 自定义工具:@tool 装饰
定义一个工具最简单的方式就是用 @tool 装饰器。
2.1 最基础的用法
from langchain_core.tools import tool
@tool
def add_numbers(a: int, b: int) -> int:
"""计算两个整数的和"""
return a + b
# 看看这个工具的属性
print(f"工具名称: {add_numbers.name}")
print(f"工具参数: {add_numbers.args}")
print(f"工具描述: {add_numbers.description}")
print(f"直接返回: {add_numbers.return_direct}")
输出:
工具名称: add_numbers
工具参数: {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}
工具描述: 计算两个整数的和
直接返回: False
写一个普通函数
加上类型注解(a: int, b: int -> int)
写个 docstring(三引号注释)
加个 @tool 装饰器
LangChain 就会自动把它变成一个工具!工具的描述会自动从 docstring 里提取,参数信息会从类型注解里推断。
2.2 带参数的装饰器
如果你想自定义工具的名字、描述等属性,可以给 @tool 传参数:
@tool(
name_or_callable="add_two_numbers", # 自定义工具名称
return_direct=True, # 是否直接返回结果
description="这是一个计算两个整数和的函数" # 自定义描述
)
def add_numbers(a: int, b: int) -> int:
"""计算两个整数的和"""
return a + b
print(f"工具名称: {add_numbers.name}")
print(f"直接返回: {add_numbers.return_direct}")
print(f"工具描述: {add_numbers.description}")
输出:
工具名称: add_two_numbers
直接返回: True
工具描述: 这是一个计算两个整数和的函数
这里的 return_direct=True 是个挺有意思的参数。如果设置为 True,Agent 调用这个工具后会直接返回结果,不会再做额外处理。如果是 False(默认值),Agent 可能会拿着结果继续思考、继续调用其他工具。
2.3 使用 Pydantic 精确控制参数
有时候你想给参数加更详细的说明,让大模型更容易理解这个参数的含义。这时候可以用 Pydantic 的 BaseModel:
from pydantic import BaseModel, Field
class FieldInfo(BaseModel):
a: int = Field(description="第一个整型参数")
b: int = Field(description="第二个整型参数")
@tool(
name_or_callable="add_two_numbers",
return_direct=True,
description="这是一个计算两个整数和的函数",
args_schema=FieldInfo # 使用 Pydantic 模型定义参数
)
def add_numbers(a: int, b: int) -> int:
"""计算两个整数的和"""
return a + b
print(add_numbers.args)
输出:
{
'a': {'description': '第一个整型参数', 'type': 'integer'},
'b': {'description': '第二个整型参数', 'type': 'integer'}
}
这样一来,大模型在分析用户输入时,会更清楚每个参数的含义,调用工具的准确度也会提高。
总结
- LangChain 的 Agent 核心价值是让大模型具备 “调用外部工具解决问题” 的能力,突破模型自身知识的局限性;
- Agent 使用的核心步骤:初始化模型 → 初始化工具 → 绑定模型 + 工具 + 提示词 → 执行并提取结果;
- 生产环境中需通过环境变量 + SecretStr 保护 API 密钥,避免硬编码带来的安全风险。
更多推荐



所有评论(0)