STscript 语言参考

什么是 STscript?

它是一种简单而强大的脚本语言,无需进行严肃的编程即可用来扩展 SillyTavern 的功能,让你能够:

  • 创建小游戏或速通挑战
  • 构建由 AI 驱动的聊天洞察
  • 释放你的创造力并与他人分享

STscript 基于斜杠命令引擎构建,利用了命令批处理、数据管道、宏和变量。 这些概念将在下文中逐一说明。

安全注意事项

能力越大,责任越大。请务必谨慎,在执行脚本之前始终先检查其内容。

Hello, World!

要运行你的第一个脚本,打开任意 SillyTavern 对话,并在聊天输入栏中输入以下内容:

stscript
/pass Hello, World! | /echo
Hello World

你应该会在屏幕顶部的 toast 通知中看到该消息。现在让我们逐段拆解它。

脚本是一批命令的集合,每条命令以斜杠开头,可带有或不带命名参数和无名参数,并以命令分隔符 | 结尾。

命令按顺序依次执行,彼此之间传递数据。

  1. /pass 命令将常量值 "Hello, World!" 作为无名参数接收,并将其写入管道。
  2. /echo 命令通过管道接收上一条命令传递的值,并将其作为 toast 通知显示出来。

由于常量无名参数与管道可以互换使用,我们可以把上面的脚本简化为:

stscript
/echo Hello, World!

用户输入

现在让我们为脚本增加一点交互性。我们将接收用户输入的值,并将其显示在通知中。

stscript
/input Enter your name |
/echo Hello, my name is {{pipe}}
  1. /input 命令用于显示一个输入框,提示文本由无名参数指定,然后将输出写入管道。
  2. 由于 /echo 已经有一个无名参数用于设置输出模板,我们使用 {{pipe}} 宏来指定管道值的渲染位置。
Slim Shady Input Slim Shady Output

其他输入/输出命令

  • /popup (text) — 显示一个阻塞式弹窗,支持简单的 HTML 格式,例如:/popup <font color=red>I'm red!</font>
  • /setinput (text) — 用提供的文本替换用户输入栏中的内容。
  • /speak voice="name" (text) — 使用所选的 TTS 引擎以及语音映射中的角色名朗读文本,例如 /speak name="Donald Duck" Quack!
  • /buttons labels=["a","b"] (text) — 显示一个带有指定文本和按钮标签的阻塞式弹窗。labels 必须是一个 JSON 序列化的字符串数组,或是一个包含此类数组的变量名。点击的按钮标签会被写入管道,若取消则返回空字符串。文本支持简单的 HTML 格式。
  • /beep — 播放消息提示音。

/popup/input 支持以下额外的命名参数:

  • large=on/off - 增大弹窗的垂直尺寸。默认值:off
  • wide=on/off - 增大弹窗的水平尺寸。默认值:off
  • okButton=string - 允许自定义 "Ok" 按钮上的文本。默认值:Ok
  • rows=number - (仅适用于 /input)增大输入控件的尺寸。默认值:1。
  • placeholder=string - 设置输入字段中的占位文本。
  • tooltip=string - 设置悬停时显示的工具提示。
  • icon=string - 为弹窗设置一个 Font Awesome 图标 class。

示例:

/echo 的参数

/echo 的额外参数 severity 可取以下值,用于设置所显示消息的样式。

  • warning
  • error
  • info(默认)
  • success

示例:

stscript
/echo severity=error Something really bad happened.

变量

变量用于在脚本中存储和操作数据,既可以通过命令,也可以通过宏来使用。变量可以是以下类型之一:

  • 局部变量 — 保存到当前对话的元数据中,且为该对话所独有。
  • 全局变量 — 保存到 settings.json 中,在整个应用中均存在。
  1. /getvar name{{getvar::name}} — 获取局部变量的值。
  2. /setvar key=name value{{setvar::name::value}} — 设置局部变量的值。
  3. /addvar key=name increment{{addvar::name::increment}} — 将 increment 加到局部变量的值上。
  4. /incvar name{{incvar::name}} — 将局部变量的值递增 1。
  5. /decvar name{{decvar::name}} — 将局部变量的值递减 1。
  6. /getglobalvar name{{getglobalvar::name}} — 获取全局变量的值。
  7. /setglobalvar key=name{{setglobalvar::name::value}} — 设置全局变量的值。
  8. /addglobalvar key=name{{addglobalvar::name:increment}} — 将 increment 加到全局变量的值上。
  9. /incglobalvar name{{incglobalvar::name}} — 将全局变量的值递增 1。
  10. /decglobalvar name{{decglobalvar::name}} — 将全局变量的值递减 1。
  11. /flushvar name — 删除局部变量的值。
  12. /flushglobalvar name — 删除全局变量的值。
  • 此前未定义的变量,其默认值为空字符串;如果它首先被用于 /addvar/incvar/decvar 命令,则默认为零。
  • /addvar 命令中,如果 increment 和变量值都可以转换为数字,则执行加法或减法运算,否则执行字符串拼接。
  • 如果某个命令参数接受变量名,且同时存在同名的局部变量和全局变量,则局部变量优先。
  • 所有用于操作变量的斜杠命令都会将结果值写入管道,供下一条命令使用。
  • 对于,只有 "get"、"inc" 和 "dec" 类型会返回值,"add" 和 "set" 类型会被替换为空字符串。

现在,让我们来看下面的示例:

stscript
/input What do you want to generate? |
/setvar key=SDinput |
/echo Requesting an image of {{getvar::SDinput}} |
/getvar SDinput |
/imagine
  1. 用户输入的值被保存到名为 SDinput 的局部变量中。
  2. 使用 getvar 宏在 /echo 命令中显示该值。
  3. 使用 getvar 命令获取变量的值,并通过管道传递。
  4. 该值被传递给 /imagine 命令(由 Image Generation 插件提供),作为其输入提示词。

由于变量会被保存且在脚本执行之间不会被清除,因此你可以在其他脚本中以及通过宏引用该变量,它将解析为与示例脚本执行时相同的值。若要确保该值被丢弃,请在脚本中添加 /flushvar 命令。

数组与对象

变量的值可以包含 JSON 序列化的数组或键值对(对象)。

示例:

  • 数组:["apple","banana","orange"]
  • 对象:{"fruits":["apple","banana","orange"]}

可以对命令应用以下修改方式来处理这些变量:

  • /len 命令获取数组中的条目数量。
  • index=number/string 命名参数可添加到 /getvar/setvar 及其全局版本中,用于按从零开始的索引(数组)或字符串键(对象)获取或设置子值。
    • 如果对不存在的变量使用数字索引,该变量将被创建为空数组 []
    • 如果对不存在的变量使用字符串索引,该变量将被创建为空对象 {}
  • /addvar/addglobalvar 命令支持向数组类型的变量中推入新值。

流程控制 - 条件语句

你可以使用 /if 命令创建条件表达式,根据定义的规则对执行进行分支。

stscript
/if left=valueA right=valueB rule=comparison else={: /echo (command on false) :} {: /echo (command on true) :}

注意,下面的语法

stscript
/if left=valueA right=valueB rule=comparison else="(command on false)" "(command on true)"

也是受支持的,不过 {: closures :} 能帮助你编写更整洁的脚本。

让我们来看下面的示例:

stscript
/input What's your favorite drink? |
/if left={{pipe}} right="black tea" rule=eq else={: /echo You shall not pass | /abort :} {: /echo Welcome to the club, {{user}} :}

该脚本将用户输入与要求的值进行比较,并根据输入值显示不同的消息。

/if 的参数

  1. left 是第一个操作数。我们称之为 A。
  2. right 是第二个操作数。我们称之为 B。
  3. rule 是要应用于操作数的运算。
  4. else 是可选的子命令字符串,在布尔比较结果为 false 时执行。
  5. 无名参数是在布尔比较结果为 true 时执行的子命令。

操作数的值按以下顺序进行求值:

  1. 数字字面量
  2. 局部变量名
  3. 全局变量名
  4. 字符串字面量

命名参数的字符串值可以用引号转义以允许多词字符串。引号随后会被丢弃。

布尔运算

支持的布尔比较规则如下。对操作数应用的运算结果为 true 或 false。

  1. eq(等于)=> A = B
  2. neq(不等于)=> A != B
  3. lt(小于)=> A < B
  4. gt(大于)=> A > B
  5. lte(小于或等于)=> A <= B
  6. gte(大于或等于)=> A >= B
  7. not(一元取反)=> !A
  8. in(包含子串)=> A 包含 B,不区分大小写
  9. nin(不包含子串)=> A 不包含 B,不区分大小写

子命令

子命令是一个字符串,其中包含要执行的一系列斜杠命令。

  1. 要在子命令中使用命令批处理,应对命令分隔符进行转义(见下文)。
  2. 由于宏的值是在进入条件语句时求值,而不是在子命令执行时求值,因此可以额外对宏进行转义,以将其求值延迟到子命令执行时。
  3. 子命令执行的结果会被管道传递给 /if 之后的命令。
  4. /abort 命令在遇到时会中断脚本的执行。

/if 命令可用作三元运算符。 下面的示例会在变量 a 等于 5 时向下一个命令传递 "true" 字符串,否则传递 "false" 字符串。

stscript
/if left=a right=5 rule=eq else={: /pass false:} {: /pass true :} |
/echo

转义序列

宏的转义方式与之前相同。不过,有了闭包,你需要转义宏的频率会比以前少得多。要么转义两个左花括号,要么将左、右花括号对都转义。

stscript
/echo \{\{char}} |
/echo \{\{char\}\}

管道

在闭包中(当管道用作命令分隔符时),管道不需要转义。在任何你想使用字面管道字符而非命令分隔符的地方,都需要对其进行转义。

stscript
/echo title="a\|b" c\|d |
/echo title=a\|b c\|d |

使用解析器标志 STRICT_ESCAPING 时,你无需对引号值中的管道进行转义。

stscript
/parser-flag STRICT_ESCAPING |
/echo title="a|b" c\|d |
/echo title=a\|b c\|d |

引号

要在引号值中使用字面的引号字符,必须对该字符进行转义。

stscript
/echo title="a \"b\" c" d "e" f

空格

要在命名参数的值中使用空格,要么用引号将值包裹起来,要么对空格字符进行转义。

stscript
/echo title="a b" c d |
/echo title=a\ b c d

闭包定界符

如果你想使用用于标记闭包开始或结束的字符组合,必须用单个反斜杠对该序列进行转义。

stscript
/echo \{: |
/echo \:}

管道截断符

stscript
||

为防止上一条命令的输出被自动注入为下一条命令的无名参数,可在两条命令之间放置双管道符。

stscript
/echo we don't want to pass this on ||
/world

闭包

stscript
{: ... :}

闭包(块语句、Lambda、匿名函数,随你怎么叫)是用 {::} 包裹的一系列命令,只有在代码执行到该部分时才会被求值。

子命令

闭包让子命令的使用变得简单得多,并消除了对管道和宏进行转义的需要。

stscript
// if without closures |
/if left=1 rule=eq right=1
    else="
        /echo not equal \|
        /return 0
    "
    /echo equal \|
    /return \{\{pipe}}
stscript
// if with closures |
/if left=1 rule=eq right=1
    else={:
        /echo not equal |
        /return 0
    :}
    {:
        /echo equal |
        /return {{pipe}}
    :}

作用域

闭包拥有自己的作用域,并支持作用域变量。作用域变量通过 /let 声明,通过 /var 设置和获取其值。获取作用域变量的另一种方式是使用 {{var::}} 宏。

stscript
/let x |
/let y 2 |
/var x 1 |
/var y |
/echo x is {{var::x}} and y is {{pipe}}.

在闭包内,你可以访问在同一闭包或其任一祖先闭包中声明的所有变量。你无法访问在闭包的后代中声明的变量。
如果某个变量的名称与在其任一祖先闭包中声明的变量同名,则在该闭包及其后代中,你将无法访问那个祖先变量。

stscript
/let x this is root x |
/let y this is root y |
/return {:
    /echo called from level-1: x is "{{var::x}}" and y is "{{var::y}}" |
    /delay 500 |
    /let x this is level-1 x |
    /echo called from level-1: x is "{{var::x}}" and y is "{{var::y}}" |
    /delay 500 |
    /return {:
        /echo called from level-2: x is "{{var::x}}" and y is "{{var::y}}" |
        /let x this is level-2 x |
        /echo called from level-2: x is "{{var::x}}" and y is "{{var::y}}" |
        /delay 500
    :}()
:}() |
/echo called from root: x is "{{var::x}}" and y is "{{var::y}}"

命名闭包

stscript
/let x {: ... :} | /:x

闭包可以赋值给变量(仅限作用域变量),以便稍后调用或作为子命令使用。

stscript
/let myClosure {:
    /echo this is my closure
:} |
/:myClosure
stscript
/let myClosure {:
    /echo this is my closure |
    /delay 500
:} |
/times 3 {{var::myClosure}}

/: 也可以用来执行快捷回复,因为它只是 /run 的简写。

stscript
/:QrSetName.QrButtonLabel |
/run QrSetName.QrButtonLabel

闭包参数

命名闭包可以接受命名参数,就像斜杠命令一样。参数可以带有默认值。

stscript
/let myClosure {: a=1 b=
    /echo a is {{var::a}} and b is {{var::b}}
:} |
/:myClosure b=10

闭包与管道参数

来自父闭包的管道值不会自动注入到子闭包的第一条命令中。
你仍然可以用 {{pipe}} 显式引用父级的管道值,但如果你将闭包内第一条命令的无名参数留空,该值不会被自动注入。

stscript
/* This used to attempt to change the model to "foo"
   because the value "foo" from the /echo outside of
   the loop was injected into the /model command
   inside of the loop.
   Now it will simply echo the current model without 
   attempting to change it.
*|
/echo foo |
/times 2 {:
	/model |
	/echo |
:} |
stscript
/* You can still recreate the old behavior by
   explicitly using the {{pipe}} macro.
*|
/echo foo |
/times 2 {:
	/model {{pipe}} |
	/echo |
:} |

立即执行的闭包

stscript
{: ... :}()

闭包可以被立即执行,这意味着它会被其返回值替换。这在没有显式支持闭包的地方很有用,也能缩短一些原本需要大量中间变量的命令。

stscript
// a simple length comparison of two strings without closures |
/len foo |
/var lenOfFoo {{pipe}} |
/len bar |
/var lenOfBar {{pipe}} |
/if left={{var::lenOfFoo}} rule=eq right={{var:lenOfBar}} /echo yay!
stscript
// the same comparison with immediately executed closures |
/if left={:/len foo:}() rule=eq right={:/len bar:}() /echo yay!

除了运行保存在作用域变量中的命名闭包之外,/run 命令也可以用于立即执行闭包。

stscript
/run {:
	/add 1 2 3 4 |
:} |
/echo |

注释

stscript
// ... | /# ...

注释是脚本代码中人类可读的解释或标注。注释不会中断管道。

stscript
// this is a comment |
/echo foo |
/# this is also a comment

块注释

块注释可用于一次性快速注释掉多条命令。它们不会在管道处终止。

stscript
/echo foo |
/*
/echo bar |
/echo foobar |
*|
/echo foo again |

流程控制

循环:/while/times

如果你需要在循环中运行某条命令直到满足特定条件,请使用 /while 命令。

stscript
/while left=valueA right=valueB rule=operation guard=on "commands"

在循环的每一步中,它会将变量 A 的值与变量 B 的值进行比较,如果条件为 true,则执行引号中包含的任何有效斜杠命令,否则退出循环。该命令不会向输出管道写入任何内容。

/while 的参数

可用的布尔比较集合、变量的处理方式、字面量值以及子命令,均与 /if 命令相同。

可选的 guard 命名参数(默认为 on)用于防止无限循环,将迭代次数限制为 100。 要禁用此限制并允许无限循环,请设置 guard=off

下面的示例会不断给 i 的值加 1,直到达到 10,然后输出结果值(本例中为 10)。

stscript
/setvar key=i 0 |
/while left=i right=10 rule=lt "/addvar key=i 1" |
/echo {{getvar::i}} |
/flushvar i

/times 的参数

将子命令运行指定的次数。

/times (repeats) "(command)" – 引号中包含的任何有效斜杠命令都会重复指定次数,例如 /setvar key=i 1 | /times 5 "/addvar key=i 1" 会将 "i" 的值加 1 共 5 次。

  • {{timesIndex}} 会被替换为迭代编号(从零开始),例如 /times 4 {:/echo {{timesIndex}}:} 会回显数字 0 到 4。
  • 循环默认限制为 100 次迭代,传入 guard=off 可禁用此限制。

跳出循环和闭包

stscript
/break |

/break 命令可用于提前跳出循环(/while/times)或闭包。/break 的无名参数可用于传递一个与当前管道不同的值。
/break 目前在以下命令中实现:

  • /while - 提前退出循环
  • /times - 提前退出循环
  • /run(带闭包或通过变量传递闭包)- 提前退出闭包
  • /:(带闭包)- 提前退出闭包
stscript
/times 10 {:
	/echo {{timesIndex}}
	/delay 500 |
	/if left={{timesIndex}} rule=gt right=3 {:
		/break
	:} |
:} |
stscript
/let x {: iterations=2
	/if left={{var::iterations}} rule=gt right=10 {:
		/break too many iterations! |
	:} |
	/times {{var::iterations}} {:
		/delay 500 |
		/echo {{timesIndex}} |
	:} |
:} |
/:x iterations=30 |
/echo the final result is: {{pipe}}
stscript
/run {:
	/break 1 |
	/pass 2 |
:} |
/echo pipe will be one: {{pipe}} |
stscript
/let x {:
	/break 1 |
	/pass 2 |
:} |
/:x |
/echo pipe will be one: {{pipe}} |

数学运算

  • 以下所有运算都接受一系列数字或变量名,并将结果输出到管道。
  • 无效运算(例如除以零),以及结果为 NaN 或无穷大的运算会返回零。
  • 乘法、加法、最小值和最大值接受由空格分隔的、数量不限的参数。
  • 减法、除法、指数和取模接受由空格分隔的两个参数。
  • 正弦、余弦、自然对数、平方根、绝对值和取整接受一个参数。

运算列表:

  1. /add (a b c d) – 对一组值执行加法运算,例如 /add 10 i 30 j
  2. /mul (a b c d) – 对一组值执行乘法运算,例如 /mul 10 i 30 j
  3. /max (a b c d) – 返回一组值中的最大值,例如 /max 1 0 4 k
  4. /min (a b c d) – 返回一组值中的最小值,例如 /min 5 4 i 2
  5. /sub (a b) – 对两个值执行减法运算,例如 /sub i 5
  6. /div (a b) – 对两个值执行除法运算,例如 /div 10 i
  7. /mod (a b) – 对两个值执行取模运算,例如 /mod i 2
  8. /pow (a b) – 对两个值执行幂运算,例如 /pow i 2
  9. /sin (a) – 对一个值执行正弦运算,例如 /sin i
  10. /cos (a) – 对一个值执行余弦运算,例如 /cos i
  11. /log (a) – 对一个值执行自然对数运算,例如 /log i
  12. /abs (a) – 对一个值执行绝对值运算,例如 /abs -10
  13. /sqrt (a) – 对一个值执行平方根运算,例如 /sqrt 9
  14. /round (a) – 对一个值执行四舍五入到最接近整数的运算,例如 /round 3.14
  15. /rand (round=round|ceil|floor from=number=0 to=number=1) – 返回 from 和 to 之间的一个随机数,例如 /rand/rand 10/rand from=5 to=10。范围是包含端点的。返回值会包含小数部分。使用 round 命名参数可获取整数值,例如 /rand round=ceil 向上取整,round=floor 向下取整,round=round 四舍五入到最接近的整数。

示例 1:计算半径为 50 的圆的面积。

stscript
/setglobalvar key=PI 3.1415 |
/setvar key=r 50 |
/mul r r PI |
/round |
/echo Circle area: {{pipe}}

示例 2:计算 5 的阶乘。

stscript
/setvar key=input 5 |
/setvar key=i 1 |
/setvar key=product 1 |
/while left=i right=input rule=lte "/mul product i \| /setvar key=product \| /addvar key=i 1" |
/getvar product |
/echo Factorial of {{getvar::input}}: {{pipe}} |
/flushvar input |
/flushvar i |
/flushvar product

使用 LLM

脚本可以使用以下命令向当前连接的 LLM API 发起请求:

  • /gen (prompt) — 使用提供的提示词为所选角色生成文本,并包含聊天消息。
  • /genraw (prompt) — 仅使用提供的提示词生成文本,忽略当前角色和聊天内容。
  • /trigger — 触发一次常规生成(等同于点击“发送”按钮)。如果在群聊中,你可以可选地提供一个从 1 开始的群成员索引或角色名让其回复,否则会根据群聊设置触发一轮群聊。
  • /swipe — 对最后一条角色消息触发一次滑动。
  • /regenerate — 重新生成最后一条角色消息。
  • /continue — 尝试继续最后一条消息。

/gen/genraw 的参数

stscript
/genraw lock=on/off stop=[] instruct=on/off (prompt)
  • lock — 可为 onoff。指定在生成进行期间是否应阻止用户输入。默认值:off
  • stop — JSON 序列化的字符串数组。仅为本次生成添加一个自定义停止字符串(如果 API 支持)。默认值:无。
  • instruct(仅 /genraw)— 可为 onoff。允许对输入提示词使用指令格式化(前提是已启用指令模式且 API 支持)。设置为 off 可强制使用纯提示词。默认值:on
  • as(用于文本补全 API)— 可为 system(默认)或 char。定义最后一行提示词的格式化方式。char 会使用角色名,system 会不使用名称或使用中性名称。

生成的文本随后会通过管道传递给下一条命令,并可以保存到变量中,或利用 I/O 能力来显示:

stscript
/genraw Write a funny message from Cthulhu about taking over the world. Use emojis. |
/popup <h3>Cthulhu says:</h3><div>{{pipe}}</div>
Cthulhu Says

或者将生成的消息作为你的角色的回复插入:

stscript
/genraw You have been memory wiped, your name is now Lisa and you're tearing me apart. You're tearing me apart Lisa! |
/sendas name={{char}} {{pipe}}

临时角色

如果你不在群聊中,脚本可以临时以另一个角色的身份向当前连接的 LLM 发起请求。

  • /ask (prompt) — 使用提供的提示词为指定角色生成文本,并包含聊天消息。请注意,来自该角色的回复的滑动会回退到当前角色。
stscript
/ask name=... (prompt)

/ask 的参数

  • name必填。要询问的角色的名称(或唯一角色标识符,例如头像键)。此参数必须作为命名参数提供。
  • return — 指定返回值的提供方式。默认为 pipe(通过命令管道输出)。如果 API 支持,还可以指定其他选项。
stscript
/ask name=Alice What is your favorite color?

提示词注入

脚本可以添加自定义的 LLM 提示词注入,这本质上等同于无限制的作者备注。

  • /inject (text) — 将任意文本插入到当前对话的正常 LLM 提示词中,并且需要一个唯一标识符。保存到聊天元数据。
  • /listinjects — 在系统消息中列出为当前对话添加的所有由脚本创建的提示词注入。
  • /flushinjects — 删除为当前对话添加的所有由脚本创建的提示词注入。
  • /note (text) — 设置当前对话的作者备注值。保存到聊天元数据。
  • /interval — 设置当前对话的作者备注插入间隔。
  • /depth — 设置聊天内位置的作者备注插入深度。
  • /position — 设置当前对话的作者备注位置。

/inject 的参数

stscript
/inject id=IdGoesHere position=chat depth=4 My prompt injection
  • id — 标识符字符串或对变量的引用。使用相同 ID 后续调用 /inject 会覆盖之前的文本注入。必填参数。
  • position — 设置注入的位置。默认值:after。可选值:
    • after:在主提示词之后。
    • before:在主提示词之前。
    • chat:聊天内。
  • depth — 设置聊天内位置的注入深度。0 表示在最后一条消息之后插入,1 表示在最后一条消息之前插入,以此类推。默认值:4。
  • 无名参数是要注入的文本。空字符串会取消为该标识符设置的上一个值。

访问聊天消息

读取消息

你可以使用 /messages 命令访问当前所选对话中的消息。

stscript
/messages names=on/off start-finish
  • names 参数用于指定是否包含角色名称,默认值:on
  • 在无名参数中,它接受 start-finish 格式的消息索引或范围。范围是包含端点的!
  • 如果范围无法满足,即请求了无效索引或超出实际存在的消息数量,则返回空字符串。
  • 从提示词中隐藏的消息(以幽灵图标表示)会被排除在输出之外。
  • 如果你想知道最新消息的索引,请使用 {{lastMessageId}} 宏;{{lastMessage}} 会获取消息本身。

要计算范围的起始索引,例如当你需要获取最后 N 条消息时,可使用变量减法。 下面的示例会获取对话中的最后 3 条消息:

stscript
/setvar key=start {{lastMessageId}} |
/addvar key=start -2 |
/messages names=off {{getvar::start}}-{{lastMessageId}} |
/setinput

发送消息

脚本可以以用户、角色、人格、中立叙述者的身份发送消息,或添加评论。

  1. /send (text) — 以当前所选人格的身份添加一条消息。
  2. /sendas name=charname (text) — 以任意角色的身份添加一条消息,按名称匹配。name 参数为必填。使用 {{char}} 宏可发送为当前角色。
  3. /sys (text) — 添加一条来自中立叙述者的消息,该消息不属于用户或角色。显示名称纯粹是装饰性的,可以通过 /sysname 命令自定义。
  4. /comment (text) — 添加一条隐藏评论,该评论在对话中显示但对提示词不可见。
  5. /addswipe (text) — 为最后一条角色消息添加一个滑动。无法为用户消息或隐藏消息添加滑动。
  6. /hide (message id or range) — 根据提供的消息索引或 start-finish 格式的包含端点范围,从提示词中隐藏一条或多条消息。
  7. /unhide (message id or range) — 根据提供的消息索引或 start-finish 格式的包含端点范围,将一条或多条消息恢复到提示词中。

/send/sendas/sys/comment 命令可选地接受一个命名参数 at,其值为从零开始的数字(或包含此类值的变量名),用于指定消息插入的精确位置。默认情况下,新消息插入到聊天记录的末尾。

下面的示例会在对话历史的最开始处插入一条用户消息:

stscript
/send at=0 Hi, I use Linux.

删除消息

这些命令具有潜在的破坏性,且没有“撤销”功能。如果你不小心删除了重要的内容,请检查 /backups/ 文件夹。

  1. /cut (message id or range) — 根据提供的消息索引或 start-finish 格式的包含端点范围,从对话中剪切一条或多条消息。
  2. /del (number) — 从对话中删除最后 N 条消息。
  3. /delswipe (1-based swipe id) — 根据提供的从 1 开始的滑动 ID,删除最后一条角色消息的一个滑动。
  4. /delname (character name) — 删除当前对话中属于指定名称角色的所有消息。
  5. /delchat — 删除当前对话。

角色管理命令

  1. /char-create — 使用命名参数提供的数据创建一个新角色。
  2. /char-update — 使用命名参数提供的数据更新当前角色。
  3. /char-get — 以 JSON 对象的形式检索当前角色的数据,并将其传递到管道。
  4. /char-delete (name) — 删除具有指定名称的角色。
  5. /char-duplicate (name) — 复制具有指定名称的角色。
  6. /tag-import (name) — 从角色卡文件导入标签。

加载器命令

加载器系统为耗时任务提供了一个可复用的遮罩,用于提供视觉反馈和/或临时阻塞 UI。

  1. /loader-show (text) — 显示一个带有指定文本的加载遮罩。
  2. /loader-hide — 隐藏加载遮罩。
  3. /loader-wrap (closure) — 显示一个加载遮罩,执行提供的闭包,并在完成后隐藏遮罩。
  4. /loader-stop — 停止并移除加载遮罩。

世界书命令

世界书(也称为 Lorebook)是一个高度实用的工具,用于将数据动态插入到提示词中。更详细的说明请参见专门页面:世界书

  1. /getchatbook – 获取绑定到当前对话的世界书文件名,如果此前未绑定则创建一个新的,并将其传递到管道。
  2. /findentry file=bookName field=fieldName [text] – 使用字段值与所提供文本的模糊匹配(默认字段:key),从指定文件(或指向文件名的变量)中查找记录的 UID,并将 UID 传递到管道,例如 /findentry file=chatLore field=key Shadowfang
  3. /getentryfield file=bookName field=field [UID] – 从指定的世界书文件(或指向文件名的变量)中获取具有该 UID 的记录的某个字段值(默认字段:content),并将该值传递到管道,例如 /getentryfield file=chatLore field=content 123
  4. /setentryfield file=bookName uid=UID field=field [text] – 设置指定的世界书文件(或指向文件名的变量)中具有该 UID(或指向 UID 的变量)的记录的某个字段值(默认字段:content)。要为键字段设置多个值,请使用以逗号分隔的列表作为文本值,例如 /setentryfield file=chatLore uid=123 field=key Shadowfang,sword,weapon
  5. /createentry file=bookName key=keyValue [content text] – 在指定文件(或指向文件名的变量)中创建一条新记录,并指定键和内容(这两个参数都是可选的),然后将 UID 传递到管道,例如 /createentry file=chatLore key=Shadowfang The sword of the king

有效的条目字段

Field UI element Value type
content 内容 String
comment 标题 / 备注 String
key 主要关键词 List of strings
keysecondary 可选过滤器 List of strings
constant 常驻状态 Boolean (1/0)
disable 禁用状态 Boolean (1/0)
order 顺序 Number
selectiveLogic 逻辑 (见下文)
excludeRecursion 不可递归 Boolean (1/0)
probability Trigger% Number (0-100)
depth 深度 Number (0-999)
position 位置 (见下文)
role 深度角色 (见下文)
scanDepth 扫描深度 Number (0-100)
caseSensitive 区分大小写 Boolean (1/0)
matchWholeWords 全词匹配 Boolean (1/0)
vectorized 向量化状态 Boolean (1/0)
automationId Automation ID String
group 包含组 String
groupOverride 优先包含 Boolean (1/0)
groupWeight 组权重 Number (0-100)
useGroupScoring 使用组评分 Boolean (1/0)
characterFilterExclude 角色过滤器排除模式 List of strings
characterFilterNames 角色过滤器名称 List of strings
characterFilterTags 角色过滤器标签 List of strings
matchCharacterDepthPrompt 匹配角色深度提示词 Boolean (1/0)
matchCharacterDescription 匹配角色描述 Boolean (1/0)
matchCharacterPersonality 匹配角色人格 Boolean (1/0)
matchCreatorNotes 匹配创作者备注 Boolean (1/0)
matchPersonaDescription 匹配人格描述 Boolean (1/0)
matchScenario 匹配场景 Boolean (1/0)

逻辑值

  • 0 = AND ANY
  • 1 = NOT ALL
  • 2 = NOT ANY
  • 3 = AND ALL

位置值

  • 0 = 主提示词之前
  • 1 = 主提示词之后
  • 2 = 作者备注顶部
  • 3 = 作者备注底部
  • 4 = 聊天内按深度
  • 5 = 对话示例顶部
  • 6 = 对话示例底部

角色值(仅 Position = 4)

  • 0 = System
  • 1 = User
  • 2 = Assistant

示例 1:按键从对话世界书中读取内容

stscript
/getchatbook | /setvar key=chatLore |
/findentry file={{getvar::chatLore}} field=key Shadowfang |
/getentryfield file={{getvar::chatLore}} field=key |
/echo

示例 2:创建一个带键和内容的对话世界书条目

stscript
/getchatbook | /setvar key=chatLore |
/createentry file={{getvar::chatLore}} key="Milla" Milla Basset is a friend of Lilac and Carol. She is a hush basset puppy who possesses the power of alchemy. |
/echo

示例 3:用对话中的新信息扩展现有的世界书条目

stscript
/getchatbook | /setvar key=chatLore |
/findentry file={{getvar::chatLore}} field=key Milla |
/setvar key=millaUid |
/getentryfield file={{getvar::chatLore}} field=content |
/setvar key=millaContent |
/gen lock=on Tell me more about Milla Basset based on the provided conversation history. Incorporate existing information into your reply: {{getvar::millaContent}} |
/setvar key=millaContent |
/echo New content: {{pipe}} |
/setentryfield file={{getvar::chatLore}} uid=millaUid field=content {{getvar::millaContent}}

文本操作

有多种实用的文本操作工具命令,可在各种脚本场景中使用。

  1. /trimtokens — 从开头或结尾将输入裁剪到指定数量的文本 token,并将结果输出到管道。
  2. /trimstart — 将输入裁剪到第一个完整句子的开头,并将结果输出到管道。
  3. /trimend — 将输入裁剪到最后一个完整句子的结尾,并将结果输出到管道。
  4. /fuzzy — 将输入文本与字符串列表进行模糊匹配,将最佳匹配的字符串输出到管道。
  5. /regex name=scriptName [text] — 对指定文本执行来自 Regex 扩展的正则脚本。该脚本必须已启用。

/trimtokens 的参数

stscript
/trimtokens limit=number direction=start/end (input)
  1. direction 设置裁剪方向,可以是 startend。默认值:end
  2. limit 设置输出中要保留的 token 数量。也可以指定包含该数字的变量名。必填参数。
  3. 无名参数是要裁剪的输入文本。

/fuzzy 的参数

stscript
/fuzzy list=["candidate1","candidate2"] (input)
  1. list 是包含候选项的、JSON 序列化的字符串数组。也可以指定包含该列表的变量名。必填参数。
  2. 无名参数是要匹配的输入文本。输出是与输入最匹配的候选项之一。

自动补全

  • 自动补全在聊天输入框和大型快捷回复编辑器中均已启用。
  • 自动补全可在输入的任意位置工作。即使存在多条管道命令和嵌套闭包也是如此。
  • 自动补全支持三种查找匹配命令的方式(用户设置 -> STscript Matching)。
  1. Starts with “旧”方式。只有完全以所输入值开头的命令才会显示。
  2. Includes 所有包含所输入值的命令都会显示。示例:当输入 /delete 时,命令 /qr-delete/qr-set-delete 会出现在自动补全列表中(/qr-delete、/qr-set-delete)。
  3. Fuzzy 所有可以与所输入值进行模糊匹配的命令都会显示。示例:当输入 /seas 时,命令 /sendas 会出现在自动补全列表中(/sendas)。
  • 命令参数同样支持自动补全。对于必填参数,列表会自动显示。对于可选参数,按 Ctrl+Space 可打开可用选项列表。
  • 当输入 /: 来执行闭包或 QR 时,自动补全会显示作用域变量和 QR 的列表。
  • 自动补全对宏(在斜杠命令中)提供有限支持。输入 {{ 可获取可用宏的列表。
  • 使用 方向键从自动补全选项列表中选择一个选项。
  • EnterTab点击一个选项,将该选项放置到光标处。
  • Escape 可关闭自动补全列表。
  • Ctrl+Space 可打开自动补全列表或切换所选选项的详情。

解析器标志

stscript
/parser-flag

解析器接受标志以修改其行为。这些标志可以在脚本中的任意时刻开启或关闭,其后的所有输入都会相应地进行求值。
你可以在用户设置中设置默认标志。

严格转义

stscript
/parser-flag STRICT_ESCAPING on |

启用 STRICT_ESCAPING 后的变化如下。

管道

引号值中的管道不需要转义。

stscript
/echo title="a|b" c\|d

反斜杠

符号前面的反斜杠可以被转义,以提供字面的反斜杠再加上功能性的符号。

stscript
// this will echo "foo \", then echo "bar" |
/echo foo \\|
/echo bar
stscript
/echo \\|
/echo \\\|

替换变量宏

stscript
/parser-flag REPLACE_GETVAR on |

当变量值包含可能被解释为宏的文本时,此标志有助于避免双重替换。{{var::}} 宏会被最后替换,并且不会对得到的文本/变量值进行进一步的替换。

将所有 {{getvar::}}{{getglobalvar::}} 宏替换为 {{var::}}。 在幕后,解析器会在带有被替换宏的命令之前插入一系列命令执行器:

  • 调用 /let 将当前 {{pipe}} 保存到作用域变量
  • 调用 /getvar/getglobalvar 获取宏中使用的变量
  • 调用 /let 将获取的变量保存到作用域变量
  • 调用 /return 并使用保存的 {{pipe}} 值,为下一条命令恢复正确的管道值
stscript
// the following will echo the last message's id / number |
/setvar key=x \{\{lastMessageId}} |
/echo {{getvar::x}}
stscript
// this will echo the literal text {{lastMessageId}} |
/parser-flag REPLACE_GETVAR |
/setvar key=x \{\{lastMessageId}} |
/echo {{getvar::x}}

快捷回复:脚本库与自动执行

快捷回复是 SillyTavern 的一个内置扩展,提供了一种简便的方式来存储和执行你的脚本。

配置快捷回复

要开始使用,请启用并打开扩展面板(堆叠积木图标),并展开 Quick Replies 菜单。

Quick Reply
Quick Reply

快捷回复默认处于禁用状态,你需要先启用它们。 之后你会看到一个出现在聊天输入栏上方的栏。

你可以设置显示的按钮文本标签(为简洁起见,我们建议使用 emoji),以及点击按钮时将执行的脚本。

按钮的数量由 Number of slots(槽位数量)设置控制(最大值 = 100),根据你的需要进行调整,完成后点击 "Apply"。

Inject user input automatically(自动注入用户输入)在使用 STscript 时建议禁用,否则它可能会干扰你的输入,请改用 {{input}} 宏在脚本中获取输入栏的当前值。

Quick Reply presets(快捷回复预设)允许拥有多套预定义的快捷回复,并可以手动切换或使用 /qrset (name of set) 命令切换。 在切换到不同的集合之前,别忘了点击 "Update" 将你的更改写入当前使用的预设!

手动执行

现在你可以将你的第一个脚本添加到库中。选择任意空闲槽位(或创建一个),在左侧框中输入 "Click me" 以设置标签,然后将以下内容粘贴到右侧框中:

stscript
/addvar key=clicks 1 |
/if left=clicks right=5 rule=eq else="/echo Keep going..." "/echo You did it!  \| /flushvar clicks"

然后在聊天栏上方出现的按钮上点击 5 次。 每次点击都会将变量 clicks 递增一,并在值等于 5 时显示不同的消息并重置该变量。

自动执行

通过点击所创建命令的 按钮打开模态菜单。

Automatic execution

在此菜单中,你可以执行以下操作:

  • 在便捷的全屏编辑器中编辑脚本
  • 将按钮从聊天栏隐藏,使其只能用于自动执行
  • 在以下一个或多个条件下启用自动执行:
    • 应用启动时
    • 向对话发送用户消息时
    • 在对话中接收到 AI 消息时
    • 打开角色或群聊时
    • 触发群成员回复时
    • 使用相同的 Automation ID 激活世界书条目时
  • 为快捷回复提供自定义工具提示(在 UI 中悬停快捷回复时显示的文本)
  • 出于测试目的执行脚本

只有在启用快捷回复扩展的情况下,命令才会自动执行。

例如,你可以通过添加以下脚本并将其设置为在用户消息时自动执行,从而在发送五条用户消息后显示一条消息。

stscript
/addvar key=usercounter 1 |
/echo You've sent {{pipe}} messages. |
/if left=usercounter right=5 rule=gte "/echo Game over! \| /flushvar usercounter"

调试器

在展开的快捷回复编辑器中存在一个基础调试器。可以在脚本中的任意位置使用 /breakpoint | 设置断点。从 QR 编辑器执行脚本时,执行会在该点中断,让你可以检查当前可用的变量、管道、命令参数等,并逐条单步执行剩余代码。

stscript
/let x {: n=1
	/echo n is {{var::n}} |
	/mul n n |
:} |
/breakpoint |
/:x n=3 |
/echo result is {{pipe}} |
QR Editor Debugger

调用过程

/run 命令可以通过标签调用快捷回复中定义的脚本,基本上提供了定义过程并从中返回结果的能力。这允许拥有可被其他脚本引用的可复用脚本块。过程管道的最后结果会传递给其后的下一条命令。

stscript
/run ScriptLabel

让我们创建两个快捷回复:


Label:

GetRandom

Command:

stscript
/pass {{roll:d100}}

Label:

GetMessage

Command:

stscript
/run GetRandom | /echo Your lucky number is: {{pipe}}

点击 GetMessage 按钮会调用 GetRandom 过程,该过程会解析 {{roll}} 宏并将数字传递给调用方,向用户显示出来。

  • 过程不接受命名或无名参数,但可以引用与调用方相同的变量。
  • 调用过程时应避免递归,因为如果处理不当可能会产生“call stack exceeded”错误。

从不同的快捷回复预设调用过程

你可以使用 a.b 语法从不同的快捷回复预设调用过程,其中 a = QR 预设名,b = QR 标签名

stscript
/run QRpreset1.QRlabel1

默认情况下,系统会首先查找快捷回复标签 a.b,因此如果你的某个标签字面上就是 "QRpreset1.QRlabel1",它会尝试运行该标签。如果没有找到这样的标签,它才会查找名为 "QRpreset1" 的 QR 预设中标有 "QRlabel1" 的 QR。

快捷回复管理命令

创建快捷回复

  • /qr-create (arguments, [message]) – 创建一个新的快捷回复,示例:/qr-create set=MyPreset label=MyButton /echo 123

参数:

  • label - string - 按钮上的文本,例如 label=MyButton
  • set - string - QR 集合的名称,例如 set=PresetName1
  • hidden - bool - 按钮是否应隐藏,例如 hidden=true
  • startup - bool - 应用启动时自动执行,例如 startup=true
  • user - bool - 用户消息时自动执行,例如 user=true
  • bot - bool - AI 消息时自动执行,例如 bot=true
  • load - bool - 加载对话时自动执行,例如 load=true
  • title - bool - 在按钮上显示的标题/工具提示,例如 title="My Fancy Button"

删除快捷回复

  • /qr-delete (set=string [label]) – 删除快捷回复

更新快捷回复

  • /qr-update (arguments, [message]) – 更新快捷回复,示例:/qr-update set=MyPreset label=MyButton newlabel=MyRenamedButton /echo 123

参数:

  • newlabel - string - 按钮的新文本,例如 newlabel=MyRenamedButton
  • label - string - 按钮上的文本,例如 label=MyButton
  • set - string - QR 集合的名称,例如 set=PresetName1
  • hidden - bool - 按钮是否应隐藏,例如 hidden=true
  • startup - bool - 应用启动时自动执行,例如 startup=true
  • user - bool - 用户消息时自动执行,例如 user=true
  • bot - bool - AI 消息时自动执行,例如 bot=true
  • load - bool - 加载对话时自动执行,例如 load=true
  • title - bool - 在按钮上显示的标题/工具提示,例如 title="My Fancy Button"

  • qr-get - 检索快捷回复的所有属性,示例:/qr-get set=myQrSet id=42

创建或更新 QR 预设

  • /qr-presetupdate (arguments [label])/qr-presetadd (arguments [label])

参数:

  • enabled - bool - 启用或禁用该预设
  • nosend - bool - 禁用发送/插入到用户输入(对斜杠命令无效)
  • before - bool - 将 QR 放在用户输入之前
  • slots - int - 槽位数量
  • inject - bool - 自动注入用户输入(如果禁用,请使用 {{input}}

创建一个新预设(会覆盖现有预设),示例:/qr-presetadd slots=3 MyNewPreset

添加 QR 上下文菜单

  • /qr-contextadd (set=string label=string chain=bool [preset name]) – 为 QR 添加上下文菜单预设,示例:/qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset

移除所有上下文菜单

  • /qr-contextclear (set=string [label]) – 从 QR 中移除所有上下文菜单预设,示例:/qr-contextclear set=MyPreset MyButton

移除单个上下文菜单

  • /qr-contextdel (set=string label=string [preset name]) – 从 QR 中移除上下文菜单预设,示例:/qr-contextdel set=MyPreset label=MyButton MyOtherPreset

快捷回复值转义

在 QR 消息/命令中,可以用反斜杠转义 |{}

例如,使用 /qr-create label=MyButton /getvar myvar \| /echo \{\{pipe\}\} 来创建一个调用 /getvar myvar | /echo {{pipe}} 的 QR。

扩展命令

SillyTavern 扩展(包括内置、可下载和第三方扩展)都可以添加自己的斜杠命令。下面仅是官方扩展中功能的示例。该列表可能并不完整,请务必查看 /help slash 以获取最完整的可用命令列表。

  1. /websearch (query) — 在线搜索与指定查询匹配的网页片段,并将结果返回到管道。由 Web Search 扩展提供。
  2. /imagine (prompt) — 使用提供的提示词生成一张图像。由 Image Generation 扩展提供。
  3. /emote (sprite) — 通过名称的模糊匹配为活动角色设置一张立绘。由 Character Expressions 扩展提供。
  4. /costume (subfolder) — 为活动角色设置立绘集覆盖。由 Character Expressions 扩展提供。
  5. /music (name) — 按名称强制更改正在播放的背景音乐文件。由 Dynamic Audio 扩展提供。
  6. /ambient (name) — 按名称强制更改正在播放的环境音文件。由 Dynamic Audio 扩展提供。
  7. /roll (dice formula) — 向对话中添加一条隐藏消息,内容为掷骰子的结果。由 D&D Dice 扩展提供。

UI 交互

脚本还可以与 SillyTavern 的 UI 交互:在对话之间导航或更改样式参数。

角色导航

  1. /random — 打开一个随机角色的对话。
  2. /go (name) — 打开具有指定名称的角色的对话。首先搜索精确名称匹配,然后按前缀,再按子串。

UI 样式

  1. /bubble — 将消息样式设置为“气泡聊天”样式。
  2. /flat — 将消息样式设置为“扁平聊天”样式。
  3. /single — 将消息样式设置为“单文档”样式。
  4. /movingui (name) — 按名称激活一个 MovingUI 预设。
  5. /resetui — 将 MovingUI 面板状态重置为其原始位置。
  6. /panels — 切换 UI 面板的可见性:顶部栏、左右抽屉。
  7. /bg (name) — 使用模糊名称匹配查找并设置背景。会遵守对话背景的锁定状态。
  8. /lockbg — 锁定当前对话的背景图像。
  9. /unlockbg — 解锁当前对话的背景图像。

更多示例

生成对话摘要(作者 @IkariDevGIT)

stscript
/setglobalvar key=summaryPrompt Summarize the most important facts and events that have happened in the chat given to you in the Input header. Limit the summary to 100 words or less. Your response should include nothing but the summary. |
/setvar key=tmp |
/messages 0-{{lastMessageId}} |
/trimtokens limit=3000 direction=end |
/setvar key=s1 |
/echo Generating, please wait... |
/genraw lock=on instruct=off {{instructInput}}{{newline}}{{getglobalvar::summaryPrompt}}{{newline}}{{newline}}{{instructInput}}{{newline}}{{getvar::s1}}{{newline}}{{newline}}{{instructOutput}}{{newline}}The chat summary:{{newline}} |
/setvar key=tmp |
/echo Done! |
/setinput {{getvar::tmp}} |
/flushvar tmp |
/flushvar s1

按钮弹窗用法

stscript
/setglobalvar key=genders ["boy", "girl", "other"] |
/buttons labels=genders Who are you? |
/echo You picked: {{pipe}}

获取第 N 个斐波那契数(使用 Binet 公式)

stscript
/setvar key=fib_no 5 |
/pow 5 0.5 | /setglobalvar key=SQRT5 |
/setglobalvar key=PHI 1.618033 |
/pow PHI fib_no | /div {{pipe}} SQRT5 |
/round |
/echo {{getvar::fib_no}}th Fibonacci's number is: {{pipe}}

递归阶乘(使用闭包)

stscript
/let fact {: n=
    /if left={{var::n}} rule=gt right=1
        else={:
            /return 1
        :}
        {:
            /sub {{var::n}} 1 |
            /:fact n={{pipe}} |
            /mul {{var::n}} {{pipe}}
        :}
:} |

/input Calculate factorial of: |
/let n {{pipe}} |
/:fact n={{var::n}} |
/echo factorial of {{var::n}} is {{pipe}}