第五单元——第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,格式仍然合法。所以测试时也要检查打印出的工具参数是否与用户要求一致。

相关推荐
eleven_4u1 小时前
RocketMQ相关问题
java·运维·中间件
涛涛ing1 小时前
React Compiler正式“Rust化”:你项目里的Babel插件,正在被编译进打包器
前端
迷途小羔羊2 小时前
项目概述
java·javascript·后端
我命由我123452 小时前
5-1.Python 编码进阶 - 函数进阶(参数、返回值、递归、函数作为参数传递、lambda 匿名函数)
java·服务器·数据库·后端·python·java-ee·python3.11
迷迭绿2 小时前
Cesium 轨迹回放播放器
前端·javascript·cesium
Mr_star_galaxy2 小时前
【Agent】使用 LangSmith 跟踪 LLM 应用
langchain
HolySaga2 小时前
我用 UTS 写了一个三端同构的蓝牙插件(Android / iOS / 鸿蒙 NEXT),把踩过的坑都讲给你听
前端
BD_Marathon2 小时前
【RAG开发】RAG介绍
langchain
小蒜学长2 小时前
基于Spring Boot+Vue3的在线游戏技术指导师匹配系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·在线游戏技术指导