精準(zhǔn)0誤差,輸入價(jià)格打骨折!OpenAI官宣API支持結(jié)構(gòu)化輸出,JSON準(zhǔn)確率100%
還在絞盡腦汁想一堆提示詞,為一頓操作后五花八門的輸出結(jié)果而頭疼?
OpenAI終于聽到了群眾的呼聲,為廣大開發(fā)者送上渴望已久的第一大功能。
OpenAI今日宣布新功能上線,ChatGPT API現(xiàn)已支持JSON結(jié)構(gòu)化輸出。
圖片
JSON(JavaScript Object Notation)是文件和數(shù)據(jù)交換格式的行業(yè)標(biāo)準(zhǔn),因?yàn)樗纫子谌祟愖x取又易于機(jī)器解析。
然而,LLM常常與JSON對著干,經(jīng)常會產(chǎn)生幻覺,要不生成僅部分遵循指令的響應(yīng),要不就生成一堆「天書」,根本無法完全解析。
圖片
這就需要開發(fā)人員使用多種開源工具、嘗試不同的提示或重復(fù)請求等來生成理想的輸出結(jié)果,耗時(shí)耗力。
結(jié)構(gòu)化輸出功能于今天發(fā)布,以上棘手的難題迎刃而解,確保模型生成的輸出與JSON中規(guī)定的schema相匹配。
一直以來,結(jié)構(gòu)化輸出功能是開發(fā)人員呼聲最高的頭號功能,奧特曼在推文中也表示,該版本是應(yīng)廣大用戶的要求發(fā)布的。
圖片
OpenAI發(fā)布的新功能確實(shí)擊中了許多開發(fā)者的心,他們一致認(rèn)為「This is a big deal」。
紛紛留言表示贊嘆,直呼「Excellent!」。
圖片
幾家歡喜幾家愁,OpenAI的這次更新,又讓人擔(dān)心會吞噬初創(chuàng)公司。
圖片
然而,對于更多的普通用戶來說,他們更關(guān)心的問題是GPT-5到底什么時(shí)候發(fā)布,至于JSON Schema,「那是什么?」
圖片
圖片
畢竟,沒有GPT-5的消息,OpenAI今年秋季的DevDay,可能與去年相比,將會顯得安靜了許多。
輕松確保模式一致性
有了結(jié)構(gòu)化輸出,只需要定義一個(gè)JSON Schema,AI就會不再「任性」,乖乖按照指令要求輸出數(shù)據(jù)。
并且,新功能不僅僅讓AI變得更加聽話,還能大大提高輸出內(nèi)容的可靠性。
在對復(fù)雜的JSON schema的跟蹤評估中,帶有結(jié)構(gòu)化輸出的新模型gpt-4o-2024-08-06獲得了100%的滿分。相比之下,gpt-4-0613的得分不到40%。
圖片
實(shí)際上,JSON Schema功能就是OpenAI在去年的DevDay上推出的。
現(xiàn)在,OpenAI在API中擴(kuò)展了這項(xiàng)功能,確保模型生成的輸出與開發(fā)人員提供的JSON Schema完全匹配。
從非結(jié)構(gòu)化輸入生成結(jié)構(gòu)化數(shù)據(jù)是當(dāng)今應(yīng)用中人工智能的核心用例之一。
開發(fā)人員使用OpenAI API構(gòu)建強(qiáng)大的助手,能夠通過函數(shù)調(diào)用獲取數(shù)據(jù)和回答問題,提取結(jié)構(gòu)化數(shù)據(jù)以進(jìn)行數(shù)據(jù)輸入,并構(gòu)建多步驟的智能體工作流(multi-step agentic workflows),從而允許LLM采取行動(dòng)。
技術(shù)原理
OpenAI采用了一種雙管齊下的方法來提高模型輸出與JSON Schema的匹配度。
最新的gpt-4o-2024-08-06模型經(jīng)過訓(xùn)練,可以更好地理解復(fù)雜的Schema并生成與之匹配的輸出。
盡管模型性能已顯著提升,在基準(zhǔn)測試中達(dá)到了93%的準(zhǔn)確性,但固有不確定性仍然存在。
為了確保開發(fā)者構(gòu)建應(yīng)用的穩(wěn)定性,OpenAI提供了一種更高準(zhǔn)確度的方法來約束模型的輸出,從而實(shí)現(xiàn)100%的可靠性。
約束解碼
OpenAI采用了一種稱為約束采樣或約束解碼的技術(shù),默認(rèn)情況下,模型生成輸出時(shí)完全不受約束,可能從詞匯表中選擇任何token作為下一個(gè)輸出。
這種靈活性可能導(dǎo)致錯(cuò)誤,例如,在生成有效JSON時(shí)隨意插入無效字符。
為了避免此類錯(cuò)誤,OpenAI使用動(dòng)態(tài)約束解碼的方法,確保生成的輸出token始終符合提供的schema。
為了實(shí)現(xiàn)這一點(diǎn),OpenAI將提供的JSON Schema轉(zhuǎn)換為上下文無關(guān)文法(CFG)。
對于每個(gè)JSON Schema,OpenAI計(jì)算出一個(gè)代表該模式的語法,并在采樣期間高效地訪問預(yù)處理的組件。
這種方法不僅使生成的輸出更準(zhǔn)確,還減少了不必要的延遲。首次請求新模式可能會有額外的處理時(shí)間,但隨后的請求通過緩存機(jī)制實(shí)現(xiàn)快速響應(yīng)。
備選方案
除了CFG方法,其他方法通常使用有限狀態(tài)機(jī)(FSM)或正則表達(dá)式來進(jìn)行約束解碼。
然而,這些方法在動(dòng)態(tài)更新有效token時(shí)能力有限。特別是對于復(fù)雜的嵌套或遞歸數(shù)據(jù)結(jié)構(gòu),F(xiàn)SM通常難以處理。
OpenAI的CFG方法在表達(dá)復(fù)雜schema時(shí)表現(xiàn)出色。例如,支持遞歸模式的JSON schema在OpenAI API上已得到實(shí)現(xiàn),但無法通過FSM方法表達(dá)。
輸入成本節(jié)省一半
支持函數(shù)調(diào)用的所有模型均可實(shí)現(xiàn)結(jié)構(gòu)化輸出,包括最新的GPT-4o和GPT-4o-mini模型,以及微調(diào)模型。
此功能可在Chat Completions API、Assistants API和Batch API上使用,并兼容視覺輸入。
與gpt-4o-2024-05-13版本相比,gpt-4o-2024-08-06版本在成本上也更具優(yōu)勢,開發(fā)者可以在輸入端節(jié)省50%的成本(2.50美元/1M oken),在輸出端節(jié)省33%的成本(10.00美元/1M token)。
如何使用結(jié)構(gòu)化輸出
在API中可以使用兩種形式引入結(jié)構(gòu)化輸出:
函數(shù)調(diào)用
通過在函數(shù)定義中設(shè)置strict: true,可以實(shí)現(xiàn)通過工具的結(jié)構(gòu)化輸出。
此功能適用于支持工具的所有型號,包括所有型號gpt-4-0613和gpt-3.5-turbo-0613及更高版本。
啟用結(jié)構(gòu)化輸出后,模型輸出將與提供的工具定義匹配。
示例請求:
POST /v1/chat/completions
{
"model": "gpt-4o-2024-08-06",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant. The current date is August 6, 2024. You help users query for the data they are looking for by calling the query function."
},
{
"role": "user",
"content": "look up all my orders in may of last year that were fulfilled but not delivered on time"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "query",
"description": "Execute a query.",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"table_name": {
"type": "string",
"enum": ["orders"]
},
"columns": {
"type": "array",
"items": {
"type": "string",
"enum": [
"id",
"status",
"expected_delivery_date",
"delivered_at",
"shipped_at",
"ordered_at",
"canceled_at"
]
}
},
"conditions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"column": {
"type": "string"
},
"operator": {
"type": "string",
"enum": ["=", ">", "<", ">=", "<=", "!="]
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "object",
"properties": {
"column_name": {
"type": "string"
}
},
"required": ["column_name"],
"additionalProperties": false
}
]
}
},
"required": ["column", "operator", "value"],
"additionalProperties": false
}
},
"order_by": {
"type": "string",
"enum": ["asc", "desc"]
}
},
"required": ["table_name", "columns", "conditions", "order_by"],
"additionalProperties": false
}
}
}
]
}
示例輸出:
{
"table_name": "orders",
"columns": ["id", "status", "expected_delivery_date", "delivered_at"],
"conditions": [
{
"column": "status",
"operator": "=",
"value": "fulfilled"
},
{
"column": "ordered_at",
"operator": ">=",
"value": "2023-05-01"
},
{
"column": "ordered_at",
"operator": "<",
"value": "2023-06-01"
},
{
"column": "delivered_at",
"operator": ">",
"value": {
"column_name": "expected_delivery_date"
}
}
],
"order_by": "asc"
}
response_format參數(shù)的新選項(xiàng)
開發(fā)人員現(xiàn)在可以通過response_format的新選項(xiàng)json_schema選擇是否需要規(guī)定格式的輸出。
當(dāng)模型不調(diào)用工具,而是以結(jié)構(gòu)化方式響應(yīng)用戶時(shí),這一功能非常有用。
此功能適用于最新的GPT-4o型號:今天發(fā)布的gpt-4o-2024-08-06和gpt-4o-mini-2024-07-18 。
將response_format設(shè)置為strict:true時(shí),模型輸出將與提供的schema匹配。
示例請求:
POST /v1/chat/completions
{
"model": "gpt-4o-2024-08-06",
"messages": [
{
"role": "system",
"content": "You are a helpful math tutor."
},
{
"role": "user",
"content": "solve 8x + 31 = 2"
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "math_response",
"strict": true,
"schema": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explanation": {
"type": "string"
},
"output": {
"type": "string"
}
},
"required": ["explanation", "output"],
"additionalProperties": false
}
},
"final_answer": {
"type": "string"
}
},
"required": ["steps", "final_answer"],
"additionalProperties": false
}
}
}
}
示例輸出:
{
"steps": [
{
"explanation": "Subtract 31 from both sides to isolate the term with x.",
"output": "8x + 31 - 31 = 2 - 31"
},
{
"explanation": "This simplifies to 8x = -29.",
"output": "8x = -29"
},
{
"explanation": "Divide both sides by 8 to solve for x.",
"output": "x = -29 / 8"
}
],
"final_answer": "x = -29 / 8"
}
開發(fā)人員可以使用結(jié)構(gòu)化輸出逐步生成答案,以引導(dǎo)達(dá)到預(yù)期的輸出。
根據(jù)OpenAI的說法,開發(fā)人員不需要驗(yàn)證或重試格式不正確的響應(yīng),并且該功能允許更簡單的提示。
原生SDK支持
OpenAI稱他們的Python和Node SDK已更新,原生支持結(jié)構(gòu)化輸出。
為工具提供架構(gòu)或響應(yīng)格式就像提供Pydantic或Zod對象一樣簡單,OpenAI的SDK能將數(shù)據(jù)類型轉(zhuǎn)換為支持的JSON模式、自動(dòng)將JSON響應(yīng)反序列化為類型化數(shù)據(jù)結(jié)構(gòu)以及解析拒絕。
from enum import Enum
from typing import Union
from pydantic import BaseModel
import openai
from openai import OpenAI
class Table(str, Enum):
orders = "orders"
customers = "customers"
products = "products"
class Column(str, Enum):
id = "id"
status = "status"
expected_delivery_date = "expected_delivery_date"
delivered_at = "delivered_at"
shipped_at = "shipped_at"
ordered_at = "ordered_at"
canceled_at = "canceled_at"
class Operator(str, Enum):
eq = "="
gt = ">"
lt = "<"
le = "<="
ge = ">="
ne = "!="
class OrderBy(str, Enum):
asc = "asc"
desc = "desc"
class DynamicValue(BaseModel):
column_name: str
class Condition(BaseModel):
column: str
operator: Operator
value: Union[str, int, DynamicValue]
class Query(BaseModel):
table_name: Table
columns: list[Column]
conditions: list[Condition]
order_by: OrderBy
client = OpenAI()
completion = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{
"role": "system",
"content": "You are a helpful assistant. The current date is August 6, 2024. You help users query for the data they are looking for by calling the query function.",
},
{
"role": "user",
"content": "look up all my orders in may of last year that were fulfilled but not delivered on time",
},
],
tools=[
openai.pydantic_function_tool(Query),
],
)
print(completion.choices[0].message.tool_calls[0].function.parsed_arguments)
而且,本機(jī)結(jié)構(gòu)化輸出支持也可用于response_format 。
from pydantic import BaseModel
from openai import OpenAI
class Step(BaseModel):
explanation: str
output: str
class MathResponse(BaseModel):
steps: list[Step]
final_answer: str
client = OpenAI()
completion = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "You are a helpful math tutor."},
{"role": "user", "content": "solve 8x + 31 = 2"},
],
response_format=MathResponse,
)
message = completion.choices[0].message
if message.parsed:
print(message.parsed.steps)
print(message.parsed.final_answer)
else:
print(message.refusal)
其他用例
開發(fā)人員經(jīng)常使用OpenAI的模型為各種用例生成結(jié)構(gòu)化數(shù)據(jù)。
其他一些例子包括:
-根據(jù)用戶意圖動(dòng)態(tài)生成用戶界面
開發(fā)人員可以使用結(jié)構(gòu)化輸出來創(chuàng)建代碼或UI生成應(yīng)用程序。
使用相同的response_format ,可根據(jù)用戶輸入生成不同的 UI。
例如,創(chuàng)建「園丁的登錄界面」:
圖片
就是用以下代碼生成的:
{
"type": "div",
"label": "",
"children": [
{
"type": "header",
"label": "",
"children": [
{
"type": "div",
"label": "Green Thumb Gardening",
"children": [],
"attributes": [{ "name": "className", "value": "site-title" }]
},
{
"type": "div",
"label": "Bringing Life to Your Garden",
"children": [],
"attributes": [{ "name": "className", "value": "site-tagline" }]
}
],
"attributes": [{ "name": "className", "value": "header" }]
},
{
"type": "section",
"label": "",
"children": [
{
"type": "div",
"label": "",
"children": [
{
"type": "div",
"label": "About Us",
"children": [
{
"type": "div",
"label": "At Green Thumb Gardening, we specialize in transforming your outdoor spaces into beautiful, thriving gardens. Our team has decades of experience in horticulture and landscape design.",
"children": [],
"attributes": [
{ "name": "className", "value": "about-description" }
]
}
],
"attributes": [{ "name": "className", "value": "about-section" }]
}
],
"attributes": [{ "name": "className", "value": "content" }]
}
],
"attributes": [{ "name": "className", "value": "about-container" }]
},
{
"type": "section",
"label": "",
"children": [
{
"type": "div",
"label": "",
"children": [
{
"type": "div",
"label": "Our Services",
"children": [
{
"type": "div",
"label": "Garden Design",
"children": [],
"attributes": [
{ "name": "className", "value": "service-item" }
]
},
{
"type": "div",
"label": "Plant Care & Maintenance",
"children": [],
"attributes": [
{ "name": "className", "value": "service-item" }
]
},
{
"type": "div",
"label": "Seasonal Cleanup",
"children": [],
"attributes": [
{ "name": "className", "value": "service-item" }
]
},
{
"type": "div",
"label": "Custom Landscaping",
"children": [],
"attributes": [
{ "name": "className", "value": "service-item" }
]
}
],
"attributes": [{ "name": "className", "value": "services-list" }]
}
],
"attributes": [{ "name": "className", "value": "content" }]
}
],
"attributes": [{ "name": "className", "value": "services-container" }]
}
],
"attributes": [{ "name": "className", "value": "landing-page" }]
}
- 將最終答案與支撐性的推理或附加評論分開
為模型提供一個(gè)單獨(dú)的思維鏈字段可以提高響應(yīng)的最終質(zhì)量。
請求:
{
"model": "gpt-4o-2024-08-06",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant"
},
{
"role": "user",
"content": "9.11 and 9.9 -- which is bigger?"
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "reasoning_schema",
"strict": true,
"schema": {
"type": "object",
"properties": {
"reasoning_steps": {
"type": "array",
"items": {
"type": "string"
},
"description": "The reasoning steps leading to the final conclusion."
},
"answer": {
"type": "string",
"description": "The final answer, taking into account the reasoning steps."
}
},
"required": ["reasoning_steps", "answer"],
"additionalProperties": false
}
}
}
}
結(jié)構(gòu)化輸出:
{
"reasoning_steps": [
"First step is to compare the numbers 9.11 and 9.9.",
"Both numbers have the same whole number part, which is 9.",
"To compare the decimal parts, convert them to the same number of decimal places.",
"9.11 has two decimal places: it is 9.11.",
"9.9 has one decimal place: it can be rewritten as 9.90.",
"Now, compare 9.11 and 9.90 by looking at the decimal parts.",
"Compare 11 with 90.",
"90 is greater than 11, so 9.90 is greater than 9.11."
],
"answer": "9.9 is bigger than 9.11."
}
- 從非結(jié)構(gòu)化數(shù)據(jù)中提取結(jié)構(gòu)化數(shù)據(jù)
例如,指示模型從會議記錄中提取待辦事項(xiàng)、截止日期和作業(yè)等內(nèi)容。
請求:
POST /v1/chat/completions
{
"model": "gpt-4o-2024-08-06",
"messages": [
{
"role": "system",
"content": "Extract action items, due dates, and owners from meeting notes."
},
{
"role": "user",
"content": "...meeting notes go here..."
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "action_items",
"strict": true,
"schema": {
"type": "object",
"properties": {
"action_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Description of the action item."
},
"due_date": {
"type": ["string", "null"],
"description": "Due date for the action item, can be null if not specified."
},
"owner": {
"type": ["string", "null"],
"description": "Owner responsible for the action item, can be null if not specified."
}
},
"required": ["description", "due_date", "owner"],
"additionalProperties": false
},
"description": "List of action items from the meeting."
}
},
"required": ["action_items"],
"additionalProperties": false
}
}
}
}
結(jié)構(gòu)化輸出:
{
"action_items": [
{
"description": "Collaborate on optimizing the path planning algorithm",
"due_date": "2024-06-30",
"owner": "Jason Li"
},
{
"description": "Reach out to industry partners for additional datasets",
"due_date": "2024-06-25",
"owner": "Aisha Patel"
},
{
"description": "Explore alternative LIDAR sensor configurations and report findings",
"due_date": "2024-06-27",
"owner": "Kevin Nguyen"
},
{
"description": "Schedule extended stress tests for the integrated navigation system",
"due_date": "2024-06-28",
"owner": "Emily Chen"
},
{
"description": "Retest the system after bug fixes and update the team",
"due_date": "2024-07-01",
"owner": "David Park"
}
]
}
安全的結(jié)構(gòu)化輸出
安全是OpenAI的首要任務(wù)——新的結(jié)構(gòu)化輸出功能將遵守OpenAI現(xiàn)有的安全政策,并且仍然允許模型拒絕不安全的請求。
為了使開發(fā)更簡單,API響應(yīng)上有一個(gè)新的refusal字符串值,它允許開發(fā)人員以編程方式檢測模型是否生成拒絕而不是與架構(gòu)匹配的輸出。
當(dāng)響應(yīng)不包含拒絕并且模型的響應(yīng)沒有過早中斷(如finish_reason所示)時(shí),模型的響應(yīng)將可靠地生成與提供的schema匹配的有效JSON。
{
"id": "chatcmpl-9nYAG9LPNonX8DAyrkwYfemr3C8HC",
"object": "chat.completion",
"created": 1721596428,
"model": "gpt-4o-2024-08-06",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"refusal": "I'm sorry, I cannot assist with that request."
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 81,
"completion_tokens": 11,
"total_tokens": 92
},
"system_fingerprint": "fp_3407719c7f"
}
參考資料:
https://openai.com/index/introducing-structured-outputs-in-the-api/