上一课给参数加了校验。这节处理下一步:
参数不合法、库存不足时,怎样把问题交回模型,让它向用户说明?
我们使用ToolNode的错误处理功能,把指定异常转换成错误类型的工具消息,再交给模型。其他未指定的代码错误仍然向外抛出,方便排查。
1. 复制上一课文件
另存为:
C:\Users\Administrator\langchain-study\unit5_lesson5_tool_errors.py添加导入:
from langchain_core.tools import ToolException上一课的ValidationError导入继续保留。
2. 将报价失败改成明确的异常
上一课在库存不足时返回普通字符串。这种方式也可以,但它在消息层面仍是一次正常返回。
本课用ToolException明确表示“本次工具操作没有成功”。
将calculate_quote函数替换为:
@tool(args_schema=QuoteInput) def calculate_quote(book_name: str, quantity: int) -> str: """检查指定图书的库存并计算报价,不下单、不扣库存。""" print("[报价工具] 参数校验通过,开始执行") name = book_name.strip().replace("《", "").replace("》", "") if name not in BOOK_PRICES or name not in BOOK_STOCK: raise ToolException( f"《{name}》的价格或库存记录不完整,无法报价。" "请用户确认书名,不要编造价格。" ) stock = BOOK_STOCK[name] if quantity > stock: raise ToolException( f"《{name}》当前只有 {stock} 本," f"无法满足 {quantity} 本的现货需求。" "请向用户说明,不要擅自减少数量或声称下单成功。" ) unit_price = BOOK_PRICES[name] total_price = unit_price * quantity return ( f"《{name}》单价 {unit_price} 元," f"{quantity} 本合计 {total_price} 元。" "这只是报价,尚未下单或扣减库存。" )raise会立即结束当前函数的正常执行,不会继续计算下面的报价。
3. 指定哪些错误交给模型处理
将原来的:
builder.add_node("tools", ToolNode(tools))替换为:
tool_node = ToolNode( tools, handle_tool_errors=( ToolException, ValidationError, ), ) builder.add_node("tools", tool_node)这里配置的是异常类型的元组,不是调用这两个类。
它的含义是:
- 预期的工具问题、参数校验问题:转换成错误消息,交回模型。
- 未包含的异常,例如
NameError:继续报错,不伪装成正常业务问题。
部分版本会把参数校验错误包装为ToolException的子类,这个配置也包含这些子类。错误消息会带上对应的工具调用编号。官方实现与说明
原来的这条边不变:
builder.add_edge("tools", "assistant")所以模型拿到错误结果后,可以解释原因,而不是继续假装报价成功。
4. 补充系统提示词
在SystemMessage中追加:
"工具返回错误时,说明具体原因。" "参数缺失或超出允许范围时,请用户提供合法参数。" "库存不足或记录缺失时,不要反复调用相同工具。" "不得为了通过校验而擅自修改用户要求的数量。"注意:这些提示是在指导模型,不能保证它绝不重复调用。上一课设置的recursion_limit继续保留。
5. 打印工具消息的状态
在程序最后找到:
if isinstance(message, ToolMessage): print("工具结果:", message.content)改成:
if isinstance(message, ToolMessage): print("工具状态:", message.status) print("工具结果:", message.content)现在可以区分:
工具状态: success和:
工具状态: error图正常运行结束,不代表其中每次工具调用都成功。
6. 做三个测试
运行:
python unit5_lesson5_tool_errors.py每个问题单独重新运行一次:
| 问题 | 观察重点 |
|---|---|
请给我2本《三体》的报价。 | 库存充足时返回success |
请给我9本《三体》的现货报价。 | 若库存仍为8本,应返回error,说明库存不足 |
请给我2本《不存在的测试书》的报价。 | 返回error,说明没有记录 |
第一项按原来的演示价格应为90元;后两项不应声称已经下单。
如果你输入“买负2本”,模型可能直接要求你修改数量,没有调用工具。这是合理行为,但不能用它证明参数校验已执行;校验本身仍用上一课的直接调用测试来验证。
本课要分清的三件事
- 参数校验:输入格式和范围是否合法。
- 工具错误处理:失败后,如何把原因交回模型。
- 自动重试:同一个操作是否再执行一次。
本课没有增加自动重试。库存不足不是临时网络故障,重复查询通常解决不了。
另外,错误内容会被发给模型,所以只放必要的业务说明,不要把 API Key、数据库连接串或完整内部日志放进去。