Skip to content

动态切换 AI 的叙事风格 ​

几个按钮,切换 AI 讲故事的风格:普通、喜剧、恐怖。玩家点一下,AI 的下一条回复就换成那种风格。风格的说明写在设定里,只有变量对上的时候才发给 AI,所以不用写代码。


你要做的东西 ​

  • 三种模式:普通、喜剧、恐怖
  • 点一下就切换:下一条回复用新风格,对话照常接着往下走
  • 看得到当前模式:玩家知道现在开的是哪一种
  • 切回去很干净:模式一变,旧风格的说明就不再发给 AI

怎么运作 ​

玩家点了「喜剧」
  → 按钮把变量 Mode 设成 "comedy"
  → 设定「喜剧风格」只在 Mode = comedy 时发给 AI,所以它进来了
  → 设定「恐怖风格」只在 Mode = horror 时发,所以它不在
  → AI 的下一条回复用喜剧风格来写

每种风格都是一条普通的设定,只是挂了一个条件。不用手动关掉哪一条,因为同一时间只会有一个条件成立。


一步一步来 ​

第 1 步:建变量 ​

点卡底下 还可以加 里的 + 变量。

字段值
名称Mode
类型文字
初始值normal

展开 AI 怎么用它,把 AI 权限 设成 AI 只读。模式由玩家的按钮来改,AI 只负责照着写。这是引擎强制的,行为规则里就不用再写「别动这个」。

想在聊天里显示当前模式,点 放到玩家界面上。


第 2 步:每种风格写一条设定 ​

加两条设定(角色和世界 右上角的 +)。

设定:喜剧风格 ​

[叙事风格:喜剧]
用幽默、搞笑的语气讲述一切。你可以:
- 用夸张的比喻和荒诞的类比
- 偶尔打破第四面墙,跟读者说句旁白
- 让 NPC 说些不合时宜的台词
- 用轻松的口吻描写严肃的场面,制造反差
故事要继续往前推。幽默放在叙述里,不能代替叙述。

设定:恐怖风格 ​

[叙事风格:恐怖]
用阴暗、让人不安的氛围讲述一切。你应该:
- 描写场景时放慢节奏,多写声音、气味和触感
- 暗示有什么东西在暗处看着,但永远不把它露出来
- 让周围的环境透着不对劲:自己关上的门、往反方向移动的影子、慢了半拍的倒影
- 让 NPC 说的话有点怪,好像知道一些他们不该知道的事
- 有时用第二人称写角色身体的反应:脖子后面的汗毛竖起来,心跳加快
紧张感要一直往上叠,但别每段都放一个怪物。最可怕的部分留给未知。

普通模式不用写设定。两条风格设定都不在的时候,AI 就按你的角色和世界设定本来的写法来写。

给每条设定挂上条件 ​

把 Mode 变量拖到「喜剧风格」这条设定上。它们之间会出现一根线,点这根线,把条件设成 Mode 等于 comedy。「恐怖风格」也一样,填 horror。

这两条设定会挪进 条件设定 那一块:只有条件成立的时候才发。(也可以打开设定,点最下面的 什么时候发给 AI 来设。)

风格说明放在哪

设定会和你的其它设定一起放进 AI 的提示里。如果 AI 老是滑回原来的写法,就打开这条设定,把 注入到 设成 放在最后:这段文字会排在整段对话之后、AI 回复之前,那里是 AI 最上心的位置。


第 3 步:加按钮 ​

点顶栏中间的 玩家界面。在对话页上加三个 按钮(加一个部件 → 按钮),分别写上普通、喜剧、恐怖。在 按下时,依次做 里,每个按钮都排这两步:

  1. 改变量:Mode,设为,normal / comedy / horror
  2. 弹出提示:已切换到喜剧模式(换成对应的模式)

想让当前的那个按钮亮起来:把按钮复制一份,给复制出来的换个样子,再用 什么时候显示 → 变量满足条件时,让它只在 Mode 等于那个值的时候出现。普通样子的那个叠在它上面,条件反过来(Mode 不等于 那个值)。

关掉 编辑,点点这些按钮。这里的预览不会调用 AI,但能看到 Mode 在变。

换成用行为来做

如果想把切换的逻辑留在画布上,就每种模式建一条行为:什么时候触发:玩家点了某个按钮,按钮 mode-comedy;效果:修改变量 Mode 设为 comedy,显示通知 已切换到喜剧模式。这样每个按钮只要一步:让一条行为生效。界面代码也能用 api.executeAction("mode-comedy") 触发同一条行为。


第 4 步:试玩 ​

点顶栏的 试玩。

  1. 在普通模式下聊几回合
  2. 点 喜剧。提示弹出来,Mode 变成 comedy
  3. 发一条消息。回复变得好笑,可能还会打破第四面墙
  4. 点 恐怖,再发一条。回复变得阴暗、紧绷
  5. 点 普通,再发一条。叙述回到你这张卡原本的风格

试玩右边的 这回合 会列出 AI 拿到了哪些设定,可以看看是不是只进了一条风格设定。

如果出了问题:

现象可能的原因怎么改
点了按钮没反应按钮没有 改变量 这一步选中按钮,看看 按下时,依次做
Mode 变了,风格没变设定的条件和值对不上按钮设的是 comedy,条件也必须一字不差地查 comedy
两种风格都发给了 AI有一条设定在每回合都发的那一块里两条设定都看一下 什么时候发给 AI
几回合后风格就淡了风格说明离提示的末尾太远把 注入到 设成 放在最后

写代码的做法:最后一条消息下面的模式按钮 ​

如果你自己写界面,就自己画按钮,改的还是同一个变量。第 2 步的设定不用动。

面板 → 前端代码 → 打开 index.tsx,把默认的 return <Chat /> 换掉:

tsx
export default function MyWorld() {
  const api = useYumina();
  const currentMode = String(api.variables["Mode"] || "normal");
  const msgs = api.messages || [];

  const modes = [
    { id: "normal", label: "普通", color: "#94a3b8", activeColor: "#e2e8f0",
      activeBg: "rgba(148,163,184,0.2)", border: "#475569", activeBorder: "#94a3b8" },
    { id: "comedy", label: "喜剧", color: "#fbbf24", activeColor: "#fef3c7",
      activeBg: "rgba(251,191,36,0.2)", border: "#a16207", activeBorder: "#fbbf24" },
    { id: "horror", label: "恐怖", color: "#f87171", activeColor: "#fecaca",
      activeBg: "rgba(248,113,113,0.2)", border: "#991b1b", activeBorder: "#f87171" },
  ];

  const switchTo = (mode) => {
    api.setVariable("Mode", mode.id);
    api.showToast("已切换到" + mode.label + "模式", "info");
  };

  return (
    <Chat renderBubble={(msg) => {
      const isLastMsg = msg.messageIndex === msgs.length - 1;
      return (
        <div>
          <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) switchTo(mode); }}
                    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["Mode"] 按变量名读这个变量
  • api.setVariable("Mode", mode.id) 写它。挂了条件的设定在 AI 下一次回复时就会用上新值
  • api.showToast(text, "info") 弹一个提示(一共三种:"success"、"error"、"info")
  • 当前的按钮带一个圆点并且高亮,再点它不会有反应
  • 想跑一条行为而不是直接改变量,就调用 api.executeAction("mode-comedy")

面板 → 前端代码 里文件旁边的状态应该是绿色的 正常。

不想自己写代码?

点画布右上角的 创作助手,说说你想要什么样的按钮。


更多玩法 ​

再加几种模式 ​

再加一条设定(比如「诗意风格」),条件是 Mode 等于 poetic,再加一个把 Mode 设成 poetic 的按钮。

只管一回合 ​

只该持续一条回复的东西,比如「角色这一句话一直在打嗝」,用一条效果是 告诉 AI 的行为。它会给 AI 塞一句玩家看不到的话,只管下一条回复。想持续几回合的话,用一个数值变量来倒数:按钮把它设成 3,一条 每回合 的行为每次减 1,设定的条件写「Countdown 大于 0」。

切换语言 ​

同样的做法也能切换回复的语言:一条设定写「只用日语回复」,条件是 Language 等于 ja,每种语言一条。

让 AI 自己换模式 ​

把 AI 权限 保持在 AI 可读可写,再在行为规则里写清楚什么时候该换模式(「队伍进了地下墓穴就切到恐怖」)。这样 AI 会自己去改 Mode,那几条设定也会跟着它走。


速查 ​

你想要怎么做
某个模式开着的时候,改变 AI 的写法一条挂了变量条件的设定(条件设定)
用按钮切换模式玩家界面的按钮:改变量
不让 AI 改模式AI 权限:AI 只读
把风格说明放在 AI 最上心的位置在这条设定上,注入到:放在最后
只管下一条回复的提示行为效果:告诉 AI
让当前模式亮起来按钮做两个版本,各自用 什么时候显示 → 变量满足条件时
从界面代码里切换api.setVariable("Mode", "comedy") 或 api.executeAction("mode-comedy")

自己试试:可导入的演示 ​

下载这个 JSON 导入进去,就能看到能跑的版本:

recipe-11-demo-zh.json

怎么导入:

  1. 点左边的 创建,选 空白项目,再选 画布
  2. 在顶栏点 ⋮ → 导入文件,选下载好的 .json 文件。它会替换掉这张新卡里的所有内容
  3. 点 保存,再点 试玩

演示用的是写代码的版本:一个模式变量,三条由按钮触发的行为,模式按钮写在界面代码里。


这是食谱 #11

同样的做法(一个变量,加上几条有条件才发给 AI 的设定)也能用来做难度档位、第一人称和第三人称叙述的切换,或者一个心情随剧情慢慢变化的角色。