第五单元——第4课:给工具参数加校验

上一课模型可以选择工具,但模型生成的参数不一定合法。

例如:

复制代码
{"book_name": "三体", "quantity": -2}

不能只靠提示词说"数量必须正确",还要让程序检查。

这节增加一个"计算报价"工具:数量必须是 1~10之间的整数。只计算报价,不下单、不扣库存。

1. 复制上一课文件

另存为:

复制代码
C:\Users\Administrator\langchain-study\unit5_lesson4_tool_validation.py

添加导入:

复制代码
from pydantic import BaseModel, Field, ValidationError

2. 定义参数规则和报价工具

放在原来的两个工具函数后面、tools 列表前面:

复制代码
class QuoteInput(BaseModel):
    book_name: str = Field(
        min_length=1,
        description="图书名称,例如三体,不包含书名号",
    )

    quantity: int = Field(
        strict=True,
        ge=1,
        le=10,
        description="购买数量,必须是1到10之间的整数",
    )


@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:
        return f"《{name}》的价格或库存记录不完整,无法报价。"

    stock = BOOK_STOCK[name]

    if quantity > stock:
        return (
            f"《{name}》只有 {stock} 本库存,"
            f"不足 {quantity} 本,无法满足本次现货需求。"
        )

    # 沿用上一课的演示价格:整数,单位为元
    unit_price = BOOK_PRICES[name]
    total_price = unit_price * quantity

    return (
        f"《{name}》单价 {unit_price} 元,"
        f"{quantity} 本合计 {total_price} 元。"
        "这只是报价,尚未下单或扣减库存。"
    )

args_schema=QuoteInput 指定工具输入的校验规则。通过工具的 .invoke() 调用时,参数会先校验,再进入函数主体。LangChain 参数模型说明

3. 看懂这段语法

复制代码
quantity: int = Field(
    strict=True,
    ge=1,
    le=10,
)
写法 含义
quantity: int 数量字段是整数
strict=True 严格检查类型,不把字符串 "2" 自动转换成整数
ge=1 大于或等于1
le=10 小于或等于10

因此:

复制代码
2       # 通过
0       # 不通过
-2      # 不通过
11      # 不通过
2.5     # 不通过
"2"     # 不通过:这是字符串

这里没有提供 default,所以数量是必填项。Field(...) 是字段规则,不是给数量赋了一个普通数值。Pydantic 字段说明

4. 注册工具并更新提示词

将工具列表改成:

复制代码
tools = [
    get_book_stock,
    get_book_price,
    calculate_quote,
]

原来的这两处继续使用同一个列表,不用修改:

复制代码
model_with_tools = model.bind_tools(tools)

builder.add_node("tools", ToolNode(tools))

在系统提示词中补充:

复制代码
"用户要求计算指定数量的总价或报价时,调用 calculate_quote。"
"报价工具已经检查库存并计算总价,不必重复查询相同信息。"
"报价需要明确的书名和数量,缺少时先询问。"
"数量必须是1到10之间的整数。"
"不要擅自修改用户数量,也不要拆分请求来绕过数量限制。"
"报价不代表订单已经创建。"

5. 先直接测试校验,再测试模型

仅向模型问"买负2本",它可能直接拒绝,根本不调用工具。这样不能证明程序校验真的生效。

为了确定校验有效,可在工具定义后、模型初始化前,临时添加:

复制代码
for quantity in [2, 0, -2, 11, 2.5, "2"]:
    print(f"\n测试数量:{quantity!r}")

    try:
        result = calculate_quote.invoke({
            "book_name": "三体",
            "quantity": quantity,
        })
        print("工具结果:", result)

    except ValidationError:
        print("校验失败:没有进入报价函数。")

# 临时结束程序,这次测试不调用模型
raise SystemExit

运行:

复制代码
python unit5_lesson4_tool_validation.py

预期只有整数 2 会出现:

复制代码
[报价工具] 参数校验通过,开始执行

其他测试值都应该被拦截。

测试完成后,删除这段临时测试代码,包括 raise SystemExit。

再运行并提问:

复制代码
买两本《三体》多少钱?请给我报价。

按原来的演示数据,应该得到90元,并说明没有下单。

6. 参数合法,不代表业务可行

再问:

复制代码
请给我9本《三体》的现货报价。

假设库存仍为8本:

  • 9 是1~10之间的整数,参数校验通过。
  • 但9本超过库存,业务检查不通过。

这两层检查职责不同:

参数校验检查"输入格式和范围是否正确";业务检查判断"当前条件下能不能办"。

另外,参数校验不能证明模型忠实提取了用户意思:如果用户说12本,模型擅自传入10,格式仍然合法。所以测试时也要检查打印出的工具参数是否与用户要求一致。

相关推荐
RuoyiOffice2 分钟前
SpringBoot+Vue3 凭证生命周期设计:审核、过账、冲销与反过账怎样保持账目一致
java·spring boot·事务·springboot3·财务系统·会计凭证·ruoyi office
GoAI12 分钟前
DeepSeek Harness Memory (一)主流记忆插件总结
后端·算法·面试
2601_9621066424 分钟前
HashMap入门|工号-姓名键值存储、两种遍历方式、根据key/value查
java
卸任31 分钟前
Vibe coding工具使用心得
前端·ai编程
Pniubi43 分钟前
力扣54.螺旋矩阵
算法·leetcode·矩阵
杨凯凡1 小时前
Spring Framework 7 API 版本管理实战:请求头路由、兼容迁移与废弃策略
java·rest api·springframework·spring boot 4·api版本管理
brien91 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
T_Wizard1 小时前
同一个问题写了 4 遍:官方 SDK / HTTP / Spring AI / LangChain4j,到底该选哪个
java·ai编程
hai_android1 小时前
AudioTrack 深度解析:从原理到实战
android·java·kotlin
修炼的dance2 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端