Youzini-afk 559312c1b6 fix: preserve isContextOnly context/target dividers in final prompt transcript rendering
Root cause: formatPromptMessageTranscript in prompt-builder.js ignored
isContextOnly, so context review and extraction target sections were
flattened into plain transcript even though the flag was correctly set
in intermediate layers. Additionally, userPromptSections (which
contained the dividers) was only a fallback that never reached the
final prompt when block-based profiles had user blocks.

Fix:
- getPromptMessageLikeDescriptor now preserves isContextOnly flag
- formatPromptMessageTranscript now inserts context/target section
  dividers when messages carry isContextOnly, ensuring the final
  LLM prompt always shows the distinction regardless of which
  rendering path (recentMessages, chatMessages, dialogueText) is used

Regression tests:
- prompt-builder-mixed-transcript: verify recentMessages block content
  includes context review and extraction target dividers
- extractor-phase3-layered-context: end-to-end test proving default
  extract profile + default structured mode produces final promptMessages
  with context/target section dividers
2026-04-12 13:04:04 +08:00
2026-04-09 22:05:45 +08:00

🧠 ST-BME — SillyTavern 图谱记忆插件

让 AI 真正记住你们的故事。

ST-BME 把对话中散落的角色、事件、地点、关系自动提取为记忆图谱,在下一轮生成前精准召回,让长期 RP 的角色不再"失忆"。


它能做什么

  • 🧩 自动提取 — 每次 AI 回复后,从上下文中抽取角色状态、事件、地点、规则、主线等结构化记忆
  • 🔍 智能召回 — 生成前根据当前对话自动检索最相关的记忆,注入 prompt
  • 🌐 图谱可视化 — 内置力导向图谱面板,直观查看记忆节点之间的关系
  • 🎨 4 套配色主题 — Crimson Synth / Neon Cyan / Amber Console / Violet Haze
  • 📱 手机端适配 — 底部 Tab Bar + 精简布局,手机也能用
  • 🔄 历史安全 — 删楼、编辑、切 swipe 时自动回滚恢复,不留脏记忆
  • 📦 不改酒馆本体 — 纯第三方扩展,即装即用

🧭 它是怎么工作的

整个插件可以拆成三件事:写入(把对话变成记忆)、读取(把记忆送回给 AI安全(出了问题能恢复)。

flowchart LR
    subgraph 写入["✏️ 写入:对话 → 记忆"]
        A["AI 回复了一条消息"] --> B["提取器读取最近几轮对话"]
        B --> C["让 LLM 识别出角色/事件/地点等"]
        C --> D["对比已有记忆,去重或更新"]
        D --> E["写入图谱 + 同步向量"]
    end

    subgraph 读取["🔍 读取:记忆 → 注入"]
        F["用户准备发送下一条"] --> G["用向量搜索找相关记忆"]
        G --> H["沿关系网络扩散,发现关联"]
        H --> I["综合打分排序"]
        I --> J["格式化后注入 prompt"]
    end

    subgraph 安全["🛡️ 安全:历史变动 → 恢复"]
        K["用户删楼/编辑/切 swipe"] --> L["检测到哪些楼层变了"]
        L --> M["回滚受影响的记忆和向量"]
        M --> N["从变动点重新提取"]
    end

    E -.-> F
    N -.-> E

写入阶段(对话 → 记忆)

每次 AI 回复后,插件会把最近几轮对话打包发给 LLM可以是你聊天用的同一个模型也可以单独配一个让它识别出"这段对话里出现了哪些角色、发生了什么事、在哪里、有什么新规则"等等。

识别出来的结果不是直接塞进去——插件会先跟已有记忆做对比(通过向量搜索找相似的),如果已经有了就更新,如果是真正的新内容才创建。

写入之后,还可能触发一些后续处理:

  • 压缩 — 太多类似的事件记忆会被合并
  • 进化 — 新信息会影响旧记忆的理解(比如"原来他当时是在演戏"
  • 概要 — 自动生成"之前发生了什么"的总结
  • 遗忘 — 很久没被用到的记忆降低优先级

读取阶段(记忆 → 注入)

当你准备发送下一条消息时,插件会抢在 AI 生成之前做一轮"召回"

  1. 向量搜索 — 根据当前对话内容,用 Embedding 找到语义最相关的记忆
  2. 图扩散 — 找到相关记忆后,沿着关系往外扩散(比如某个角色参与了某个事件,那个事件发生在某个地点...
  3. 混合评分 — 把向量相似度、图扩散能量、节点重要性、时间新旧综合排序
  4. 格式化注入 — 选出最终入围的记忆,分类整理后注入到 prompt 里

注入的内容分成两层:

  • 常驻层 — 规则、概要、主线这类始终需要的
  • 动态层 — 根据当前对话语境召回的

安全机制(历史变动 → 恢复)

这是很多记忆插件忽略的问题:如果用户删了某条消息、编辑了内容、或者切了 swipe已经基于那条消息提取的记忆就变成"脏"的了。

ST-BME 的处理方式是:

  1. 给每条已处理的消息计算 hash指纹
  2. 发现 hash 变了 → 找到最早受影响的位置
  3. 把那之后产生的记忆和向量全部回滚
  4. 从变动点重新走一遍提取流程

如果恢复日志损坏了,会退化为全量重建——慢一点但保证正确。


🚀 安装

方法一:通过 SillyTavern 扩展安装

  1. 打开 SillyTavern → 扩展 → 安装扩展
  2. 输入仓库地址:
    https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology
    
    注意:请粘贴仓库根地址,不要使用像 /graphs/code-frequency 这样的 GitHub 子页面地址。
  3. 刷新页面

方法二:手动安装

cd SillyTavern/data/default-user/extensions/third-party
git clone https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git st-bme

重启 SillyTavern 即可。


快速上手

  1. 打开面板 — 左上角 ≡ 菜单 →「🧠 记忆图谱」
  2. 启用插件 — 进入面板的「配置 → 功能开关」,打开 ST-BME 自动记忆
  3. 配置 Embedding — 进入「配置 → API 配置」,选择向量模式并填好模型
  4. 开始聊天 — 正常跟角色对话,插件会自动在后台提取和召回

最少配置: 只勾选"启用"就能跑起来。默认会复用你当前的聊天模型做提取。


📝 记忆类型

插件会把对话拆解成以下几种记忆节点:

类型 说明 举例
🧑 角色 角色的当前状态、性格、外貌变化 "小明因为淋雨感冒了"
事件 发生过的事 "河边的告白"
📍 地点 地点状态 "废弃实验室,门被锁上了"
📌 规则 世界观设定、约束 "魔法会消耗生命力"
🧵 主线 任务线/剧情线 "寻找失踪的项链"
📜 概要 自动生成的前情提要
💭 反思 长期规律总结 "他们经常在夕阳下聊天"

这些节点之间还会建立关系(参与、发生在、推动、矛盾、更新等),形成一张完整的记忆网络。


🔧 设置说明

记忆 LLM

用来做提取、压缩、进化、概要等任务的模型。

  • 留空 → 复用当前 SillyTavern 的聊天模型(最简配置)
  • 填写 → 你可以指定一个独立的 OpenAI-compatible 模型专门处理记忆

Embedding向量搜索

向量搜索是"智能召回"的关键。支持两种模式:

后端模式(推荐)

走 SillyTavern 后端的向量 API最稳定

  • 支持 OpenAI / Cohere / Mistral / Ollama / LlamaCpp / vLLM 等
  • 在设置面板选择「后端向量源」,填好模型名即可
  • 不需要单独填 API Key复用酒馆已有的

直连模式

如果你需要完全独立的 Embedding 服务(比如酒馆后端不支持的源):

  • 填入 Embedding API 地址、Key、Model
  • 插件直接请求你的 Embedding 服务
  • 注意浏览器跨域问题CORS

切换向量模式/模型后,建议点一次"重建向量"。

提取设置

设置 默认 说明
每 N 条回复提取 1 每几条 AI 回复做一次提取
提取上下文轮数 2 提取时向前看几轮对话
启用近邻对照 写入前对比现有记忆,避免重复
启用记忆进化 新记忆会影响旧记忆理解
启用自动概要 定期生成前情提要
启用反思 让 AI 总结长期模式
启用主动遗忘 太久没用的记忆降低优先级

召回设置

设置 默认 说明
提取上下文轮数 2 按轮计的提取上下文,通常约等于向前补 4 层普通消息
向量预筛 Top-K 20 向量预筛阶段最多保留多少个候选
LLM 精排候选池 30 进入 LLM 精排阶段前的候选池大小
LLM 最终选择上限 8 LLM 精排后最多保留多少条记忆
图扩散 Top-K 100 图扩散阶段最多保留多少个候选
注入深度 9999 当前走 IN_CHAT@Depth数值越大越靠前插入
Token 估算 注入内容的 token 估算(仅用于展示,当前版本不强制裁剪)

🖥️ 操控面板

从左上角 ≡ 菜单点「🧠 记忆图谱」打开面板。

总览 Tab

  • 统计数据(活跃节点、边、归档数、碎片率)
  • 运行状态(聊天 ID、向量状态、历史状态
  • 最近提取 / 召回的记忆

记忆 Tab

  • 搜索和筛选记忆节点
  • 点击节点查看详情
  • 支持按类型过滤

注入 Tab

  • 预览当前注入内容
  • 查看 token 消耗

操作 Tab

  • 手动提取 — 立即从当前对话提取
  • 手动压缩 — 合并重复/过时记忆
  • 执行遗忘 — 主动降级低价值记忆
  • 更新概要 — 重新生成前情提要
  • 导出 / 导入图谱
  • 重建图谱 — 从当前聊天重新提取全部记忆
  • 重建向量 — 重建全部向量索引
  • 强制进化 — 让新记忆影响旧记忆

配置 Tab

配置页现在是一个完整的工作区,分成 5 个子页:

  • API 配置
  • 功能开关
  • 详细参数
  • 系统提示词
  • 面板外观

桌面端会显示左侧竖向子导航,右侧显示宽版配置表单;移动端则改成顶部横向子页切换。 检索流水线现在可以分别配置向量预筛、图扩散、混合评分和 LLM 精排。 注入深度使用 IN_CHAT@Depth 语义,默认 9999 表示尽量靠前插入,减少对最近几层对话的直接控制感。

图谱可视化

桌面端右侧大区域显示力导向图谱,节点可拖拽、缩放、点击查看详情。支持 4 套配色主题切换。


🔄 历史安全

这是最重要的功能之一。

当你在 SillyTavern 里做以下操作时:

  • 删除某条消息
  • 编辑某条消息
  • 切换 swipe

插件会自动检测到历史发生了变化,然后:

  1. 止损 — 停止当前推进,清空可能失效的注入
  2. 回滚 — 找到受影响的批次,删除相关记忆和向量
  3. 恢复 — 从变动点重新提取

这样你就不用担心"改了历史但记忆还留着错的内容"的问题。


📋 手动操作速查

操作 说明
手动提取 不等自动触发,立刻提取当前对话
手动压缩 把重复/冗余的事件合并
执行遗忘 降低长期未使用记忆的优先级
更新概要 重新生成全局前情提要
导出图谱 下载当前图谱 JSON不含向量
导入图谱 导入图谱文件(导入后需重建向量)
重建图谱 ⚠️ 清空现有图谱,从聊天记录重新提取
重建向量 重建全部节点的向量索引
范围重建向量 只重建指定楼层范围内的向量
强制进化 让新记忆深度影响旧记忆认知

🏗️ 开发者参考

文件结构

ST-BME/
├── index.js           # 主入口:事件绑定、流程调度、历史恢复
├── bme-db.js          # IndexedDB(Dexie) 持久化层(本地主存储)
├── bme-sync.js        # 跨设备同步(/user/files/ 镜像 + 合并)
├── bme-chat-manager.js# chatId -> BmeDatabase 生命周期管理
├── lib/dexie.min.js   # vendored Dexie浏览器端动态注入加载
├── graph.js           # 图数据模型、序列化、版本迁移
├── extractor.js       # 记忆提取、概要、反思
├── retriever.js       # 向量候选、图扩散、混合评分、召回
├── injector.js        # 召回结果格式化注入
├── runtime-state.js   # 运行时状态:楼层 hash、dirty 标记、恢复日志
├── recall-persistence.js # 持久召回记录message.extra.bme_recall不变
├── recall-message-ui.js # 消息级召回卡片 UI子图渲染 + 侧边栏编辑,不变)
├── vector-index.js    # 向量索引管理backend / direct 双模式)
├── embedding.js       # 直连 Embedding API 封装
├── llm.js             # 记忆 LLM 请求封装
├── compressor.js      # 层级压缩与遗忘
├── diffusion.js       # 图扩散算法
├── dynamics.js        # 动态调节(重要度衰减等)
├── schema.js          # 节点类型定义
├── themes.js          # 4 套主题配色
├── graph-renderer.js  # Canvas 力导向图谱渲染器
├── panel.js           # 操控面板交互逻辑
├── panel.html         # 面板 HTML 模板
├── style.css          # 全部样式
├── manifest.json      # SillyTavern 扩展清单
└── tests/             # 测试脚本

数据存储

  • 图谱主存储(本地优先)IndexedDBDexie
    • DB 名固定:STBME_{chatId}
    • 运行时主读取路径:优先 IndexedDB
  • 跨设备同步镜像 → SillyTavern 文件 API /user/files/
    • 文件名固定:ST-BME_sync_{sanitizedChatId}.json
    • 冲突合并:updatedAt 新者胜tombstone deletedAt 优先;lastProcessedFloor/extractionCountmax
    • meta 为同步 JSON 顶层首字段,revision 全程单调递增
  • 兼容兜底(迁移窗口)chat_metadata.st_bme_graph
    • 仅用于 legacy 兼容与迁移,不再是主路径
  • 墓碑tombstones → 保留期固定 30 天
  • 插件设置 → SillyTavern 的 extension_settings.st_bme
  • 向量索引 → 后端模式走酒馆 API直连模式存在节点内
  • 召回持久注入(不变)chat[x].extra.bme_recall(消息级)

兼容迁移策略legacy metadata → IndexedDB

  • 触发:聊天加载/切换后,若目标 STBME_{chatId} 为空且存在 legacy chat_metadata 图谱
  • 行为:自动一次性迁移到 IndexedDB并立即尝试同步到 /user/files/
  • 幂等:
    • migrationCompletedAt > 0,跳过
    • 若 IndexedDB 已非空,跳过
  • 迁移记录:
    • migrationCompletedAt
    • migrationSource(默认 chat_metadata
    • legacyRetentionUntil30 天)

事件挂载

SillyTavern 事件 做什么
CHAT_CHANGED IndexedDB 优先加载 + 自动同步
GENERATION_AFTER_COMMANDS AI 回复后提取记忆
GENERATE_BEFORE_COMBINE_PROMPTS 生成前召回并注入
MESSAGE_RECEIVED 触发图谱持久化IndexedDB 主写)
删除 / 编辑 / Swipe 触发历史变动检测与恢复

召回流水线

用户输入 → 向量预筛 → 图扩散 → 混合评分 → [可选 LLM 精排] → 场景重构 → 分桶注入

注入格式

召回结果分成两层注入:

  • Core(常驻):规则、概要、主线
  • Recalled(动态):根据当前对话召回

每层内进一步按用途分桶:当前状态 / 情景事件 / 反思锚点 / 规则约束。

持久召回注入(message.extra.bme_recall

从本版本开始,召回注入支持消息级持久化,存放在对应用户楼层:

  • 路径:chat[x].extra.bme_recall
  • 主要字段:
    • version
    • injectionText
    • selectedNodeIds
    • recallInput
    • recallSource
    • hookName
    • tokenEstimate
    • createdAt / updatedAt
    • generationCount在该持久注入被实际用作生成回退时递增)
    • manuallyEdited(仅表示来源是否为人工编辑)

注入优先级(避免双重注入):

  1. 本轮有新召回成功:仅使用新召回注入(临时注入),并覆盖写入目标用户楼层的 bme_recall
  2. 本轮无新召回结果:仅从“当前生成对应的用户楼层”读取 bme_recall 作为回退注入。
  3. 两者都无:清空注入。

manuallyEdited 不参与优先级判断,不会强制覆盖系统召回。

消息级 UI

  • 带有 bme_recall 的用户消息会显示内联卡片(含用户消息 + 🧠 召回条 + 记忆数 badge
  • 显示前提:必须同时满足 用户楼层message.extra.bme_recall 存在、且 injectionText 为非空字符串。
  • 点击召回条展开,显示力导向子图(仅渲染被召回的节点和它们之间的边,复用 GraphRenderer)。
  • 子图中节点可拖拽/缩放,点击节点打开右侧边栏查看节点详情。
  • 操作按钮(展开态底部):
    • ✏️ 编辑:打开侧边栏编辑注入文本(实时 token 计数),保存后标记 manuallyEdited=true
    • 🗑 删除:二次确认(按钮变红 3s 超时重置),确认后移除持久召回记录。
    • 🔄 重新召回:重新执行召回并覆盖记录,manuallyEdited 重置为 false
  • 不再使用 prompt() / alert() / confirm() 浏览器原生对话框。
  • 当聊天 DOM 延迟插入时,插件会执行有界重试 + 短生命周期 MutationObserver 补偿,避免单次刷新错过挂载。

兼容性说明:

  • 旧聊天(无 extra 或无 bme_recall)会自动按“无持久记录”处理,不会报错。
  • Recall Card 依赖消息楼层存在稳定索引属性(如 mesid / data-mesid / data-message-id),不会再回退到 DOM 顺序猜测,以避免误挂载到错误楼层。
  • 第三方主题至少需要保留 #chat .mes 外层消息节点;卡片会优先尝试挂载到 .mes_block,其次 .mes_text 的父节点,最后回退到 .mes 根节点。
  • 若第三方主题完全移除了这些锚点或稳定索引属性,插件会选择跳过挂载并输出 [ST-BME] 调试日志,而不是静默挂到错误位置。

排障建议(数据存在但 UI 不显示时):

  1. 打开浏览器控制台,搜索 [ST-BME] Recall Card UI[ST-BME] Recall Card persist 调试日志。
  2. 确认目标楼层是否为用户消息,并检查 message.extra.bme_recall.injectionText 是否非空。
  3. 检查消息 DOM 是否仍带有稳定楼层索引属性(mesiddata-mesiddata-message-id 等)。
  4. 若使用第三方主题,确认消息节点仍包含 #chat .mes,且消息内容区域未完全移除 .mes_block / .mes_text 相关结构。
  5. 如果聊天是异步渲染的,等待一小段时间后再次观察;插件会在短时间内自动补偿重试,而不是只尝试一次。

⚠️ 已知限制

  1. 记忆质量取决于 LLM — 模型提取不准,记忆也会不准
  2. 直连模式有跨域风险 — 浏览器的 CORS 限制可能导致请求失败
  3. 后端向量仅支持酒馆已有 provider — 不在列表里的需要用直连
  4. 恢复优先正确性 — 批次日志缺失时会退化为全量重建,可能较慢

📄 License

AGPLv3 — 详见 LICENSE

Description
ST-BME(Bionic Memory Ecology)是一个 SillyTavern 第三方前端扩展。它会把长期聊天中出现的角色、事件、地点、规则、主线、反思和总结抽取成一张可视化记忆图谱,并在下一轮生成前自动召回最相关的记忆注入 prompt。
Readme AGPL-3.0 6.1 MiB
Languages
JavaScript 93.3%
HTML 3.1%
CSS 3%
Rust 0.6%