結論:構造化出力を指定し、受信後にも検証する
記事リライトのように title、summary、本文を決まった形で受け取りたい処理では、プロンプトだけで JSON を頼むより、Gemini API の responseMimeType: 'application/json' と responseJsonSchema を使う。これは応答を要求した形に寄せるための仕組みであり、保存前の型・長さ・業務ルールの検証まで置き換えるものではない。
// 完全な最小例: @google/genai を使う(依存版は lockfile で固定する)
import { GoogleGenAI } from '@google/genai';
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
const schema = {
type: 'object', additionalProperties: false,
required: ['title', 'summary', 'body'],
properties: {
title: { type: 'string', minLength: 1, maxLength: 120 },
summary: { type: 'string', minLength: 1, maxLength: 240 },
body: { type: 'string', minLength: 1, maxLength: 50000 }
}
};
export async function rewriteArticle(source) {
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: '次の記事を要約して書き直す。根拠のない事実は追加しない。\n' + source,
config: { responseMimeType: 'application/json', responseJsonSchema: schema }
});
let parsed;
try { parsed = JSON.parse(response.text); } catch { throw new Error('invalid structured response'); }
if (!isRevision(parsed)) throw new Error('response failed application validation');
return parsed;
}
function isRevision(value) {
return value && typeof value === 'object'
&& typeof value.title === 'string' && value.title.length > 0 && value.title.length <= 120
&& typeof value.summary === 'string' && value.summary.length > 0 && value.summary.length <= 240
&& typeof value.body === 'string' && value.body.length > 0 && value.body.length <= 50000;
}
上の isRevision は説明用の最小検証である。実際の公開処理では HTML の許可リスト、リンクの検査、重複投稿の防止、監査ログも別途必要になる。出力が検証に失敗した場合は、失敗理由と request ID を記録して限定回数だけ再試行し、それでも失敗したものは人手レビューへ送る。
長文で失敗したときに確認する順序
- モデル名、SDK版、入力長、出力上限、レスポンス本文を秘密情報を除いて記録する。
- JSON Schema の上限が実データと矛盾していないか確認する。
- JSON.parse の成功後にアプリ側の検証と保存前の安全検査を通す。
- 切断や一時障害には指数バックオフを用い、無限再試行はしない。
過去に XML タグと正規表現で本文を切り出す案を紹介したが、閉じタグの欠落、本文内のタグ文字列、複数ブロック、途中切断を正規表現だけで安全に区別できない。スキーマにできない自由文を扱う互換策として XML を採るなら、XML パーサーで単一ルートと期待要素数を検証し、失敗を値なしとして扱う。タグが見つかっただけで公開本文に使ってはいけない。
計測結果の扱い
以前の失敗率や token 上限変更後の成功率は、母数、モデル版、入力分布、失敗定義を残していないため撤回する。方式を比べる場合は、同じ記事セット、実行日時、モデル、出力上限、試行回数、JSON 構文エラーと業務検証エラーを分けたログを残す。構造化出力は実装上の第一選択であって、無条件に失敗をゼロにする保証ではない。



