LangChain Tool 异常处理实战
LangChain Tool 异常处理是构建稳定智能体的关键。本文介绍了 ToolException 的作用,以及三种异常处理方式:自动兜底、固定提示文本和自定义处理函数。通过天气查询工具的实战案例,展示了如何分层捕获参数错误、网络超时和 API 错误,并结合生产环境最佳实践,强调日志记录、降级策略和智能重试的重要性,确保智能体在工具调用失败时能优雅应对。

LangChain Tool 异常处理是构建稳定智能体的关键。本文介绍了 ToolException 的作用,以及三种异常处理方式:自动兜底、固定提示文本和自定义处理函数。通过天气查询工具的实战案例,展示了如何分层捕获参数错误、网络超时和 API 错误,并结合生产环境最佳实践,强调日志记录、降级策略和智能重试的重要性,确保智能体在工具调用失败时能优雅应对。


之前我们学了怎么给大模型绑定 Tool 工具,让它能查天气、做计算、调 API——但实际用起来会发现,工具调用就像人干活一样,难免'掉链子':
这些问题如果不处理,后果很糟:
就像开车没装安全气囊,遇到突发情况就容易出大问题。而 LangChain 的 ToolException,就是给工具装'安全气囊'——让智能体遇到错误时,能优雅处理、友好反馈,甚至自动恢复。

ToolException 是 LangChain 专门用来处理工具调用异常的类,核心作用就两个:
简单说,ToolException 就像给各种混乱的错误'统一发了身份证',智能体拿着这张身份证,就知道该怎么应对了。
LangChain 提供了三种处理工具异常的方式,从简单到灵活,覆盖不同场景,我们一个个实战学:
只需在定义工具时加个 handle_tool_error=True,工具就会自动捕获异常,并返回标准化的错误信息(包含异常原因),不用自己写额外代码。
from langchain_core.tools import StructuredTool, ToolException
# 1. 定义一个可能出错的工具(模拟搜索失败)
def search(query: str) -> str:
"""执行搜索查询,返回结果"""
# 模拟异常:比如搜索结果为空、网络超时
raise ToolException(f"搜索失败:未找到'{query}'的相关结果(可能网络超时)")
# 2. 创建工具时开启自动异常处理
search_tool = StructuredTool.from_function(
func=search,
name="SearchTool",
description="用于搜索信息的工具",
handle_tool_error=True # 关键:自动处理异常
)
# 3. 调用工具(故意触发异常)
try:
result = search_tool.invoke({"query": "不存在的关键词 12345"})
print("工具返回结果:", result)
except Exception as e:
# 注意:开启 handle_tool_error 后,不会走到这里,异常已被工具内部处理
print("捕获到异常:", e)
工具返回结果: SearchTool tool failed with error: SearchTool: 搜索失败:未找到'不存在的关键词 12345'的相关结果(可能网络超时)
快速开发、不需要自定义错误信息的场景,让工具自己搞定异常。
如果觉得自动生成的错误信息太技术化,可以用 handle_tool_error 直接指定一句友好的提示(比如'网络有点卡,换个关键词试试?'),让用户更容易理解。
from langchain_core.tools import StructuredTool, ToolException
def weather_query(city: str) -> str:
"""查询指定城市的天气"""
# 模拟异常:城市名不合法(比如'火星市')
if city not in ["北京", "上海", "广州"]:
raise ToolException(f"无效城市:{city}(不在支持的城市列表中)")
return f"{city}今天晴,25℃"
# 创建工具时指定固定错误提示
weather_tool = StructuredTool.from_function(
func=weather_query,
name="WeatherTool",
description="查询天气的工具,参数为城市名",
handle_tool_error="抱歉,暂时查不到这个城市的天气~ 试试北京、上海或广州吧!" # 自定义友好提示
)
# 调用工具(传入无效城市)
result = weather_tool.invoke({"city": "火星市"})
print("工具返回结果:", result)
工具返回结果: 抱歉,暂时查不到这个城市的天气~ 试试北京、上海或广州吧!
需要给用户友好反馈的场景,隐藏技术细节,只说用户能懂的话。
如果想更灵活(比如记录错误日志、自动重试、切换备用工具),可以定义一个异常处理函数,让它根据错误类型做不同操作——这是生产环境最常用的方式。
from langchain_core.tools import StructuredTool, ToolException
import time
# 1. 定义可能出错的工具(模拟 API 调用次数超限)
def stock_query(code: str) -> str:
"""查询股票价格,参数为股票代码(如 600036)"""
# 模拟异常:每日调用次数超限
raise ToolException(f"股票查询失败:{code},今日调用次数已达上限")
# 2. 定义自定义异常处理函数(核心)
def handle_stock_error(error: ToolException) -> str:
"""
处理股票查询工具的异常
1. 记录错误日志(实际项目中可写入文件或日志系统)
2. 根据错误类型返回降级方案
"""
# 记录错误(模拟日志)
error_msg = f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 错误:{str(error)}"
print("【错误日志】", error_msg)
# 根据错误内容返回不同的降级提示
if "调用次数已达上限" in str(error):
return "今日股票查询次数已用完~ 明天再试吧,或查看大盘指数替代"
else:
return f"查询失败:{str(error)},请检查股票代码是否正确"
# 3. 创建工具时绑定自定义处理函数
stock_tool = StructuredTool.from_function(
func=stock_query,
name="StockTool",
description="查询股票价格的工具",
handle_tool_error=handle_stock_error # 绑定自定义函数
)
# 4. 调用工具(触发次数超限异常)
result = stock_tool.invoke({"code": "600036"})
print("工具返回结果:", result)
【错误日志】 [2025-11-07 15:30:00] 错误:StockTool: 股票查询失败:600036,今日调用次数已达上限
工具返回结果: 今日股票查询次数已用完~ 明天再试吧,或查看大盘指数替代
复杂业务场景,需要根据错误类型做精细化处理(日志、重试、降级方案等)。

结合之前学的天气查询工具,我们来做一个实战——给它加上完整的异常处理,应对'网络超时''城市不存在''API 密钥过期'三种常见错误。
import requests
from langchain_core.tools import StructuredTool, ToolException
from pydantic import BaseModel, Field
# 1. 定义参数模型(校验城市名格式)
class WeatherInput(BaseModel):
city: str = Field(description="城市名称,如北京、上海")
# 2. 定义天气查询工具(可能抛出多种异常)
def get_weather(city: str) -> str:
"""调用天气 API 查询实时天气"""
# 模拟 API 密钥(实际项目中用环境变量存储)
api_key = "无效的密钥"
# 故意用无效密钥模拟权限错误
# 异常 1:城市名格式错误(非中文)
if not all('\u4e00' <= c <= '\u9fff' for c in city):
raise ToolException(f"参数错误:城市名'{city}'必须是中文(如北京)")
# 调用天气 API(模拟)
try:
url = f"https://api.weather.com/{city}?key={api_key}"
response = requests.get(url, timeout=5) # 超时 5 秒
# 异常 2:API 返回错误(如密钥无效)
if response.status_code != 200:
raise ToolException(f"API 错误:状态码{response.status_code}(可能密钥过期)")
# 正常返回天气(模拟)
return f"{city}实时天气:晴,22℃,微风"
# 异常 3:网络超时
except requests.exceptions.Timeout:
raise ToolException(f"网络超时:查询'{city}'天气时连接超时,请检查网络")
# 3. 定义自定义异常处理函数(全链路防护)
def handle_weather_error(error: ToolException) -> str:
"""根据不同错误类型返回处理方案"""
error_str = str(error)
# 处理参数错误
if "参数错误" in error_str:
return f"输入有误:{error_str.split(':')[1]},请重新输入中文城市名~"
# 处理网络超时
elif "网络超时" in error_str:
return "网络有点慢呢~ 请稍后再试,或换个城市查询"
# 处理 API 错误(如密钥问题)
elif "API 错误" in error_str:
return "系统暂时无法查询天气,工程师正在抢修中,抱歉啦~"
# 其他未知错误
else:
return "查询遇到小问题,请稍后重试~"
# 4. 创建带异常处理的工具
weather_tool = StructuredTool.from_function(
func=get_weather,
name="WeatherTool",
description="查询中文城市的实时天气",
args_schema=WeatherInput, # 绑定参数模型
handle_tool_error=handle_weather_error # 绑定异常处理函数
)
# 5. 测试三种异常场景
print("=== 测试 1:城市名非中文 ===")
print(weather_tool.invoke({"city": "beijing"}))
print("\n=== 测试 2:网络超时(模拟) ===")
# (代码中已模拟超时,实际可通过断网测试)
print(weather_tool.invoke({"city": "北京"}))
print("\n=== 测试 3:API 密钥无效 ===")
print(weather_tool.invoke({"city": "上海"}))
=== 测试 1:城市名非中文 ===
输入有误:城市名'beijing'必须是中文(如北京),请重新输入中文城市名~
=== 测试 2:网络超时(模拟) ===
网络有点慢呢~ 请稍后再试,或换个城市查询
=== 测试 3:API 密钥无效 ===
系统暂时无法查询天气,工程师正在抢修中,抱歉啦~

在实际项目中,处理工具异常不能只靠 ToolException,还要结合这三个要点:
logging 模块写入日志文件);tenacity 库实现重试逻辑)。这篇我们学了 LangChain Tool 工具的异常处理——用 ToolException 统一捕获错误,通过三种方式(自动处理、固定提示、自定义函数)实现兜底降级,让智能体在工具'掉链子'时也能优雅应对。
核心就是一句话:好的智能体不仅要'会干活',更要'会处理麻烦'。就像优秀的员工,不仅能完成任务,遇到问题还能想办法解决,而不是直接摆烂。
结合之前学的 Agent,我们还能让智能体根据错误自动调整策略(比如天气 API 超时了,自动切换备用数据源),这就是更高级的'容错 Agent'。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online