工具调用出错捕获提升程序健壮性
一、这是什么?(概念解释)
在工具调用过程中,可能会出现各种错误:参数类型错误、参数缺失、工具执行失败等。如果不进行错误处理,程序会直接崩溃。
解决方案:通过三种策略提升程序健壮性:
- 错误捕获(Try-Except):捕获工具执行异常并返回友好提示
- 回退处理(Fallbacks):当主方案失败时,自动切换到备用方案
- 错误反馈重试:将错误信息反馈给 LLM,让其自我纠正并重试
二、有什么用?(应用场景)
| 场景 | 说明 |
|---|---|
| 参数错误 | LLM 传递了错误的参数类型或缺失必需参数 |
| API 失败 | 外部 API 调用失败(网络错误、超时等) |
| 模型切换 | 当一个模型失败时,自动切换到备用模型 |
| 自我纠正 | 让 LLM 根据错误信息自动修正参数重试 |
| 降级处理 | 当高级功能失败时,降级到简单方案 |
| 用户体验 | 避免程序崩溃,给用户友好的错误提示 |
三、策略一:错误捕获(Try-Except)
python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from typing import Any
import dotenv
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
# 定义工具
@tool
def complex_tool(int_arg: int, float_arg: float, dict_arg: dict) -> int:
"""使用复杂工具进行复杂计算操作"""
return int_arg * float_arg
# 定义带错误处理的工具执行函数
def try_except_tool(tool_args: dict, config: RunnableConfig) -> Any:
try:
return complex_tool.invoke(tool_args, config=config)
except Exception as e:
# 捕获错误并返回友好提示
return f"""调用工具时使用以下参数:
{tool_args}
引发了以下错误:
{type(e).__name__}: {e}"""
# 创建模型并绑定工具
llm = ChatOpenAI(model="moonshot-v1-8k", temperature=0)
llm_with_tools = llm.bind_tools([complex_tool])
# 创建链:模型 -> 提取参数 -> 执行工具(带错误处理)
chain = (
llm_with_tools |
(lambda msg: msg.tool_calls[0]["args"]) |
try_except_tool
)
# 调用
result = chain.invoke("使用复杂工具,对应参数为5和2.5")
print(result)
# 如果缺少 dict_arg 参数,会返回:
# 调用工具时使用以下参数: {'int_arg': 5, 'float_arg': 2.5}
# 引发了以下错误: TypeError: missing 1 required positional argument: 'dict_arg'
四、策略二:回退处理(Fallbacks)
python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import dotenv
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
# 定义工具
@tool
def complex_tool(int_arg: int, float_arg: float, dict_arg: dict) -> int:
"""使用复杂工具进行复杂计算操作"""
return int_arg * float_arg
# 创建两个模型:主模型和备用模型
llm = ChatOpenAI(model="moonshot-v1-8k").bind_tools([complex_tool])
better_llm = ChatOpenAI(model="moonshot-v1-32k").bind_tools([complex_tool])
# 创建两个链:主链和备用链
better_chain = (
better_llm |
(lambda msg: msg.tool_calls[0]["args"]) |
complex_tool
)
chain = (
llm |
(lambda msg: msg.tool_calls[0]["args"]) |
complex_tool
).with_fallbacks([better_chain]) # 关键:设置回退链
# 调用:如果 llm 失败,自动使用 better_llm
result = chain.invoke("使用复杂工具,对应参数为5和2.1,不要忘记了dict_arg参数")
print(result)
回退策略适用场景:
- 主模型失败,切换到备用模型
- 高级工具失败,降级到简单工具
- 主 API 失败,切换到备用 API
五、策略三:错误反馈重试(Self-Correction)
python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from typing import Any
import dotenv
from langchain_core.messages import ToolCall, AIMessage, ToolMessage, HumanMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
# 1. 定义自定义异常
class CustomToolException(Exception):
def __init__(self, tool_call: ToolCall, exception: Exception) -> None:
super().__init__()
self.tool_call = tool_call
self.exception = exception
# 2. 定义工具
@tool
def complex_tool(int_arg: int, float_arg: float, dict_arg: dict) -> int:
"""使用复杂工具进行复杂计算操作"""
return int_arg * float_arg
# 3. 定义带自定义异常的执行函数
def tool_custom_exception(msg: AIMessage, config: RunnableConfig) -> Any:
try:
return complex_tool.invoke(msg.tool_calls[0]["args"], config=config)
except Exception as e:
# 抛出自定义异常,携带工具调用信息和原始异常
raise CustomToolException(msg.tool_calls[0], e)
# 4. 定义异常转换函数:将异常转换为消息列表
def exception_to_messages(inputs: dict) -> dict:
exception = inputs.pop("exception")
# 构造消息历史:
# - AIMessage: 模型决定调用工具
# - ToolMessage: 工具返回错误信息
# - HumanMessage: 提示模型不要重复犯错
messages = [
AIMessage(content="", tool_calls=[exception.tool_call]),
ToolMessage(
tool_call_id=exception.tool_call["id"],
content=str(exception.exception)
),
HumanMessage(
content="最后一次工具调用引发了异常,请尝试使用更正的参数再次调用该工具,请不要重复犯错"
),
]
inputs["last_output"] = messages
return inputs
# 5. 创建 Prompt(包含占位符,用于插入错误信息)
prompt = ChatPromptTemplate.from_messages([
("human", "{query}"),
("placeholder", "{last_output}") # 占位符,会被错误信息替换
])
# 6. 创建模型并绑定工具
llm = ChatOpenAI(model="moonshot-v1-8k", temperature=0).bind_tools(
tools=[complex_tool],
tool_choice="complex_tool" # 强制使用该工具
)
# 7. 创建主链和回退链
chain = prompt | llm | tool_custom_exception
self_correcting_chain = chain.with_fallbacks(
[exception_to_messages | chain], # 失败时:转换异常 -> 重新调用链
exception_key="exception"
)
# 8. 调用
result = self_correcting_chain.invoke({"query": "使用复杂工具,对应参数为5和2.1"})
print(result)
六、流程对比图
scss
┌─────────────────────────────────────────────────────────────────────────┐
│ 三种错误处理策略流程对比 │
└─────────────────────────────────────────────────────────────────────────┘
================== 策略一:错误捕获 ==================
用户输入 LLM 工具执行
│ │ │
▼ ▼ │
"调用工具" ────────────▶│ 返回 tool_calls │
│ │ │
│ 提取参数 │ │
▼ │ │
try_except_tool() ────────────────────────────▶│
│ 执行工具
│◀──────────────────────────────────────│
│ │ │
│ [成功] │ │
▼ ▼ ▼
返回正常结果 ✓
OR
│ [失败] │ │
│◀──────────────────────────────────────│ 抛出异常
│ │ │
▼ ▼ ▼
返回错误信息 友好提示
================== 策略二:回退处理 ==================
用户输入 主LLM 工具执行
│ │ │
▼ ▼ │
"调用工具" ────────────▶│ 返回 tool_calls │
│ │ │
│ 提取参数 │ │
▼ │ │
主链执行 ──────────────────────────────────────▶│
│ 执行工具
│◀──────────────────────────────────────│
│ │ │
│ [失败] │ │
│ │ ▼
自动切换到备用链 ✗ 失败
│ │
▼ ▼
备用LLM ──────────────▶│ 重新生成 tool_calls
│ │
▼ │
备用链执行 ──────────────────────────────────────▶│
│ 执行工具
│◀──────────────────────────────────────│
│ │ │
▼ ▼ ▼
返回结果 ✓ 成功
================== 策略三:错误反馈重试 ==================
用户输入 LLM 工具执行
│ │ │
▼ ▼ │
"调用工具" ────────────▶│ 返回 tool_calls │
│ │ │
│ 提取参数 │ │
▼ │ │
执行工具 ──────────────────────────────────────▶│
│ 执行工具
│◀──────────────────────────────────────│
│ │ │
│ [失败] │ │
│ │ ▼
抛出 CustomToolException ✗ 失败
│
▼
exception_to_messages()
(构造错误消息)
│
▼
将错误信息插入 Prompt
│
▼ │ │
重新调用 LLM ──────────▶│ 看到错误信息 │
│ │ "参数错误:缺失 dict_arg"
│ │ │
│ ▼ │
│ 重新生成 tool_calls │
│ (修正参数) │
│ │ │
│ 提取参数 │ │
▼ │ │
重新执行工具 ───────────────────────────────────▶│
│ 重新执行
│◀──────────────────────────────────────│
│ │ │
▼ ▼ ▼
返回结果 ✓ 成功
┌─────────────────────────────────────────────────────────────────────────┐
│ 策略选择建议 │
└─────────────────────────────────────────────────────────────────────────┘
场景 推荐策略
─────────────────────────────────────────────────────────
简单错误提示 策略一:错误捕获
模型/API 备份 策略二:回退处理
LLM 自我纠正 策略三:错误反馈重试
生产环境健壮性 策略二 + 策略三 组合