Files
Agent-System-Documents/docs/records/2026-10-04-sillytavern-review.md
T
weichen f3f823ef8d Record the SillyTavern design borrow and what shipped
Why: The comparison has to say what was borrowed, what was refused, and which product behaviors changed, without copying upstream text.
Evidence: The record lists the verified security gaps and the features implemented in this task. Proposal section 2.5 links to it.
Impact: Deferred and rejected items stay deferred and rejected. Adopted context and secret behavior is no longer described as a later batch.
Refs: feature/sillytavern-review
2026-10-04 18:20:10 +00:00

6.4 KiB
Raw Blame History

调研:SillyTavern 1.19.0

  • 状态:记录(2026-10-04);owner:weichen;任务:Dev 仓 feature/sillytavern-review
  • 对象:SillyTavern/SillyTavern 06bde939(release 1.19.0,2026-09-14)。AGPL-3.0,另有标注 GPLv3 的 RisuAI 片段。按固定提交只读对照,不建长期克隆,不拷源码。
  • 本仓事实以任务起点 Dev 4fd03eee 为准。前序:2026-10-03-pi-zcode-second-pass.md、2026-10-02-agent-stack-completion.md。
  • 规则与 pi / ZCode 两份记录相同:只借设计。不拷代码、提示词或界面文案。本仓各子仓没有 LICENSE、以单二进制分发,拷入 AGPL 代码会让整个产品落入 AGPL,网络使用条款同样生效。

结论先说:上下文工程值得借的是「统一注入登记 + 按预算组装」,平台侧值得借的是带标签的密钥、设置快照、以及听口令时拒绝无认证的对外监听。本仓的 SSRF 与账户已经更严,不借。安全缺口(输出未清洗、缺响应头、不校验来源、对外监听无认证)和下面的采纳项在同一任务里落地。扩展系统、脚本宏、群聊、角色卡、自定义 CSS、JSONL 存储、删除消息和自动触发的快捷回复不借。


1. 来源现在是什么

SillyTavern 把要注入模型的文本放进统一登记表:每条有位置、深度、角色、顺序和扫描范围,再由一次按 token 预算的组装把它们收成请求。预算不够时只裁最老的历史。世界书、作者注、记忆和向量库都走这条登记,而不是各写一条拼接。

平台侧用带标签的多密钥(同一服务一把生效)、设置快照、以及「监听且未开认证就拒绝启动」。请求侧还有 Host / Origin 检查。聊天用 JSONL,带滑动候选和父指针检查点。扩展是未沙箱的同源脚本。

本仓已经有更严的 SSRF(fetch_url 和链接预览在拨号时校验解析出的 IP,黑名单含 0.0.0.0/8 和 100.64.0.0/10)和账户(argon2id、重置密码即吊销服务端会话、登录限流)。这些不改。

2. 对照时核实的安全缺口

行号是任务起点那一版本仓源码,实施后位置会移动。

缺口 起点位置 处置
模型输出、卡片和文档预览把 HTML 直接写入页面。marked 15.0.12 不清洗 Frontend Conversation.tsx、previewsetup.ts;UI UIRenderer.tsx、FilePreview.tsx @ui 的 safeHtml 成为唯一写入点。外部图片改成「外部图片 · 域名」链接。同源地址不再用 allow-same-origin 内嵌
没有 CSP / nosniff。HTML 工件按页面直出 Backend server.go 的静态页与 artifactContent 页面 CSP、nosniff、Referrer-Policy。html/svg/xml 工件加 sandbox
不校验 Origin / Host。SQLite 档默认不开认证 app.go 的认证开关 RequestGuard 包在组合根。写请求核对 Origin 或 Sec-Fetch-Site。未开认证时 Host 只允许 IP、localhost 和 allowedHosts
对外监听且未开认证时照常启动 .env.dev 曾把 dev 听在 0.0.0.0:8788 非回环、未开认证、又没有 --insecure-listen 时,打开数据库之前拒绝启动

3. 采纳并已在本任务实施

原先记为后续批次。任务范围改为同一任务做完,落点如下。

主题 上游设计 本仓落点
密钥独立存储 src/endpoints/secrets.js:多把带标签的 key,一把生效,读出只给掩码,迁移前备份 secret_keys。GET 只返回掩码。启动时先写设置快照,再把 models.*.apiKey 和 tools.tavily.apiKeys 迁入并从表单设置清掉。运行时优先用生效密钥
设置快照 启动和保存后留档。上游恢复前不留档,这一点不照搬 settings_snapshots。正文与上一份相同则跳过。保存间隔短于 10 分钟则跳过
个人指令与会话要求 persona 与 Author's Note context.userInstructions 与会话 sessionNote。经 agent-native reminders.Attach 附在最后一条用户或工具消息上,系统前缀不动
条件注入 世界书的关键词、次关键词逻辑、扫描深度、常驻、顺序、预算 context_entries。matchModule 仍只看本轮子串;条目在组装末尾另判
上下文快照 提示词分段视图 ContextObservation 增加 sections 与 prefixHash。界面折叠条显示估算 token 和前缀哈希
估算口径 上游按 token 预算裁历史 统一为 UTF-8 字节 / 3.35,再用上一请求的 prompt token 校准。context.contextWindow 进入 schema,默认 32000,只丢掉最早的历史
消息整形 src/prompt-converters.js PrepareMessages。merge 合并相邻同角色。strict 再把第一条之后的 system 收成 user。工具调用和结果不合并
复制、排除、候选回复 滑动候选与检查点 复制在界面完成。排除按消息及其同一 run 成组,记录仍在。重新生成不删旧回复,写入同一 variantGroup,并按用户消息做文件回滚
用户 / 命令 快捷指令,但不做自动触发 user_commands。名称符合 user.<slug> 时登记为 agent-command 的 SourceUser。发送时把模板展开成普通用户消息

4. 暂缓

  • Claude 缓存断点:等 Anthropic 原生协议。
  • 编辑重发与会话分支:等候选回复用过再看父指针方案。
  • 窗口外同会话召回、用户级知识库:等会话经常超出窗口再看。
  • 世界书的概率、递归、包含组和定时效果。
  • 密钥静态加密。
  • i18n:真要做时不能拿原文当键。
  • 出站代理:只能用于模型端点。
  • 画像导入导出。

5. 拒绝

  • 拷代码、提示词和界面文案。
  • 扩展系统(同源 JS、没有沙箱)和带完整 Node 权限的服务器插件。
  • STscript 和宏语言。
  • 群聊、角色卡、视觉小说。
  • 自定义 CSS 和平滑打字流式:与 DESIGN.md 冲突。
  • JSONL 整文件覆盖和逐行扫描搜索。
  • 删除消息。用「排除出上下文」代替。
  • 自动执行的快捷回复触发器。

6. 不回退的既有决定

认证默认仍是 auto:显式 on,或不是 off 且配置了 DSN,才开认证。不加 CSRF token,不加 CSP 上报端点。不改 SSRF 黑名单,不改账户哈希和会话吊销。