Skip to content

动态 AI 人格切换

做几个按钮,点一下就能切换 AI 的人格、说话风格或语言。用「告诉 AI」和「停止告诉 AI」动态改变 AI 的系统提示词——不需要重新开会话,对话中途就能无缝切换。


你要做的东西

一个嵌入在聊天里的人格切换器:

  • 三种模式——普通叙述者、喜剧模式、恐怖模式
  • 一键切换——点按钮就能切换 AI 的说话风格,立刻生效
  • 视觉反馈——当前激活的模式按钮高亮显示,玩家一眼就知道现在是什么模式
  • 无缝过渡——切换时不会中断对话,AI 的下一段回复就会用新风格

原理

Yumina 的行为系统有两个强大的动作:「告诉 AI」和「停止告诉 AI」。

  • 告诉 AIinject-directive)—— 往 AI 的系统提示词里注入一段指令。只要这段指令还在,AI 每次回复时都会看到它、遵守它。你可以指定它出现在提示词的哪个位置、是否永久保留、多少回合后自动消失。
  • 停止告诉 AIremove-directive)—— 通过指令 ID 把之前注入的指令移除。一旦移除,AI 就不再看到那段指令了。

用这两个动作配合,我们可以做到:

玩家点击「喜剧模式」按钮
  → 行为触发:先移除旧的人格指令(如果有)
  → 再注入新的喜剧风格指令
  → AI 的系统提示词里多了一段:"用幽默搞笑的口吻叙述..."
  → 下一次 AI 回复就会变成搞笑风格

和知识条目的区别是什么? 知识条目(启用知识条目/禁用知识条目)适合大段的世界观设定。而「告诉 AI」注入的指令更轻量、更灵活——它不是条目,而是直接插在系统提示词里的一小段文字。适合短小的风格指令、临时规则、一次性提示。两者可以配合使用。


一步步来

第 1 步:创建变量

我们需要一个变量来追踪当前激活的模式。消息渲染器会读它来决定哪个按钮高亮。

编辑器 → 左侧边栏 → 变量 标签页 → 点击「添加变量」

字段填什么为什么这样填
显示名称当前模式给你自己看的,方便识别
IDcurrent_mode行为和消息渲染器用这个 ID 来读写
类型字符串因为值是文字("normal""comedy""horror"
默认值normal新会话从普通模式开始
分类自定义人格系统专用分类
行为规则不要修改这个变量。它由玩家的 UI 按钮控制。告诉 AI 不要自己改这个变量——只有玩家按钮能改

第 2 步:创建行为

我们需要 3 条行为——每种模式一条。每条行为的逻辑是:先移除旧指令 → 注入新指令 → 更新变量 → 通知玩家

编辑器 → 行为 标签页 → 逐个添加行为

行为 1:切换到喜剧模式

WHEN(什么时候触发):

字段填什么为什么这样填
触发器类型动作当代码调用 executeAction("mode-comedy") 时触发
动作 IDmode-comedy消息渲染器里的按钮会调用这个 ID

DO(做什么):

按顺序添加以下动作:

#动作类型设置作用
1停止告诉 AI指令 ID:personality-override先移除之前注入的人格指令(如果有的话)。如果没有,什么也不会发生,不会报错
2告诉 AI指令 ID:personality-override,内容见下方,位置:角色之后注入喜剧风格指令
3修改变量current_mode 设为 comedy更新变量,让渲染器知道当前模式
4显示通知消息:已切换到喜剧模式,样式:info给玩家一个视觉反馈

「告诉 AI」的指令内容:

[叙述风格:喜剧模式]
从现在开始,用幽默搞笑的口吻叙述一切。你可以:
- 加入夸张的比喻和荒诞的类比
- 偶尔打破第四面墙,对读者说悄悄话
- 让NPC说出不合时宜但好笑的台词
- 用轻松的语气描述严肃的场景,制造反差幽默
保持故事推进,不要只是讲笑话。幽默应该融入叙述,而不是取代叙述。

为什么先「停止告诉 AI」再「告诉 AI」? 因为两条指令用的是同一个 ID(personality-override)。如果玩家从恐怖模式切换到喜剧模式,不先移除旧指令的话,injectDirective 虽然会自动替换同 ID 的旧指令,但显式地先移除再注入是个好习惯——逻辑更清晰,也避免了在某些边界情况下的潜在问题。


行为 2:切换到恐怖模式

WHEN:

字段填什么
触发器类型动作
动作 IDmode-horror

DO:

#动作类型设置作用
1停止告诉 AI指令 ID:personality-override移除旧的人格指令
2告诉 AI指令 ID:personality-override,内容见下方,位置:角色之后注入恐怖风格指令
3修改变量current_mode 设为 horror更新变量
4显示通知消息:已切换到恐怖模式,样式:danger用 danger 样式的通知,红色更有恐怖感

「告诉 AI」的指令内容:

[叙述风格:恐怖模式]
从现在开始,用阴暗不安的氛围叙述一切。你应该:
- 用缓慢、压迫性的节奏描写场景,注重感官细节(声音、气味、触感)
- 暗示有什么东西在暗处注视着角色,但不要直接揭露
- 让环境本身散发出不对劲的感觉——门自己关上、影子移动的方向不对、镜子里的倒影慢了半拍
- NPC的对话带着微妙的违和感,好像他们知道什么不该知道的事
- 偶尔用第二人称直接描写角色的生理反应(后颈汗毛竖起、心跳加速、瞳孔放大)
营造持续的紧张感,但不要每段都跳出怪物。真正的恐怖在于未知。

行为 3:切换回普通模式

WHEN:

字段填什么
触发器类型动作
动作 IDmode-normal

DO:

#动作类型设置作用
1停止告诉 AI指令 ID:personality-override移除自定义人格指令。移除后 AI 的提示词里不再有风格覆盖,回到默认叙述风格
2修改变量current_mode 设为 normal更新变量
3显示通知消息:已恢复普通模式,样式:info反馈

注意: 普通模式不需要注入任何指令。只需要移除之前的覆盖指令,AI 就会回到你在角色条目和系统指令里定义的默认风格。


第 3 步:理解指令位置和持久性

在配置「告诉 AI」动作时,你会看到两个重要的设置:位置持久性/持续回合数。这里详细解释一下。

指令位置

位置决定了注入的指令出现在系统提示词的哪个部分。

位置中文标签说明适合什么场景
auto自动引擎自动选择最佳位置(通常放在角色设定之后)大多数情况下用这个就够了
top顶部放在系统提示词的最开头,优先级最高紧急的全局规则(如"从现在开始只用英语回复")
before_char角色之前放在角色设定之前影响 AI 如何理解角色的全局设定
after_char角色之后放在角色设定之后风格指令、语气调整(本配方使用这个)
bottom底部放在系统提示词的最末尾最后关头的提醒、"jailbreak"式指令
depth深度按深度插入(在最近的第 N 条消息之前)需要出现在对话中间而非系统提示词里的指令

本配方为什么用「角色之后」? 因为人格切换指令是对叙述风格的覆盖。放在角色设定之后,AI 会先读到"我是谁"(角色设定),然后读到"我该怎么说话"(风格指令)。顺序自然,效果最好。

持久性 vs 临时指令

设置说明用法
持久(默认)指令一直保留在系统提示词里,直到被「停止告诉 AI」显式移除本配方用这个——模式切换后一直生效,直到玩家再次切换
临时(设置持续回合数)指令在指定回合数后自动消失适合一次性的效果,比如"接下来 3 回合里,角色处于醉酒状态,说话含糊不清"

举例: 如果你在「告诉 AI」里设置持续回合数为 3,那这段指令会在注入后的第 3 个回合结束时自动消失,不需要手动移除。


第 4 步:做模式切换消息渲染器

在聊天界面最后一条消息下方显示三个模式按钮,当前激活的模式按钮高亮。

编辑器 → 消息渲染器 标签页 → 选「自定义 TSX」→ 粘贴以下代码:

tsx
export default function Renderer({ content, renderMarkdown, messageIndex }) {
  const api = useYumina();

  // ---- 读取当前模式 ----
  const currentMode = String(api.variables.current_mode || "normal");

  // ---- 三种模式的配置 ----
  const modes = [
    {
      id: "normal",
      label: "普通叙述",
      actionId: "mode-normal",
      color: "#94a3b8",
      activeColor: "#e2e8f0",
      activeBg: "rgba(148,163,184,0.2)",
      border: "#475569",
      activeBorder: "#94a3b8",
    },
    {
      id: "comedy",
      label: "喜剧模式",
      actionId: "mode-comedy",
      color: "#fbbf24",
      activeColor: "#fef3c7",
      activeBg: "rgba(251,191,36,0.2)",
      border: "#a16207",
      activeBorder: "#fbbf24",
    },
    {
      id: "horror",
      label: "恐怖模式",
      actionId: "mode-horror",
      color: "#f87171",
      activeColor: "#fecaca",
      activeBg: "rgba(248,113,113,0.2)",
      border: "#991b1b",
      activeBorder: "#f87171",
    },
  ];

  // ---- 判断是否是最后一条消息 ----
  const msgs = api.messages || [];
  const isLastMsg = messageIndex === msgs.length - 1;

  return (
    <div>
      {/* 正常渲染消息文字 */}
      <div
        style={{ color: "#e2e8f0", lineHeight: 1.7 }}
        dangerouslySetInnerHTML={{ __html: renderMarkdown(content) }}
      />

      {/* 模式切换按钮——只在最后一条消息上显示 */}
      {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" — 让按钮状态变化有平滑过渡动画

不想自己写代码?用工作室 AI

编辑器顶部 → 点击「进入工作室」→ AI 助手面板 → 用中文描述你想要什么,AI 会帮你生成代码。


第 5 步:保存并测试

  1. 点击编辑器顶部的「保存」
  2. 点击「开始游戏」或回到首页开一个新会话
  3. 正常和 AI 对话几回合,此时是普通叙述模式
  4. 点击「喜剧模式」按钮——按钮高亮变为金色,弹出通知「已切换到喜剧模式」
  5. 发一条消息——AI 的回复应该变得幽默、夸张、可能会打破第四面墙
  6. 点击「恐怖模式」按钮——按钮变为红色高亮
  7. 再发一条消息——AI 的回复变得阴暗、紧张、充满不安的暗示
  8. 点击「普通叙述」按钮——回到默认风格
  9. 再发一条消息——确认 AI 恢复了正常的叙述风格

如果遇到问题:

现象可能的原因解决方法
看不到模式按钮消息渲染器代码没保存或有语法错误检查消息渲染器底部的编译状态,应该显示绿色「OK」
点按钮没反应行为的动作 ID 和代码里的不匹配确认行为的动作 ID 是 mode-comedymode-horrormode-normal,和代码中 executeAction() 的参数一致
按钮状态不变化变量没有被行为正确修改检查每条行为的「修改变量」动作是否正确设置了 current_mode 的值
切换后 AI 风格没变指令内容为空或位置不对检查「告诉 AI」动作的指令内容是否填写了,位置建议选「角色之后」
切回普通模式后风格还是之前的「停止告诉 AI」的指令 ID 不匹配确认三条行为里的指令 ID 都是同一个:personality-override

进阶用法

添加更多模式

想加一个「诗意模式」?只需要:

  1. modes 数组里加一项(ID、标签、颜色)
  2. 创建一条新行为,动作 ID 是 mode-poetic,动作和喜剧/恐怖模式一样(移除旧指令 → 注入新指令 → 改变量 → 通知)
  3. 完成。按钮会自动出现在渲染器里

用临时指令做"一次性人格爆发"

假设你想做一个「醉酒按钮」——点击后 AI 用醉酒的方式说话 3 回合,然后自动恢复:

在「告诉 AI」动作里,把持续回合数设为 3。3 回合后指令自动消失,不需要玩家再点一次按钮来取消。

语言切换

同样的模式可以用来切换 AI 的回复语言。指令内容改成"从现在开始全部用英语回复"或"从现在开始用日语回复",就能做出一个语言切换器。


速查表

你想做的事怎么做
动态修改 AI 的系统提示词行为动作用「告诉 AI」(inject-directive),填指令 ID、内容和位置
移除之前注入的指令行为动作用「停止告诉 AI」(remove-directive),填要移除的指令 ID
让指令在 N 回合后自动消失「告诉 AI」里设置持续回合数
让指令永久保留(直到手动移除)「告诉 AI」里不设置持续回合数(默认行为)
风格指令放在角色设定之后位置选「角色之后」(after_char)
紧急规则覆盖放在最前面位置选「顶部」(top)
最后一刻提醒放在最后面位置选「底部」(bottom)
切换前移除旧指令用同一个指令 ID,先「停止告诉 AI」再「告诉 AI」
按钮高亮显示当前状态消息渲染器里读变量,用条件样式(isActive)控制高亮

直接试试——可导入的示例世界

下载这个 JSON 文件,导入即可体验完整效果:

recipe-11-demo-zh.json

导入方法:

  1. 进入 Yumina → 我的世界 → 创建新世界
  2. 在编辑器顶部点「更多操作」→「导入包」
  3. 选择下载的 .json 文件
  4. 世界会被创建,所有变量、行为和渲染器都已预配置好
  5. 开一个新会话试试看

包含内容:

  • 1 个变量(current_mode 追踪当前人格模式)
  • 3 条行为(切换喜剧模式 / 切换恐怖模式 / 恢复普通模式)
  • 一个消息渲染器(三个模式切换按钮,带高亮指示)

这是实战配方 #11

这个配方展示了「告诉 AI」/「停止告诉 AI」的核心用法——动态注入和移除系统提示词里的指令。同样的模式可以用来做语言切换、难度调节、叙事视角切换(第一人称/第三人称)、甚至"AI 性格渐变"(每隔几回合自动注入不同强度的指令)。