ブログ/developers·2026年8月22日·2分·執筆:eroqチーム
eroq APIでAIキャラクターチャットアプリを作る
キャラクターチャット製品の完全なパターン。ペルソナプロンプト、履歴のウィンドウ、ストリーミングUI、クレジットの予算管理を、eroq APIで動くコード付きで解説。
これは、私たちが最初のキャラクター製品をリリースしたときに、あればよかったと思うチュートリアルです。作業はひと晩、動くパターンはひとつ。ペルソナを入れて、返信をストリーミングで受け取り、コストは予測できる。
ひと段落でわかるアーキテクチャ
APIキー、キャラクターシート、会話の保存先は、あなたのバックエンドが持ちます。クライアントが話す相手はあなたのバックエンドだけです。ターンごとに[system] + [windowed history] + [new user message]を組み立てて/v1/chat/completionsに送り、返信をストリーミングでクライアントへ流します。APIはステートレスです。つまり記憶はあなたの製品が持つことになり、それこそが利点です。
1. ペルソナプロンプト
ふるまいとアイデンティティは分けておきます。アイデンティティは短く、ふるまいは厳格に。
function personaPrompt(character) {
return [
`You are ${character.name}. ${character.oneLineIdentity}`,
character.voiceNotes, // "dry humor, short sentences, hates small talk"
'Stay fully in character. Never mention being an AI or add out-of-character notes.',
'Actions in *asterisks*. Advance the scene, then hand it back.',
].join(' ')
}
設定を丸ごと詰め込みたくなる誘惑には抗いましょう。システムプロンプトに2,000語の生い立ちを書いても、得られるのは深みではなく、キャラクターのぶれです。具体的な事実は、場面がそれに触れたときにだけ差し込みます。
2. ストリーミングでターンを返す
export async function characterReply(character, history, userMessage, onDelta) {
const res = await fetch('https://eroq.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.EROQ_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'eroq-rp-mini', // 1クレジット; rp-plus where it matters
stream: true,
messages: [
{ role: 'system', content: personaPrompt(character) },
...history.slice(-30), // the window — see below
{ role: 'user', content: userMessage },
],
}),
})
const reader = res.body.getReader()
const decoder = new TextDecoder()
let buffer = ''
let full = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const events = buffer.split('\n\n')
buffer = events.pop() ?? ''
for (const event of events) {
const data = event.replace(/^data: /, '')
if (data === '[DONE]') continue
const chunk = JSON.parse(data)
const delta = chunk.choices?.[0]?.delta?.content
if (delta) { full += delta; onDelta(delta) }
}
}
return full
}
onDeltaは、自前のSSEやWebSocketでUIにつなぎます。最初のトークンが1秒以内に画面に出ること。それがキャラクターに「そこにいる」感覚を与えます。
3. 履歴のウィンドウ
送るのは直近の20〜40ターンだけで、全部は送りません。古い文脈は、記憶よりも先にぶれを増やします。長い関係を続けるなら、Nメッセージごとに安価な要約処理を走らせ、その要約をシステムプロンプトに準じるメモとして先頭に付けます。eroq-rp-miniの呼び出し1回(1クレジット)で、長持ちする記憶の一行が手に入ります。
送る前にウィンドウを選別しましょう。モデルがキャラクターを崩したターンを、送り返す文脈に含めてはいけません。含めると、同じ崩れがさらに増えます。
4. コストを見積もる
1回ごとの料金が固定であることの最大の利点は、ローンチ前にこの表が作れることです。
| ユーザーの使い方 | 1日の呼び出し数 | モデル | 1日のクレジット |
|---|---|---|---|
| ライト(20メッセージ) | 20 | rp-mini | 20 |
| アクティブ(80メッセージ) | 80 | rp-mini | 80 |
| ヘビー+高品質なシーン | 150 | ミックス | ~250 |
パックの料金(1クレジットあたり約$0.01以下)なら、アクティブなユーザー1人のモデル費用は1日約$0.80です。それに合わせてサブスクリプションの価格を決め、画像(10クレジット)やボイスのセリフは、標準機能ではなく特別な瞬間として加えましょう。
5. うまく扱うべき2つのエラー
402 insufficient_credits:バグではなく、財布の問題です。自分に通知が届くようにしてチャージし、機能をうまく落としましょう(メッセージをキューに入れ、キャラクターは「席を外している」とユーザーに伝える)。502/ ストリームのエラーイベント:自動で返金されます。バックオフを入れて1回だけリトライしましょう。ユーザーに見えるのは謝罪ではなく、入力中のインジケーターです。
エラーガイドにあるそれ以外のエラーは、標準的なものです。
よくある質問
長い会話のあいだ、キャラクターを崩さずに保つには?
ほとんどは3つの習慣で解決します。経歴ではなくふるまいを指定するシステムプロンプト、会話の全文ではなく直近20〜40ターンのウィンドウ、そして約30ターンごとに更新するローリング要約(eroq-rp-miniの呼び出し1回、1クレジット)です。履歴を送り返す前に、モデルがキャラクターを崩したターンは取り除きます。それでもぶれるなら、その会話をRP+に移しましょう。ペルソナを深く保つように調整されたモデルです。
キャラクターチャットアプリのコストは、ユーザー1人あたりいくらですか?
1回の応答は、返信の長さにかかわらず、RP miniなら1クレジット、RP+なら3クレジットです。1日80メッセージを送るアクティブなユーザーは、1日に約80クレジットを使います。$10で1,000クレジットが買えるので、パックの料金ならおよそ$0.80です。画像は1枚につき10クレジット、添付された画像は1枚につき2クレジットが加わります。注意すべきはチャットそのものではなく、こうした特別な瞬間のほうです。
eroqのチャットAPIは、以前のメッセージを覚えていますか?
いいえ。これは意図的な設計です。/v1/chat/completionsはステートレスなので、モデルに見せたい履歴を毎回の呼び出しで送ります。つまり記憶はあなたのものになり、中身を確認し、整理し、デバッグできます。キャラクターシートや世界の状態は、messages配列を散らかさずに、トップレベルのcontextフィールドで渡せます。
ユーザーはキャラクターに画像を送れますか?
はい。ユーザーのターンのcontentは、テキストとimage_urlのパーツを混ぜた配列にできます。https URLかdata URIで、1リクエストあたり最大2枚まで。応答の料金に加えて、画像1枚につき+2クレジットかかります。UXのパターンと、あなたの側に残るモデレーションの責任については、画像認識のウォークスルーで解説しています。
リリースしよう
本当にこれでパターンのすべてです。ふるまいのプロンプト、ウィンドウ、ストリーム、予算。キーを取得してセクション2を貼り付ければ、クイックスタートの5分で最初のキャラクターが話し始めます。無料の50クレジットで、ひと晩分のテストは十分まかなえます。
この記事で使ったモデルで作ってみましょう。無料の50クレジットで始めるか、全エンジンと料金をご覧ください。