AIパーソナリティの動的切り替え
ワンクリックでAIの性格、話し方、言語を切り替えるボタンをいくつか作りましょう。「Tell AI」と「Stop Telling AI」を使ってAIのシステムプロンプトを動的に変更します — セッションを再起動する必要はなく、会話の途中でシームレスに切り替わります。
これから作るもの
チャット内に組み込まれたパーソナリティ切り替え機能:
- 3つのモード — Normal Narrator、Comedy Mode、Horror Mode
- ワンクリック切り替え — ボタンをタップしてAIの話し方を変更、即座に有効
- 視覚的フィードバック — 現在アクティブなモードのボタンがハイライトされ、プレイヤーは常に現在のモードがわかる
- シームレスな遷移 — 切り替え時に会話が中断されず、AIの次の返信から新しいスタイルが使われる
仕組み
Yuminaのビヘイビアシステムには2つの強力なアクションがあります:「Tell AI」と「Stop Telling AI」です。
- Tell AI (
inject-directive) — AIのシステムプロンプトにディレクティブを注入します。ディレクティブが存在する限り、AIはそれを見て返信のたびに従います。プロンプト内のどこに表示されるか、永続的か、自動失効までのターン数を指定できます。 - Stop Telling AI (
remove-directive) — IDで以前注入されたディレクティブを削除します。削除されると、AIはそのディレクティブを見なくなります。
これら2つのアクションを組み合わせて、こうします:
プレイヤーが「Comedy Mode」ボタンをクリック
→ ビヘイビアが発火:まず古いパーソナリティディレクティブを削除(あれば)
→ 次に新しいコメディスタイルのディレクティブを注入
→ AIのシステムプロンプトに「すべてユーモラスで面白いトーンでナレーション...」が含まれるように
→ AIの次の返信がコメディスタイルに切り替わるロアエントリとはどう違うのか? ロアエントリ(enable entry / disable entry)は世界観の大きなブロックに最適です。「Tell AI」で注入するディレクティブはより軽量で柔軟です — エントリではなく、システムプロンプトに直接挿入される小さなテキストスニペットです。短いスタイル指示、一時的なルール、ワンオフのヒントに最適です。両方を組み合わせて使えます。
ステップバイステップ
ステップ1:変数の作成
現在アクティブなモードを追跡する変数が1つ必要です。Root Componentがこれを読み取って、どのボタンをハイライトするか決めます。
エディタ → サイドバー → Variables タブ → 「Add Variable」をクリック
| フィールド | 値 | 理由 |
|---|---|---|
| Display Name | Current Mode | 自分用の識別名 |
| ID | current_mode | ビヘイビアとRoot ComponentがこのIDで読み書き |
| Type | String | 値がテキスト("normal"、"comedy"、"horror")だから |
| Default Value | normal | 新規セッションはノーマルモードで開始 |
| Category | Custom | パーソナリティシステム専用カテゴリ |
| Behavior Rules | Do not modify this variable. It is controlled by the player's UI buttons. | AIにこの変数を自分で変更しないよう伝える — プレイヤーボタンのみ可能 |
ステップ2:ビヘイビアの作成
3つのビヘイビアが必要です — モードごとに1つです。各ビヘイビアのロジックは:古いディレクティブを削除 → 新しいディレクティブを注入 → 変数を更新 → プレイヤーに通知。
エディタ → Behaviors タブ → ビヘイビアを1つずつ追加
ビヘイビア1:Comedy Modeに切り替え
WHEN(トリガー):
| フィールド | 値 | 理由 |
|---|---|---|
| Trigger Type | Action | コードがexecuteAction("mode-comedy")を呼ぶと発火 |
| Action ID | mode-comedy | Root Component内のボタンがこのIDを呼ぶ |
DO(アクション):
以下のアクションを順番に追加:
| # | Action Type | 設定 | 目的 |
|---|---|---|---|
| 1 | Stop Telling AI | ディレクティブID:personality-override | 以前のパーソナリティディレクティブを削除(あれば)。なければ何も起きない — エラーなし |
| 2 | Tell AI | ディレクティブID:personality-override、内容は下記、ポジション:After Character | コメディスタイルのディレクティブを注入 |
| 3 | Modify Variable | current_modeをcomedyに設定 | 変数を更新してRoot Componentに現在のモードを伝える |
| 4 | Show Notification | メッセージ:Switched to Comedy Mode、スタイル:info | プレイヤーに視覚的フィードバックを与える |
「Tell AI」ディレクティブの内容:
[Narration Style: Comedy Mode]
From now on, narrate everything in a humorous, comedic tone. You may:
- Use exaggerated metaphors and absurd analogies
- Occasionally break the fourth wall and whisper asides to the reader
- Have NPCs deliver hilariously ill-timed lines
- Describe serious scenes in a lighthearted voice for comedic contrast
Keep the story moving — don't just tell jokes. Humor should be woven into the narration, not replace it.なぜ「Tell AI」の前に「Stop Telling AI」? 両方のディレクティブが同じID(
personality-override)を使うからです。プレイヤーがHorrorからComedyに切り替えるとき、古いディレクティブを先に削除しないと、同じIDで自動置換するinjectDirectiveに依存することになります — 実際そうしますが、明示的に削除してから再注入する方が良い習慣です。ロジックがより明確で、潜在的なエッジケースを避けられます。
ビヘイビア2:Horror Modeに切り替え
WHEN:
| フィールド | 値 |
|---|---|
| Trigger Type | Action |
| Action ID | mode-horror |
DO:
| # | Action Type | 設定 | 目的 |
|---|---|---|---|
| 1 | Stop Telling AI | ディレクティブID:personality-override | 古いパーソナリティディレクティブを削除 |
| 2 | Tell AI | ディレクティブID:personality-override、内容は下記、ポジション:After Character | ホラースタイルのディレクティブを注入 |
| 3 | Modify Variable | current_modeをhorrorに設定 | 変数を更新 |
| 4 | Show Notification | メッセージ:Switched to Horror Mode、スタイル:danger | dangerスタイルの通知を使う — 赤がホラーの雰囲気に合う |
「Tell AI」ディレクティブの内容:
[Narration Style: Horror Mode]
From now on, narrate everything with a dark, unsettling atmosphere. You should:
- Use slow, oppressive pacing for scene descriptions, focusing on sensory details (sounds, smells, textures)
- Hint that something is watching the character from the shadows, but never reveal it directly
- Make the environment itself feel wrong — doors close on their own, shadows move the wrong way, reflections in mirrors lag by half a beat
- Give NPCs dialogue with subtle wrongness, as if they know something they shouldn't
- Occasionally use second person to describe the character's physiological reactions (neck hairs standing up, heartbeat quickening, pupils dilating)
Build sustained tension, but don't throw a monster into every paragraph. True horror lives in the unknown.ビヘイビア3:Normal Modeに戻る
WHEN:
| フィールド | 値 |
|---|---|
| Trigger Type | Action |
| Action ID | mode-normal |
DO:
| # | Action Type | 設定 | 目的 |
|---|---|---|---|
| 1 | Stop Telling AI | ディレクティブID:personality-override | カスタムパーソナリティディレクティブを削除。削除されると、システムプロンプトにスタイルオーバーライドがなくなり — AIはデフォルトのナレーションスタイルに戻る |
| 2 | Modify Variable | current_modeをnormalに設定 | 変数を更新 |
| 3 | Show Notification | メッセージ:Restored Normal Mode、スタイル:info | フィードバック |
注意: ノーマルモードはディレクティブを注入しません。以前のオーバーライドを削除するだけで十分 — AIはキャラクターエントリやシステム指示で定義されたデフォルトスタイルに戻ります。
ステップ3:ディレクティブの位置と永続性を理解する
「Tell AI」アクションを設定するとき、2つの重要な設定が見えます:位置と永続性 / ターン期間です。それぞれの意味は次のとおりです。
ディレクティブの位置
位置は注入されたディレクティブがシステムプロンプトのどこに表示されるかを制御します。
| 位置 | ラベル | 説明 | 使い時 |
|---|---|---|---|
auto | Auto | エンジンが最適な場所を選択(通常はキャラクター定義の後) | ほとんどのケースで十分 |
top | Top | システムプロンプトの最初、最高優先度 | 緊急のグローバルルール(例:「これ以降、英語のみで返信」) |
before_char | Before Character | キャラクター定義の前 | AIがキャラクターを解釈する方法に影響するグローバル設定 |
after_char | After Character | キャラクター定義の後 | スタイルディレクティブ、トーン調整(このレシピで使用) |
bottom | Bottom | システムプロンプトの最後 | 最終リマインダー、「ジェイルブレイク」スタイルの指示 |
depth | Depth | 深さ別に挿入(N番目に最近のメッセージの前) | システムプロンプトではなく会話の途中に表示する必要があるディレクティブ |
なぜこのレシピは「After Character」を使うのか? パーソナリティ切り替えディレクティブはナレーションのスタイルオーバーライドだからです。キャラクター定義の後に配置すると、AIはまず「自分は誰か」(キャラクター)を読み、次に「どう話すべきか」(スタイルディレクティブ)を読みます。順序が自然で最高の結果を生みます。
永続的 vs 一時的なディレクティブ
| 設定 | 説明 | 使い方 |
|---|---|---|
| Persistent(デフォルト) | 「Stop Telling AI」で明示的に削除されるまでディレクティブはシステムプロンプトに残る | このレシピで使用 — プレイヤーが再び切り替えるまでモードはアクティブのまま |
| Temporary(ターン期間を設定) | 指定されたターン数後にディレクティブが自動失効 | ワンオフのエフェクトに最適、例:「次の3ターン、キャラクターは酔っ払って言葉が不明瞭」 |
例: 「Tell AI」アクションのターン期間を3に設定すると、ディレクティブは注入後3ターンの終わりに自動的に消えます — 手動削除不要です。
ステップ4:Root Componentにモード切り替えボタンを追加
チャットの最後のメッセージの下に3つのモードボタンを表示します。現在アクティブなモードのボタンがハイライトされます。
エディタ → Custom UI セクション → index.tsxを開く → 以下を貼り付け(デフォルトのreturn <Chat />を置き換え):
export default function MyWorld() {
const api = useYumina();
// ---- Read current mode ----
const currentMode = String(api.variables.current_mode || "normal");
// ---- Three mode configs ----
const modes = [
{
id: "normal",
label: "Normal",
actionId: "mode-normal",
color: "#94a3b8",
activeColor: "#e2e8f0",
activeBg: "rgba(148,163,184,0.2)",
border: "#475569",
activeBorder: "#94a3b8",
},
{
id: "comedy",
label: "Comedy",
actionId: "mode-comedy",
color: "#fbbf24",
activeColor: "#fef3c7",
activeBg: "rgba(251,191,36,0.2)",
border: "#a16207",
activeBorder: "#fbbf24",
},
{
id: "horror",
label: "Horror",
actionId: "mode-horror",
color: "#f87171",
activeColor: "#fecaca",
activeBg: "rgba(248,113,113,0.2)",
border: "#991b1b",
activeBorder: "#f87171",
},
];
// ---- Message list, used to find the last one ----
const msgs = api.messages || [];
return (
<Chat renderBubble={(msg) => {
const isLastMsg = msg.messageIndex === msgs.length - 1;
return (
<div>
{/* Render message text normally (platform already produced HTML — just use contentHtml) */}
<div
style={{ color: "#e2e8f0", lineHeight: 1.7 }}
dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
/>
{/* Mode-switching buttons — only on the last message */}
{isLastMsg && (
<div style={{
display: "flex",
gap: "8px",
marginTop: "16px",
flexWrap: "wrap",
}}>
{modes.map((mode) => {
const isActive = currentMode === mode.id;
return (
<button
key={mode.id}
onClick={() => {
if (!isActive) {
api.executeAction(mode.actionId);
}
}}
style={{
padding: "8px 16px",
background: isActive ? mode.activeBg : "transparent",
border: `2px solid ${isActive ? mode.activeBorder : mode.border}`,
borderRadius: "8px",
color: isActive ? mode.activeColor : mode.color,
fontSize: "13px",
fontWeight: isActive ? "700" : "500",
cursor: isActive ? "default" : "pointer",
opacity: isActive ? 1 : 0.7,
transition: "all 0.2s ease",
}}
>
{isActive ? "● " : ""}{mode.label}
</button>
);
})}
</div>
)}
</div>
);
}} />
);
}行ごとの解説:
api.variables.current_mode— 現在のモード変数の値を読み取るmodes— 各モードのID、表示ラベル、対応するビヘイビアアクションID、カラー設定を定義する配列isActive— 現在のモードがこのボタンのモードと一致するかチェック。一致すればボタンがハイライト、そうでなければグレーアウトされ半透明api.executeAction(mode.actionId)— ステップ2で作成したビヘイビアをトリガー。!isActiveのときのみ発火することに注意 — すでにこのモードにいる場合、クリックしても何も起きない"● "— アクティブボタンには視覚的インジケータとして小さなドットプレフィックスtransition: "all 0.2s ease"— ボタン状態変化時のスムーズなアニメーション
自分でコードを書きたくない?Studio AIを使おう
エディタ上部 → 「Enter Studio」をクリック → AI Assistantパネル → 平易な言葉で説明すると、AIがコードを生成します。
ステップ5:保存してテスト
- エディタ上部のSaveをクリック
- Start Gameをクリックするか、ホームページに戻って新規セッションを開始
- AIと数ターン普通にチャット — ノーマルモードにいる
- Comedyボタンをクリック — ボタンが金色にハイライト、通知が「Switched to Comedy Mode」と表示
- メッセージを送信 — AIの返信がユーモラスで誇張的になり、第四の壁を壊すかもしれない
- Horrorボタンをクリック — ボタンが赤くハイライト
- 別のメッセージを送信 — AIの返信が暗く緊張感に満ち、不気味なヒントで満たされる
- Normalボタンをクリック — デフォルトスタイルに戻る
- もう一つメッセージを送信 — AIが通常のナレーションに戻ったことを確認
うまく動かない場合:
| 症状 | 想定される原因 | 対処 |
|---|---|---|
| モードボタンが見えない | Root Componentのコードが保存されていない、または構文エラー | Custom UIパネル下部のコンパイル状態を確認 — 緑の「OK」が表示されるべき |
| ボタンをクリックしても何も起きない | ビヘイビアアクションIDがコードと一致しない | ビヘイビアアクションIDがmode-comedy、mode-horror、mode-normalで、コード内のexecuteAction()パラメータと一致することを確認 |
| ボタン状態が変わらない | ビヘイビアが変数を更新していない | 各ビヘイビアの「Modify Variable」アクションがcurrent_modeを正しく設定しているか確認 |
| 切り替え後AIのスタイルが変わらない | ディレクティブ内容が空、または位置が間違っている | 「Tell AI」アクションにディレクティブ内容が記入され、位置が「After Character」に設定されていることを確認 |
| Normalに戻ってもスタイルが残る | 「Stop Telling AI」のディレクティブIDが一致しない | 3つのビヘイビアすべてが同じディレクティブID personality-override を使っていることを確認 |
上級者の使い方
モードを追加
「Poetic Mode」を追加したい?やることは:
modes配列にエントリを追加(ID、ラベル、カラー)- アクションID
mode-poeticの新しいビヘイビアを作成、comedy/horrorと同じアクションパターン(古いディレクティブを削除 → 新しいディレクティブを注入 → 変数を更新 → 通知) - 完了。Root Componentに自動的にボタンが表示される
ターン制限ディレクティブによる一時的な「パーソナリティバースト」
「Drunk Button」が欲しいとします — クリックするとAIが3ターン酔っ払って話し、その後自動的に戻る:
「Tell AI」アクションでターン期間を3に設定。ディレクティブは3ターン後に自動失効 — プレイヤーがキャンセルのために再クリックする必要なし。
言語切り替え
同じパターンでAIの返信言語を切り替えられます。ディレクティブ内容を「これ以降すべて英語で返信」または「これ以降日本語で返信」に変更すれば、言語スイッチャーの完成です。
クイックリファレンス
| やりたいこと | 方法 |
|---|---|
| AIのシステムプロンプトを動的に変更 | ビヘイビアアクション内で「Tell AI」(inject-directive)を使用 — ディレクティブID、内容、位置を記入 |
| 以前注入したディレクティブを削除 | 「Stop Telling AI」(remove-directive)を使用 — 削除するディレクティブIDを記入 |
| Nターン後にディレクティブを自動失効 | 「Tell AI」でターン期間を設定 |
| ディレクティブを永続的に保持(手動削除まで) | 「Tell AI」でターン期間を設定しない(デフォルト動作) |
| キャラクター定義の後にスタイルディレクティブを配置 | 位置を「After Character」(after_char)に設定 |
| 緊急のルールオーバーライドを最上部に配置 | 位置を「Top」(top)に設定 |
| 最終リマインダーを末尾に配置 | 位置を「Bottom」(bottom)に設定 |
| 切り替え前に古いディレクティブを削除 | 同じディレクティブIDを使用 — まず「Stop Telling AI」、次に「Tell AI」 |
| アクティブボタンをハイライト | Root Component内で変数を読み取り、条件スタイル(isActive)でハイライトを制御 |
自分で試す — インポート可能なデモワールド
このJSONをダウンロードし、新規ワールドとしてインポートしてすべての動作を確認してください:
インポート方法:
- Yumina → My Worlds → Create New World に移動
- エディタで More Actions → Import Package をクリック
- ダウンロードした
.jsonファイルを選択 - 全変数、ビヘイビア、Root Componentが事前設定された新規ワールドが作成されます
- 新規セッションを開始して試す
含まれるもの:
- 1つの変数(
current_modeがアクティブなパーソナリティモードを追跡) - 3つのビヘイビア(Comedyに切り替え / Horrorに切り替え / Normalに戻す)
- Root Component(3つのモード切り替えボタンとハイライトインジケータ)
これはRecipe #11です
このレシピは「Tell AI」/「Stop Telling AI」のコア使用法を示しています — システムプロンプト内でディレクティブを動的に注入・削除します。同じパターンを言語切り替え、難易度調整、ナラティブ視点切り替え(一人称/三人称)、または「AIパーソナリティの段階的ドリフト」(数ターンごとに強度の異なるディレクティブを自動注入)にも使えます。
