1. Overview (简介)
StreamingGeneration 是 SillyTavern 的异步多线程生成引擎。它允许开发者通过 Slash Commands 或 JavaScript API,在不阻塞主聊天流的情况下,并行调用 LLM 执行后台任务(如摘要生成、思维链推理、世界观自动补全)。
- 并行处理:支持最多 10 个并发生成会话 (
1-10)。 - 上下文控制:提供“全自动上下文” (
/xbgen) 和“像素级拼接” (/xbgenraw) 两种模式。 - 协议无关:底层抹平了 OpenAI, Claude, MakerSuite, DeepSeek 等不同 API 的差异。
⚡ Quick Start
在输入框执行以下命令,观察后台生成的控制台日志或网络请求,界面不会被阻塞:
/xbgen id=xb1 model=gpt-3.5-turbo "请生成一段关于此时此刻的天气描述,不用太长。"关键机制
-
Session Slots (会话槽): 系统硬编码了 10 个槽位 (
xb1-xb10)。每个槽位独立维护AbortController,允许你随时通过 ID 终止特定任务,而不影响其他生成或主对话。 -
Addon System (组件挂载): 在
xbgenraw模式下,通过_withTemporaryPromptToggles方法,模块会创建一个“沙盒环境”。它临时劫持 SillyTavern 的 Prompt 构建管线,仅提取你指定的组件(如仅提取世界书,不提取聊天记录),实现极其精准的 Token 控制。
3.2 生成控制参数 (Generation Params)
用于微调 LLM 的输出风格。如果不传,默认使用 SillyTavern 全局设置。
| 参数名 | 类型 | 说明 |
|---|---|---|
temperature | 0-2.0 | 随机性。值越高越疯癫。 |
max_tokens | Int | 最大生成长度。注意:部分模型若此值过小会截断输出。 |
top_p | 0-1.0 | 核采样。 |
top_k | Int | 仅保留概率最高的 K 个 Token(Claude/Gemini 常用)。 |
presence_penalty | -2.0~2.0 | 存在惩罚(抑制重复话题)。 |
frequency_penalty | -2.0~2.0 | 频率惩罚(抑制重复用词)。 |
stop | JSON | 停止词。 需传入 JSON 字符串。例如: stop='["User:", "\n"]'。 |
3.3 /xbgen 专用参数
该模式自动携带当前聊天记录。
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
prompt | String | Required | 指令内容。 即 Unnamed Argument(不需要写 prompt=)。 |
as | Enum | system | 指令角色。system: 以系统指令插入。user: 伪造用户发言。assistant: 伪造 AI 续写。 |
position | Enum | bottom | 插入位置。bottom: 插在对话最末尾。history: 尝试插在最后一条 User/Char 消息之前(深层注入)。 |
/xbgenraw 专用参数
| 参数名 | 类型 | 格式示例 | 详细说明 |
|---|---|---|---|
prompt | String | - | 核心指令。通常放在最后。 |
top | String | role=content;... | 顶部复合注入。 格式: system=指令;user=第一句;assistant=回复。支持分号分隔的多轮对话构建。 |
bottom | String | role=content;... | 底部复合注入。 语法同 top,但在核心指令之后拼接。 |
top64 / bottom64 | Base64 | - | Base64 复合注入。 如果内容包含 ; 或 = 等特殊字符,请先编码为 Base64 (URL-safe) 后传入此参数。 |
addon | List | worldInfo, ... | 组件挂载。 逗号分隔。可用值: - worldInfo: 激活世界书(需配合关键词)。- chatHistory: 强制引入聊天历史。- charDescription: 角色描述。- charPersonality: 性格。- scenario: 场景。- personaDescription: 用户画像。 |
topsys | String | - | 快捷注入:顶层 System 消息。 |
topuser | String | - | 快捷注入:顶层 User 消息。 |
topassistant | String | - | 快捷注入:顶层 Assistant 消息。 |
命令示例
1. 带上下文,默认走主 API/模型:
/xbgen 继续写刚才的故事
2. 选择后端与模型:
/xbgen api=gemini model=gemini-2.5-pro 续写这段文本
3. 插入到聊天历史底部:
/xbgen position=history 作为系统补充一段规则
4. 纯提示,拼装聊天历史和预设,插入到历史底部:
/xbgenraw addon=preset,chatHistory position=history 继续摘要这场对话
5. 纯提示,拼装世界书与角色卡字段,并加顶/底提示:
/xbgenraw addon=worldInfo,charDescription,charPersonality,scenario,personaDescription bottomuser=请严格遵守上述规范 topuser=下面是提供给你的资料,请按资料回答问题: 输出正式答案UI前端交互
由于是流式生成,实际上直接在酒馆用管道传输的文本是很短和不完整的,故这两个命令主要用于UI侧
UI侧
// 通过 STscript 执行命令(返回会话ID)
const sessionId = await STscript('/xbgen 写一个故事')
// 获取对象
const streaming = window.xiaobaixStreamingGeneration || window.parent.xiaobaixStreamingGeneration
// 监听事件通知(生成完成)
// window message:type = 'xiaobaix_streaming_completed'
// payload: { finalText, originalPrompt, sessionId }其他常用方法:
streaming.getLastGeneration(sessionId?)->stringstreaming.getStatus(sessionId?)->{ isStreaming, text, sessionId }streaming.cancel(sessionId)取消该会话streaming.startSession(id, prompt?)-> 标准化会话IDstreaming.getLastSessionId()-> 最近一次会话ID
并发与展示示例
//启动并发任务
const s1 = await STscript('/xbgen id=1 继续剧情')
const s2 = await STscript('/xbgenraw id=2 api=claude 总结全文')
//轮询展示(建议 80-200ms 间隔)
setInterval(() => {
el1.textContent = streaming.getLastGeneration(1);
el2.textContent = streaming.getLastGeneration(2);
}, 120)
//取消某个任务(会话id为2)
streaming.cancel(2)快速开始
认识两条流式命令:
/xbgenraw:用“原始提示”为主,按需拼接上下文(更自由)/xbgen:走完整上下文(更贴近日常对话)
本篇将会用两条命令 /xbgenraw 与 /xbgen 做“流式生成”,并搭一个可用的前端小页面来测试与控制生成过程(开始、停止、查看完成事件)
命令用法
仅用原始提示的流式生成(自由拼装):
/xbgenraw as=system id=1 你的提示词
使用完整上下文管线的流式生成:
/xbgen id=xb2 你的提示词
常用命令参数
# 指定模型和后端
/xbgenraw api=openai model=gpt-4 写个故事
# 指定角色
/xbgen as=system 解释概念
# 添加上下文(仅xbgenraw)
/xbgenraw addon=worldInfo,preset 基于设定写故事sessionId 规则(参数的 id=):
- 数字 1~10
- 同时跑多个会话互不影响(示例里用 1 与 2)
会话与状态
在前端可用以下方法操作会话(与示例相同):
- 获取最新文本(流式累计结果):
window.parent.xiaobaixStreamingGeneration.getLastGeneration(sessionId) - 查询状态:
window.parent.xiaobaixStreamingGeneration.getStatus(sessionId) - 停止当前会话:
window.parent.xiaobaixStreamingGeneration.cancel(sessionId)
示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>流式生成</title>
<style>
body { font-family: Arial; padding: 20px; background: white;}
.box { border: 1px solid #ccc; padding: 15px; margin: 10px 0; }
input { width: 300px; padding: 8px; }
button { padding: 8px 15px; margin: 5px; background: #007cba; color: white; border: none; }
.output { background: #f5f5f5; padding: 10px; height: 150px; overflow-y: auto; font-family: monospace; }
</style>
</head>
<body>
<h2>XB流式生成测试</h2>
<div className="box">
<h3>测试1: xbgenraw</h3>
<input id="input1" value="写个小故事" placeholder="输入提示词">
<button onclick="test1()">开始</button>
<button onclick="stop1()">停止</button>
<div className="output" id="output1">等待...</div>
</div>
<div className="box">
<h3>测试2: xbgen</h3>
<input id="input2" value="解释AI" placeholder="输入提示词">
<button onclick="test2()">开始</button>
<button onclick="stop2()">停止</button>
<div className="output" id="output2">等待...</div>
</div>
<script>
let timer1, timer2;
// 测试1: 使用xbgenraw,会话ID=1
async function test1() {
const prompt = document.getElementById('input1').value;
if (!prompt) return;
document.getElementById('output1').textContent = '生成中...';
// 执行命令
await STscript(`/xbgenraw as=system id=1 ${prompt}`);
// 开始监控会话1
timer1 = setInterval(() => {
const text = window.parent.xiaobaixStreamingGeneration.getLastGeneration(1);
document.getElementById('output1').textContent = text || '生成中...';
}, 300);
}
function stop1() {
clearInterval(timer1);
window.parent.xiaobaixStreamingGeneration.cancel(1);
}
// 测试2: 使用xbgen,会话ID=2
async function test2() {
const prompt = document.getElementById('input2').value;
if (!prompt) return;
document.getElementById('output2').textContent = '生成中...';
// 执行命令
await STscript(`/xbgen id=2 ${prompt}`) ;
// 开始监控会话2
timer2 = setInterval(() => {
const text = window.parent.xiaobaixStreamingGeneration.getLastGeneration('2');
document.getElementById('output2').textContent = text || '生成中...';
}, 300);
}
function stop2() {
clearInterval(timer2);
window.parent.xiaobaixStreamingGeneration.cancel('2');
}
// 监听完成事件
window.addEventListener('message', (e) => {
if (e.data?.type === 'xiaobaix_streaming_completed') {
const id = e.data.payload.sessionId;
const text = e.data.payload.finalText;
if (id === 1) {
clearInterval(timer1);
document.getElementById('output1').textContent = '完成!\n\n' + text;
}
if (id === '2') {
clearInterval(timer2);
document.getElementById('output2').textContent = '完成!\n\n' + text;
}
}
});
</script>
</body>
</html>// 执行命令
await STscript(`/xbgenraw id=1 ${prompt}`);
// 获取结果
const text = window.parent.xiaobaixStreamingGeneration.getLastGeneration(1);
// 停止生成
window.parent.xiaobaixStreamingGeneration.cancel(1);