零基础学AI大模型之LangChain Tool异常处理

大家好,我是工藤学编程 🦉 一个正在努力学习的小博主,期待你的关注
实战代码系列最新文章😉 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异常处理

一、 工具调用也会"掉链子":不处理异常,智能体就"罢工"

之前我们学了怎么给大模型绑定Tool工具,让它能查天气、做计算、调API------但实际用起来会发现,工具调用就像人干活一样,难免"掉链子":

  • 查天气时网络突然断了,API没响应;
  • 调用股票接口时,密钥过期导致权限不足;
  • 用户输入的城市名是错的(比如"北上广"),参数不合法;
  • 免费API每天限100次,调用超了就被限流。

这些问题如果不处理,后果很糟:

  • 程序直接崩了:一行错误堆栈甩给用户,谁看得懂?
  • 智能体卡壳了:不知道该重试还是换工具,任务直接卡住;
  • 用户体验崩了:本来想查天气,结果看到"503 Service Unavailable",直接劝退。

就像开车没装安全气囊,遇到突发情况就容易出大问题。而LangChain的ToolException,就是给工具装"安全气囊"------让智能体遇到错误时,能优雅处理、友好反馈,甚至自动恢复。

二、 ToolException:给工具错误"定规矩"的关键

ToolException是LangChain专门用来处理工具调用异常的类,核心作用就两个:

  1. 统一错误格式:不管是网络错、权限错还是参数错,都转成智能体能看懂的标准格式;
  2. 传递关键信息:把"哪个工具错了""为什么错了"这些上下文传给智能体,让它知道该怎么处理(重试?换工具?还是提示用户?)。

简单说,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 关键亮点:

  1. 分层异常捕获:从参数校验、网络请求到API响应,全链路覆盖;
  2. 用户友好提示:不同错误返回不同的引导话术,避免技术术语;
  3. 可扩展性:后续新增错误类型(如"城市未开通服务"),只需在处理函数里加分支。

五、 生产环境最佳实践:异常处理的"三要素"

在实际项目中,处理工具异常不能只靠ToolException,还要结合这三个要点:

  1. 日志要全 :异常发生时,记录"哪个工具、什么参数、什么时间、错误详情",方便排查(比如用logging模块写入日志文件);
  2. 降级要柔:不能直接报错,要给用户可操作的替代方案(比如"查不了实时天气,要不要看明天的预报?");
  3. 智能重试 :对网络超时等临时错误,可在处理函数里自动重试1-2次(比如用tenacity库实现重试逻辑)。

六、 小结:异常处理是智能体"成熟度"的试金石

这篇我们学了LangChain Tool工具的异常处理------用ToolException统一捕获错误,通过三种方式(自动处理、固定提示、自定义函数)实现兜底降级,让智能体在工具"掉链子"时也能优雅应对。

核心就是一句话:好的智能体不仅要"会干活",更要"会处理麻烦"。就像优秀的员工,不仅能完成任务,遇到问题还能想办法解决,而不是直接摆烂。

结合之前学的Agent,我们还能让智能体根据错误自动调整策略(比如天气API超时了,自动切换备用数据源),这就是更高级的"容错Agent"。

如果本文对你有帮助,欢迎点赞+关注+收藏🌟 ,有任何问题或实战需求,欢迎在评论区留言交流~ 我是工藤学编程,陪你从零到一玩转AI大模型!🚀

相关推荐
予枫的编程笔记几秒前
Elasticsearch深度搜索与查询DSL实战:精准定位数据的核心技法
java·大数据·人工智能·elasticsearch·搜索引擎·全文检索
小北方城市网1 分钟前
第 6 课:云原生架构终极落地|K8s 全栈编排与高可用架构设计实战
大数据·人工智能·python·云原生·架构·kubernetes·geo
创作者mateo2 分钟前
机器学习基本概念简介(全)
人工智能·机器学习
飞睿科技4 分钟前
乐鑫ESP32-S3-BOX-3,面向AIoT与边缘智能的新一代开发套件
人工智能·嵌入式硬件·esp32·智能家居·乐鑫科技
Rabbit_QL6 分钟前
【数学基础】机器学习中的抽样:你的数据是样本,不是世界
人工智能·机器学习
金融RPA机器人丨实在智能11 分钟前
深度拆解 RPA 机器人:定义、应用、价值与未来方向
人工智能·rpa·实在rpa
青主创享阁12 分钟前
技术破局农业利润困局:玄晶引擎AI数字化解决方案的架构设计与落地实践
大数据·人工智能
datamonday15 分钟前
[EAI-037] π0.6* 基于RECAP方法与优势调节的自进化VLA机器人模型
人工智能·深度学习·机器人·具身智能·vla
Toky丶21 分钟前
【文献阅读】Pt2-Llm: Post-Training Ternarization For Large Language Models
人工智能·语言模型·自然语言处理
梵得儿SHI21 分钟前
(第七篇)Spring AI 核心技术攻坚:国内模型深度集成与国产化 AI 应用实战指南
java·人工智能·spring·springai框架·国产化it生态·主流大模型的集成方案·麒麟系统部署调优