LLM 实例化?其实是 Sub Agent 的前身
在项目最开始,TavernHeadless 并没有明确要做 Agentic。 但 LLM 实例化是一开始就定下来的。 后来看,这一层其实已经是 Sub Agent 的前身。
最开始并不是为了 Agentic
项目刚开始时,我没有计划做一套完整的 Agentic 系统。
当时更明确的目标,是把 AI RP 后端的基础能力搭起来:会话、楼层、消息页、变量、记忆、提示词编排、酒馆资产兼容。
但 LLM 实例化从一开始就在设计里。
原因很简单。社区里已经有不少大型角色卡的作者,在用多个 LLM 优化体验。
常见做法是:主 LLM 先完成一轮回复,然后一个或多个次级 LLM 接着做特定任务。
这些任务可能是:
- 润色文本。
- 处理变量。
- 整理摘要。
- 提取记忆。
- 驱动后台 NPC 行为。
- 推进世界演化。
- 检查回复是否符合设定。
这些做法不一定被称为 Agentic,但它们已经有了多实例协作的形态。
所以 TavernHeadless 一开始就不能假设“一个会话只会有一个 LLM”。
一个 LLM 不应该承担所有职责
在轻量聊天里,一个 LLM 同时负责所有事情是可以接受的。
它读取上下文,生成回复,也顺便完成一点总结和状态判断。
但大型 AI RP 项目不同。
一个回合里可能同时需要:
- 写出最终正文。
- 判断当前剧情节奏。
- 整理哪些事实应该进入记忆。
- 检查角色行为是否越界。
- 计算变量变化。
- 推动后台角色或世界状态。
这些任务的目标不同,输出格式不同,对温度、模型能力、提示词和权限的要求也不同。
让同一个 LLM 全部完成,会有几个问题。
第一,提示词会变得很重。正文生成提示词里混入太多后台任务要求,会影响叙事输出。
第二,输出格式容易互相干扰。用户想看的是正文,但变量更新、记忆建议和检查结果不应该混在正文里。
第三,权限边界不清楚。生成正文的模型不应该顺手决定长期状态写入,也不应该无边界地改动变量或记忆。
第四,成本和速度不好控制。有些任务可以用便宜模型,有些任务需要更强模型。有些任务可以异步执行,有些任务必须在本轮回复前完成。
所以需要把 LLM 按职责拆开。
这就是 LLM 实例化的基本动机。
LLM 实例化是什么
LLM 实例化不是指真的在本地启动多个模型进程。
它指的是:在逻辑上把不同职责的 LLM 调用区分开。
每个实例都有自己的槽位、配置和运行边界。
TavernHeadless 当前的基础槽位包括:
| 槽位 | 职责 |
|---|---|
narrator | 生成最终 RP 正文 |
memory | 整理摘要、提取和维护记忆 |
director | 给出叙事方向、节奏和约束 |
verifier | 检查回复是否符合人设、世界事实和玩家主导权 |
每个槽位可以有自己的 LLM Profile,也可以有自己的参数覆盖。
Profile 解决的是“连接哪个模型”。
Instance Config 解决的是“哪个槽位怎么使用这个模型”。
比如,Narrator 可以使用更适合长文本和风格表达的模型。Memory 可以使用更稳定、更便宜、输出结构化能力更好的模型。Verifier 可以使用低温度参数,减少判断波动。
这样一来,系统就不再只有一个模糊的 LLM。它开始有了职责划分。
它解决的不是多模型炫技
LLM 实例化的重点不在于“用了几个模型”。
重点在于职责边界。
如果只是把同一个任务随意丢给多个模型,那只会增加复杂度。
真正有用的是:
- 哪个实例负责最终正文。
- 哪个实例只负责提出建议。
- 哪个实例可以读取记忆。
- 哪个实例可以调用工具。
- 哪个实例只能检查,不能改写状态。
- 哪个实例的输出会展示给用户。
- 哪个实例的输出只进入内部记录。
这些边界一旦明确,多 LLM 才有意义。
否则它只是把一个黑箱拆成几个黑箱。
从次级 LLM 到 Sub Agent
社区里已经存在的多 LLM 用法,通常还停留在“次级 LLM”的层面。
也就是说,它们会在主回复之后做一些附属工作。
例如:
Narrator 生成正文
→ Polish LLM 润色
→ Variable LLM 解析变量变化
→ Memory LLM 提取摘要
→ Background LLM 推进后台 NPC这种做法已经很接近 Sub Agent。
区别在于,Sub Agent 不只是“多调用一个 LLM”。
Sub Agent 需要更明确的结构:
- 它有固定职责。
- 它有输入和输出契约。
- 它有权限边界。
- 它有运行时机。
- 它有可审计记录。
- 它的结果要么进入提示词,要么进入状态提案,要么进入内部 trace。
当一个次级 LLM 被赋予这些边界之后,它就不再只是附属调用。
它开始成为一个 Sub Agent。
所以,LLM 实例化其实是 Sub Agent 的前身。
为什么说它是前身
LLM 实例化已经提前解决了几件事。
第一,它承认不同职责需要不同实例。
Narrator、Memory、Director、Verifier 这些槽位,已经不是单纯的模型配置名。它们代表不同的运行角色。
第二,它允许不同实例使用不同模型和参数。
这为后续按任务选择模型打下基础。不是所有任务都需要同一个模型,也不是所有任务都应该用同一组参数。
第三,它给启用和关闭留下了入口。
compat_strict 下可以只启用 Narrator,保持酒馆兼容。更复杂的模式下,再按需开启其他实例。
第四,它给后续权限控制留下了位置。
一个实例能不能调用工具,能不能提出状态写入,能不能影响正史,都需要建立在“它是谁”这个前提上。
第五,它给可审计性留下了位置。
如果 Director、Memory、Verifier 都只是普通的临时调用,后续很难解释它们各自做了什么。实例化之后,每个槽位的输出都可以被单独记录和检查。
这些能力加在一起,就已经接近 Sub Agent 的基础形态。
但实例还不是完整 Agent
这里也要保持边界。
LLM 实例不等于完整 Agent。
一个实例只是有了身份、配置和职责。
完整的 Agent 还需要更多东西:
- 它需要明确的任务输入。
- 它需要结构化输出。
- 它需要权限系统。
- 它需要工具调用边界。
- 它需要失败处理。
- 它需要记录运行轨迹。
- 它需要和状态提交门配合。
- 它需要放进 NodeGraph 或类似的编排结构里。
所以更准确的说法是:
LLM 实例化不是 Agentic 本身。
LLM 实例化是 Agentic 的前置结构。它先把“一个模型”拆成“多个有职责的槽位”。
后续再在这些槽位上增加任务、权限、工具、trace 和提交门,才会形成真正的 Sub Agent。
和兼容模式的关系
LLM 实例化不能破坏酒馆兼容。
在严格兼容路径里,系统应该尽量保持酒馆行为。也就是说,默认只有 Narrator 参与最终回复,其他实例不应该改变输出语义。
这很重要。
因为很多用户导入的是已有酒馆资产。他们需要的是稳定复现,而不是一导入就被后台实例改写行为。
所以比较合理的边界是:
compat_strict:只启用 Narrator,不引入会改变语义的 Sub Agent。compat_plus:可以做轻量增强,但必须明确说明增强内容。native或后续native_graph:正式承载 Director、Verifier、Memory Agent、State Agent 等更强的编排能力。
这样既保留兼容性,也给未来能力留下空间。
和 Message、Prompt Runtime 的关系
多实例一旦开始工作,就会产生更多中间产物。
Director 可能会输出本轮叙事方向。
Memory 可能会输出记忆提取建议。
Verifier 可能会输出检查结论。
这些内容不应该都混进用户看到的正文里。
它们需要被记录,也需要有自己的可见性和来源信息。
这正好和 Message 层、Prompt Runtime 对上。
Message 层提供了记录位置。一个 Page 下面可以放下同一回合内部的多条消息和中间产物。
Prompt Runtime 提供了观察位置。使用者可以知道这些实例在提示词组装和运行过程中产生了什么影响。
所以 LLM 实例化、Message 层和 Prompt Runtime 是相互配合的。
实例负责分工。
Message 负责记录。
Prompt Runtime 负责解释。