Skip to content

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 NameCurrent Mode自分用の識別名
IDcurrent_modeビヘイビアとRoot ComponentがこのIDで読み書き
TypeString値がテキスト("normal""comedy""horror")だから
Default Valuenormal新規セッションはノーマルモードで開始
CategoryCustomパーソナリティシステム専用カテゴリ
Behavior RulesDo not modify this variable. It is controlled by the player's UI buttons.AIにこの変数を自分で変更しないよう伝える — プレイヤーボタンのみ可能

ステップ2:ビヘイビアの作成

3つのビヘイビアが必要です — モードごとに1つです。各ビヘイビアのロジックは:古いディレクティブを削除 → 新しいディレクティブを注入 → 変数を更新 → プレイヤーに通知

エディタ → Behaviors タブ → ビヘイビアを1つずつ追加

ビヘイビア1:Comedy Modeに切り替え

WHEN(トリガー):

フィールド理由
Trigger TypeActionコードがexecuteAction("mode-comedy")を呼ぶと発火
Action IDmode-comedyRoot Component内のボタンがこのIDを呼ぶ

DO(アクション):

以下のアクションを順番に追加:

#Action Type設定目的
1Stop Telling AIディレクティブID:personality-override以前のパーソナリティディレクティブを削除(あれば)。なければ何も起きない — エラーなし
2Tell AIディレクティブID:personality-override、内容は下記、ポジション:After Characterコメディスタイルのディレクティブを注入
3Modify Variablecurrent_modecomedyに設定変数を更新してRoot Componentに現在のモードを伝える
4Show 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 TypeAction
Action IDmode-horror

DO:

#Action Type設定目的
1Stop Telling AIディレクティブID:personality-override古いパーソナリティディレクティブを削除
2Tell AIディレクティブID:personality-override、内容は下記、ポジション:After Characterホラースタイルのディレクティブを注入
3Modify Variablecurrent_modehorrorに設定変数を更新
4Show Notificationメッセージ:Switched to Horror Mode、スタイル:dangerdangerスタイルの通知を使う — 赤がホラーの雰囲気に合う

「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 TypeAction
Action IDmode-normal

DO:

#Action Type設定目的
1Stop Telling AIディレクティブID:personality-overrideカスタムパーソナリティディレクティブを削除。削除されると、システムプロンプトにスタイルオーバーライドがなくなり — AIはデフォルトのナレーションスタイルに戻る
2Modify Variablecurrent_modenormalに設定変数を更新
3Show Notificationメッセージ:Restored Normal Mode、スタイル:infoフィードバック

注意: ノーマルモードはディレクティブを注入しません。以前のオーバーライドを削除するだけで十分 — AIはキャラクターエントリやシステム指示で定義されたデフォルトスタイルに戻ります。


ステップ3:ディレクティブの位置と永続性を理解する

「Tell AI」アクションを設定するとき、2つの重要な設定が見えます:位置永続性 / ターン期間です。それぞれの意味は次のとおりです。

ディレクティブの位置

位置は注入されたディレクティブがシステムプロンプトのどこに表示されるかを制御します。

位置ラベル説明使い時
autoAutoエンジンが最適な場所を選択(通常はキャラクター定義の後)ほとんどのケースで十分
topTopシステムプロンプトの最初、最高優先度緊急のグローバルルール(例:「これ以降、英語のみで返信」)
before_charBefore Characterキャラクター定義の前AIがキャラクターを解釈する方法に影響するグローバル設定
after_charAfter Characterキャラクター定義の後スタイルディレクティブ、トーン調整(このレシピで使用)
bottomBottomシステムプロンプトの最後最終リマインダー、「ジェイルブレイク」スタイルの指示
depthDepth深さ別に挿入(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 />を置き換え):

tsx
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:保存してテスト

  1. エディタ上部のSaveをクリック
  2. Start Gameをクリックするか、ホームページに戻って新規セッションを開始
  3. AIと数ターン普通にチャット — ノーマルモードにいる
  4. Comedyボタンをクリック — ボタンが金色にハイライト、通知が「Switched to Comedy Mode」と表示
  5. メッセージを送信 — AIの返信がユーモラスで誇張的になり、第四の壁を壊すかもしれない
  6. Horrorボタンをクリック — ボタンが赤くハイライト
  7. 別のメッセージを送信 — AIの返信が暗く緊張感に満ち、不気味なヒントで満たされる
  8. Normalボタンをクリック — デフォルトスタイルに戻る
  9. もう一つメッセージを送信 — AIが通常のナレーションに戻ったことを確認

うまく動かない場合:

症状想定される原因対処
モードボタンが見えないRoot Componentのコードが保存されていない、または構文エラーCustom UIパネル下部のコンパイル状態を確認 — 緑の「OK」が表示されるべき
ボタンをクリックしても何も起きないビヘイビアアクションIDがコードと一致しないビヘイビアアクションIDがmode-comedymode-horrormode-normalで、コード内のexecuteAction()パラメータと一致することを確認
ボタン状態が変わらないビヘイビアが変数を更新していない各ビヘイビアの「Modify Variable」アクションがcurrent_modeを正しく設定しているか確認
切り替え後AIのスタイルが変わらないディレクティブ内容が空、または位置が間違っている「Tell AI」アクションにディレクティブ内容が記入され、位置が「After Character」に設定されていることを確認
Normalに戻ってもスタイルが残る「Stop Telling AI」のディレクティブIDが一致しない3つのビヘイビアすべてが同じディレクティブID personality-override を使っていることを確認

上級者の使い方

モードを追加

「Poetic Mode」を追加したい?やることは:

  1. modes配列にエントリを追加(ID、ラベル、カラー)
  2. アクションID mode-poetic の新しいビヘイビアを作成、comedy/horrorと同じアクションパターン(古いディレクティブを削除 → 新しいディレクティブを注入 → 変数を更新 → 通知)
  3. 完了。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をダウンロードし、新規ワールドとしてインポートしてすべての動作を確認してください:

recipe-11-demo.json

インポート方法:

  1. Yumina → My WorldsCreate New World に移動
  2. エディタで More ActionsImport Package をクリック
  3. ダウンロードした.jsonファイルを選択
  4. 全変数、ビヘイビア、Root Componentが事前設定された新規ワールドが作成されます
  5. 新規セッションを開始して試す

含まれるもの:

  • 1つの変数(current_modeがアクティブなパーソナリティモードを追跡)
  • 3つのビヘイビア(Comedyに切り替え / Horrorに切り替え / Normalに戻す)
  • Root Component(3つのモード切り替えボタンとハイライトインジケータ)

これはRecipe #11です

このレシピは「Tell AI」/「Stop Telling AI」のコア使用法を示しています — システムプロンプト内でディレクティブを動的に注入・削除します。同じパターンを言語切り替え、難易度調整、ナラティブ視点切り替え(一人称/三人称)、または「AIパーソナリティの段階的ドリフト」(数ターンごとに強度の異なるディレクティブを自動注入)にも使えます。