Skip to content

架构设计

这份文档面向想要理解系统设计、参与开发或进行二次开发的开发者。


1. 整体思路

传统的 AI RP 工具(比如 SillyTavern)是以「角色卡」为中心的前端应用。TavernHeadless 走了一条不同的路:

  • 后端优先:核心逻辑全部跑在服务端,前端只是一个可选的管理界面。
  • 项目即角色卡:不再需要一张 PNG 角色卡文件。一个 TavernHeadless 项目本身就包含了角色设定、世界观、预设、正则规则等所有内容。
  • 兼容但不受限:可以导入酒馆的预设和世界书直接用,但也提供了更强大的原生能力。

系统分为三个主要部分:

text
┌─────────────────────────────────────────────────┐
│                  apps/web                       │
│              管理前端(可选)                      │
└──────────────────────┬──────────────────────────┘
                       │ HTTP / WebSocket
┌──────────────────────▼──────────────────────────┐
│                  apps/api                       │
│              Fastify 后端服务                     │
│  ┌───────────────────────────────────────────┐   │
│  │            packages/core                  │   │
│  │  消息管理 · 变量系统 · 提示词编排            │   │
│  │  LLM 调度 · 记忆系统 · 事件总线             │   │
│  └───────────────────────────────────────────┘   │
│  ┌───────────────────────────────────────────┐   │
│  │      packages/adapters-sillytavern        │   │
│  │  预设导入 · 正则导入 · 世界书导入            │   │
│  └───────────────────────────────────────────┘   │
└─────────────────────────────────────────────────┘

2. 消息结构:会话 → 楼层 → 消息页

这是整个系统的数据骨架。我们把一次聊天拆成三层:

会话(Session)

一次完整的聊天。创建会话时会绑定预设、世界书、正则规则、模型配置等。

Workspace / Project 阶段一归属

阶段一里,每个新 Session 都会写入 workspace_idproject_id。这两个字段用于服务端隔离、查询和审计。

为了保持旧客户端可用,普通客户端仍然不需要知道 Workspace / Project:

  • POST /sessions 不传 project_id 仍然成功。
  • 服务端会使用当前账号默认 Workspace,并为新 Session 创建 session_default Project。
  • 只有高级调用方需要把 Session 放进已有 Project 时,才传 project_id
  • GET /sessions/:id 和列表响应默认不返回 workspace_idproject_id
  • Prompt Asset、角色、用户卡、LLM 配置、工具定义和 MCP 配置的旧列表接口默认只读当前账号默认 Workspace。

楼层(Floor)

一次「回合」。你发一条消息、AI 回一条消息,这就是一个楼层。

楼层的关键能力:

  • 分支:从任意楼层新开一条故事线,不影响原来的内容。
  • 状态机:每个楼层有状态——draft(草稿)→ generating(生成中)→ committed(已提交)或 failed(失败)。
  • 提交后不可改:一旦提交,楼层内容就锁定了。想改?新建一个楼层。

消息页(MessagePage)

楼层内的一个「版本」。比如你点了重新生成(regen),就会在同一个楼层里新建一个消息页,旧的还在。

消息页的作用:

  • 保存重试/重新生成的不同版本。
  • 流式生成时先写到消息页里,生成完再标记为生效。
  • 每个楼层有且只有一个「当前生效页」。

消息(Message)

最小单位。每条消息有角色(user / assistant / system / narrator)、内容、格式等。

关系图

text
Session
 └── Floor 1 (committed)
 │   ├── MessagePage v1 (inactive)    ← 第一次生成的版本
 │   │   ├── Message: user "你好"
 │   │   └── Message: assistant "你好呀"
 │   └── MessagePage v2 (active)      ← 重新生成后的版本
 │       ├── Message: user "你好"
 │       └── Message: assistant "嗨!很高兴见到你"
 └── Floor 2 (generating)
     └── MessagePage v1 (active)
         ├── Message: user "讲个故事吧"
         └── Message: assistant "从前有座山..."  ← 正在流式生成

3. 变量系统

变量是用来存储叙事状态的容器。比如「角色当前的心情」「某个物品是否被拿走了」「好感度数值」等等。

五个层级

层级作用域典型用途生命周期
全局(global)整个项目世界观设定、全局开关永久
会话(chat)一次聊天好感度、已触发事件会话期间
分支(branch)一条分支分支内持续状态、分支走向分支存在期间
楼层(floor)一个回合本回合判定结果、临时标记楼层提交后冻结
页(page)一次生成尝试生成中间状态、工具调用暂存生成完成后决定去留

读写规则

读取时,按从小到大的顺序查找,找到就停:

text
page → floor → branch → chat → global

比如读取变量 mood:先看当前页有没有,没有就看楼层,再看分支,再看会话,最后看全局。

写入时,默认写到 page(最小范围)。这是一种保护机制——页级变量就像沙箱,不会意外改到全局状态。

提升:如果确实需要把变量保存到更高层级,需要显式提升。比如一次回合结束后,把 page.mood 提升到 branch.moodchat.mood。这个过程由编排器控制,不会默默发生。

为什么要有「页」这一层?

主要解决重新生成时的隔离问题。假设 AI 生成了一个回复并且写了 mood = happy,你觉得不好点了重试,新的生成写了 mood = sad。如果没有页级变量,两次生成会互相覆盖。有了页级变量,每次生成都在自己的沙箱里,只有你选定的那个版本才会被提升到楼层、分支或会话。

为什么要有「分支」这一层?

RP 中经常出现分支:从某个楼层开始,同一段故事可以走出完全不同的方向。假设聊到第 10 楼时,你想试试"角色 A 选择战斗"和"角色 A 选择逃跑"两种走向。如果没有分支级变量,两条分支共用同一份 chat 级状态——一条分支里"角色 A 受伤了",另一条里"角色 A 安然无恙",双方的变量会互相覆盖。

有了 branch 这一层,每条故事线都有独立的状态空间。分支 A 和分支 B 各自积累自己的变量,不会干扰对方,也不会污染主线。


4. 提示词系统

提示词系统负责把预设、世界书、变量、聊天记录等拼成一份完整的提示词,发给大模型。

双轨设计

我们提供两条路径:

路径一:酒馆兼容模式(compat)

直接导入酒馆的预设和世界书,按照酒馆的方式拼接提示词。适合已经有成熟预设的用户,导入即用。

这条路径又分两档:

  • compat_strict:严格复刻酒馆行为,变量展开、世界书触发、拼接顺序都尽量一致。
  • compat_plus:在兼容基础上加入少量已声明的增强能力,比如记忆注入,但不改变兼容层的基本边界。

路径二:原生编排模式(native)

当前会走原生编排路径。API 主链会把导入的 ST preset 转换成一种内部中间结构(Native Imported Group),再通过图编译器(PromptGraphDocument → PromptIR)输出统一的中间格式。

这一模式不承诺 ST preset 的保真执行。导入 ST preset 时,默认应优先使用 compat_strict

原生图节点做什么
static_text普通文本 prompt 节点
marker锚点或插槽标记
chat_history聊天历史
character角色描述、个性、场景、system prompt、post-history
persona用户 persona 描述
worldbook世界书 before / after / depth 注入
example_dialogue示例对话
memory记忆摘要
tool_result工具结果
variable_template变量模板文本

原先 packages/core 中的 native-pipeline 节点链仍然保留,主要作为过渡执行层和测试基础;但 apps/api 的 native 主链已经优先使用 graph compiler。

当前 v1 已落地最小 PromptGraph 文档模型与 compilePromptGraph() 闭环。创建会话时通过 prompt_mode 字段选择使用哪种编排模式。

当前 Prompt Runtime 另外提供了独立的 session 级 mode 控制面:

  • GET /sessions/:id/prompt-runtime/mode
  • PATCH /sessions/:id/prompt-runtime/mode

这个控制面不会引入第二份存储。底层仍然只写 sessions.prompt_mode

统一中间格式(Prompt IR)

不管走哪条路径,最终都会先编译成一个统一的中间格式(内部叫 Prompt IR),再交给大模型。这意味着:

  • 酒馆预设和原生编排共享同一个渲染器。
  • 加新功能只需要加新节点,不需要改兼容层。
  • 调试时可以看到中间格式的完整内容,方便排查问题。

ST 宏运行时边界

兼容模式下的宏系统不是简单的正则替换。

当前后端实现已经把 ST 宏运行时放到明确的执行边界中:

场景只读宏写宏是否写库
导入不执行主求值不执行
预览 / 编辑器预览执行只记录 preview mutation
Prompt dry-run执行只记录 preview mutation
respond / regenerate assemble执行进入 staged mutation buffer
turn commit不重新展开宏文本消费 assemble 冻结结果

当前宏运行时内部已经采用最小稳定节点结构,而不是纯字符串级替换。可以把它理解为以下四类节点:

  • text node
  • macro node
  • if block node
  • raw fragment node

这样做的目的有三点:

  1. 保持求值顺序稳定
  2. 保持 if block 只展开命中的分支
  3. 在 parse 失败、unsupported condition、cycle detect 等场景下保留原文,并输出 warning / trace

变量兼容层当前也不是简单的一张扁平 map。运行时会区分:

  • ST local 兼容视图
  • ST global 兼容视图
  • 分阶段 overlay

因此:

  • .name / getvar 读取 local 兼容视图
  • $name / getglobalvar 读取 global 兼容视图
  • setvarsetglobalvar 的同轮可见性彼此隔离

当前还支持以下宏运行时调试信息:

  • warning 列表
  • used macro 名称
  • mutation preview
  • staged mutation
  • macro trace

这些信息会在 Prompt dry-run 和部分调试输出中返回。详见 Chat API宏系统参考


5. LLM 实例化

在复杂的角色扮演场景中,一个大模型不够用。你可能需要一个「叙述者」负责写故事,一个「记忆员」负责整理摘要,一个「导演」负责把控剧情方向。

我们的做法是:在逻辑上把大模型拆成多个「实例」——不是真的开多个模型进程,而是用不同的配置去调用同一个(或不同的)在线大模型。

实际上,"怎么调用大模型"涉及三层配置:

概念解决什么问题举例
预设提示词模板和生成参数温度 0.9、top_p 0.95、酒馆格式模板
实例槽位谁来调用大模型、负责什么叙述者写故事,记忆员整理摘要
LLM Profile(凭证配置)调用哪个大模型、用什么密钥OpenAI gpt-4o、Anthropic claude-3.5-sonnet

三者的关系:会话绑定一个预设(决定提示词怎么拼)→ 每个实例槽位绑定一个 Profile(决定调用哪个大模型)→ 实例还可以用自己的预设覆盖会话级别的预设。

下面分别展开。

每个实例包含什么

配置项说明
身份名字和职责描述(如「narrator」「memory」)
模型配置用哪个模型、温度、最大 token 数等
提示词约定这个实例的系统提示词模板,以及输入输出格式
变量权限能读写哪些层级的变量
预算限制单次最多用多少 token、超时时间、重试次数

预设的实例类型

实例职责什么时候用
叙述者(Narrator)生成角色扮演文本,产出故事内容每个回合都用
记忆员(Memory)整理摘要、提取关键事实回合结束后
导演(Director)规划剧情走向、给叙述者提供结构化指令可选,复杂场景开启
校验员(Verifier)检查角色行为是否符合设定可选,严格场景开启

调度顺序

一次回合中,实例按以下顺序执行:

text
1. 导演(可选):分析当前局势,给出本回合指令

2. 记忆员:检索相关记忆,准备注入上下文

3. 叙述者:根据指令和记忆,生成本回合内容

4. 校验员(可选):检查生成内容是否合理

5. 提交楼层

兼容模式下的行为

在严格兼容模式(compat_strict)下,只启用叙述者,其余实例全部关闭,行为和酒馆完全一致。切到增强兼容(compat_plus)或原生(native)模式后才会按需开启其他实例。

实例配置的几个关键行为:

  • 叙述者被禁用时,请求会返回明确错误。导演、校验员、记忆员被禁用时,对应子流程直接跳过。
  • 叙述者的预设可以覆盖会话级别的预设。
  • 生成参数按浅层合并,同名参数以实例配置为准。

LLM Profile:凭证配置

每个实例可以独立绑定不同的 LLM Profile。Profile 是一组加密存储的大模型凭证,包含提供商、模型名称、API 密钥等信息。

每次回合启动时,系统会把当前的 Profile 配置冻结成快照。运行中的回合只使用自己的快照,不会被中途的 Profile 修改影响。

四个实例槽位

槽位对应实例说明
narrator叙述者生成故事内容
director导演规划剧情走向
verifier校验员检查内容一致性
memory记忆员整理记忆

此外还有通配符 *,表示"所有未单独绑定的槽位"。

绑定方式

Profile 可以按作用域(全局或会话)和槽位粒度绑定。例如:

  • 全局绑定一个 Profile 到 * → 所有实例默认使用这个 Profile
  • 某会话单独绑定导演到另一个 Profile → 该会话的导演使用不同模型

解析优先级

对于某个会话的某个槽位,系统按以下顺序查找 Profile:

text
1. 会话 + 指定槽位  →  最高优先
2. 全局 + 指定槽位
3. 会话 + 通配符    →  通配回退
4. 全局 + 通配符
5. 都没有           →  使用环境变量配置

每个槽位独立解析,互不影响。这意味着你可以:

  • 让叙述者用高质量大模型(如 Claude 3.5 Sonnet)
  • 让导演、校验员、记忆员用便宜快速的模型(如 GPT-4o-mini)
  • 按会话粒度切换叙述者模型,而不影响其他实例

环境变量回退

如果某个槽位在五级优先级中都没有找到匹配的 Profile,就使用 LLM_API_KEYLLM_PROVIDER 等环境变量配置。这保证了不做任何 Profile 配置也能正常运行。


6. 记忆系统

记忆系统解决一个核心问题:聊天越来越长,大模型的上下文窗口装不下所有内容,怎么办?

记忆从哪来

来源一:大模型自己写的摘要

很多酒馆预设会引导大模型在回复末尾输出类似这样的内容:

text
<summary>角色A向角色B表白,被婉拒。角色B透露自己即将离开这座城市。</summary>

系统会自动识别并提取这些摘要。支持多种标签名(<summary><摘要><memory> 等),也可以在预设里自定义标签名。

提取顺序很重要:

  1. 先从大模型原始输出中提取摘要标签。
  2. 然后再跑正则处理(该隐藏的隐藏,该替换的替换)。
  3. 这样既不影响用户看到的文本,也不会丢掉摘要信息。

来源二:记忆员实例主动整理

在高级模式下,记忆员实例会在每个回合结束后:

  • 读取最近几个楼层的内容。
  • 结合已有的摘要。
  • 输出结构化的记忆操作:新增事实、更新事实、标记过时事实。

记忆员实例的输出是严格的 JSON 格式,不是自由文本。比如:

json
{
  "turn_summary": "角色A向角色B表白被拒,角色B将离开城市",
  "facts_add": [
    { "key": "角色B即将离开", "value": "计划下周离开", "scope": "chat" }
  ],
  "facts_update": [
    { "id": "fact_001", "value": "角色A的心情变为沮丧" }
  ],
  "facts_deprecate": [
    { "id": "fact_002", "reason": "之前推测角色B会留下,现已矛盾" }
  ]
}

记忆怎么存

每条记忆是一个独立的记录,包含:

  • 类型:事实(fact)、摘要(summary)、开放剧情线(open_loop)。摘要进一步分为短摘要(micro,覆盖一两个回合)和长摘要(macro,由多条短摘要压缩而来)。
  • 层级:全局、会话或楼层,决定这条记忆在什么范围内可见。
  • 来源:哪个楼层、哪条消息产生的,方便追溯。
  • 评分:重要度和可信度评分,影响注入优先级。
  • 生命周期:活跃(active)→ 已压缩(compacted)→ 已弃用(deprecated)。已弃用的记忆不再注入提示词。

记忆之间可以有六种关系:支持(supports)、矛盾(contradicts)、更新(updates)、派生(derived_from)、压缩(compacts)、消解(resolves)。

记忆怎么用

每次组装提示词时,编排器会:

  1. 按 token 预算分配记忆可用空间。
  2. 按重要程度和相关性选取记忆条目。
  3. 打包成「记忆摘要块」注入到提示词中。
  4. 在兼容模式下,还会按酒馆的方式将摘要放到旧楼层的位置(替代被隐藏的完整内容)。

主聊天链读取记忆时,会先按当前上下文展开 global → chat → floor 三层可见范围, 再按既有的 importance / balanced / dual-summary 规则统一排序、裁剪和注入。

安全机制

  • 记忆员实例的输出需要经过校验才会写入数据库,不会直接写入。
  • 摘要文本会做基本的清洗,过滤掉可能的提示词注入(比如「忽略以上所有指令」这种内容)。
  • 所有记忆操作都有完整的来源追溯,可以知道每条记忆是什么时候、从哪个楼层产生的。

自动维护

系统在后台运行维护任务,不阻塞对话:

  • 衰减排序:按半衰期对记忆做时间衰减,越老的记忆权重越低。
  • 过期弃用:自动把过期的摘要标记为弃用。
  • 宏摘要压缩:当短摘要(micro)积累到一定数量时,自动触发长摘要(macro)压缩。触发条件包括 active micro 数量、token 总量和楼层跨度阈值。

这些任务由后台作业系统调度,支持重试和超时处理。每个作用域有独立的作业状态。


7. 一次完整回合的流程

把前面所有系统串起来,一次用户发消息到 AI 回复的完整过程是这样的:

text
用户发送消息


① 创建新楼层(状态:draft)
   创建消息页 v1,写入用户消息


② 导演实例(可选)
   分析当前局势,输出本回合结构化指令


③ 记忆检索
   查找相关记忆条目,准备注入


④ 提示词编排
   收集:预设 + 世界书命中 + 记忆 + 历史楼层 + 变量
   按 token 预算裁剪
   正则前处理
   拼装成最终 messages[]


⑤ 叙述者实例生成
   楼层状态 → generating
   流式写入消息页


⑥ 后处理
   提取摘要标签 → 送入记忆候选池
   正则后处理 → 生成用户可见文本


⑦ 校验员实例(可选)
   检查生成内容一致性


⑧ 记忆员实例整理
   整理记忆,输出新增、更新、弃用操作
   校验通过后从页级提升到楼层或会话级


⑨ 提交楼层
   楼层状态 → committed
   页级变量按策略提升
   触发 floor.committed 事件


返回结果给用户

如果中间任何步骤失败,楼层标记为 failed,保留现场方便排查。


8. 数据库设计

使用 SQLite 数据库,通过 Drizzle ORM 操作。核心表结构详见 数据库字典


9. API 概览

所有接口都是 REST 风格,返回 JSON 格式数据。详见 API 参考

会话管理

方法路径说明
POST/sessions创建会话
GET/sessions列出会话
GET/sessions/:id获取会话详情
PATCH/sessions/:id更新会话配置
DELETE/sessions/:id删除会话

生成与聊天

方法路径说明
POST/sessions/:id/respond发送消息并获取 AI 回复(核心接口)
POST/sessions/:id/respond/streamSSE 流式返回 AI 回复片段
POST/sessions/:id/respond/dry-run仅组装 Prompt 并返回调试元信息(无副作用)
POST/sessions/:id/regenerate重新生成最后一个楼层
GET/sessions/:id/timeline获取完整时间线(楼层 + 消息页)

其中 dry-run 会返回本轮提示词组装的完整运行结果,包括生成意图、预填充状态、世界书命中情况等。字段说明见官方集成层 - assembly 字段

导入(酒馆兼容)

方法路径说明
POST/import/preset导入酒馆预设
POST/import/worldbook导入酒馆世界书
POST/import/regex导入酒馆正则规则
POST/import/character导入酒馆角色卡
POST/import/chat导入聊天文件(自动识别 .thchat 原生 / ST .jsonl

导出

方法路径说明
GET/export/chat/:id导出会话(.thchat 无损 / .jsonl ST 兼容)
GET/export/preset/:id导出预设(ST 原始 JSON)
GET/export/worldbook/:id导出世界书(ST 格式 JSON)
GET/export/regex/:id导出正则配置(ST 格式 JSON 数组)
GET/export/character/:id导出角色卡(ST Character Card V2 JSON)

导入和导出形成对称关系:导入解析外部格式写入数据库,导出从数据库序列化为标准文件。聊天文件额外有一套 TavernHeadless 原生格式(.thchat),能无损保留完整四层数据结构、变量、记忆,以及 superseded 楼层历史关系。


10. 事件系统

系统内部通过事件总线来解耦各模块。当某件事发生时,系统广播对应事件,其他模块可以监听并做出反应。以下是主要事件:

事件名触发时机携带数据
session.created创建会话后session 对象
floor.created创建楼层后floor 对象
floor.committed楼层提交后floor 对象 + 变量变更
floor.failed楼层生成失败floor 对象 + 错误信息
page.activated切换生效消息页page 对象
message.appended新消息写入message 对象
generation.started开始调用大模型模型配置 + token 预算
generation.chunk收到流式片段文本片段
generation.completed生成完成完整文本 + token 统计
generation.failed生成失败错误信息
memory.extracted提取到摘要摘要内容 + 来源
memory.committed记忆写入数据库记忆操作列表
worldbook.matched世界书条目命中命中的条目列表
regex.applied正则规则执行规则 ID + 替换结果

| tool.call_started | 工具调用开始 | 工具名 + 参数 + 调用方实例 | | tool.call_completed | 工具调用完成 | 工具名 + 返回值 + 耗时 | | tool.call_failed | 工具调用失败 | 工具名 + 错误信息 | | tool.call_denied | 工具调用被拒绝 | 工具名 + 拒绝原因 |

| mcp.connected | MCP 服务器连接成功 | serverId, serverName, toolCount | | mcp.disconnected | MCP 服务器断开连接 | serverId, serverName | | mcp.error | MCP 服务器连接出错 | serverId, serverName, error |

这些事件可以用来:

  • 在前端实时显示生成进度(通过 WebSocket 转发)。
  • 记录日志和调试信息。
  • 触发自定义逻辑(插件系统的基础)。

11. 工具调用

工具调用让大模型在角色扮演回合中执行结构化操作——读写变量、掷骰子、查询记忆等——而不仅仅是生成自由文本。

设计目标

  • 所有实例(叙述者、导演、校验员、记忆员)都可以调用工具,每个实例的权限独立配置。
  • 每次工具调用都会生成一条执行记录,归属到当前楼层,方便审计和排查。
  • 运行时工具目录是会话级快照,通过 /sessions/:id/tools/runtime 查看当前会话真正可调用的工具;它不直接展开未来 run / node / step overlay。
  • 当前公开配置的 toolMode 仍只有 inline,即大模型在生成过程中自主决定是否调用工具。但部分 allowlisted 工具在 inline 回合内部可以先返回 deferred receipt,再由后台 runtime_job 延后完成。

运行时工具目录中的单个工具仍可能带有 defaultDeliveryMode = async_job。这表示该工具在当前内联回合里会返回受理回执,并由后台 runtime_job 继续完成真实执行。

工具来源

来源说明
内置工具引擎自带的 7 个通用工具:读写变量、掷骰子、随机选择、获取时间、查询记忆、获取角色信息
资源管理工具23 个资源操作工具,覆盖角色卡、世界书、正则配置、预设的读写操作。允许大模型在对话中主动读写这些资源。
预设/角色卡工具从数据库加载的自定义工具定义,支持脚本执行
MCP 工具通过 MCP 协议连接外部工具服务器。支持标准输入输出和 HTTP 两种传输方式。通过环境变量 ENABLE_MCP=true 启用。

当前支持的执行模式

模式说明
inline大模型在生成过程中自主决定是否调用工具。这是默认模式,也是当前唯一可用的公开配置模式。对于允许延后执行的工具,本轮也可能先返回 deferred receipt,再通过 runtime_job 继续执行。
standalone当前未实现。服务端会返回结构化配置错误。
both当前未实现。服务端会返回结构化配置错误。

权限控制

每个会话可以独立配置工具权限,控制哪些实例能调用哪些工具:

  • 白名单和黑名单:按实例槽位设置允许或禁止调用的工具列表。
  • 不可撤销工具开关:是否允许调用有外部副作用的工具(如 MCP 外部接口)。
  • 调用次数上限:单回合最大调用次数。
  • 生成步数上限:大模型单次生成中最多执行多少步工具调用(默认 5 步)。

副作用级别

每个工具都标记了副作用级别,系统据此决定如何处理工具的写入操作:

级别含义示例
none纯查询,不产生任何变更读取变量、掷骰子
sandbox写入操作先存到页级沙箱,楼层提交时才正式生效设置变量
irreversible不可撤销的外部操作,一旦执行无法回滚MCP 外部接口调用

执行审计与隔离

  • 每次工具调用都会生成一条 tool_execution_record,归属到当前楼层。
  • 重新生成创建新楼层,工具重新执行,不复用旧记录。
  • 工具的写入副作用先停留在页级沙箱,只有楼层提交时才正式生效。
  • 系统同时保留了一套兼容的 tool_call_record 查询接口,供旧版客户端使用。
  • MCP 工具目录在 live 列举失败时,可以回退到 cached 快照;会话级运行时目录会通过 catalog_source 标记 livecached
  • script handler 在 Beta3 默认关闭。
  • 只有服务端显式设置 ENABLE_UNSAFE_SCRIPT_HANDLER=true 时,/tools/definitions 的 script 创建、更新和重新启用才会放行。
  • 默认关闭时,历史 definition-backed script tools 会继续出现在 /sessions/:id/tools/runtime 中,但会被标记为 unavailable,不会进入可执行目录。
  • /mcp/servers/mcp/servers/:id 现在会回显 live_status,用来说明数据库配置是否已经进入 live runtime manager。
  • ENABLE_MCP=true 时,MCP 配置 create / update / enable / disable / delete 会直接同步 McpConnectionManager,避免数据库状态和运行时状态分裂。
  • 世界书 position=outlet 现在会进入真实 prompt 组装:优先按同名 outlet marker/placement 注入;没有匹配 marker 时,会回退为显式 section,而不是静默丢弃。
  • Regex 主链现在会透传 runOnEdit 与 depth 上下文:edit-and-regenerate 使用 channel="edit",USER_INPUT / AI_OUTPUT / at-depth WORLD_INFO 会消费 minDepth / maxDepth

API 端点

方法路径说明
GET/tools/builtin列出内置工具
GET/tools/definitions列出自定义工具定义
GET/tools/definitions/:id获取单个工具定义
POST/tools/definitions创建自定义工具
PATCH/tools/definitions/:id更新工具定义
PATCH/tools/definitions/:id/toggle启用/禁用工具
DELETE/tools/definitions/:id删除工具定义
GET/tool-executions查询主执行审计记录
GET/tools/call-records查询兼容调用记录
GET/sessions/:id/tools/runtime获取会话级运行时工具目录
GET/sessions/:id/tool-permissions获取会话基础工具权限
PUT/sessions/:id/tool-permissions替换会话基础工具权限
PATCH/sessions/:id/tool-permissions合并更新会话基础工具权限
GET/mcp/servers列出 MCP 服务器配置
GET/mcp/servers/:id获取单个 MCP 服务器配置
POST/mcp/servers创建 MCP 服务器配置
PATCH/mcp/servers/:id更新 MCP 服务器配置
DELETE/mcp/servers/:id删除 MCP 服务器配置
PATCH/mcp/servers/:id/toggle启用/禁用 MCP 服务器
GET/mcp/servers/:id/status查看连接状态
GET/mcp/statuses查看所有连接状态
POST/mcp/servers/:id/connect连接/重连
POST/mcp/servers/:id/disconnect断开连接
GET/mcp/servers/:id/tools查看服务器工具列表
POST/mcp/servers/:id/test测试连接

连接状态接口还会返回 reconnect_required(是否需要重连)和 last_timeout_at(上次超时时间)。超时类型为"结果不确定"时表示工具调用的结果未知,需要重连后重试,而不是普通的调用失败。

同一会话同一分支上的生成排队只在当前进程内生效。