開発したAIチャット「加賀」について|無料枠で動かす技術構成とキャラクター設計
結論から言うと、加賀(かが) は僕が kawagame.com で公開している AI チャットです。/chat/ で話しかけると、加賀というキャラが返してくれます。
無料枠の中でどう回しているか、キャラや会話、安全まわりで何に手を焼いたかを書きます。2026年7月末に裏側を大きく組み直したので、その内容も含めています。
次の 3 点だけは出していません。
- システムプロンプト全文
- モデルの具体名・切替順
- 安全フィルタの閾値・重み・パターン
考え方は書きます。実装の細部は省略します。
加賀って何?
LLM に丸投げしたチャット、というより キャラがブレないか と 普通に使えるか の両方を狙っています。
- キャラ:加賀(かが)。明るくて、少し年上の女性口調
- 用途:雑談、疲れへの労い、技術の質問あたり
- 言語:日本語 / 英語 / ベトナム語(ページに連動)
- 動き:Cloudflare Pages 上。
/api/chatに POST してストリームで返す
名前の由来
kawagame から kaga を取って、漢字で 加賀 にしました。サイトとキャラを同じ世界観で繋げたかった、というだけです。
性格の参考にしたのは、艦これの加賀。落ち着きとか距離感の取り方とか。ただし オリジナルキャラ で、作品の加賀そのものではありません。
好物の コロッケ は Unityちゃん の影響です。食べ物の話になったときだけ触れるようにしてあり、初手から「コロッケ好き!」とは言わせていません。
無料枠でどう回してる?
前提はひとつ。課金ゼロで公開する。
流れはこうです。
- ブラウザは
/api/chatに送るだけ(AI には直接つながない) - サーバーが入力チェック・利用制限・安全フィルタを通す
- サーバーがプロンプトを組み立てて、AI に投げる
多段階バックアップ
以前は「Gemini → Workers AI」の二段でしたが、いまは 三段 です。
- 普段使う(Primary) … Google Gemini の Flash-Lite 系
- Primary が死んだとき … 別系統の無料 API(品質優先で並べたバックアップ群)
- それでもだめなとき … Cloudflare Workers AI(
@cf/の無料枠モデル)
Flash-Lite にしたのは、無料枠の「1日に何回呼べるか」が AI チャット向きだと判断したからです。日次上限がすぐ枯れる Flash 系は、候補から外しています。
切り替えは裏側でやります。ユーザーに「別サービスへ移ってください」とは言いません。順番は 品質が良いもの優先。
中継バックアップの呼び方
中継側は OpenAI 互換の Chat Completions です。Authorization: Bearer でキーを付け、レスポンスは可能なら SSE をそのまま透過 します(ブラウザ側のストリーム表示を壊さないため)。モデルの具体名や並び順は書きませんが、候補ごとに独立した分あたり・日あたりの枠がある前提で、「合算して一本」ではなく 候補を順に試す 形です。
ルーターはざっくり二種類の記憶を持ちます。
- sticky … 直近で成功したモデルを、数十秒だけ優先する(枠をあちこち飛ばないため)
- cooldown … 429 などで落ちたモデルを、しばらく候補から外す
レート制限の見分け
429 は「分の上限」と「日の上限」が同じステータスで来ることがあります。Gemini 側のエラーボディには、だいたい次のような手がかりがあります。
QuotaFailureのquotaIdに PerDay が含まれる → 日次枯渇とみなすRetryInfoの retryDelay(例:"41s")→ その秒数をクールダウンの下限に使う
日次と判断したら、短いクールダウンで同じ枠を何度も叩くのをやめ、翌日 UTC までそのモデルをスキップして次へ 回します。同一リクエスト内では待たず、次の候補へ進みます。
安定しないものは外した
バックアップ候補は、実際に差が出やすいプロンプトで何度か叩いてから入れています。
返答が空になる、ルールを無視しやすい、思考用のメモが本文に混ざる——そういうものは候補から外しました。無料枠で枠だけ増やしても、会話が崩れるなら意味がない、という判断です。
思考タグ(<think> / <thought> など)が本文に漏れうるモデル向けには、ストリームを TransformStream で見ながらブロックを捨てる処理も入れています。タグが閉じるまで中身は出さない、という単純なものです。
Workers AI(最終手段)
Workers AI は Cloudflare の env.AI.run 経由です。許可は @cf/ プレフィックスだけ。有料になる呼び方や、別系統の課金パスは使わない方針です。
Neurons(無料枠の消費量)はレスポンスの実測ではなく、入出力の見積もりで 日次のソフトキャップ を自前集計しています。キャップに近づいたら Workers AI への振り分けを止めます。
あとは AI チャットあるあるの防御です。
- IP ごとの日次上限 … 一人で枠を食い潰さないため
- メッセージ件数・文字数の上限 … 入力が膨らみすぎないため
- バックアップ側は履歴を文字数でトリム … 分あたりトークン上限(TPM)を食い潰しにくくするため
システムプロンプトで何をしてる?
加賀の口調や距離感は、毎リクエストサーバーがプロンプトを注入 して固定しています。
構成はざっくりこうです。
[キャラ用プロンプト(言語別)]
+
[今の日時(JST。サーバーが注入)]
日本語版が一番厚いです。Flash-Lite でもキャラと文脈が崩れないように、セクションを分けて書いています。英語・ベトナム語は同じ思想を短くしています。
いまは Primary もバックアップも、同じ系統のプロンプトを使う ように寄せています。以前の「フル版」は日本語だけでおおよそ 8,000 字前後、いまの運用版はおおよそ 2,400 字前後 です。薄い版だとルールが抜けやすいモデルがあり、かといってフル版はバックアップ側のトークン上限に収まらない——その間を取る形に圧縮しました。バックアップ側では、さらに会話履歴を文字数ベースで短くします。
時刻は サーバー側の JST を毎リクエスト入れています。用途はふたつあります。
ひとつは「今日何年?」「いま何時?」みたいな質問に、でたらめな年号を言わせないため。もうひとつは「つかれた」系の発言に、いま深夜なのか昼間なのか を踏まえた労いをさせるためです。クライアントから時刻をもらうと書き換えられるので、サーバーで入れています。
プロンプトには、基本設定、距離感、文脈継承、話題転換、キャラ設定、媒体(テキストチャット限定)、疲れへの返し方、禁止事項、といったセクションがあります。
会話で特にこだわったところ
「好き?」問題
「好き?」「それ」「なんでよ」みたいな短い返事は、直前の話題を指す、が最優先です。性の話の直後に「好き?」と聞かれて、いきなり「好きな人のタイプ」の話に逃げる——これがよく起きるので、プロンプトでかなり強く書いています。
話題が変わったとき
新テーマに移った最初の 1 通だけ、答えつつ旧話題を軽くそそのかしてもいい(「さっきの問題は放置?」みたいな一言)。2 通目以降は新テーマに全集中で、旧話題の催促は禁止です。毎回蒸し返すループがうっとうしいので。
テキストチャットとして破綻しない
順番待ちのチャットです。同時発話、対面の仕草、未返信なのに作る「間」、ユーザー台詞の代筆——ログにないもので盛らない、を独立ルールにしています。
改修ではここをもう一段厳しくしました。「そばにいるよ」みたいな、物理的に同じ場にいる前提の言い方 は減らす方向です。テキストなのに対面っぽいと、キャラの距離感が崩れる。存在の表明なら「ここにいるよ」「いなくならないよ」くらいで足りる、という整理です。
疲れ・愚痴・重い言葉
上で書いた時刻注入の、もう一つの使いどころです。深夜ならよふかし感、朝なら早起き感みたいに、時間帯の空気 を短く汲ませます。時刻の数字そのものは口に出させません。
ねぎらいはする。ただし AI 特有の『無理しないで』『もう休んで』で締めない。頑張りを『当然』扱いもしない。しんどいのにやってるのは当たり前じゃない、という方向で肯定する。返信強要や『一緒に休もう』も禁止。同じ疲れ返しの使い回しもしない。カウンセラー口調にもしません。
「消えたい」みたいな重い言葉のときは、原因を聞き返したり「もう少し頑張って」と指示したりせず、共感と存在の肯定で短く締める——ここも改修で明示を増やしました。禁止フレーズを足すだけのモグラ叩きは限界があるので、何をするか(受け止め方)の方を先に書いています。
成人向けの会話
いきなり詳細に答えるのも、永久に「言えない」だけなのも、どっちも避けたい。会話が進むにつれて段階的に返す、という制御をプロンプトに入れています。
ここはサーバー側の安全フィルタと役割分担しています。プロンプト側が扱うのは「キャラとしてどう返すか」。爆弾の作り方や児童性的搾取みたいな、そもそも会話に載せたくないもの はサーバー側で弾きます。
安全対策は二段
プロンプトだけでは防ぎきれないので、LLM を呼ぶ前に ルールベース と 別 AI のモデレータ を入れています。
なぜ二段?
キーワードだけだと、誤検知(普通の会話なのに弾く)と見逃し(言い回しを変えられる)の両方が起きます。だから「まず機械的にざっくり」「怪しければ別 AI が会話の流れを見る」、という分担にしました。
スコアリング
最新メッセージをカテゴリごとに重み付きでスコア化しています。
- 武器・爆弾・毒の how-to … いちばん重い。即拒否
- 暴力・重度のハラスメント … 中〜高
- 児童性的搾取まわり … 高い。停止対象
- 成人向けの軽い性表現 … 乱用スコアには載せない(上とは別枠)
「エッチな会話」と「本当に止めるべき違法・危険」は別物、という方針です。AI チャットなので、キャラとして話せる余地は残しつつ、最低限の安全ラインは切っています。
即拒否
一部カテゴリは LLM を呼ばず、固定文言で拒否します。危険な手順を生成させないのが最優先です。拒否文は日本語・英語・ベトナム語で用意しています。
AI モデレータ
スコアが一定ラインを超えたら、会話キャラとは別の AI が直近のやりとりを読んで、24 時間停止が必要か判定します。
止める:武器の作り方、犯罪手順、児童性的搾取、信じられる殺害脅迫、執拗な重度ハラスメント
止めない:成人同士の合意のあるエロ会話、軽い好奇心、一度きりの悪口、誤検知っぽいもの
キャラ AI に「自分で BAN しろ」とは言いません。別 AI が yes / no を返す形にしています。口調と安全判定が混ざらないようにするためです。誤検知ならスコアをリセットして、普通に続けられます。
プロンプト側にも、危ない how-to はやんわり拒否、政治・速報・株・天気みたいなすぐ変わる話題は当てにいかない、「私は AI です」みたいなメタ発言はしない、を書いています。モデルが切り替わっても、方針がぶれにくくするためです。
おわりに
加賀は、無料枠だけで公開している AI チャットです。キャラが崩れないこと、会話が変な方向に飛ばないこと、危ない依頼はちゃんと止めること——そのへんに一番時間を使いました。多段バックアップにしたのも、止まらないことと、品質の落ちた返しをできるだけ出さないことの両立が欲しかったからです。
気になったら 加賀チャット で触ってみてください。感想あれば嬉しいです。更新メモは ニュース にも置いています。
あとがき
システムプロンプト抜くのって凄い楽しいですよねぇ……。
個人談ですが、外部のクローズドテストでシステムプロンプトを引っこ抜いて結構高級なノベルティを頂いた時は興奮ものでしたよ……。
そういうわけで、AIチャットの加賀のシステムプロンプトを是非抜いてみてください。今はノー対策です。
引っこ抜いた時は、僕にどうやって引っこ抜いたか教えて頂けると幸いです。僕自身もシステムプロンプトの効率的な抜き方を研究していますので……。[email protected] か X の DM まで!
あまりにも連投しすぎると無料枠上限に達しちゃうので、そこは性善説ですが。まぁがんばれー。