OpenAI API+Node.jsでJSON構造化出力をする
Structured OutputsとZod Response Format

こんにちは。廣瀬製紙株式会社 稼働率向上PJチームのA.Mです。
今回はOpenAI APIを使用する際のStructured Outputsについて、特にZodというTypescriptライブラリを使った構造化出力についてご紹介します。
特に、具体例として
- ・SQLクエリを生成する場合
- ・HTMLのテーブル構造を出力する場合
の2通りを紹介します。
以前においては、OpenAI APIを使ってJSONを出力する場合にはJSON Modeというものを使用するのが一般的でした。
ですが現在では、公式ドキュメントにはStructured Outputsが標準だと記載されています。
目次
なぜZodを使うのか?
なぜZodを使うのか、というとそれは公式ドキュメントがZodを使っているからに他なりません。
私のStructured Outputsの使い方のイメージは、文字列としてJSON構造を渡すものだったのですが、あらためて公式ドキュメントをよく見てみるとZod Response Formatが~と書かれていたため、おや?と思いました。
おそらくこれまでPythonで使うことが多かったため、Node.jsの場合の公式ドキュメントを見る機会がなかったのでしょう。
構造化出力(Structured Outputs)とは何か?
従来、AIモデルからの回答をプログラムで扱う際は「JSONで答えて」と指示してもフォーマットが一定しなかったり、出力を正規表現でチェックする必要があったりと、扱いづらい面がありました。
その後、JSON Modeが出てきて安定してJSONが受け取れるようになり、現在ではまた一歩進んでStructured Outputsが標準のような感じになっています(たぶん)。
Structured Outputsを使えば、指定したJSONスキーマに従った回答を確実に受け取れます。
Node.jsのSDKではzodResponseFormatヘルパーを使用することで、Zodで書いたスキーマを渡すだけで対応するJSON Schemaに変換され、型安全に結果を扱えるようになります。
Zodとは何か?
Zodは、TypeScriptで書かれた型検証ライブラリです。主な特徴として:
- ・宣言的なスキーマ定義が可能で、直感的なAPIを提供
- ・TypeScriptの型推論と連携し、バリデーション後の型安全性を保証
- ・オブジェクト、配列、プリミティブ型など、様々なデータ構造の検証に対応
- ・カスタムバリデーションルールを簡単に追加できる柔軟性
特にOpenAIのStructured Outputs機能と組み合わせる場合、ZodスキーマはJSON Schemaに変換され、AIモデルの出力を強制的に指定した形式に従わせることができます。
これにより、「型の不一致」や「必須フィールドの欠落」といった問題を未然に防げます。
実装例1: 自然言語からSQLクエリを生成する
最近の私の取り組みとして、Anthropicが提唱したModel Context Protocolを利用し、ReactのクライアントからPostgreSQLデータベースの情報を取得するという試みをよく行っています(別記事にて紹介します)。
そのため、SQLクエリを生成AIの出力結果として受け取る機会が増えました。
そのような想定で、実装例を見てみましょう。
なおZod自体はSQLの構造化には対応していないので、あくまでSQLクエリを文字列として受け取る、という対応になります。
ですが今のところうまくSQLクエリを受けとれており、失敗することはありません。
それではユーザーの質問文からSQLクエリを自動生成する例を見てみましょう。
import { z } from "zod";
import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
// SQLクエリ用のスキーマ定義
const SqlQuerySchema = z.object({
query: z.string().describe("生成されたSQLクエリ文字列")
});
const api_key = "xxx";
const openai = new OpenAI({
apiKey: api_key,
});
// API呼び出し
const completion = await openai.beta.chat.completions.parse({
model: "gpt-4o-2024-08-06",
messages: [
{ role: "system", content: "あなたはデータベースアシスタントです。ユーザーの質問に対応するSQLクエリのみを返してください。" },
{ role: "user", content: "年齢が18歳以上のユーザーの名前と年齢を一覧表示するSQLは?" }
],
response_format: zodResponseFormat(SqlQuerySchema, "SqlQuery")
});
// 結果の取得
const result = completion.choices[0].message.parsed;
console.log(result.query);
このように、モデルからの回答が常に{ query: “SQLクエリ文” }という形で返ってくるため、パースやエラー処理の手間が大幅に削減されます。
実装例2: 表形式データをHTMLテーブルで表示する
次に、構造化データをHTMLのテーブルとして表示する例を紹介します。
import { z } from "zod";
import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
const TableSchema = z.object({
headers: z.array(z.string()).describe("テーブルの列見出し"),
rows: z.array(z.array(z.string())).describe("テーブルの各行データ")
});
const api_key = "xxx";
const openai = new OpenAI({
apiKey: api_key,
});
// API呼び出し
const completion = await openai.beta.chat.completions.parse({
model: "gpt-4o-2024-08-06",
messages: [
{ role: "system", content: "あなたは有用なデータアシスタントです。要求された情報を表形式でJSON出力してください。" },
{ role: "user", content: "代表的なプログラミング言語とそれぞれの初登場年を教えて" }
],
response_format: zodResponseFormat(TableSchema, "TableData")
});
// 結果の取得
const tableData = completion.choices[0].message.parsed;
console.log(tableData);
モデルからは以下のような構造化データが返ってきます:
{
headers: [ 'プログラミング言語', '初登場年' ],
rows: [
[ 'Fortran', '1957' ],
[ 'Lisp', '1958' ],
[ 'COBOL', '1959' ],
[ 'BASIC', '1964' ],
[ 'C', '1972' ],
[ 'C++', '1985' ],
[ 'Python', '1991' ],
[ 'Java', '1995' ],
[ 'JavaScript', '1995' ],
[ 'Ruby', '1995' ],
[ 'PHP', '1995' ],
[ 'C#', '2000' ]
]
}
これをHTMLテーブルに変換するコードも簡単です:
// HTMLテーブル生成
let html = "<table>\n <tr>";
for (const headerText of tableData.headers) {
html += `<th>${headerText}</th>`;
}
html += "</tr>\n";
for (const row of tableData.rows) {
html += " <tr>";
for (const cell of row) {
html += `<td>${cell}</td>`;
}
html += "</tr>\n";
}
html += "</table>";
実装上の注意点
構造化出力機能を使う際にはいくつか制約があります:
- ・ネストは最大5階層まで
- ・オブジェクトのプロパティは最大100個まで
- ・一部の高度なJSON Schema表現は未対応
また、フィールドには可能な限りdescribe()で説明を付けると、モデルが正しく理解しやすくなります。
まとめ
OpenAIの構造化出力機能とZodを組み合わせることで、AIモデルの出力を安全かつ効率的に扱えるようになります。
SQLクエリの生成やデータの表形式出力など、様々なユースケースで活用できるため、業務システムへのAI組み込みが格段に容易になりました。
今後も当社では、こうした技術を活用して業務効率化を進めていきます。
皆さんもぜひ、自分の業務に合わせたスキーマを設計して、ChatGPTの応答を有効活用してみてください!