Day 6・AIをフロントから呼ぶ(この教材の看板)
プロンプトと型で出力を固定、検算・安全(ハルシネーション/コスト)
出力の形をプロンプトと型で固定し、検算・上限設定でハルシネーションとコストに備える。
LLMの出力は「そのまま信じない」
LLMは便利ですが、2つの弱点があります。1つはハルシネーション(=もっともらしいウソをつくこと。事実でない内容を自信満々に出す現象)。もう1つはコスト(=使うほどお金がかかること)。この2つに対して、型と検算(=出てきた結果を自分で確かめること)で守るのがプロの作り方です。
出力の形をプロンプトで固定する
LLMに「自由に答えて」と頼むと、返事の形が毎回バラバラになり、プログラムで扱いにくくなります。そこで「必ずこのJSONの形で返して」と指示(プロンプト)で固定します。形を決めておけば、受け取る側も型で受けられます。
// LLMにこの形で返させたい、と先に決めておく
interface Recipe {
title: string; // 料理名
minutes: number; // 調理時間(分)
steps: string[]; // 手順
}
// プロンプト側でも同じ形を明示的にお願いする
const instruction =
"次のJSON形式のみで返答してください。前後に説明文を付けないこと。\n" +
"{ \"title\": string, \"minutes\": number, \"steps\": string[] }";なぜ型とプロンプトの両方で固定するのか
プロンプトは「こう返してね」というお願いなので、LLMが従わないことがあります。型は「受け取った後にこの形か確かめる」役です。お願い(プロンプト)と確認(型・検算)の二段構えにすると、LLMが形を外したときにこちらで気づけます。
検算: 受け取った後に必ず確かめる
「型を付けた」だけでは実は不十分です。TypeScriptの as は「この形のはずだと信じる」だけで、実際の中身は確かめません。だから受け取った直後に、本当にその形か・値が妥当かを自分のコードでチェックします。これが検算です。
信じるだけ: 形が違っても素通り
const recipe = JSON.parse(text) as Recipe;
// もし minutes が "30分" という文字列で来ても
// as は何もしてくれない。後で計算してバグる検算する: 形と値を確かめる
const data = JSON.parse(text);
if (
typeof data.title !== "string" ||
typeof data.minutes !== "number" ||
!Array.isArray(data.steps)
) {
throw new Error("LLMの出力が期待した形ではありません");
}
const recipe: Recipe = data; // ここまで来たら安心して使えるハルシネーションへの備え
検算で形は守れても、内容が事実かどうかは別問題です。数値や固有名詞など「間違うと困る情報」は、LLMの出力をそのまま最終結果にせず、確かな情報源(データベースや計算)と突き合わせる設計にします。LLMは下書き役、確定はこちらの検算で、が基本です。
コストへの備え
LLMは送る文章と受け取る文章の量(トークン)に応じて課金されます。長い入力や無制限の出力は、そのまま料金に響きます。最大出力の上限を決める、無駄に長い入力を送らない、といった歯止めをコードに入れておきます。
- 出力の最大長(max tokens など)を必ず指定して、際限なく生成させない。
- 同じ質問には結果を再利用(キャッシュ)して、毎回LLMを呼ばない。
- ユーザー入力の長さに上限を設け、極端に長い入力を弾く。
- 呼び出し回数やエラーを記録して、異常な増え方に早く気づく。
AIにはこう聞く
「LLMからのJSON出力を受け取ったあと、title:string / minutes:number / steps:string[] の形かを実行時に検算する関数をTypeScriptで書いて。形が違えばエラーを投げること」と頼むと、検算関数のたたき台が得られます。zodなどの検証ライブラリを提案してもらうのも有効です。
まとめると、AIプロダクトは「実装はAIに任せ、型で守り、検算で確かめ、説明できる」状態を目指します。プロンプトで出力の形をお願いし、型と検算で本当にその形か確かめ、コストの歯止めを入れる。この3点を押さえれば、LLMの弱点に振り回されずに使えます。
読んでも腑に落ちない所は、AIに噛み砕いてもらいましょう。
理解度チェック
答えられれば、面接で説明できる状態に一歩近づきます。
ハルシネーションの説明として最も適切なのは?
TypeScriptで const recipe = JSON.parse(text) as Recipe; と書いたとき、asが実際にやってくれることは?
LLMの出力を安全に扱うために、プロンプト・型・検算・コスト対策をどう組み合わせるか説明してください。
記述式です。まず自分の言葉で答えを考えてから、模範解答を開いて自己採点してください。