AI会話ゲームの作り方|NPCと話すコード全体と、20往復の費用の試算【2026年版】

AIと会話するゲームは、ゲームの進行と正解をコードで持ち、台詞だけをAIに作らせる形で作ります。AIの返事をそのまま信じず、形を検査してから好感度や進行に反映します。この記事のコードは、宿屋の主人と話して「倉庫の鍵の場所」を聞き出す1本のHTMLです。
コードの動作は、決まった返事を返す偽のAIで確かめました。本物のAPIは呼んでいません。その代わり、20往復の会話にかかるAPI費用を、公式の料金表から計算して載せています。
この記事で分かること
- AI会話ゲームの仕組みを、4つの部品に分けた全体像
- そのまま動くコード全体と、動かして確かめた結果
- AIに任せることと、コードで持つことの分け方
- 20往復の会話にかかる費用の試算と、キャッシュが効く条件
- 確かめられていないこと
すでに遊べるAI会話ゲームを探している場合は、AIと会話できるゲームおすすめが向いています。ここでは、自分で作る側の話です。
この記事の目次
AI会話ゲームの仕組みは4つの部品
プレイヤーが1回話すたびに、次の4つの処理が順に動きます。

AIを呼ぶ部分は、コードの中の1つの関数にまとめます。偽のAIから本物のAPIへ替えるときに、書き換える場所がそこだけで済むためです。NPCにAIを使う考え方の全体は、AI NPCとはで扱っています。
コード全体
次のコードを、index.htmlという名前で保存してブラウザで開くと、そのまま動きます。サーバーは要りません。「こんにちは」「ありがとう」「お願い」を含む発言で好感度が上がり、「うるさい」「黙れ」で下がる、偽のAIが入っています。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>宿屋の主人と話すゲーム</title>
<style>
body{font:16px/1.6 sans-serif;max-width:560px;margin:0 auto;padding:12px}
#log{height:320px;overflow-y:auto;border:1px solid #bbb;padding:8px;background:#fafafa}
.me{color:#235}.npc{color:#820}.sys{color:#777;font-size:14px}
form{display:flex;gap:6px;margin-top:8px}input{flex:1;padding:8px;font-size:16px}
</style>
</head>
<body>
<h1>宿屋の主人ハル</h1>
<p>好感度を3以上にして、倉庫の鍵の場所を聞き出してください。</p>
<div id="log"></div>
<form id="f"><input id="t" autocomplete="off" placeholder="話しかける"><button>送る</button></form>
<p class="sys">好感度 <span id="aff">0</span> / 5</p>
<script>
// 1. ゲームの状態と正解は、コードの側で持つ(AIには渡さない)
const state = { affinity: 0, history: [] };
const KEY_PLACE = '鍵は、厨房の棚の裏にあるよ。';
const MAX_TURNS = 6; // AIに送る履歴は、直近6往復まで
const SYSTEM = 'あなたは宿屋の主人ハル。気さくだが、礼儀のない客には冷たい。' +
'必ず次のJSONだけを返す。{"reply":"台詞","mood":"happy|neutral|angry","delta":-1|0|1}';
// 2. AIを呼ぶ部分。ここだけ差し替えれば、本物のAPIにできる
async function askNpc(messages) {
// 本物のAPIにするときは、自分のサーバーの中継先に送る(APIキーをHTMLに書かない)
// const r = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({ system: SYSTEM, messages }) });
// return (await r.json()).text;
return mockAi(messages); // 動作確認用の、決まった返事を返す偽物
}
function mockAi(messages) {
const last = messages[messages.length - 1].content;
if (last.includes('壊れ')) return 'JSONではない返事';
if (/ありがとう|お願い|こんにちは/.test(last)) return '{"reply":"おや、礼儀正しいね。気に入ったよ。","mood":"happy","delta":1}';
if (/うるさい|黙れ/.test(last)) return '{"reply":"口の利き方に気をつけな。","mood":"angry","delta":-1}';
return '{"reply":"ふうん。それで?","mood":"neutral","delta":0}';
}
// 3. AIの返事は、そのまま信じず、形を検査してから使う
function parseReply(text) {
try {
const o = JSON.parse(text);
if (typeof o.reply !== 'string') throw 0;
const delta = Number.isInteger(o.delta) ? Math.max(-1, Math.min(1, o.delta)) : 0;
return { reply: o.reply, delta };
} catch (e) {
return { reply: '……(聞き取れなかったようだ)', delta: 0 }; // 失敗しても、ゲームは止めない
}
}
function trimHistory(h) { return h.slice(-MAX_TURNS * 2); }
const log = document.getElementById('log');
function say(cls, text) {
const p = document.createElement('p'); p.className = cls; p.textContent = text;
log.appendChild(p); log.scrollTop = log.scrollHeight;
}
async function send(text) {
say('me', 'あなた:' + text);
state.history.push({ role: 'user', content: text });
const raw = await askNpc(trimHistory(state.history));
const { reply, delta } = parseReply(raw);
state.history.push({ role: 'assistant', content: reply });
state.affinity = Math.max(0, Math.min(5, state.affinity + delta));
document.getElementById('aff').textContent = state.affinity;
say('npc', 'ハル:' + reply);
// 4. 鍵の場所は、好感度の条件をコードで確かめてから出す
if (text.includes('鍵')) {
say('npc', 'ハル:' + (state.affinity >= 3 ? KEY_PLACE : 'そんな話は、まだできないね。'));
}
}
document.getElementById('f').addEventListener('submit', (e) => {
e.preventDefault();
const t = document.getElementById('t');
if (t.value.trim()) send(t.value.trim());
t.value = '';
});
</script>
</body>
</html>
動かして確かめた結果

画面を出さないChromeで、発言を順に送って確かめました。結果は次の表のとおりです。
| 確かめたこと | 結果 |
|---|---|
| 好感度0で「鍵」と聞く | 「そんな話は、まだできないね。」と断られた |
| 「こんにちは」を3回送る | 好感度が0から3になった |
| 好感度3で「鍵」と聞く | 「鍵は、厨房の棚の裏にあるよ。」と出た |
| JSONではない返事が返る | 「……(聞き取れなかったようだ)」と出て、好感度は変わらず、ゲームは続いた |
| 「うるさい」と送る | 好感度が3から2に下がった |
| さらに10回話して、履歴が34件になる | AIに送る履歴は12件(6往復分)に収まった |
| deltaが5の返事や、整数でない返事 | 5は1に切り詰められ、0.5は0として扱われた |
設計の要点

正解の文章は、AIに渡さずコードで持つ
鍵の場所のような正解は、コードの定数に置いています。システムプロンプトに入れると、会話の中で聞き出される恐れがあるためです。AIに渡すのは、キャラクターの口調と、返事の形だけにしました。この点は、本物のAIでは試していません。
AIの返事は、検査してから使う
返事の形を指定しても、JSONが崩れたり、決めていない値が入ったりすることがあります。コードのparseReplyは、replyが文字列かを確かめて、deltaを-1から1の整数に収めます。失敗したときは、決めておいた台詞を出して、ゲームを続けます。
履歴は直近だけ送る
会話が長くなると、毎回送る文字量が増えて、費用も増えます。コードでは、直近6往復だけを送っています。どのくらい違うかは、次の節で試算します。
20往復の費用を試算する
1プレイの費用は、「送る入力」と「返ってくる出力」のトークン数に、各社の単価をかけて出します。入力は、毎回「設定+履歴+今回の発言」を送るため、往復が増えるほど膨らみます。
試算の前提は次のとおりです。設定(キャラクターと世界観)は800トークンと仮定しました。プレイヤーの発言とNPCの返事は、この記事のコードの台詞に近い短文を用意して、OpenAI系のトークナイザー(o200k_base)で数えた平均です。プレイヤーは約7トークン、NPCは約30トークンでした。Claude・Geminiのトークンの数え方は、これと違います。

料金は、2026年10月10日に各社の公式ページで確かめた値(Claude Haiku 5.5は入力100万トークンあたり0.10ドル・出力0.50ドル)を使い、1ドル158.3円で計算しました。最新の料金は、公式ページか、当サイトのAI API料金計算機で確かめられます。
40往復にすると、Claude Haiku 5.5では、全履歴で約1.1円、直近6往復だけで約0.73円でした。往復が増えるほど、履歴を絞った効果が大きくなります。
キャッシュは、設定が一定の長さを超えないと効かない
毎回同じ設定の部分は、キャッシュに載せると、読み込みの単価が下がります。ただし、キャッシュに載せられる長さに下限があります。公式の資料では、次のとおりでした(2026年10月26日に確認)。
- Claude:Claude Haiku 5.5・Claude Sonnet 5.5は512トークン以上
- OpenAI:GPT-5.6以降は1,024トークン以上
- Gemini:表にあるGemini 2.5 Flashは2,048、Gemini 3.8 Flashは4,096トークン以上
この記事の試算の設定は800トークンです。Claudeは512トークンを超えるので、キャッシュの試算に入れました。gpt-6-lunaは、1,024トークンに届かないため対象外にしています。Gemini 2.5 Flash-Liteは、公式の表に載っていないので計算していません。キャッシュの書き込み時の割増も、試算に入れていません。
APIキーは、HTMLの中に書かない
ブラウザで動くHTMLは、ソースを開けば誰でも読めます。APIキーを書くと、第三者に使われて、請求が自分に来るおそれがあります。コードのコメントにあるように、自分のサーバーの中継先にだけ送り、キーはサーバー側で付けるのが基本です。中継先のサーバーは、この記事では作っていません。公開の手順は、ブラウザゲームの公開方法にあります。
確かめられていないこと
- 本物のAPIでの動作偽のAIで確かめています。本物のAIが、指定した形で返すかは、確かめていません。
- 返事の速さ(待ち時間)と、会話が長くなったときの質
- 鍵の場所を、会話で聞き出そうとする入力への強さ
- トークン数は、OpenAI系の数え方での概算で、Claude・Geminiの実際の数ではありません
- スマートフォンでの操作
- 中継サーバーの作り方と、悪用を防ぐ上限の設定
確かめていないものは、動かして確かめてから、別の記事で扱います。
うまくいかないとき
| 症状 | 確認すること |
|---|---|
| 送っても何も起きない | ブラウザのコンソールにエラーがないかを見ます。コードを途中で切って貼っていないかも確かめます |
| いつも代わりの台詞が出る | AIの返事が、JSONだけになっているかを確かめます。説明の文章が前後に付くと、JSON.parseが失敗します |
| 好感度が一気に上がる | delta の上限(1)をコードで掛けているかを確かめます |
| 費用が思ったより高い | 設定の長さと、送る履歴の件数(MAX_TURNS)を見直します |
| キャッシュが効かない | 設定の長さが、各社の下限を超えているかを確かめます |
関連記事AIと会話できるゲームおすすめ【2026年版】スマホ・Steam・ブラウザ別
よくある疑問
プログラムが苦手でも作れますか
この記事のコードは、1本のHTMLです。AIに書かせる方法は、ChatGPT・Geminiでゲームを作る手順にあります。ただし、本物のAPIをつなぐ部分は、この記事では確かめていません。
どのAIを使うのが安いですか
この試算では、20往復で、軽量モデルは1円未満、中間クラスのモデルは数円から10円弱でした。ただし、返事の質は比べていません。
ゲームの正解を、AIに判定させてはいけませんか
この記事では、判定をコードで持つ形にしました。AIに判定させた場合の安定性は、確かめていません。
この記事のまとめ
この記事のまとめ
- ゲームの正解と進行はコードで持ち、台詞だけをAIに作らせる
- AIの返事は検査してから使い、失敗しても止めない
- 履歴を直近6往復に絞ると、40往復では費用が約3割下がった(Claude Haiku 5.5の試算)
- キャッシュは設定が各社の下限を超えたときだけ効く
- 本物のAPIでの動作は、確かめていない












