笔记10:软件框架设计------模块化B/S架构
一、总体思路
架构选型
采用 B/S(浏览器/服务器)架构,参考 Dify 的设计模式,前后端分离。
核心理念:模块化、可配置、可扩展。每个功能组件定义一套统一协议(接口),具体实现注册到系统中,用户通过配置文件或前端面板选择使用哪种实现。
设计原则
- 协议优先:先定接口,后写实现。模块可替换的前提是接口统一
- 用户可选择:同步/异步、全自动/半自动/手动,这些都做成配置项而非硬编码
- 后端 Python:利用 LangChain、向量数据库等现成生态
- 世界书参考 SillyTavern:JSON 存储 + 触发词机制 + 递归激活
二、模块化核心机制:插件协议 + 注册表
┌─────────────────────────────────────────────────┐
│ 配置层 │
│ config.yaml / 前端配置面板 │
│ 选择用哪个 RAG、哪个 LLM、同步还是异步...... │
└────────────────────┬────────────────────────────┘
│
┌────────────────┼────────────────┐
↓ ↓ ↓
┌────────┐ ┌──────────┐ ┌──────────┐
│RAG模块 │ │世界书模块 │ │LLM模块 │
│注册表 │ │注册表 │ │注册表 │
├────────┤ ├──────────┤ ├──────────┤
│·混合检索│ │·ST风格 │ │·OpenAI │
│·GraphRAG│ │·语义增强 │ │·GLM │
│·纯向量 │ │·轻量级 │ │·本地模型 │
│·关键词 │ │·用户手动 │ │ │
└────────┘ └──────────┘ └──────────┘
插件协议示例
# RAG 模块接口
class BaseRAGAgent(ABC):
@abstractmethod
def retrieve(self, query: str, context: dict) -> list[RetrievedDoc]:
"""检索:输入查询+当前上下文 → 返回文档列表"""
...
@abstractmethod
def index(self, documents: list[Document]) -> None:
"""索引:输入文档 → 建立索引"""
...
# 世界书模块接口
class BaseWorldBook(ABC):
@abstractmethod
def get_active_entries(self, context_tokens: list[str]) -> list[Entry]:
"""根据当前上下文触发词,返回应注入的条目"""
...
@abstractmethod
def update_entry(self, entry_id: str, data: dict) -> None:
"""更新单条条目"""
...
@abstractmethod
def commit(self, message: str) -> str:
"""保存当前状态为一个版本 → 返回 commit hash"""
...
@abstractmethod
def revert(self, commit_hash: str) -> None:
"""回滚到指定版本"""
...
# LLM 模块接口
class BaseLLMProvider(ABC):
@abstractmethod
def invoke(self, prompt: str, **kwargs) -> str:
...
@abstractmethod
def stream(self, prompt: str, **kwargs) -> Iterator[str]:
...
# L2 专家接口
class BaseExpert(ABC):
@abstractmethod
def speak(self, outline: Outline, context: dict) -> ExpertOpinion:
...
三、后端核心模块
后端服务
├── 项目管理
│ ├── 多项目/多书支持
│ ├── 项目级配置(模型选择、风格预设、平台目标)
│ └── 版本历史(世界书的 commit 链)
│
├── 创作流水线引擎
│ ├── L1 种子生成器(用户输入 → 故事愿景)
│ ├── L2 专家会议编排器
│ │ ├── 三专家 Agent 调度(按驱动模式决定发言顺序)
│ │ ├── 用户介入点管理(主编决策、驳回、重议)
│ │ └── 迭代控制(最多3轮)
│ ├── L3 映射编译器(效果→叙事指令,含 RAG-技法检索)
│ └── L4 渲染执行器(细纲→正文,含 RAG-技法检索)
│
├── RAG 服务
│ ├── 历史回顾索引(当前作品的动态向量库)
│ ├── 技法参考库(静态:L3映射关系库 + L4微观技法库)
│ └── 检索编排(多维度切片、混合检索、Agent压缩)
│
├── 世界书服务
│ ├── 状态管理(核心层/活跃层/归档层/索引层 CRUD)
│ ├── 自动更新(管理员Agent:提取变化、冲突检测、压缩归档)
│ ├── 触发词引擎(基于当前上下文决定注入哪些条目)
│ └── 版本控制(commit/revert/diff)
│
└── 外部集成
├── LLM API 管理(多模型切换、密钥管理、速率限制)
├── Embedding 服务(在线/本地可切换)
└── 向量数据库(Chroma/Milvus)
四、创作层模块协议
L1 种子层
本质是结构化采集器,将用户模糊创意转为《故事愿景文档》。
class BaseSeedGenerator(ABC):
@abstractmethod
def generate(self, user_input: dict) -> VisionDocument:
...
# 实现示例
class GuidedFormGenerator(BaseSeedGenerator):
"""引导式表单 → 填充模板"""
class ConversationalGenerator(BaseSeedGenerator):
"""对话式:和用户聊天,逐步提取信息"""
class OneShotGenerator(BaseSeedGenerator):
"""一次性:用户给一段长文本,AI 直接提取结构"""
L2 架构层
最复杂的一层,包含多 Agent 编排。专家会议本身也做成可配置。
class BaseL2Architect(ABC):
@abstractmethod
def generate_outline(
self,
vision: VisionDocument,
world_book: BaseWorldBook,
rag_history: BaseRAGAgent,
rag_technique: BaseRAGAgent,
llm: BaseLLMProvider,
human_feedback: Callable,
) -> Outline:
...
class ExpertMeetingL2(BaseL2Architect):
"""三专家会议(当前方案)"""
def __init__(self, driving_mode: str = "plot"):
self.driving_mode = driving_mode
self.max_iterations = 3
class SinglePassL2(BaseL2Architect):
"""单次生成,快速出稿"""
驱动模式决定专家发言顺序:
| 模式 | 顺序 | 适用场景 |
|---|---|---|
character |
人物设计师 → 剧情架构师 → 网络编辑 | 文青文、种田文 |
plot |
剧情架构师 → 人物设计师 → 网络编辑 | 无限流、系统文 |
market |
网络编辑 → 剧情架构师 ↔ 人物设计师 | 跟风热点、商业定制 |
L3 叙事层
class BaseL3Narrative(ABC):
@abstractmethod
def generate_chapter_plan(
self,
outline: Outline,
rag_technique: BaseRAGAgent,
world_book: BaseWorldBook,
) -> ChapterPlan:
...
class MappingCompilerL3(BaseL3Narrative):
"""效果→叙事指令 查表映射(当前方案)"""
class DirectInferenceL3(BaseL3Narrative):
"""不查表,让 LLM 自己推断叙事指令"""
L4 渲染层
class BaseL4Renderer(ABC):
@abstractmethod
def render(
self,
chapter_plan: ChapterPlan,
rag_technique: BaseRAGAgent,
world_book: BaseWorldBook,
) -> GeneratedText:
...
class ConstrainedRenderer(BaseL4Renderer):
"""严格按 L3 的视角/节奏约束生成"""
class FreeRenderer(BaseL4Renderer):
"""L3 只给建议,L4 有更多自由度"""
五、同步/异步配置
区分两种异步:
| 类型 | 含义 | 例子 |
|---|---|---|
| 执行异步 | 调用后不等待返回,后台执行 | RAG 索引重建、世界书归档压缩 |
| 流程异步 | 不需要用户介入就自动推进 | L2 全自动 vs 半自动 |
流程协作模式(用户可配置)
L2 协作模式:
├── manual(全手动)
│ └── 每轮专家发言后暂停,用户必须审核决策才能继续
│
├── semi_auto(半自动)
│ └── 专家发言自动推进,但关键决策点暂停:
│ 引入新角色、世界观大变更、序列逻辑争议
│
└── full_auto(全自动)
└── 用户只输入 L1,系统一口气跑完 L1→L4
配置示例
pipeline:
strategy: expert_meeting # L2 策略
l2:
driving_mode: plot # character / plot / market
max_iterations: 3
collaboration_mode: semi_auto # manual / semi_auto / full_auto
experts:
architect: plot_architect_v2 # v1=Propp, v2=多维标注
editor: web_editor_v1
character: character_designer_v1
l3:
strategy: mapping_compiler # 查表映射
l4:
strategy: constrained_renderer # 严格约束生成
rag:
history: hybrid_retriever # 历史回顾用混合检索
technique: simple_vector # 技法参考用纯向量
worldbook:
strategy: st_style # SillyTavern 风格
auto_manage: true # 管理员Agent自动维护
llm:
primary: glm-5
embedding: text-embedding-v3
六、前端页面结构
前端
├── 项目仪表盘
│ ├── 项目列表、创建新项目
│ └── 当前进度概览(已完成序列数、世界书条目数、字数统计)
│
├── L1 种子页
│ ├── 引导式表单(核心梗/阅读契约/粗略大纲/热点元素)
│ ├── AI 辅助补充(根据输入自动展开)
│ └── 产出:《故事愿景文档》(可编辑)
│
├── L2 专家会议页(核心交互)
│ ├── 左侧:序列可视化画布
│ │ ├── 功能节点 → 序列连线 → 情节结构图
│ │ └── 多维标注展示(剧情轴/人物轴/世界轴/读者效果 颜色标记)
│ ├── 中间:专家发言区
│ │ ├── 三专家 Tab 切换
│ │ ├── 每位专家的结构化发言卡片
│ │ └── 用户(主编)决策按钮:通过/修改/驳回
│ ├── 右侧:参考面板
│ │ ├── 世界书当前状态(可折叠树形视图)
│ │ ├── RAG-历史回顾结果
│ │ └── RAG-技法参考结果
│ └── 底部:精修大纲实时预览
│
├── L3 叙事页
│ ├── 左侧:L2 产出的大纲(只读参考)
│ ├── 中间:细纲编辑器
│ │ ├── 场景卡片(视角/节奏/话语模式/字数)
│ │ ├── 映射推荐(AI 根据读者效果自动推荐叙事指令)
│ │ └── 情绪曲线可视化
│ └── 右侧:技法参考面板
│
├── L4 渲染页
│ ├── 左侧:L3 细纲指令(只读参考)
│ ├── 中间:正文生成区 + 用户反馈
│ └── 右侧:参考文本范例
│
├── 世界书管理页
│ ├── 树形结构浏览器(核心层/活跃层/归档层/索引层)
│ ├── 条目编辑器
│ ├── 触发词管理
│ └── 变更日志/版本历史
│
└── 设置页
├── 模块选择(RAG策略、世界书策略、LLM配置等)
└── 各专家提示词模板(高级用户可自定义)
七、流水线编排引擎
最外层引擎负责把 L1→L2→L3→L4 串起来,本身也可配置:
class PipelineEngine:
def __init__(self, config: PipelineConfig):
# 从注册表按配置实例化各模块
self.l1 = MODULE_REGISTRY["l1"][config.l1_strategy]()
self.l2 = MODULE_REGISTRY["l2"][config.l2_strategy](config.l2)
self.l3 = MODULE_REGISTRY["l3"][config.l3_strategy]()
self.l4 = MODULE_REGISTRY["l4"][config.l4_strategy]()
self.rag_history = MODULE_REGISTRY["rag"][config.rag.history]()
self.rag_technique = MODULE_REGISTRY["rag"][config.rag.technique]()
self.world_book = MODULE_REGISTRY["worldbook"][config.wb_strategy]()
self.llm = MODULE_REGISTRY["llm"][config.llm.primary]()
def run_full_auto(self, user_input: dict) -> GeneratedText:
"""全自动模式:一口气跑完"""
vision = self.l1.generate(user_input)
outline = self.l2.generate_outline(vision, self.world_book,
self.rag_history, self.rag_technique,
self.llm, human_feedback=None)
chapter = self.l3.generate_chapter_plan(outline, self.rag_technique,
self.world_book)
text = self.l4.render(chapter, self.rag_technique, self.world_book)
return text
def run_stepwise(self, user_input: dict) -> Generator:
"""分步模式:每层暂停,等前端信号"""
vision = self.l1.generate(user_input)
yield {"stage": "l1_done", "output": vision}
outline = self.l2.generate_outline(
vision, self.world_book, self.rag_history, self.rag_technique,
self.llm,
human_feedback=lambda: self._wait_for_user_feedback()
)
yield {"stage": "l2_done", "output": outline}
chapter = self.l3.generate_chapter_plan(
outline, self.rag_technique, self.world_book
)
yield {"stage": "l3_done", "output": chapter}
text = self.l4.render(chapter, self.rag_technique, self.world_book)
yield {"stage": "l4_done", "output": text}
RAG 历史索引使用异步执行:
# 序列完成后不阻塞
async def on_sequence_complete(self, sequence: Sequence):
"""序列/章节完成后异步建索引"""
self.world_book.commit(f"完成序列: {sequence.name}")
asyncio.create_task(self.rag_history.index(sequence.to_documents()))
# 前台立即返回,索引后台跑
八、世界书模块参考 SillyTavern 的关键点
SilkyTavern 世界书在技术上的可借鉴之处:
| 特性 | ST 做法 | 我们的扩展 |
|---|---|---|
| 存储格式 | JSON 文件(一条目一个对象) | 保持 JSON,增加版本链 |
| 触发机制 | 正则/keyword 匹配 | 可选增加语义匹配 |
| 注入位置 | 可配置注入到 prompt 哪个位置 | 保持,按 L2/L3/L4 不同注入策略 |
| 条目类型 | constant(常驻)/ selective(按需) | 保持,对应核心层/活跃层 |
| 递归激活 | 激活的条目可触发其他条目 | 保持,用于世界观关联规则 |
| 版本历史 | 无(ST 不做版本管理) | 新增:commit/revert/diff |
ST 风格条目的数据结构参考
{
"id": "char_lizhou_001",
"keys": ["老周", "周师傅", "鉴定师"],
"secondary_keys": ["拍卖行", "青云城"],
"content": "老周,金丹期鉴定师,青云拍卖行首席。性格:专业、中立、见钱眼开。对主角态度:从质疑到崇敬。",
"constant": false,
"priority": 10,
"position": "before_char",
"selective": true
}
九、RAG 历史索引的实时性策略
笔记7 的全量索引 pipeline 约需 25 分钟。如果每完成一个序列就要等 25 分钟才能检索历史,体验不可接受。
建议方案:
序列完成
│
├── 立即:写入临时索引(关键词倒排,秒级)
│ ↓
│ 检索时可立即命中新内容(精度低但延迟为零)
│
└── 异步:启动后台向量索引 pipeline(25分钟)
↓
完成后替换临时索引(精度高)
检索时自动选最优:
- 如果向量索引已覆盖该序列 → 用向量索引
- 如果向量索引尚未建好 → 降级为关键词检索
- 用户可以手动触发全量重建
十、L2 交互模式:群聊式专家会议
为什么不需要画布
L2 的本质是多角色协作会议,不是流程图编辑器。三个专家、用户(主编)、RAG Agent、世界书管理员围绕一个议题轮流发言,最终产出一段符合 L3 格式的文本。这和 Discord/Slack 群聊是同构的。画布编辑器的学习成本在此场景下是额外负担。
群聊界面设计
┌─────────────────────────────────────────────────────────
│ L2 专家会议 [半自动 ▾] [暂停]
├─────────────────────────────────────────────────────────
│
│ 📗 剧情架构师
│ ┌─────────────────────────────────────────
│ │ 基于愿景文档,我拆解出以下序列:
│ │
│ │ 序列一:拍卖会打脸
│ │ 功能链:入场 → 被嘲讽(压抑) → 亮宝(爆发) → 震惊(余韵)
│ │ 逻辑说明:典型欲扬先抑,压抑积累势能,爆发释放爽感。
│ │
│ │ 请网络编辑和人物设计师补充。 @网络编辑 @人物设计师
│ └─────────────────────────────────────────
│
│ 📕 网络编辑
│ ┌─────────────────────────────────────────
│ │ 爽点评估:中。压抑阶段太短,建议增加"反派炫耀+众人
│ │ 吹捧"铺垫拉长压抑时间。鉴定环节可制造"质疑→打脸"
│ │ 的二次爽点。详见修改建议。
│ └─────────────────────────────────────────
│
│ 📘 人物设计师
│ ┌─────────────────────────────────────────
│ │ 行动元分配:主体(主角)、敌对者(赵少)、帮助者(老周)。
│ │ 主角沉默被嘲 ✅ 符合"高冷苟道"人设。
│ │ 建议新增鉴定师老周作为帮助者,核心特性:专业、中立。
│ └─────────────────────────────────────────
│
│ 💬 你 (主编)
│ ┌─────────────────────────────────────────
│ │ 逻辑没问题。但设定里主角现在很穷,交易环节要体现
│ │ "付不起手续费"的反差,大佬免单也能侧面装逼。
│ └─────────────────────────────────────────
│
│ 📔 RAG-历史回顾 [自动]
│ ┌─────────────────────────────────────────
│ │ 检索到上次拍卖相关:第12章主角曾说"拍卖行的人都是
│ │ 势利眼"→ 可作为伏笔在此回收
│ └─────────────────────────────────────────
│
│ 📓 世界书管理员 [自动]
│ ┌─────────────────────────────────────────
│ │ ⚠️ 注意:当前主角灵石余额为50,若天价成交需确认
│ │ 收入来源合理(晶核来源是否已在前文交代?)
│ └─────────────────────────────────────────
│
│ ──────────────────────────────────────────
│ [通过 ✓] [要求修改 ✎] [驳回 ↩]
│
├─────────────────────────────────────────┤
│ 右侧面板:实时大纲预览 │
│ 讨论成果沉淀为结构化文本,随专家共识更新 │
└─────────────────────────────────────────┘
核心交互规则
- 每个角色有独特头像和颜色标识,快速区分发言人
- 用户可随时插话(和群聊一样),输入框始终可用
- RAG Agent 和世界书管理员作为机器人自动插入信息,不等用户触发
- 专家可以
@其他专家来定向点名 - 底部决策按钮控制流程走向
协作模式切换
前端一个开关,控制后端行为:
| 模式 | 行为 |
|---|---|
| 半自动 | 每位专家发言后暂停,用户点"继续"才进入下一位 |
| 全自动 | 专家一轮接一轮自动发言,用户纯旁听。但用户随时可打字插话,插入后自动切换回半自动 |
| 手动 | 用户指定哪一位专家发言,类似点名提问 |
后端都是同一个事件流,唯一区别是 yield 之后等不等用户信号。
后续可选的"画布感"补充
不需要现在就做。未来可在右上角加一个只读小地图------将大纲文本实时渲染为缩略树形结构图,帮助一目了然看清当前讨论成果。不影响群聊交互模式。
十一、专家会议结构的模块化
问题
当前专家会议固定三个专家,但发言顺序和迭代逻辑应该做成可插拔。来源是笔记4中定义的三种驱动模式:
| 模式 | 顺序 | 适用场景 |
|---|---|---|
| 人物驱动流 | 人物设计师 → 剧情架构师 → 网络编辑 | 文青文、种田文、成长向 |
| 剧情驱动流 | 剧情架构师 → 人物设计师 → 网络编辑 | 无限流、系统文、副本向 |
| 市场驱动流 | 网络编辑 → 剧情架构师 ↔ 人物设计师 | 跟风热点、商业定制 |
方案:会议协议 + 专家注册表
会议结构本身也做成插件:
class BaseMeetingProtocol(ABC):
"""会议结构协议------定义专家如何协作,与专家本身解耦"""
@abstractmethod
def get_speaking_order(self, context: dict) -> list[tuple[str, str]]:
"""返回发言顺序 [(expert_id, 发言类型), ...]
发言类型:
- "main": 主导发言
- "review": 审核评议
- "supplement": 补充发言
"""
...
@abstractmethod
def should_continue(self, rounds: int, consensus: float) -> bool:
"""判断是否继续迭代。True = 再来一轮"""
...
@abstractmethod
def format_output(self, history: list[ExpertOpinion]) -> Outline:
"""将会议记录整理为 L3 可接收的大纲格式"""
...
# ── 三种驱动模式实现 ──
class CharacterDriven(MeetingProtocol):
"""人物驱动流:人物 → 架构师 → 编辑"""
def get_speaking_order(self, context):
return [
("character", "main"),
("architect", "supplement"),
("editor", "review"),
]
def should_continue(self, rounds, consensus):
return rounds < 3
class PlotDriven(MeetingProtocol):
"""剧情驱动流:架构师 → 人物 → 编辑"""
def get_speaking_order(self, context):
return [
("architect", "main"),
("editor", "review"),
("character", "supplement"),
]
def should_continue(self, rounds, consensus):
return rounds < 3
class MarketDriven(MeetingProtocol):
"""市场驱动流:编辑 → 架构师 ↔ 人物"""
def get_speaking_order(self, context):
return [
("editor", "main"),
("architect", "supplement"),
("character", "review"),
]
def should_continue(self, rounds, consensus):
# 市场驱动更重共识,允许更多反覆
return consensus < 0.8 and rounds < 4
# ── 注册表 ──
MEETING_REGISTRY = {
"character_driven": CharacterDriven,
"plot_driven": PlotDriven,
"market_driven": MarketDriven,
}
配置使用
l2:
meeting_protocol: plot_driven # 选择驱动模式
max_rounds: 3
experts: # 参与会议的专家
- id: architect
type: plot_architect_v2
- id: editor
type: web_editor_v1
- id: character
type: character_designer_v1
关键设计原则
- 会议结构和专家本身完全解耦:协议只管"谁、什么时候、说什么",不管"说什么内容"
- 专家只实现
speak()接口,不关心自己在会议中的顺序定位 - 协议可控制发言类型(main/review/supplement),不同类型给专家的 prompt 前缀和上下文范围不同
- RAG Agent 和世界书管理员的插话 由协议中的
should_interrupt()钩子控制 - 扩展性 :即便目前只有三种模式,协议接口保留
get_speaking_order允许返回任意长度列表,未来加第四专家只需改配置并按需写一个新协议类,不改核心代码
十一、L3/L4 的标签化指令系统
核心思路
L1/L2 是文本对话 为主------用户和AI聊创意、开会讨论。而 L3/L4 的产出天然是结构化的写作指令,更适合可视化标签编排。
L1 → 用户和AI聊创意(自然语言对话,发散)
L2 → 用户和三个专家开会(自然语言对话,收敛)
=== 分界线 === 从"聊想法"切换到"下达指令"
L3 → 用户编排叙事标签(结构化指令,精确)
L4 → 用户编排风格标签(技法指令,精确)
交互模式
用户拿到 L2 产出的精修大纲(一段描述清晰的要求文本)。在 L3/L4 有两种修改方式:
- 直接改文本:手动编辑指令文本
- 拖标签:从标签库拖拽标签到指令文本中,每个标签本质上是一段预设提示词
L3 标签库(叙事指令)
L3 标签面板
├── 视角标签
│ ├── 内聚焦·反派视角
│ ├── 内聚焦·主角视角
│ ├── 内聚焦·路人视角
│ ├── 外聚焦·客观镜头
│ ├── 零聚焦·全知叙述
│ ├── 自由间接引语·展示心理
│ └── 不定内聚焦·多人物视角切换
│
├── 节奏标签
│ ├── 慢速扩述·细节展开
│ ├── 中速等述·正常推进
│ ├── 快速概述·一笔带过
│ ├── 停顿·环境道具描写
│ └── 省略·跳跃留白
│
├── 信息控制标签
│ ├── 减少信息·制造悬念
│ ├── 增加信息·内心独白
│ ├── 限制视角·读者比角色知道得少
│ └── 叙事空白·关键处不说破
│
├── 话语模式标签
│ ├── 大量对话·对峙冲突
│ ├── 动作描写为主·战斗推进
│ ├── 心理描写为主·情感展开
│ ├── 环境烘托为主·氛围渲染
│ └── 对话+动作交替·日常场景
│
├── 情绪锚点标签
│ ├── 压抑积累
│ ├── 爆发释放
│ ├── 余韵回味
│ ├── 紧张渐进
│ └── 温馨舒缓
│
└── 智能推荐区(AI根据当前场景自动推荐最相关标签)
L4 标签库(渲染技法)
L4 标签面板
├── 风格标签
│ ├── 古龙式冷硬对话
│ ├── 海明威式动作描写
│ ├── 修真文言风格
│ ├── 轻松网路口语
│ └── 严肃史诗叙事
│
├── 句式标签
│ ├── 短句动词密集
│ ├── 长短句交替
│ ├── 排比增强气势
│ └── 省略主语制造紧张
│
├── 描写技法标签
│ ├── 五感描写·视觉听觉嗅觉
│ ├── 环境参与叙事
│ ├── 只写动作不写心理
│ ├── 侧面烘托·通过他人反应
│ └── 陌生化·新奇视角描写平常事物
│
├── 叙事转文标签 ★ 核心创新
│ │ 将叙事术语直接转换成具体文本模板
│ ├── "震惊反应" → 人物失态+环境凝滞模板
│ ├── "压抑氛围" → 环境阴暗+心理沉重模板
│ ├── "装逼时刻" → 外聚焦+短句+冷淡反应模板
│ └── "战斗爆发" → 动词链+快节奏切换模板
│
└── 参考范例标签(拖入即插入可参考的范文片段)
L3 示例界面
┌──────────────────────────────────────────────────────
│ 场景:鉴定师老周接过晶核
│
│ ┌────────────────────────────────────┐ ┌──────────┐
│ │ 指令文本区(可直接编辑) │ │ 标签库 │
│ │ │ │ │
│ │ [内聚焦·反派视角] [慢速扩述] │ │ 视角 ▾ │
│ │ [减少信息] │ │ 节奏 ▾ │
│ │ │ │ 信息 ▾ │
│ │ 老周接过晶核,他漫不经心地扫了 │ │ 话语 ▾ │
│ │ 一眼......(标签决定生成约束,AI │ │ 情绪 ▾ │
│ │ 根据标签组合自动生成预览) │ │ 推荐 ▾ │
│ │ │ └──────────┘
│ └────────────────────────────────────┘
│
│ 操作方式:
│ ① 直接编辑文本 → AI 推测相应标签
│ ② 从右侧拖标签到左侧 → 自动注入指令
│ ③ 删除某个标签 → 对应指令移除
│ ④ 点击标签展开 → 查看/修改背后的完整提示词
标签的本质
每个标签不是 UI 装饰,而是一段结构化的配置数据:
{
"id": "focal_internal_villain",
"name": "内聚焦·反派视角",
"category": "视角",
"layer": "L3",
"prompt": "使用内聚焦视角,以反派角色的认知范围呈现场景。只描写反派能看到、听到、想到的内容。不写全知信息。",
"conflicts": ["零聚焦·全知叙述", "外聚焦·客观镜头"],
"synergies": ["慢速扩述·细节展开", "大量对话·对峙冲突"],
"example": "他看着那个穷小子,嘴角勾起一抹讥笑。又是一个来凑热闹的乞丐吗?"
}
标签间的关系:
- conflicts:互斥的标签(如内聚焦和外聚焦不能同时用)
- synergies:搭配效果好的标签组合推荐
- prompt:实际注入的指令文本,高级用户可展开修改
核心优势
-
标签即 API:每个标签定义了一套标准化的指令参数。AI 不需要"理解"用户意图,只需要执行精确指令
-
可组合性 :
[外聚焦] + [短句密集] + [减少信息]组合出的叙事效果比自然语言描述精确得多 -
降低门槛 :用户不需要知道"自由间接引语"是什么,只需看到标签
展现众人心理崩溃并拖进去 -
可积累:用户可保存标签组合为"自定义模板"------如"我的打脸三件套"
-
初学者友好 + 专家可深入:拖标签即可用,也可点开看完整提示词并修改
十二、待讨论问题
- 前端技术栈:倾向 Vue(模板语法接近 HTML,Python 开发者上手快,中文文档好)。如需快速出原型可先用 Gradio/Streamlit 验证核心流程,后期再迁移到正式前端。
- 实时通信 :倾向 SSE(单向推送,精确匹配 LLM 流式输出场景,FastAPI
StreamingResponse一行代码,浏览器自带重连)。WebSocket 的双工能力在当前场景用不上。 - 多用户协作 :先做单用户。数据模型预留
project.owner_id,后续升级不动核心架构。 - 部署方案 :参考 SillyTavern 模式------
git clone→ 双击start.sh/bat→ 自动装依赖启动。不需要 Docker。Chroma 嵌入式运行,无需单独数据库服务。 - API 设计 :REST POST/GET(用户操作)+ SSE(LLM 流式推送)。约二十个端点,分项目管理/L1/L2/L3/L4/世界书/RAG/设置几组。SSE 事件流格式:
event: {type}\ndata: {json}\n\n,前端EventSource原生接收。 - 会议结构注册 :采用插件目录自动发现。新协议丢
meeting_protocols/目录下即可被扫描注册,配置文件只声明选哪个,不枚举有哪些可选。 - 专家发言格式化 :前端使用
markdown-it等标准库渲染表格和代码块。唯一注意点是流式渲染时末端 Markdown 语法未闭合,需暂存不完整块。
十三、已初步讨论的议题
1. 数据持久化方案
参考 SillyTavern,核心数据用 JSON 文件,日志用 SQLite:
| 数据类型 | 存储 | 理由 |
|---|---|---|
| 世界书条目 | JSON | 结构化文档,人类可读可编辑,量不大 |
| 项目配置 | JSON | 配置文件天然就该是文本 |
| L1-L4 产出 | JSON/Markdown | 它们是"文档",不是"记录" |
| 会议日志 | SQLite | 只追加不修改,需按时间查,量大 |
| 版本历史 | SQLite | 同上 |
如果不想碰 SQLite,全 JSON 也不会翻车(SillyTavern 全 JSON 也跑得好好的)。
2. 用户级 vs 项目级数据
标签库、提示词模板、技法标签等是用户级配置,跨项目共享:
数据目录/
├── user/ # 用户级(跨项目共享)
│ ├── config.yaml # 全局配置(默认模型、API key)
│ ├── tags/
│ │ ├── l3_tags.json # L3 叙事标签库
│ │ └── l4_tags.json # L4 风格标签库
│ ├── prompts/
│ │ ├── architect_v1.txt # 架构师提示词模板
│ │ ├── editor_v1.txt
│ │ └── character_v1.txt
│ └── templates/
│ └── my_dalian_set.json # 用户保存的标签组合模板
│
└── projects/ # 项目级(每本书独立)
├── 都市剑说/
│ ├── project.json
│ ├── worldbook.json
│ ├── outputs/
│ └── logs/meeting.db
└── 异界倒爷/
└── ...
3. LLM 调用层
所有功能的基础,统一内部调用接口,屏蔽各模型 API 差异。前端提供模型配置页面,用户自行填写 API 地址和密钥。
- 多模型适配 :OpenAI 兼容接口 / GLM / 本地模型,统一内部
BaseLLMProvider接口 - 速率限制与排队:API 有 QPM 限制,需内置排队和退避
- 失败重试与降级:主模型挂了自动切备用模型
- Token 计数与成本追踪:用户能看到"这次 L2 会议花了 ¥0.38"
- 流式输出统一封装:所有模型流式 API 差异大,需抽象为统一事件流
4. 错误处理与恢复
策略由协作模式决定,用户可配置:
| 模式 | 超时重试 | 失败后 |
|---|---|---|
| 半自动 | 重试一次 | 群聊显示错误提示(不计入上下文),用户决定跳过还是手动触发 |
| 全自动 | 重试直到成功 | 自动重试,后台无人值守 |
流式中断处理:SSE 推送中途断线时,前段保留已接收的部分文本。用户可选择:
- 继续:将断点内容作为上下文传给 LLM,从断处续写
- 重来:删除该条消息,重新触发生成
SSE 断线由浏览器 EventSource 自带重连,已显示的消息不丢失。
5. 日志与调试
对系统开发者(你自己)至关重要:
- 会议完整记录:每次 L2 会议谁说了什么、用了多少 token、耗时
- 产出版本快照:每层产出的版本,追溯"这段正文是哪个版本的大纲生成的"
- RAG 检索日志:查了什么、命中什么、得分多少
- 世界书变更日志:谁改了哪个条目、改了什么
6. 初始设置与引导
新用户打开系统看到什么:
- 创建新项目向导:选类型(玄幻/都市/......)→ 选驱动模式 → 初始化世界书核心层
- 导入已有作品:上传 txt 或粘贴文本 → 自动分章 → 生成世界书初稿 → 建 RAG 历史索引
- 示例/模板:预置几个类型示范,降低空项目门槛
7. 长篇小说导航
写到第 200 章时回看第 50 章:
- 章节树形导航:左侧目录,支持折叠和跳转
- 上下文切换:点击某章后加载该章的世界书快照(当时的角色状态)
- 编辑锁定:已完成章节是否允许修改?修改后如何影响后续?
8. 导入导出
- 导出:正文导出 txt/markdown/epub;大纲导出为结构化文件;世界书导出
- 导入:已有小说导入 → 自动分章 → 快速生成世界书初稿 → 建 RAG 历史索引
创建时间:2026-04-27 状态:已初步讨论