| 大家好,我是工藤学编程 🦉 | 一个正在努力学习的小博主,期待你的关注 |
|---|---|
| 实战代码系列最新文章😉 | C++实现图书管理系统(Qt C++ GUI界面版) |
| SpringBoot实战系列🐷 | 【SpringBoot实战系列】SpringBoot3.X 整合 MinIO 存储原生方案 |
| 分库分表 | 分库分表之实战-sharding-JDBC分库分表执行流程原理剖析 |
| 消息队列 | 深入浅出 RabbitMQ-RabbitMQ消息确认机制(ACK) |
| AI大模型 | 零基础学AI大模型之LLM绑定Tool工具实战 |
前情摘要
1、零基础学AI大模型之读懂AI大模型
2、零基础学AI大模型之从0到1调用大模型API
3、零基础学AI大模型之SpringAI
4、零基础学AI大模型之AI大模型常见概念
5、零基础学AI大模型之大模型私有化部署全指南
6、零基础学AI大模型之AI大模型可视化界面
7、零基础学AI大模型之LangChain
8、零基础学AI大模型之LangChain六大核心模块与大模型IO交互链路
9、零基础学AI大模型之Prompt提示词工程
10、零基础学AI大模型之LangChain-PromptTemplate
11、零基础学AI大模型之ChatModel聊天模型与ChatPromptTemplate实战
12、零基础学AI大模型之LangChain链
13、零基础学AI大模型之Stream流式输出实战
14、零基础学AI大模型之LangChain Output Parser
15、零基础学AI大模型之解析器PydanticOutputParser
16、零基础学AI大模型之大模型的"幻觉"
17、零基础学AI大模型之RAG技术
18、零基础学AI大模型之RAG系统链路解析与Document Loaders多案例实战
19、零基础学AI大模型之LangChain PyPDFLoader实战与PDF图片提取全解析
20、零基础学AI大模型之LangChain WebBaseLoader与Docx2txtLoader实战
21、零基础学AI大模型之RAG系统链路构建:文档切割转换全解析
22、零基础学AI大模型之LangChain 文本分割器实战:CharacterTextSplitter 与 RecursiveCharacterTextSplitter 全解析
23、零基础学AI大模型之Embedding与LLM大模型对比全解析
24、零基础学AI大模型之LangChain Embedding框架全解析
25、零基础学AI大模型之嵌入模型性能优化
26、零基础学AI大模型之向量数据库介绍与技术选型思考
27、零基础学AI大模型之Milvus向量数据库全解析
28、零基础学AI大模型之Milvus核心:分区-分片-段结构全解+最佳实践
29、零基础学AI大模型之Milvus部署架构选型+Linux实战:Docker一键部署+WebUI使用
30、零基础学AI大模型之Milvus实战:Attu可视化安装+Python整合全案例
31、零基础学AI大模型之Milvus索引实战
32、零基础学AI大模型之Milvus DML实战
33、零基础学AI大模型之Milvus向量Search查询综合案例实战
33、零基础学AI大模型之新版LangChain向量数据库VectorStore设计全解析
34、零基础学AI大模型之相似度Search与MMR最大边界相关搜索实战
35、零基础学AI大模型之LangChain整合Milvus:新增与删除数据实战
36、零基础学AI大模型之LangChain+Milvus实战:相似性搜索与MMR多样化检索全解析
37、零基础学AI大模型之LangChain Retriever
38、零基础学AI大模型之MultiQueryRetriever多查询检索全解析
39、零基础学AI大模型之LangChain核心:Runnable接口底层实现
40、零基础学AI大模型之RunnablePassthrough
41、零基础学AI大模型之RunnableParallel
42、零基础学AI大模型之RunnableLambda
43、零基础学AI大模型之RunnableBranch
44、零基础学AI大模型之Agent智能体
45、零基础学AI大模型之LangChain Tool工具
46、零基础学AI大模型之LLM绑定Tool工具实战
本文章目录
- [零基础学AI大模型之LangChain Tool异常处理](#零基础学AI大模型之LangChain Tool异常处理)
-
- [一、 工具调用也会"掉链子":不处理异常,智能体就"罢工"](#一、 工具调用也会“掉链子”:不处理异常,智能体就“罢工”)
- [二、 ToolException:给工具错误"定规矩"的关键](#二、 ToolException:给工具错误“定规矩”的关键)
- [三、 三种异常处理方式:从"简单兜底"到"自定义降级"](#三、 三种异常处理方式:从“简单兜底”到“自定义降级”)
- [四、 实战:给天气查询工具加"全链路异常防护"](#四、 实战:给天气查询工具加“全链路异常防护”)
-
- [4.1 完整代码(带详细注释)](#4.1 完整代码(带详细注释))
- [4.2 运行结果(模拟三种异常)](#4.2 运行结果(模拟三种异常))
- [4.3 关键亮点:](#4.3 关键亮点:)
- [五、 生产环境最佳实践:异常处理的"三要素"](#五、 生产环境最佳实践:异常处理的“三要素”)
- [六、 小结:异常处理是智能体"成熟度"的试金石](#六、 小结:异常处理是智能体“成熟度”的试金石)
零基础学AI大模型之LangChain Tool异常处理
一、 工具调用也会"掉链子":不处理异常,智能体就"罢工"

之前我们学了怎么给大模型绑定Tool工具,让它能查天气、做计算、调API------但实际用起来会发现,工具调用就像人干活一样,难免"掉链子":
- 查天气时网络突然断了,API没响应;
- 调用股票接口时,密钥过期导致权限不足;
- 用户输入的城市名是错的(比如"北上广"),参数不合法;
- 免费API每天限100次,调用超了就被限流。
这些问题如果不处理,后果很糟:
- 程序直接崩了:一行错误堆栈甩给用户,谁看得懂?
- 智能体卡壳了:不知道该重试还是换工具,任务直接卡住;
- 用户体验崩了:本来想查天气,结果看到"503 Service Unavailable",直接劝退。
就像开车没装安全气囊,遇到突发情况就容易出大问题。而LangChain的ToolException,就是给工具装"安全气囊"------让智能体遇到错误时,能优雅处理、友好反馈,甚至自动恢复。
二、 ToolException:给工具错误"定规矩"的关键

ToolException是LangChain专门用来处理工具调用异常的类,核心作用就两个:
- 统一错误格式:不管是网络错、权限错还是参数错,都转成智能体能看懂的标准格式;
- 传递关键信息:把"哪个工具错了""为什么错了"这些上下文传给智能体,让它知道该怎么处理(重试?换工具?还是提示用户?)。
简单说,ToolException就像给各种混乱的错误"统一发了身份证",智能体拿着这张身份证,就知道该怎么应对了。
三、 三种异常处理方式:从"简单兜底"到"自定义降级"
LangChain提供了三种处理工具异常的方式,从简单到灵活,覆盖不同场景,我们一个个实战学:
方式一:自动处理(handle_tool_error=True)------最简单的"兜底"
只需在定义工具时加个handle_tool_error=True,工具就会自动捕获异常,并返回标准化的错误信息(包含异常原因),不用自己写额外代码。
代码示例:
python
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="提示文本")------给用户"说人话"
如果觉得自动生成的错误信息太技术化,可以用handle_tool_error直接指定一句友好的提示(比如"网络有点卡,换个关键词试试?"),让用户更容易理解。
代码示例:
python
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)
输出结果:
工具返回结果: 抱歉,暂时查不到这个城市的天气~ 试试北京、上海或广州吧!
适用场景:
需要给用户友好反馈的场景,隐藏技术细节,只说用户能懂的话。
方式三:自定义处理函数------灵活"降级",智能应对
如果想更灵活(比如记录错误日志、自动重试、切换备用工具),可以定义一个异常处理函数,让它根据错误类型做不同操作------这是生产环境最常用的方式。
代码示例:
python
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密钥过期"三种常见错误。
4.1 完整代码(带详细注释)
python
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": "上海"}))
4.2 运行结果(模拟三种异常)
=== 测试1:城市名非中文 ===
输入有误:城市名'beijing'必须是中文(如北京),请重新输入中文城市名~
=== 测试2:网络超时(模拟) ===
网络有点慢呢~ 请稍后再试,或换个城市查询
=== 测试3:API密钥无效 ===
系统暂时无法查询天气,工程师正在抢修中,抱歉啦~
4.3 关键亮点:
- 分层异常捕获:从参数校验、网络请求到API响应,全链路覆盖;
- 用户友好提示:不同错误返回不同的引导话术,避免技术术语;
- 可扩展性:后续新增错误类型(如"城市未开通服务"),只需在处理函数里加分支。
五、 生产环境最佳实践:异常处理的"三要素"

在实际项目中,处理工具异常不能只靠ToolException,还要结合这三个要点:
- 日志要全 :异常发生时,记录"哪个工具、什么参数、什么时间、错误详情",方便排查(比如用
logging模块写入日志文件); - 降级要柔:不能直接报错,要给用户可操作的替代方案(比如"查不了实时天气,要不要看明天的预报?");
- 智能重试 :对网络超时等临时错误,可在处理函数里自动重试1-2次(比如用
tenacity库实现重试逻辑)。
六、 小结:异常处理是智能体"成熟度"的试金石
这篇我们学了LangChain Tool工具的异常处理------用ToolException统一捕获错误,通过三种方式(自动处理、固定提示、自定义函数)实现兜底降级,让智能体在工具"掉链子"时也能优雅应对。
核心就是一句话:好的智能体不仅要"会干活",更要"会处理麻烦"。就像优秀的员工,不仅能完成任务,遇到问题还能想办法解决,而不是直接摆烂。
结合之前学的Agent,我们还能让智能体根据错误自动调整策略(比如天气API超时了,自动切换备用数据源),这就是更高级的"容错Agent"。
如果本文对你有帮助,欢迎点赞+关注+收藏🌟 ,有任何问题或实战需求,欢迎在评论区留言交流~ 我是工藤学编程,陪你从零到一玩转AI大模型!🚀