Skip to Content

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 "请生成一段关于此时此刻的天气描述,不用太长。"

关键机制

  1. Session Slots (会话槽): 系统硬编码了 10 个槽位 (xb1 - xb10)。每个槽位独立维护 AbortController,允许你随时通过 ID 终止特定任务,而不影响其他生成或主对话。

  2. Addon System (组件挂载): 在 xbgenraw 模式下,通过 _withTemporaryPromptToggles 方法,模块会创建一个“沙盒环境”。它临时劫持 SillyTavern 的 Prompt 构建管线,仅提取你指定的组件(如仅提取世界书,不提取聊天记录),实现极其精准的 Token 控制。

3.2 生成控制参数 (Generation Params)

用于微调 LLM 的输出风格。如果不传,默认使用 SillyTavern 全局设置。

参数名类型说明
temperature0-2.0随机性。值越高越疯癫。
max_tokensInt最大生成长度。注意:部分模型若此值过小会截断输出。
top_p0-1.0核采样。
top_kInt仅保留概率最高的 K 个 Token(Claude/Gemini 常用)。
presence_penalty-2.0~2.0存在惩罚(抑制重复话题)。
frequency_penalty-2.0~2.0频率惩罚(抑制重复用词)。
stopJSON停止词
需传入 JSON 字符串。例如:stop='["User:", "\n"]'

3.3 /xbgen 专用参数

该模式自动携带当前聊天记录。

参数名类型默认值说明
promptStringRequired指令内容
即 Unnamed Argument(不需要写 prompt=)。
asEnumsystem指令角色
system: 以系统指令插入。
user: 伪造用户发言。
assistant: 伪造 AI 续写。
positionEnumbottom插入位置
bottom: 插在对话最末尾。
history: 尝试插在最后一条 User/Char 消息之前(深层注入)。

/xbgenraw 专用参数

参数名类型格式示例详细说明
promptString-核心指令。通常放在最后。
topStringrole=content;...顶部复合注入
格式:system=指令;user=第一句;assistant=回复
支持分号分隔的多轮对话构建。
bottomStringrole=content;...底部复合注入
语法同 top,但在核心指令之后拼接。
top64 / bottom64Base64-Base64 复合注入
如果内容包含 ;= 等特殊字符,请先编码为 Base64 (URL-safe) 后传入此参数。
addonListworldInfo, ...组件挂载
逗号分隔。可用值:
- worldInfo: 激活世界书(需配合关键词)。
- chatHistory: 强制引入聊天历史。
- charDescription: 角色描述。
- charPersonality: 性格。
- scenario: 场景。
- personaDescription: 用户画像。
topsysString-快捷注入:顶层 System 消息。
topuserString-快捷注入:顶层 User 消息。
topassistantString-快捷注入:顶层 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?) -> string
  • streaming.getStatus(sessionId?) -> { isStreaming, text, sessionId }
  • streaming.cancel(sessionId) 取消该会话
  • streaming.startSession(id, prompt?) -> 标准化会话ID
  • streaming.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);