TypeScript 指南

TypeScript 版 OpenAI Decisions API

OpenAI 尚未为其 Decisions API 发布 SDK——没有 client.decisions.create 可抄。本页展示针对本站端点的可用、完整类型化的 TypeScript 代码——该端点是可调用的 OpenAI Decisions API 替代方案,由 decisions-1 提供服务,遵循相同的受限决策模式。

更新于

定义契约

把请求类型定义一次,到处复用。model 是字面量联合类型,固定的 ID 不会写成手误。questions 是以你的 ID 为键的 record,每个问题的类型为 noul、choice 或 score。

把 answers 定义为以 type 为判别字段的联合类型——这正是让响应处理安全的原因:noul 答案有 noul 字段,choice 答案有 choice 和 probabilities——按 type 收窄就能得到正确的字段。

TypeScript

interface DecisionQuestion {
  type: 'noul' | 'choice' | 'score'
  instructions: string
  criteria?: Record<string, string> | string[]
}

interface DecisionRequest {
  model: 'decisions-1' | 'decisions-latest'
  state: string | Record<string, unknown> | unknown[]
  questions: Record<string, DecisionQuestion>
}

interface DecisionAnswers {
  [questionId: string]:
    | { type: 'noul'; noul: number }
    | { type: 'choice'; choice: string; probabilities: Record<string, number>; confidence: number }
    | { type: 'score'; score: number; legend: string[]; probabilities: Record<string, number>; confidence: number }
}

发起调用

一次 POST,携带 Bearer 密钥。satisfies DecisionRequest 在编译期检查请求体;AbortSignal.timeout 防止挂起的调用卡住流水线。读取 answers 之前先按 type 字段收窄。

TypeScript

const res = await fetch('https://decisions-api.net/api/v1/decisions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.DECISIONS_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(request satisfies DecisionRequest),
  signal: AbortSignal.timeout(30000),
})
const { answers } = (await res.json()) as { answers: DecisionAnswers }

const team = answers.team
if (team.type === 'choice' && team.confidence >= 0.7) {
  routeTo(team.choice)
}

超时与重试

429 和 502 值得短退避重试——失败的调用不计费。402 表示余额不足:去充值,不要重试。422 是校验错误;报错信息会指明字段,应该修正请求体而不是重试。

TypeScript

async function decide(body: DecisionRequest, attempts = 3): Promise<DecisionAnswers> {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch('https://decisions-api.net/api/v1/decisions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.DECISIONS_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(30000),
    })
    if (res.status === 429 || res.status === 502) {
      await new Promise(r => setTimeout(r, 2 ** i * 1000))
      continue
    }
    if (res.status === 402) throw new Error('out of credits')
    if (!res.ok) throw new Error(`decision call failed: ${res.status}`)
    return (await res.json() as { answers: DecisionAnswers }).answers
  }
  throw new Error('decision call failed')
}

把调用封装成一个 helper

调用方看到的应该是一个「文本进、标签出」的函数——而不是 HTTP 细节。把超时、402 检查和重试循环一次性放进 decide(),每个调用点都保持干净。

TypeScript

// Keep the call behind one typed function — timeouts, 402s,
// and retries live inside it, not at every call site.
export async function routeTicket(text: string): Promise<string> {
  const answers = await decide({
    model: 'decisions-1',
    state: text,
    questions: {
      team: {
        type: 'choice',
        instructions: 'Which team should own this ticket?',
        criteria: {
          payments: 'Checkout or billing.',
          frontend: 'Rendering or browser behavior.',
          account: 'Login or permissions.',
        },
      },
    },
  })
  const team = answers.team
  return team.type === 'choice' && team.confidence >= 0.7 ? team.choice : 'triage'
}

常见问题

OpenAI 官方有 Decisions API 的 SDK 示例吗?

没有。OpenAI 尚未发布其 Decisions API 的 SDK 方法或请求模式——任何展示 client.decisions.create 的代码都是虚构的。本页使用纯 HTTP。

这些类型需要代码生成器吗?

不需要——本页的接口已覆盖整个契约。把它们复制进项目即可;代码量小到可以审查,且随代码一起版本管理。

如何发送结构化上下文?

state 不只接受字符串,也接受 JSON 对象或数组——在 json 请求体里直接传 dict,模型会把它作为上下文读取。

在浏览器里跑一次决策

跳过配置——在试用台用新访客的 1 个免费积分发起真实调用。