结构化输出with_structured_output(抽卡)

python 复制代码
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
from langchain.tools import tool
from pydantic import BaseModel, Field
from typing import Literal

load_dotenv(override=True)

class Ticket(BaseModel):
    category: Literal["退款", "物流", "其他"] = Field(description="问题类别")

# @tool
# def get_weather(city: str) -> str:
#     """获取指定城市的天气信息"""
#     return f"{city}今天大部分晴朗!"

model = init_chat_model(
    "deepseek:deepseek-v4-flash",
    temperature=0,
    extra_body={"thinking": {"type": "disabled"}},
)
# model_with_tools = model.bind_tools([get_weather])
model_structured_output = model.with_structured_output(Ticket)

result = model_structured_output.invoke("我的快递三题没更新,我很着急")

print(result)

这段代码的作用

这是用 Pydantic 定义一个结构化数据模型 Ticket,用来描述"工单"这类数据。逐部分拆解:

python 复制代码
class Ticket(BaseModel):                     # ① 继承 Pydantic 的 BaseModel
    category: Literal["退款", "物流", "其他"] = Field(description="问题类别")
部分 作用
BaseModel Pydantic 的数据模型基类,自动提供类型校验 、解析 、model_dump() 序列化等能力
Literal["退款", "物流", "其他"] 限定该字段只能取这三个值 ,传别的值会抛 ValidationError(相当于枚举约束)
Field(description="问题类别") 给字段加元数据说明,不影响校验;主要给 LLM / API 文档 / JSON Schema 看
没有 default= 所以 category 是必填字段 (注意:Field(...) 不带 default 不等于有默认值)

典型使用场景

1. LLM 结构化输出(最常见)

LangChain / OpenAI function calling 里,把这个模型当作 schema,让大模型按固定格式返回:

python 复制代码
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o")
structured = llm.with_structured_output(Ticket)

result = structured.invoke("我要退款,订单号 12345 没收到货")
# Ticket(category='退款')

这里的 Literal 特别关键------它把可选值约束为中文枚举,防止模型自由发挥 (否则可能返回 "Refund"、"售后" 等变体)。description 会写进 JSON Schema,帮助模型理解字段含义。

2. 数据校验 / 清洗

python 复制代码
Ticket(category="退款")      # ✅ OK
Ticket(category="退货")      # ❌ ValidationError: Input should be '退款', '物流' or '其他'
Ticket()                     # ❌ ValidationError: category Field required

3. FastAPI 请求体

python 复制代码
@app.post("/tickets")
def create(ticket: Ticket):   # 自动校验 + 自动生成 OpenAPI 文档
    ...

两个容易踩的坑

① 想设默认值要显式写 default:

python 复制代码
category: Literal["退款", "物流", "其他"] = Field(default="其他", description="问题类别")

② 输出仍是字符串,不是枚举对象 :Literal 只是类型层面的约束,result.category 是 str,不能直接 .value。

③ Pydantic v1 vs v2 :v2 里 description 正常用;v1 里字段说明也支持,但整体 API(dict() vs model_dump())不同,混用会报错。

相关推荐
尹人入圣2 小时前
目前热门的IP驱动产业新场景新工具
网络·python·网络协议·tcp/ip
Nuanyt2 小时前
Agent 学习笔记(下篇三):上线工程、进阶选择与操作界面(Stage 7 / 7.5 / 8 + 延伸路线)
笔记·python·学习·memory·aiagent·harness
吴卫斌2 小时前
定投ETF实战指南(四):行业/主题ETF——定投的“进攻端“
开发语言·python·股票·量化
2601_967987373 小时前
Python——入门级(定义功能def函数)
python·函数·可变参数·默认参数·关键字参数
LMT流氓兔5 小时前
第五单元——第十六课:给书店助手加上网页界面
java·开发语言·jvm·人工智能·oracle·langchain·1024程序员节
小许学java5 小时前
AI通识与基础
人工智能·langchain
专注仿真5 小时前
梯度提升类(XFBoost)算法详解与代码实例
开发语言·人工智能·python·算法·大模型训练·控制算法
外收内放5 小时前
Python基础语法练习题(75)
开发语言·python·学习
黑十6 小时前
PyTorch 报 “no CUDA GPUs are available“ 怎么办?3 步排查
服务器·网络·人工智能·pytorch·python