Hacker News ★ 97 4 min

Decisions API is in public beta

🔗 https://developers.openai.com/api/docs/guides/decisions

📌 【OpenAI 新品】Decisions API 公測,結構化判斷快 10 倍

TL;DR:OpenAI Decisions API 公測上線,用單一端點回傳機率、分類或評分,據稱比 Responses API 快約 10 倍。

一篇在 Hacker News 衝到 381 分、221 則留言的貼文,主題其實是工程師每天都在手動解決的問題:怎麼快速用 LLM 做分類、路由、排序這類結構化判斷。過去得自己寫 prompt、解析回傳的 JSON、再處理各種邊界情況,OpenAI 現在把它收斂成一個專用的 API 端點。

🤔 不是每次呼叫都需要「生成」

當應用程式需要的只是「這個條件成立的機率有多高」「該選哪個固定選項」「依照評分標準打幾分」,用完整的 Responses API 跑一輪生成流程其實是殺雞用牛刀。Decisions API 針對文字、影像或兩者混合的輸入,直接回傳型別化(typed)的答案,速度號稱比 Responses API 快約 10 倍。

🧩 一個請求,三個部分

每個請求包含三個欄位:

欄位用途
model評估請求的模型,目前只支援 gpt-6-luna
input問題共用的證據,可以是純文字,也可以是含文字與影像的訊息
questions要評估的內容,包括每個問題的類型、指示,以及允許的選項或評分等級

每個問題可以取一個獨一的名稱,API 會在回傳的 answers 陣列中原樣回傳這個名稱,方便對應結果。

三種問題類型對應三種用途:

  • predicate:檢查某個條件是否成立,例如「這張照片上的商品有明顯損傷嗎?」,回傳 0 到 1 之間代表該條件成立機率的 probability。
  • choice:從一組固定選項中選一個,例如該由哪個部門處理,回傳其中一個 choice 值,適合沒有順序關係的分類。
  • score:依照有序等級評分,例如問題嚴重程度,結果是各等級機率加權後的平均值,因此分數可以落在等級之間,適合有順序關係的評估。

官方建議:需要上述三種型別答案時用 Decisions API;需要依照自訂 JSON schema 產生物件(例如抽取欄位、生成說明文字)時用 Responses API 搭配 Structured Outputs;需要模型請求帶參數的工具呼叫時用 function calling。

目前支援的 SDK 版本為 Python 3.26.0、JavaScript 7.30.0、Go 3.73.0、Ruby 0.101.0、Java 4.78.0 及以上。

🎯 一個檢查商品損傷的實例

以下是用 predicate 問題檢查商品照片是否有明顯損傷的簡化範例:

decision = client.decisions.create(
    model="gpt-6-luna",
    input=[{
        "role": "user",
        "content": [
            {"type": "input_text", "text": "Inspect the product in this photo."},
            {"type": "input_image", "image_url": f"data:image/png;base64,{image_base64}"}
        ]
    }],
    questions=[{
        "type": "predicate",
        "name": "visible_damage",
        "instructions": "Does the product have visible damage, such as a crack, tear, or dent?"
    }]
)

類似的邏輯可以套用在工單分類、客服回覆相關性檢查、內容審核或優先度評分等場景,端點固定為 POST /v1/decisions。

⚠️ 公測階段的限制

目前 Decisions API 仍在公測,官方預期「未來幾週內」會正式 GA(正式發布),目前唯一可用的模型是 gpt-6-luna;若需要產生自訂結構的 JSON 物件,仍得搭配 Structured Outputs 與 Responses API,而非 Decisions API。

🎯 實務啟示

如果你的應用程式大量呼叫 LLM 只是為了拿到「是否、選哪個、打幾分」這類結構化判斷,Decisions API 省去了自行解析生成文字的環節,也避免了用完整生成管線處理簡單判斷的延遲成本,值得在內容審核、工單路由、優先度排序等場景先行試用評估。

🔗 來源

#OpenAI #API #LLM #MachineLearning #AIEngineering #StructuredOutputs #ContentModeration #DeveloperTools #GPT6 #AIProductivity

原始資料 Hacker News · 收集於 2026-10-07
來源原標題
Decisions API is in public beta
作者
chiefstorm
原始標籤
hackernews
來源訊號
HN points 381 HN 留言 221
原始連結
https://developers.openai.com/api/docs/guides/decisions

摘要原文

The Decisions API evaluates text, images, or both and returns typed answers about 10x faster than the Responses API. Get the probability that a condition is true, a choice from a fixed set, or a score against a rubric. Use those answers to classify content, route requests, and prioritize work in your application. Try the Decisions API in the Playground to experiment with questions and inputs before writing code. The Decisions API is in public beta, and we expect to GA in the coming weeks. gpt-6-luna is the only model currently available. Use the dedicated POST /v1/decisions endpoint. To run the SDK examples below, use these OpenAI SDK versions or later: Python 3.26.0, JavaScript 7.30.0, Go 3.73.0, Ruby 0.101.0, and Java 4.78.0. See OpenAI SDK for installation instructions. How decisions work A request has three parts: Field Purpose model The model that evaluates the request. Currently, only gpt-6-luna is supported. input Shared evidence for the questions: a text string or user messages containing text and images. questions What to evaluate, including each question’s type, instructions, and any allowed choices or score levels. The response contains an answers array. Give each question a unique name to identify its answer; the API echoes that name in the response. Choose a question type Type Use it to Main result predicate Check a condition, such as visible damage or passage relevance. probability : an estimate from 0 to 1 that the condition is true. choice Select one option, such as a department or content category. choice : one of your supplied values. score Rate an input against ordered levels, such as issue severity. score : the probability-weighted average of the level indices. Both choice and score return probabilities over discrete options. Use choice for categories without an order, such as departments. Use score for ordered levels, such as severity; it takes the probability-weighted average of their numeric indices to produce a score that can fall between levels. Use Decisions when your application needs one of these answer types. Use Structured Outputs with the Responses API when you need to generate an object that follows your own JSON schema, such as extracted fields or a written explanation, or function calling when you need a model to request a tool call with arguments. Check an image for visible damage Use a predicate question to check a product photo for visible damage. This request combines the image with instructions to look for a crack, tear, or dent. Dent Cancellation Answer relevance Does this item have a dent? probability 1.00 “ Please cancel my subscription before the next renewal. Can I switch to annual billing and keep my current plan? I don’t need this anymore. How do I stop future charges? probability 1.00 Question Can I return an item after 30 days? Answer Returns are accepted within 30 days of delivery. After that, items aren’t eligible for a refund. Start a return from the Orders page in your account. Standard shipping takes three to five business days. probability 0.90 Check an image for visible damage curl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 IMAGE_BASE64 = "$( base64 < product.png | tr -d '\r\n')" curl https://api.openai.com/v1/decisions \ -H "Authorization: Bearer $OPENAI_API_KEY " \ -H "Content-Type: application/json" \ --data-binary @- << JSON { "model": "gpt-6-luna", "input": [{ "role": "user", "content": [ {"type": "input_text", "text": "Inspect the product in this photo."}, {"type": "input_image", "image_url": "data:image/png;base64, $IMAGE_BASE64 "} ] }], "questions": [{ "type": "predicate", "name": "visible_damage", "instructions": "Does the product have visible damage, such as a crack, tear, or dent? Ignore shadows and damage to the packaging." }] } JSON 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import { readFile } from "node:fs/promises"; import OpenAI from "openai"; const client = new OpenAI(); const imageBase64 = (await readFile("product.png")).toString("base64"); const decision = await client.decisions.create({ model: "gpt-6-luna", input: [ { role: "user", content: [ { type: "input_text", text: "Inspect the product in this photo." }, { type: "input_image", image_url: `data:image/png;base64,${imageBase64}`, }, ], }, ], questions: [ { type: "predicate", name: "visible_damage", instructions: "Does the product have visible damage, such as a crack, tear, or dent? Ignore shadows and damage to the packaging.", }, ], }); const answer = decision.answers[0]; if (answer.type === "refusal") { console.log(`Refused: ${answer.name}`); } else if (answer.type === "predicate") { console.log(`Visible damage probability: ${answer.probability}`); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import base64 from pathlib import Path from openai import OpenAI client = OpenAI() image_base64 = base64.b64encode(Path("product.png").read_bytes()).decode("ascii") decision = client.decisions.create( model="gpt-6-luna", input=[ { "role": "user", "content": [ {"type": "input_text", "text": "Inspect the product in this photo."}, { "type": "input_image", "image_url": f"data:image/png;base64,{image_base64}", }, ], } ], questions=[ { "type": "predicate", "name": "visible_damage", "instructions": ( "Does the product have visible damage, such as a crack, tear, or dent? " "Ignore shadows and damage to the packaging." ), } ], ) answer = decision.answers[0] if answer.type == "refusal": print(f"Refused: {answer.name}") elif answer.type == "predicate": print(f"Visible damage probability: {answer.probability}") 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package main import ( "context" "encoding/base64" "fmt" "os" "github.com/openai/openai-go/v3" ) func main() { image, err := os.ReadFile("product.png") if err != nil { panic(err) } client := openai.NewClient() decision, err := client.Decisions.New(context.Background(), openai.DecisionNewParams{ Model: "gpt-6-luna", Input: openai.DecisionNewParamsInputUnion{ OfDecisionInputMessageArray: []openai.DecisionInputMessageParam{{ Content: openai.DecisionInputMessageContentUnionParam{ OfParts: []openai.DecisionInputPartUnionParam{ {OfInputText: &openai.DecisionInputTextParam{Text: "Inspect the product in this photo."}}, {OfInputImage: &openai.DecisionInputImageParam{ ImageURL: "data:image/png;base64," + base64.StdEncoding.EncodeToString(image), }}, }, }, }}, }, Questions: []openai.DecisionNewParamsQuestionUnion{{ OfPredicate: &openai.DecisionNewParamsQuestionPredicate{ Name: openai.String("visible_damage"), Instructions: "Does the product have visible damage, such as a crack, tear, or dent? Ignore shadows and damage to the packaging.", }, }}, }) if err != nil { panic(err) } switch answer := decision.Answers[0].AsAny().(type) { case openai.DecisionAnswerPredicate: fmt.Println(answer.Probability) case openai.DecisionAnswerRefusal: fmt.Printf("Decision refused for %s\n", answer.Name) default: panic("unexpected answer type") } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import com.openai.models.decisions.DecisionCreateParams; import com.openai.models.decisions.DecisionInputImage; import com.openai.models.decisions.DecisionInputMessage; import com.openai.models.decisions.DecisionInputPart; import com.openai.models.decisions.DecisionInputText; import java.nio.file.Files; import java.nio.file.Path; import java.util.Base64; import java.util.List; String imageBase64 = Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of("product.png"))); var message = DecisionInputMessage.builder() .contentOfParts( List.of( DecisionInputPart.ofInputText( DecisionInputText.builder() .text("Inspect the product in this photo.") .build()), DecisionInputPart.ofInputImage( DecisionInputImage.builder() . (381 points, 221 comments on Hacker News)

由 claude-code/sonnet 自動生成