OpenAI / GPT-4o:Python 返回结构化 / JSON 输出
在调用 OpenAI(比如:GPT-4o)接口时,希望返回的结果是能够在后续任务中自动化处理的结构化 / JSON 输出。GPT 版本:gpt-4o-2024-08-06,提供了这样的功能。
目标:从非结构化输入到结构化数据(比如:JSON)。
目录
1. 结构化输出
1.1 简介
1.2 优点
2. 接口
2.1 官方代码
2.2 Pydantic
2.2.1 简介
2.2.2 示例
2.2.3 特点
2.3 Python 代码
3. 异常
3.1 ValidationError
3.2 解决
3.3 例子
3.3.1 Prompt
3.3.2 Pydantic
3.3.3 API
3.3.4 数据验证
1. 结构化输出
1.1 简介
来源:Introducing Structured Outputs in the API | OpenAI
Introducing Structured Outputs in the API
We are introducing Structured Outputs in the API—model outputs now reliably adhere to developer-supplied JSON Schemas.
在 API 中引入结构化输出
我们在 API 中引入了结构化输出 — 模型输出现在可靠地遵循开发人员提供的 JSON 架构。
来源:Structured Outputs - OpenAI API
JSON is one of the most widely used formats in the world for applications to exchange data.
Structured Outputs is a feature that ensures the model will always generate responses that adhere to your supplied JSON Schema, so you don't need to worry about the model omitting a required key, or hallucinating an invalid enum value.
在 Pydantic 中,可以通过 Optional 或者 default 参数来设置可选字段和默认值。
结构化输出是一项功能,可确保模型始终生成符合您提供的 JSON 模式的响应,因此您不必担心模型会遗漏必需的键或产生无效枚举值的幻觉。
1.2 优点
- Reliable type-safety: No need to validate or retry incorrectly formatted responses
- Explicit refusals: Safety-based model refusals are now programmatically detectable
- Simpler prompting: No need for strongly worded prompts to achieve consistent formatting
2. 接口
2.1 官方代码
官方的文档指出:
In addition to supporting JSON Schema in the REST API, the OpenAI SDKs for Python and JavaScript also make it easy to define object schemas using Pydantic and Zod respectively.
除了在 REST API 中支持 JSON Schema 之外,OpenAI 的 Python 和 JavaScript SDK 还可以轻松使用 Pydantic 和 Zod 分别定义对象模式
表明,对于 Python 程序,可选的方法有两种:一种是 JSON Schema,另一种是使用 Pydantic。
2.2 Pydantic
2.2.1 简介
Pydantic 是 Python 使用最广泛的数据验证库。
2.2.2 示例
这里先展示一个 Pydantic 官方文档给的示例。
from datetime import datetimefrom pydantic import BaseModel, PositiveIntclass User(BaseModel):id: int name: str = 'John Doe' signup_ts: datetime | None tastes: dict[str, PositiveInt] external_data = {'id': 123,'signup_ts': '2019-06-01 12:22', 'tastes': {'wine': 9,b'cheese': 7, 'cabbage': '1', },
}user = User(**external_data) print(user.id)
#> 123
print(user.model_dump())
"""
{'id': 123,'name': 'John Doe','signup_ts': datetime.datetime(2019, 6, 1, 12, 22),'tastes': {'wine': 9, 'cheese': 7, 'cabbage': 1},
}
"""
这里,类 User 继承自 pydantic.BaseModel,其定义了四个属性:id, name, signup_ts, tastes,同时定义了他们的数据类型。
后续定义了一个 Python 的 dict 型变量 external_data 并赋值。
在 user = User(**external_data) 中,将 dict 以字典解包的方式传递给类 User,得到对象 user。
需要注意的是,如果 external_data 中有多余的字段或类型不匹配,Pydantic 会抛出相应的错误。如下展示官方提供的示例。
# continuing the above example...from datetime import datetime
from pydantic import BaseModel, PositiveInt, ValidationErrorclass User(BaseModel):id: intname: str = 'John Doe'signup_ts: datetime | Nonetastes: dict[str, PositiveInt]external_data = {'id': 'not an int', 'tastes': {}} try:User(**external_data)
except ValidationError as e:print(e.errors())"""[{'type': 'int_parsing','loc': ('id',),'msg': 'Input should be a valid integer, unable to parse string as an integer','input': 'not an int','url': 'https://errors.pydantic.dev/2/v/int_parsing',},{'type': 'missing','loc': ('signup_ts',),'msg': 'Field required','input': {'id': 'not an int', 'tastes': {}},'url': 'https://errors.pydantic.dev/2/v/missing',},]"""
这里报异常:ValidationError。由于 external_data 中的 id 类型不是 int,且缺少了 signup_ts。故异常为两个方面。
2.2.3 特点
***个人感觉,Pydantic 与 Java 的类有很多相似之处,尤其是在数据模型和验证方面:
- 数据结构定义
在 Java 中,通常通过类(Class)来定义数据结构,其中属性由类成员变量表示。
Pydantic 也是通过 Python 的类来定义数据模型,属性通常是类的字段(Field)。
- 类型约束
Java 是强类型语言,类的成员变量通常都有明确的类型(如 int, String 等)。
Pydantic 也允许在类定义时指定字段的类型,并且在创建实例时进行类型检查和验证。
- 数据验证
在 Java 中,可以通过构造函数、setter 方法或其他工具进行输入数据的验证。
Pydantic 内置了强大的数据验证功能,它会根据你定义的类型自动进行验证并在必要时提供详细的错误信息。
- 默认值和可选值
在 Java 类中,可以通过构造函数或者设置默认值来定义可选字段。
在 Pydantic 中,可以通过 Optional 或者 default 参数来设置可选字段和默认值。
2.3 Python 代码
这里直接粘贴官方代码。
from pydantic import BaseModel
from openai import OpenAIapi_key = '你的KEY'
base_url = '你的URL'client = OpenAI(api_key=api_key, base_url=base_url)class Step(BaseModel):explanation: stroutput: strclass MathReasoning(BaseModel):steps: list[Step]final_answer: strcompletion = client.beta.chat.completions.parse(model="gpt-4o-2024-08-06",messages=[{"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},{"role": "user", "content": "how can I solve 8x + 7 = -23"}],response_format=MathReasoning,
)math_reasoning = completion.choices[0].message.parsed# Example response
"""
{"steps": [{"explanation": "Start with the equation 8x + 7 = -23.","output": "8x + 7 = -23"},{"explanation": "Subtract 7 from both sides to isolate the term with the variable.","output": "8x = -23 - 7"},{"explanation": "Simplify the right side of the equation.","output": "8x = -30"},{"explanation": "Divide both sides by 8 to solve for x.","output": "x = -30 / 8"},{"explanation": "Simplify the fraction.","output": "x = -15 / 4"}],"final_answer": "x = -15 / 4"
}
"""
这里需要注意的是,调用的 API 接口是 client.beta.chat.completions.parse,接口参数中有一个 response_format=MathReasoning,其中赋值的是自定义的继承自 pydantic.BaseModel 的类。
官方给出两种形式的结构化输出:function calling 和 json_schema。前者就是这里的自定义类,后者是 JSON。具体使用哪种应需求选择。
- If you are connecting the model to tools, functions, data, etc. in your system, then you should use function calling
- If you want to structure the model's output when it responds to the user, then you should use a structured response_format
- 如果要将模型连接到系统中的工具、函数、数据等,则应使用函数调用
- 如果你想在响应用户时构建模型的输出,那么你应该使用结构化的 response_format
说白了,就是 GPT-4o 返回的结果是需要后续程序调用,则选 function calling,如果直接返回给用户,则提供 JSON 格式。
3. 异常
3.1 ValidationError
但实际操作时,执行官方提供的代码,我个人遇到了不可解决的问题。
pydantic_core._pydantic_core.ValidationError: 1 validation error for MathReasoningInvalid JSON: expected value at line 1 column 1 [type=json_invalid, input_value="To solve the equation \\...ation or more examples!", input_type=str]For further information visit https://errors.pydantic.dev/2.8/v/json_invalid
即,提示解析的 ValidationError。
3.2 解决
经多次尝试,仍提示数据验证异常。不清楚是什么原因导致,OpenAI 社区也有同样的问题:Official example MathResponse raise invalid json - API - OpenAI Developer Forum。但还没看到有效的解决方案。
我的解决方案是,采用 json_object 格式来获取 OpenAI response,然后再交于自定义的 MathReasoning 进行数据验证。但这需要增加额外的 Prompt,且 Prompt必须严格给出 JSON 格式的示例,并强制要求 GPT-4o 返回 JSON 格式。官方就此给出了重要说明:
- When using JSON mode, you must always instruct the model to produce JSON via some message in the conversation, for example via your system message. If you don't include an explicit instruction to generate JSON, the model may generate an unending stream of whitespace and the request may run continually until it reaches the token limit. To help ensure you don't forget, the API will throw an error if the string "JSON" does not appear somewhere in the context.
- JSON mode will not guarantee the output matches any specific schema, only that it is valid and parses without errors. You should use Structured Outputs to ensure it matches your schema, or if that is not possible, you should use a validation library and potentially retries to ensure that the output matches your desired schema.
- 使用 JSON 模式时,您必须始终指示模型通过对话中的某些消息生成 JSON,例如通过您的系统消息。如果您不包含生成 JSON 的明确指令,则模型可能会生成无休止的空格流,并且请求可能会持续运行,直到达到令牌限制。为了帮助确保您不会忘记,如果字符串 “JSON” 未出现在上下文中的某个位置,API 将引发错误。
- JSON 模式不保证输出与任何特定架构匹配,只保证它是有效的并且解析没有错误。您应该使用结构化输出来确保它与您的架构匹配,或者如果无法匹配,则应使用验证库并可能重试,以确保输出与所需的架构匹配。
此时,需要修改调用的 API 接口,同时修改参数 response_format={"type": "json_object"},切记得额外输入带有强制要求输出 JSON 的 Prompt。
3.3 例子
这里展示我实际生产中的一个例子。
3.3.1 Prompt
给一个 JSON 的例子,且强调返回 JSON 格式。
Background:
XXX.Task Description:
Given a text pair, such as '{"left": "XXX", "right": "XXX"}'. Step 1: First, XXX. Step 2: Then, XXX. Step 3: Finally, XXX.Example:
input:
{"left": "XXX","right": "XXX"
}output (must be in JSON format):
{"relation": {"type": "XXX","subtype": "XXX","description": "XXX"},"left": {"type": "XXX","content": "XXX","explanation": "XXX"},"right": {"type": "XXX","content": "XXX","explanation": "XXX"},"category": "XXX"
}Note: You have to return the correct JSON format.
3.3.2 Pydantic
自定义的结构化数据类,可以有层次结构,详细见官方文档:https://platform.openai.com/docs/guides/structured-outputs/supported-schemas。
from pydantic import BaseModelclass Relation(BaseModel):type: strsubtype: strdescription: strclass Detail(BaseModel):type: strcontent: strexplanation: strclass Annotation(BaseModel):relation: Relationleft: Detailright: Detailcategory: str
3.3.3 API
API 的参数额外输入 prompt,同时修改 response_format={"type": "json_object"}。
prompt = 'XXX. Note: You have to return the correct JSON format.'
content = 'XXX'completion = client.chat.completions.create(model="gpt-4o-2024-08-06",messages=[{"role": "system","content": "You are a helpful assistant designed to output JSON."},{"role": "user","content": prompt},{"role": "user","content": content}],response_format={"type": "json_object"},
)
response = completion.choices[0].message.content
3.3.4 数据验证
上述返回的 response 为 str 类型的 JSON。首先需要使用 json.loads(response) 转换为 JSON 对象。
然后使用自定义类 Annotation (3.3.2 中定义的) 验证 JSON 是否合规,即符合 Annotation 类定义的数据结构以及子结构。
如果无异常,则可通过对象.属性方法获取对应的值。
import jsontry:row: json = json.loads(response)
except json.decoder.JSONDecodeError as e:print(e)try:annotation: Annotation = Annotation.model_validate(row)
except ValidationError as e:print(e)print(annotation.relation.type)
个人实践中,错误率在 1% 左右,可按照 3.2 中官方的重要说明中讲的,进行多次重试,我的经验是重试一次即可。
相关文章:

OpenAI / GPT-4o:Python 返回结构化 / JSON 输出
在调用 OpenAI(比如:GPT-4o)接口时,希望返回的结果是能够在后续任务中自动化处理的结构化 / JSON 输出。GPT 版本:gpt-4o-2024-08-06,提供了这样的功能。 目标:从非结构化输入到结构化数据&…...

通信工程学习:什么是EDFA掺铒光纤放大器
EDFA:掺铒光纤放大器 EDFA,即掺铒光纤放大器(Erbium-Doped Fiber Amplifier),是一种在光纤通信中广泛使用的光放大器件。以下是对EDFA的详细解释: 一、定义与基本原理 EDFA是在石英光纤中掺入少量的稀土元…...

机器学习与深度学习的区别
随着人工智能技术的迅猛发展,机器学习(Machine Learning, ML)和深度学习(Deep Learning, DL)这两个术语越来越频繁地出现在人们的视野中。尽管它们之间有着紧密的联系,但实际上二者存在显著的区别。本文旨在…...

标准库标头 <barrier>(C++20)学习
此头文件是线程支持库的一部分。 类模板 std::barrier 提供一种线程协调机制,阻塞已知大小的线程组直至该组中的所有线程到达该屏障。不同于 std::latch,屏障是可重用的:一旦到达的线程组被解除阻塞,即可重用同一屏障。与 std::l…...

如何测量一个(传输网络)系统的容量
Little 定律就能反算系统容量,但我这篇文章要正着算。 假想一个理发店场景。李大爷拥有一家占地 50 平米的理发店,经理到店里理发如果已经有经理在理发,就要拿个券等待,请问李大爷需要印多少等待券? 这是个系统容量问…...

【MySQL】MySQL和Workbench版本兼容问题
1、安装MySQL WorkBench 最新版本下载:https://dev.mysql.com/downloads/workbench/ 历史版本下载:https://downloads.mysql.com/archives/workbench/ 2、问题描述 本人在Windows下安装了一个旧版本的MySQL(5.1),同…...

项目实战 ---- 商用落地视频搜索系统(10)---后台搜索Cache优化
目录 背景 技术实现策略 视频预处理阶段的cache技术 视频搜索阶段的cache技术 技术实现 预处理阶段cache策略实现 逻辑 代码 运行结果 问题及注意点 搜索阶段cache策略实现 系统配置层面 逻辑 低版本 GPU CPU 本项目的配置 高版本 描述 go ahead 策略 cac…...

客户端(服务器下载文件)
一、客户端代码 客户端代码 //实现TCP客户端通信 #include<stdio.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h> #include<sys/socket.h> #include<string.h> #include<netinet/ip.h> #include<netinet/in…...

P1544 三倍经验 (记忆化搜索)
三倍经验 题目描述 数字金字塔由 n n n 行整数组成,第 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1≤i≤n) 行有 i i i 个数字,一个示例如下。 73 98 1 02 7 4 4 4 5 2 6 5现在你在金字塔的顶部(第一行)&…...

【在Python中创建简单界面计算器】
在Python中创建带有简单界面的计算器,我们可以继续使用Tkinter库,这是一个非常流行且易于使用的GUI库。下面是一个简单的计算器实现,它支持加、减、乘、除四种基本运算。 首先,确保你的Python环境中已经安装了Tkinter。Tkinter通…...

【四范式】浅谈NLP发展的四个范式
自然语言处理(Natural Language Processing,NLP)是计算机科学,人工智能,语言学关于计算机和人类自然语言之间的相互作用的领域,是计算机科学领域与人工智能领域中的一个重要方向。NLP发展到今天已经进入到了…...

--- 数据结构 优先级队列 --- java
之前提高到队列是一种先进先出的结构,但是在某些情况下操作的数据具有优先级,那么对他先进行操作,这时队列就不能满足需求了,因为队列只能操作对头的元素,而具有优先级的数据不一定是在对头,这样就需要优先…...

鸿萌数据恢复服务:如何恢复 Mac 系统中被擦除的文件?
天津鸿萌科贸发展有限公司从事数据安全服务二十余年,致力于为各领域客户提供专业的数据备份、数据恢复解决方案与服务,并针对企业面临的数据安全风险,提供专业的相关数据安全培训。 公司是多款国际主流数据恢复软件的授权代理商,为…...

片段阅读2_中心理解以外题型
目录 一、标题拟定二、下文推断1.三种简单结构:2.三种不易识别结构:三、语句填入1.在开头2.在中间3.在尾句4.盯细节四、语句排序1.宏观把握2.盯住细节五、细节判断一、标题拟定 题型说明:主旨意图题的变型,就是把主旨意图进行“标题化”的改造;正确选项要求:标题中需包含…...

【网络安全 | 渗透工具】IIS 短文件名枚举工具—shortscan安装使用教程
未经许可,不得转载。 文章目录 shortscan安装使用Shortutil 工具shortscan ShortScan 是一种用于在 Microsoft IIS (Internet Information Services) Web 服务器上进行短文件名枚举的工具。该工具可以帮助攻击者利用 IIS 的文件名处理特性,通过预测性扫描枚举服务器上的文件…...

数据结构——栈和队列(队列的定义、顺序队列以及链式队列的基本操作)
目录 队列(queue)的定义 顺序队——队列的顺序表示和实现 顺序队列(循环队列)的类型定义 顺序队列上溢问题的解决方法 编辑 循环队列的基本操作 队列的基本操作——队列的初始化 队列的基本操作——求队列的长度 队列的…...

el-table 的单元格 + 图表 + 排序
<el-table border :data"tableDataThree" height"370px" style"width: 100%"><el-table-column :key"activeName 8" width"50" type"index" label"序号" align"center"></el…...

FPGA第 9 篇,Verilog 中的关键字和基数
前言 在 Verilog 中,关键字(Keywords)和基数(Radix)是语言的重要组成部分,它们有助于描述和定义硬件设计。上期分享了 Verilog 的基本使用,以及数据类型、逻辑值和算数运算符的简单应用&#x…...

什么是单元测试?怎么做?
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 一、什么是单元测试? 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小…...

论文复现--基于LeNet网络结构的数字识别
前言 一直就听说学习深度学习无非就是看论文,然后复现,不断循环,这段时间也看了好几篇论文(虽然都是简单的),但是对于我一个人自学,复现成功,我感觉还是挺开心的 本人初学看论文的思路:聚焦网络…...

Vue3 响应式工具函数isRef()、unref()、isReactive()、isReadonly()、isProxy()
isRef() isRef():检查某个值是否为 ref。 isRef函数接收一个参数,即要判断的值。如果该参数是由ref创建的响应式对象,则返回true;否则,返回false。 import { ref, isRef } from vue const normalValue 这是一个普通…...

数据结构之简单选择排序介绍与举例
简单选择排序 简单选择排序是一种排序算法,其基本思想是:通过n-i次关键字间的比较,从n-i1个记录中选出关键字最小的记录,并和第i个记录交换之。 举例: 给定数组 [64, 25, 12, 22, 11],进行简单选择排序。…...

九、Redis 的实际使用与Redis的设计
一、多级缓存架构 在线上系统中,一定不会单纯的只部署一个Redis集群,而是使用Redis结合其他的多级缓存应用进行架构。 使用多级缓存架构的优点就是可以使不同类型的数据分布在不同的应用中,比如redis的热点key可以存储到nginx本地缓存、服务…...

乔拓云模板助力,微信小程序快速上线无需愁备案
想要快速打造并上线自己的微信小程序吗?乔拓云平台是您的不二之选!无需担心复杂的备案流程,乔拓云提供免费服务,远程协助您轻松完成微信小程序的备案工作。 只需简单几步,您的小程序就能闪亮登场:首先&…...

Android命令行查看CPU频率和温度
在 Android 设备上,你可以通过命令行工具 adb 来查看 CPU 温度和 CPU 频率,并确定是否有降频情况。以下是具体步骤: 1. 查看 CPU 频率 你可以使用以下命令来查看 CPU 各个核心的当前频率: adb shell cat /sys/devices/system/c…...

力扣: 翻转字符串里的单词
文章目录 需求分析代码结尾 需求 给你一个字符串 s ,请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。 注意:输入字符…...

Wophp靶场寻找漏洞练习
1.命令执行漏洞 打开网站划到最下,此处的输入框存在任意命令执行漏洞 输入命令whoami 2.SQL注入 搜索框存在SQL注入,类型为整数型 最终结果可以找到管理员账户和密码 3.任意文件上传漏洞 在进入管理员后台后,上传木马文件 访问该文件&…...

国内智能运维厂商月度动态 202408
作为市场人员,虽然也添加了各类行业媒体、同行厂商的关注,但被同事问起业内动向时,常常也是记忆模糊、拍破脑袋也说不完整一件事。 所以找机会翻看了一下各大厂商的公号,先做个简单的8月汇总。 格式暂时是这样的: 整…...

C++ 左值与右值浅谈
左值与右值 序言概念左值和右值的划分理解右值引用常量左值引用与右值引用 移动语义引用折叠完美转发 参考资料 序言 虽然平常都算是了解左值,右值的用法,但是好记性不如烂笔头,记下来供大家评鉴,有错改错,有善赞善&a…...

oracle 如何查死锁
在Oracle中查看死锁通常涉及查询数据字典视图和动态性能视图。以下是一个基本的查询示例,用于检测和显示最近的死锁: SELECT dd.inst_id, dd.name, o.object_id, o.object_type, s.sid, s.serial#, s.username, p.spid, s.program,d.xidusn,d.xidslot,d…...