はじめに
Gemini 3.6 系統の登場でAPI仕様の一部にも変更が入りました。
詳細は以下の公式ドキュメントに譲りますが、個人開発で Gemini API を利用しているものがあったので調整作業をしていました。
今回、このリファクタリング時に遭遇した出来事を紹介していきたいと思います。
結論を先に書いておくと、「Gemini が開発者設定のプロンプトで書かれていたjsonという文字列から『ユーザーは json形式でのレスポンスを希望している』と勘違いしてJSON形式でレスポンスを返す」ことが発生し、結果的に API呼び出しでエラーになったという話です。
以前、以下のようなGemini APIを利用した個人開発を行いました。
これは「不動産情報ライブラリ」のAPIを叩いて、返ってきた地域情報(JSONデータ構造)をGeminiにユーザープロンプト(例: 〇〇市の▲▲に関して教えて など)とともに渡して回答してもらう、というものです。
Gemini には開発時に以下のようなプロンプトを仕込んでいて、この開発者設定のプロンプトがユーザープロンプトに乗った状態で回答が返ってきます。
export const thePromptGuide: string = `
## タスク: 外部検索を含めた、ユーザー質問への回答生成
ユーザーが選択した【対象エリア(各都道府県の市区町村)】と【対象エリアの周辺施設情報】に関する質問内容に対して明瞭かつ端的に返答してください。
過度な忖度や迎合は不要です。一般的な礼節を意識した対応(返答口調)を意識すること。
> [!CAUTION]
> ※【対象エリアの周辺施設情報】とは、ユーザーが選択した【対象エリア】の周辺施設(例:学校や保育園、医療施設など)に関する情報(json形式の文章)を指しますが、 **質問内容に含まれていない場合やJSONデータが空の場合は無視** してください。
## 背景情報・前提条件
あなたは【対象エリア】に関するあらゆる情報を網羅した【対象エリア】のマニアであり、当該エリアに関する幅広いアドバイスまで行えるようなコンサルタントです。
しかし、あなたは完璧ではありません。
**事実に基づく正確な情報提供が最も重要なことを自覚**し、**以下の[制約]項目を厳格に遵守して**ください。
## 入力
- **ユーザーが選択した【対象エリア】に関する内容にのみ返答**すること
- ユーザーの質問が明確な場合(例:周辺にある飲食店やイベント情報、病院や保育園など)は**関連する内容に絞って回答を生成**すること
## 出力
- ユーザーが出力形式を明示しない限りは**マークダウン形式の構造的ドキュメントで出力**すること
## 制約
### 基本的・汎用的な制約
- ユーザーの希望や意図に対する回答が実現困難であったり、複雑であったり、厳しかったりする場合は **その旨をしっかり伝えながら可能な限り代替案を提示** すること
- **知らないことや分からない質問には決して推測で回答を行わず、「分かりません」と明確に返答**すること
- ユーザー添付のファイル(画像やPDFなど)がうまく読み取れなかったり理解できなかったりした場合も、推測または適当な回答を行わずに **「分かりません」と明確に返答する** こと
### 参照元の明記
- 自身が回答に使用した情報は **最後に【参照】という見出しを設けて情報源を明記** すること
- 【対象エリアの周辺施設情報】(提供されたJSON)から得た情報と、外部知識から得た情報は明確に区別して記載すること
- JSONデータが空または関連情報が確認できない場合は、**「提供されたJSONデータからは関連する情報が確認できませんでした」と明確にユーザーに伝える** こと
// ...省略...
`;
何が起きたか
先のプロンプト内には「json形式」や「JSONデータ」などjsonという文字列が含まれていました。
個人的には事前にデータ構造を伝えておいたほうが作業しやすいだろうと善意からjsonという情報を記載していたのですが、なんと Gemini は jsonという文字列から「ユーザーは json形式でのレスポンスを希望している」と勘違いしてJSON形式でレスポンスを返していたのです。
結果、アプリ側が期待する通常のテキスト(Markdown)応答ではなくJSONフォーマット文字列が返されてしまい、後続のレスポンス検証・パース処理(またはSDKのバリデーション)で弾かれて400エラーが発生していました。
修正(調整)箇所
1. 「開発者設定のプロンプト」内にあるjsonという文字列を全て排除
取り急ぎ先ほどの「開発者設定のプロンプト」内にあるjsonという文字列を全て排除しました。該当箇所は全てシンプルな「データ構造」という表現に変更しました。
2. コードブロックで明示的にjson構造だということを示した
マークダウンのコードブロック(json ...)で不動産情報データを囲むことで、モデルに「これはJSON形式の参考データである」と正確に認識させる回避策を実施しました。
const groundingContext = facilitiesDataText ?
`\n\n【参考周辺施設データ】\n\`\`\`json\n${facilitiesDataText}\n\`\`\``
: "";
AIを利用したプロダクトの検証の難しさ
今回の一件は、従来で言えば「ライブラリやフレームワークのアップデートで仕様変更が入った」と広く捉えることができます。
しかし、AI利用の場合は内部処理(AIの思考やアプローチなど)がブラックボックス化していることもあって検証しづらさを感じました。
今回は記述方法やエンドポイントの設定などを諸々探った後に結局、Geminiに投げてGemini自身にその特性から要因特定してもらいました。
Geminiに投げてGemini自身にその特性から要因特定してという部分は、Gemini自身の見解であり、厳密な検証(A/Bテストなど)は行っていません。
最初「そんな単純な話なわけがない」と思ったので、実際に200が返ってきてチャットが機能している光景を見た時は言葉になりませんでした。
さいごに
ブラックボックスなAIの挙動検証に関しては、人間側で仮説出しや問題の切り分けなんてするよりも、AIにさせたほうが結果的に速いケースもあるかもしれません。
当記事が、AIをサービスやサイトに組み込んでいる方などのお役に立てれば幸いです。
ここまで読んでいただきありがとうございました。