はじめに
さくらのAI Engine 3,000リクエスト使い切りチャレンジの投稿記事です。
小学生の娘のために個人開発している学習Webアプリ「キッズドリルゲーム」に、漢字の読みを4択で答える「かんじクイズ」を追加した。
正解を用意するのは教育漢字のデータさえあれば機械的にできるが、難しいのはむしろ誤答の方で、「なんとなく正解っぽいけど違う」選択肢を字1つ1つに何十個も考えるのは、本来なら人力が必要な作業だった。
これをさくらのAI Engine(gpt-oss-120b)に丸ごとお願いした。
また、学習指導要領の改訂に対応している且つ学年別の配当漢字・読み・用例をまとめて返してくれるAPIがなかったため、自作で教育漢字APIを作って公開している。
全体の流れ
- 教育漢字APIから学年別の漢字データ(読み・用例・画数)を取得
- その漢字が実際に使われる熟語(用例)をもとに、出題する読みを1つ選ぶ
- さくらのAI Engineに、紛らわしい誤答を字ごとに複数個考えてもらう
- 結果を静的JSONとして出力
ゲーム画面はこのJSONを読み込んで出題するだけで、実行時にAPIやAIは一切呼ばない。生成は手元で一度回すだけの前処理として割り切っている。
誤答生成をさくらのAI Engineに任せる
さくらのAI EngineはOpenAI互換のChat Completions APIなので、クライアントはごく薄いラッパーで済む。
export const chatCompletion = async (
messages: ChatMessage[],
options: { temperature?: number } = {},
): Promise<string> => {
const res = await fetch(`${baseUrl}/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
body: JSON.stringify({ model, messages, temperature: options.temperature ?? 0.7 }),
});
// ...
const data = await res.json();
return data.choices[0].message.content;
};
プロンプトでは、正解の読みと文字種(音読み=カタカナ/訓読み=ひらがな)を渡し、「音の響きが似ている・文字数が近い、まぎらわしいもの」を3つ返すよう指示している。
const buildPrompt = (target: DistractorTarget, exclude: readonly string[]): string => `あなたは小学生向けの漢字よみクイズを作る教材編集者です。
次の問題について、「ただしいよみ」とまぎらわしい・まちがえそうなダミーの選択肢(誤答)を
ちょうど3つ考えてください。
かんじ: "${target.kanji}"
ただしいよみ: "${target.correctReading}"(${target.readingType === "on" ? "音読み・カタカナ表記" : "訓読み・ひらがな表記"})
${exclude.length > 0 ? `すでに使った誤答(これらとは違うものにする): ${exclude.join("、")}` : ""}
条件:
- ただしいよみと同じ文字種で答える
- ただしいよみに文字を継ぎ足しただけの水増しは禁止
- 出力は次の形式のJSON配列のみ: ["よみ1", "よみ2", "よみ3"]`;
1回のリクエストで大量に生成させるのではなく、字ごとに3ラウンド×3個ずつ、直前までに使った誤答をexcludeとして渡しながら積み増す方式にした。
1回で「9個ちょうだい」と聞くより、ラウンドを分けて「さっきと違うのをもう3つ」と聞いた方が語彙が広がりやすかったため、このようにしている。
実際に生成された誤答プールはこんな感じになった。
{
"kanji": "一", "correctReading": "イチ",
"distractorPool": ["イツ", "イシ", "キチ", "ヒチ", "シチ", "ニチ", "ウチ", "ミチ", "ロチ"]
}
{
"kanji": "七", "correctReading": "シチ",
"distractorPool": ["シツ", "ジチ", "シジ", "ジツ", "ニチ", "サチ", "ツチ", "サツ", "ジヒ"]
}
「イチ」に対する「イツ」「イシ」、「シチ」に対する「シツ」「ジチ」のように、字面・音がどちらも近い誤答が並んでいて、水増し禁止・文字種統一の指示がちゃんと効いていることが分かる。
3,000リクエストにちょうど収まる設計
教育漢字は学年1〜6を合わせて約1,000字。これを1字1リクエストで3ラウンド回すと、約1,000字 × 3ラウンド ≒ 3,000リクエストと、無償枠の上限にほぼぴったり収まる。字数・ラウンド数はこの上限から逆算して決めた。せっかくの無償チャレンジなので、枠を使い切る方向で設計するのは単純に楽しかった。
(この実装にたどり着くまでに、すでにリクエストを消費してしまっていたのを失念しており、小学4年生分の誤答生成を終えたところで枠が尽きてしまった。結果、現在ゲームに搭載できているのは1〜4年生分のみで、5・6年生分は今後の宿題として残っている)
出来上がったクイズ
「七時(◯◯じ)」のように熟語の一部を隠して読みを答えさせる形式で、選択肢はAIが作った誤答プールからその場でランダムに4つ選ぶ。
同じ字でも毎回違う顔ぶれの誤答が出るので、答えを覚えてしまう心配がない。
さいごに
誤答を漢字1字ずつ何十個も自分でひねり出すのはかなりの時間を要するため、漢字周りの機能は追加を見送っていた。
そこを丸投げできる且つ無料枠で収まるさくらのAI Engineは、個人開発者にとって非常にありがたい存在だった。
コードやアプリはすべて公開している。
