动态 AI 人格切换
做几个按钮,一键切换 AI 的人格、说话风格或语言。使用「告诉 AI」和「停止告诉 AI」来动态修改 AI 的系统提示词 -- 不需要重启会话,在对话中途无缝切换。
你将构建什么
一个嵌入聊天中的人格切换器:
- 三种模式 -- 普通叙事、喜剧模式、恐怖模式
- 一键切换 -- 点击按钮即可改变 AI 的说话风格,立即生效
- 视觉反馈 -- 当前激活的模式按钮会高亮显示,玩家始终知道当前处于什么模式
- 无缝过渡 -- 切换不会中断对话;AI 的下一条回复就会使用新风格
工作原理
Yumina 的行为系统有两个强大的动作:「告诉 AI」和「停止告诉 AI」。
- 告诉 AI(
inject-directive)-- 向 AI 的系统提示词中注入一条指令。只要指令存在,AI 在每次回复时都能看到并遵循它。你可以指定它在提示词中的位置、是否永久存在,以及多少轮后自动过期。 - 停止告诉 AI(
remove-directive)-- 通过 ID 移除之前注入的指令。一旦移除,AI 就不再看到该指令。
将这两个动作组合使用,我们可以这样做:
玩家点击「喜剧模式」按钮
→ 行为触发:先移除旧的人格指令(如果有的话)
→ 然后注入新的喜剧风格指令
→ AI 的系统提示词中现在包含:「用幽默、有趣的语气叙述一切……」
→ AI 的下一条回复转变为喜剧风格这和知识条目有什么区别? 知识条目(启用条目/禁用条目)非常适合大段的世界观文本。通过「告诉 AI」注入的指令更轻量、更灵活 -- 它们不是条目,而是直接插入系统提示词中的小片段文本。非常适合简短的风格指令、临时规则或一次性提示。你可以同时使用两者。
逐步操作
第 1 步:创建变量
我们需要一个变量来跟踪当前激活的模式。Root Component 读取它来决定高亮哪个按钮。
编辑器 → 侧边栏 → 变量 选项卡 → 点击「添加变量」
| 字段 | 值 | 原因 |
|---|---|---|
| 显示名称 | Current Mode | 供你自己参考 |
| ID | current_mode | 行为和 Root Component 通过此 ID 读写 |
| 类型 | String | 因为值是文本("normal"、"comedy"、"horror") |
| 默认值 | normal | 新会话从普通模式开始 |
| 分类 | Custom | 人格系统专用分类 |
| 行为规则 | Do not modify this variable. It is controlled by the player's UI buttons. | 告诉 AI 不要自行修改 -- 只有玩家按钮可以 |
第 2 步:创建行为
我们需要 3 个行为 -- 每种模式一个。每个行为的逻辑是:移除旧指令 → 注入新指令 → 更新变量 → 通知玩家。
编辑器 → 行为 选项卡 → 逐个添加行为
行为 1:切换到喜剧模式
WHEN(触发器):
| 字段 | 值 | 原因 |
|---|---|---|
| 触发类型 | Action | 当代码调用 executeAction("mode-comedy") 时触发 |
| Action ID | mode-comedy | Root Component 中的按钮调用此 ID |
DO(动作):
按顺序添加以下动作:
| # | 动作类型 | 设置 | 用途 |
|---|---|---|---|
| 1 | 停止告诉 AI | 指令 ID:personality-override | 移除之前的人格指令(如果有的话)。如果不存在,什么都不会发生 -- 不会报错 |
| 2 | 告诉 AI | 指令 ID:personality-override,内容见下方,位置:角色定义之后 | 注入喜剧风格指令 |
| 3 | 修改变量 | current_mode 设为 comedy | 更新变量,让 Root Component 知道当前模式 |
| 4 | 显示通知 | 消息:Switched to Comedy Mode,样式:info | 给玩家视觉反馈 |
「告诉 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.为什么在「告诉 AI」之前要「停止告诉 AI」? 因为两个指令使用相同的 ID(
personality-override)。如果玩家从恐怖模式切换到喜剧模式,不先移除旧指令的话,需要依赖injectDirective自动替换同一 ID -- 它确实会这样做 -- 但显式地先移除再注入是更好的习惯。逻辑更清晰,也避免了潜在的边界情况。
行为 2:切换到恐怖模式
WHEN:
| 字段 | 值 |
|---|---|
| 触发类型 | Action |
| Action ID | mode-horror |
DO:
| # | 动作类型 | 设置 | 用途 |
|---|---|---|---|
| 1 | 停止告诉 AI | 指令 ID:personality-override | 移除旧的人格指令 |
| 2 | 告诉 AI | 指令 ID:personality-override,内容见下方,位置:角色定义之后 | 注入恐怖风格指令 |
| 3 | 修改变量 | current_mode 设为 horror | 更新变量 |
| 4 | 显示通知 | 消息:Switched to Horror Mode,样式:danger | 使用 danger 样式的通知 -- 红色符合恐怖氛围 |
「告诉 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:切换回普通模式
WHEN:
| 字段 | 值 |
|---|---|
| 触发类型 | Action |
| Action ID | mode-normal |
DO:
| # | 动作类型 | 设置 | 用途 |
|---|---|---|---|
| 1 | 停止告诉 AI | 指令 ID:personality-override | 移除自定义人格指令。移除后,系统提示词中不再有风格覆盖 -- AI 回退到默认叙事风格 |
| 2 | 修改变量 | current_mode 设为 normal | 更新变量 |
| 3 | 显示通知 | 消息:Restored Normal Mode,样式:info | 反馈 |
注意: 普通模式不注入任何指令。只要移除之前的覆盖就够了 -- AI 会恢复到你在角色条目和系统指令中定义的默认风格。
第 3 步:理解指令位置和持久性
配置「告诉 AI」动作时,你会看到两个重要设置:位置 和 持久性/轮次时长。以下是它们的含义。
指令位置
位置控制注入的指令在系统提示词中出现的位置。
| 位置 | 标签 | 描述 | 何时使用 |
|---|---|---|---|
auto | 自动 | 引擎选择最佳位置(通常在角色定义之后) | 大多数情况下足够用 |
top | 顶部 | 系统提示词的最开头,最高优先级 | 紧急全局规则(例如「从现在开始只用英文回复」) |
before_char | 角色定义之前 | 在角色定义之前 | 影响 AI 如何解读角色的全局设置 |
after_char | 角色定义之后 | 在角色定义之后 | 风格指令、语气调整(本食谱使用这个) |
bottom | 底部 | 系统提示词的最末尾 | 最后提醒、「越狱」式指令 |
depth | 深度 | 按深度插入(在倒数第 N 条最近消息之前) | 需要出现在对话中间而非系统提示词中的指令 |
为什么本食谱使用「角色定义之后」? 因为人格切换指令是叙事风格的覆盖。放在角色定义之后,AI 先读到「我是谁」(角色),然后读到「我应该怎么说话」(风格指令)。顺序自然,效果最好。
持久性 vs. 临时指令
| 设置 | 描述 | 用途 |
|---|---|---|
| 持久(默认) | 指令一直留在系统提示词中,直到被「停止告诉 AI」显式移除 | 本食谱使用此设置 -- 模式保持激活,直到玩家再次切换 |
| 临时(设置轮次时长) | 指令在指定轮次数后自动过期 | 适合一次性效果,例如「接下来 3 轮,角色喝醉了说话含糊不清」 |
示例: 如果你在「告诉 AI」动作中将轮次时长设为 3,指令会在注入后第 3 轮结束时自动消失 -- 不需要手动移除。
第 4 步:在 Root Component 中添加模式切换按钮
在最后一条消息下方显示三个模式按钮。当前激活的模式按钮会高亮显示。
编辑器 → 自定义 UI 部分 → 打开 index.tsx → 粘贴以下代码(替换默认的 return <Chat />):
export default function MyWorld() {
const api = useYumina();
// ---- 读取当前模式 ----
const currentMode = String(api.variables.current_mode || "normal");
// ---- 三种模式配置 ----
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",
},
];
// ---- 消息列表,用于找到最后一条 ----
const msgs = api.messages || [];
return (
<Chat renderBubble={(msg) => {
const isLastMsg = msg.messageIndex === msgs.length - 1;
return (
<div>
{/* 正常渲染消息文本(平台已将 Markdown 转为 HTML -- 直接使用 contentHtml) */}
<div
style={{ color: "#e2e8f0", lineHeight: 1.7 }}
dangerouslySetInnerHTML={{ __html: msg.contentHtml }}
/>
{/* 模式切换按钮 -- 仅在最后一条消息上 */}
{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、显示标签、对应的行为 action ID 和颜色配置isActive-- 检查当前模式是否匹配此按钮的模式。如果匹配,按钮高亮显示;否则灰色半透明api.executeAction(mode.actionId)-- 触发我们在第 2 步创建的行为。注意只有在!isActive时才触发 -- 如果你已经在此模式中,点击什么都不做"● "-- 激活的按钮获得一个小圆点前缀作为视觉指示transition: "all 0.2s ease"-- 按钮状态变化时的平滑动画
不想自己写代码?使用 Studio AI
编辑器顶部 → 点击「进入 Studio」→ AI 助手面板 → 用自然语言描述你想要什么,AI 会为你生成代码。
第 5 步:保存并测试
- 点击编辑器顶部的 保存
- 点击 开始游戏 或返回主页开始新会话
- 正常与 AI 聊几轮 -- 你处于普通模式
- 点击 Comedy 按钮 -- 按钮高亮为金色,通知显示「Switched to Comedy Mode」
- 发送一条消息 -- AI 的回复应该变得幽默、夸张,可能还会打破第四面墙
- 点击 Horror 按钮 -- 按钮高亮为红色
- 再发送一条消息 -- AI 的回复变得阴暗、紧张,充满不安的暗示
- 点击 Normal 按钮 -- 回到默认风格
- 再发一条消息 -- 确认 AI 已恢复正常叙事
如果出了问题:
| 症状 | 可能原因 | 修复方法 |
|---|---|---|
| 看不到模式按钮 | Root Component 代码没有保存或有语法错误 | 检查自定义 UI 面板底部的编译状态 -- 应该显示绿色「OK」 |
| 点击按钮没有反应 | 行为的 action ID 与代码不匹配 | 确认行为的 action ID 是 mode-comedy、mode-horror、mode-normal,与代码中 executeAction() 的参数一致 |
| 按钮状态没有变化 | 变量没有被行为更新 | 检查每个行为的「修改变量」动作是否正确设置了 current_mode |
| 切换后 AI 风格没有变化 | 指令内容为空或位置不对 | 检查「告诉 AI」动作中是否填写了指令内容,位置是否设为「角色定义之后」 |
| 切回普通模式后风格依然保留 | 「停止告诉 AI」的指令 ID 不匹配 | 确认所有三个行为都使用相同的指令 ID:personality-override |
进阶用法
添加更多模式
想添加一个「诗意模式」?只需:
- 在
modes数组中添加一个条目(ID、标签、颜色) - 创建一个新行为,action ID 为
mode-poetic,与喜剧/恐怖相同的动作模式(移除旧指令 → 注入新指令 → 更新变量 → 通知) - 完成。按钮会自动出现在 Root Component 中
使用轮次限制的临时「人格爆发」
假设你想要一个「醉酒按钮」-- 点击后 AI 以醉态说话 3 轮,然后自动恢复:
在「告诉 AI」动作中,将轮次时长设为 3。指令在 3 轮后自动过期 -- 不需要玩家再次点击取消。
语言切换
同样的模式也适用于切换 AI 的回复语言。将指令内容改为「From now on reply entirely in English」或「从现在开始用日语回复」,你就有了一个语言切换器。
快速参考
| 你想做什么 | 怎么做 |
|---|---|
| 动态修改 AI 的系统提示词 | 在行为动作中使用「告诉 AI」(inject-directive)-- 填写指令 ID、内容和位置 |
| 移除之前注入的指令 | 使用「停止告诉 AI」(remove-directive)-- 填写要移除的指令 ID |
| 让指令在 N 轮后自动过期 | 在「告诉 AI」中设置轮次时长 |
| 永久保留指令(直到手动移除) | 不设置轮次时长(默认行为) |
| 将风格指令放在角色定义之后 | 将位置设为「角色定义之后」(after_char) |
| 将紧急规则覆盖放在顶部 | 将位置设为「顶部」(top) |
| 将最后提醒放在末尾 | 将位置设为「底部」(bottom) |
| 切换前移除旧指令 | 使用相同的指令 ID -- 先「停止告诉 AI」,再「告诉 AI」 |
| 高亮显示激活的按钮 | 在 Root Component 中读取变量,使用条件样式(isActive)控制高亮 |
自己试试 -- 可导入的演示世界
下载此 JSON 并作为新世界导入,查看完整效果:
如何导入:
- 前往 Yumina → 我的世界 → 创建新世界
- 在编辑器中,点击 更多操作 → 导入包
- 选择下载的
.json文件 - 一个新世界会被创建,所有变量、行为和 Root Component 都已预配置
- 开始新会话并试用
包含内容:
- 1 个变量(
current_mode跟踪当前激活的人格模式) - 3 个行为(切换到喜剧 / 切换到恐怖 / 恢复普通)
- 一个 Root Component(三个带高亮指示的模式切换按钮)
这是食谱 #11
本食谱演示了「告诉 AI」/「停止告诉 AI」的核心用法 -- 动态地在系统提示词中注入和移除指令。同样的模式可以用于语言切换、难度调整、叙事视角切换(第一人称/第三人称),甚至「AI 人格渐变」(每隔几轮自动注入不同强度的指令)。
