版本:2026-07-16
定位:用“先解决一个写作问题,再被下一个工程问题追上”的方式,讲清 CursorBlinking Blog 的个人写作分身为什么会演进成今天这套架构。
0. 先把答案放在门口
问:当前项目里的个人写作分身,究竟是不是 Agent?
答:产品形态上是,执行机制上仍是一套受约束的显式工作流。
它不是一个可以随意挑工具、自己循环推理、顺手把文章发布出去的通用 Agent Runtime。它更像一位坐在编辑器旁边的写作搭档:会读作者认可的历史文章,会使用风格、修订偏好和个人事实,会提交修改候选,但没有文章保险柜的钥匙。
当前系统已经实现:
- Writing Persona 聚合根;
- 风格画像、修订偏好快照和个人记忆快照;
- 按写作分身隔离的来源与版本;
- 持久化写作会话、消息、AI Run、Task 和 Suggestion;
- 选区、块级和全文三类已接入 UI 的写作入口;
- 服务端内部的 selection / block / section / document 范围协议;
- 自动参考文章召回和自动 Personal Memory 检索;
- Personal Memory Grounding 与一次修复重试;
- 不可变文章版本、块修订、Diff、恢复和并发保护;
- 接受 / 拒绝建议产生的 Learning Observation;
- 新分身版本生成时重建并固定 Learning Snapshot;
- 模型结构化输出修复、FlowDocument 领域校验和复杂块保护;
- 中性的 AI Provider 传输层,以及彼此独立的阅读 / 写作资源策略;
better-sqlite3 + WAL + busy_timeout数据库运行时;- 只允许 Persona 级 Style / Learning / Memory 生效的严格隔离;
- 服务端拥有迁移和 Seed、npm 单一包管理器与统一 CI 门禁。
当前尚未实现:
- 真正工作的通用 Writing Agent Orchestrator;
- Personal Mind;
- 独立 Review Agent;
- 通用 Tool Registry 和自主工具循环;
- 向量数据库或语义向量召回;
- AI 自动发布;
- 多节点分布式 Worker。
一句话总结:
当前个人写作分身不是“让模型接管编辑器”,而是“让模型在可追溯、可拒绝、可恢复的版本系统里提交候选变更”。
1. 写作分身、风格、学习和记忆到底是什么关系?
问:写作分身是不是换了名字的风格画像?
答:不是。风格画像只是写作分身的一项能力。
当前领域关系是:
三项能力平行存在:
- Style 回答“这个分身通常怎么写”;
- Learning 回答“这个分身对助手修改有哪些稳定偏好”;
- Memory 回答“这个分身可以使用哪些有来源证据的个人事实”。
Writing Persona Version 不负责把三者熬成一锅“人格浓汤”,它只固定一次可复现的组件组合。
在当前完整能力配置下,这个组合包含 Style、Learning 和 Memory;从表结构与功能开关看,Style 是生成分身版本的必需能力,Learning 和 Memory 关闭时,对应指针允许为空。
问:为什么 Memory 和 Learning 不直接挂在风格画像下面?
答:因为它们不是风格,而且更新节奏不同。
- Style 常在代表作或来源语料变化时更新;
- Learning 随建议接受 / 拒绝积累观察;
- Memory 随来源文章版本、事实冲突和证据资格变化而重建。
如果三者共用一条版本链,新增一段工作经历也会看起来像“文风突变”。数据库会很礼貌地保存这个误会,排查的人就没那么礼貌了。
2. 当前能力状态,先讲清再开麦
问:哪些能力是真落地,哪些只是架构插槽?
答:
问:既然还没有通用 Orchestrator,为什么仍然叫 Agent?
答:因为产品已经围绕“理解作者并协助写作”形成完整闭环。只是工程实现没有急着把所有控制流塞进一个会自己转圈的框架里。
先把事实、权限、版本和失败边界做实,再讨论自主性,通常比先搭一座“智能指挥中心”、然后发现它没有刹车更划算。
3. 整个演进过程,可以压成一张图吗?
问:从选区润色到写作分身,中间发生了什么?
答:
每一步都不是为了“看起来更像 AI”,而是为了回答前一步回答不了的问题。
4. 第一阶段:我只想让 AI 改一下选中的文字
问:最简单的 AI 编辑是什么样?
答:选中文字,点击“改善表达”“精简”或“扩写”,模型返回一段纯文本建议。
当前项目仍保留 editor-assistant 兼容路径。当新版 Writing Assistant 或版本能力关闭时,浮动工具栏可以退回这条直接流式路径。
优点很明显:短、快、演示时不容易冷场。
问题也很明显:它只能回答“这次生成了什么”,回答不了“它后来有没有成为文章事实”。
5. 第一次转向:正文不是字符串,而是 FlowDocument
问:为什么不能让模型直接返回 Markdown 或 HTML?
答:因为当前项目的正文主事实是 FlowDocument。
{ "schemaVersion": 1, "blocks": [ { "id": "block-stable-id", "type": "paragraph", "richText": [{ "text": "正文" }] } ]}它不仅有段落,还包含标题、引用、列表、代码、公式、媒体、表格、details、columns、bookmark、file、embed 等结构。
如果模型只交回一大段自由文本,系统就不知道:
- 修改的是哪个稳定块;
- 链接、加粗和行内公式是否保留;
- 图片、表格和代码是否被吞了;
- 新增、删除、移动如何进入块级历史;
- 接受后该形成局部修订还是全文新版本。
FlowDocument 因此是正文世界的“海关”。模型可以带东西回来,但先开箱检查。
6. 第二次转向:模型生成的不是正文,而是候选操作
问:系统怎样描述一次 AI 修改?
答:使用共享的 Suggestion Operation 协议。
当前支持的核心操作包括:
| 操作 | 含义 |
|---|---|
replace_text_range | 替换同一文本块内的选区 |
replace_block | 保留块 ID,替换整个块 |
insert_blocks_before | 在目标块前插入块 |
insert_blocks_after | 在目标块后插入块 |
delete_block | 删除块,需要明确确认 |
replace_document | 用合法 FlowDocument 形成全文候选 |
建议还会固定 baseVersionId、baseRenderHash 和 operations。
问:为什么操作逻辑放在 `shared`?
答:客户端要预览,服务端要真正应用,两端必须说同一种“修改语言”。
共享层只做纯逻辑,不碰数据库、不碰 DOM,也不趁机看看环境变量。纯函数的职业操守相当稳定。
7. 第三个问题:AI 改完以后,历史在哪里?
问:只覆盖 `posts.content_document` 不够吗?
答:它适合保存当前文章,不适合解释写作历史。
当前版本底座包括:
| 数据 | 作用 |
|---|---|
post_versions | 完整文章版本,如 v1.r0、v1.r1、v2.r0 |
post_version_blocks | 一个版本中块修订的顺序 |
post_blocks | 稳定块身份与删除状态 |
post_block_revisions | 某块不可变的内容修订 |
post_block_lineage | revise、split、merge、restore 等来源关系 |
post_edit_transactions | 一次用户可感知的提交 |
post_edit_events | 事务内的新增、删除、修改和移动 |
posts.content_document 负责快读,版本表负责讲清来龙去脉。一个负责上班,一个负责写回忆录。
8. 谁真正拥有正文写入权?
问:AI 服务、版本服务和页面都碰正文,会不会出现多条写入路径?
答:所以持久化写入收敛到 posts.commitContentChange。
模型、TaskRunner、Session、React 面板都不能直接写正文。
Agent 系统里最危险的能力通常不是“生成”,而是“写入”。Prompt 里写十遍“请谨慎”,也没有一个数据库事务靠谱。
9. 当前界面到底支持哪些修改范围?
问:聊天框里还需要用户手工选择“全文 / 小节 / 块”吗?
答:当前 UI 已把范围改成由入口自动决定。
| 入口 | 当前范围 | 是否选择 Writing Persona |
|---|---|---|
| 文章页打开写作助手 | 全文 document | 可以 |
| 编辑器区块菜单打开写作助手 | 当前块 block | 可以 |
| 浮动选区工具栏 | 同一文本块选区 selection | 当前不传 Persona,属于快速建议 |
section | 服务端协议已支持 | 当前主 UI 未开放入口 |
主聊天框默认全文,区块入口自动块级。用户负责说“想怎么改”,系统负责知道“改哪里”。这比让用户先填写一份小型 API 表单更像写作工具。
问:浮动工具栏还是旧路径吗?
答:分情况。
- 新版 Assistant 与 Versioning 开启时,它会创建隐藏会话、Run、Task 和 pending Suggestion,接受后走统一版本事务;
- 新版能力关闭时,才降级到旧
editor-assistant流式路径。
兼容路径不是另一套未来,只是旧桥还没拆。桥可以留,导航不能写错。
10. 为什么需要 Session、Run、Task 和 Suggestion 四个对象?
问:这四个名字看起来像开会名单,分别负责什么?
答:
| 对象 | 回答的问题 |
|---|---|
| Assistant Session | 这篇文章里的对话上下文是什么? |
| Assistant Message | 用户和助手分别说过什么? |
| AI Run | 这次业务运行用了什么模型、Prompt 和上下文? |
| AI Task | 谁执行、执行到第几次、能否取消和重试? |
| Suggestion | 哪组候选操作可以被接受、拒绝或判定冲突? |
Message 适合展示,Suggestion 适合做决定。助手消息可以说“候选已经生成”,真正能进入正文的是它关联的 operations。
11. TaskRunner 已经是什么,又不是什么?
问:当前已经是分布式任务队列了吗?
答:不是。当前是 SQLite 持久任务表加进程内执行器。
它能:
- 持久化 queued / running / succeeded / failed / cancelled;
- 用 handler registry 分发任务;
- 用
setImmediate或定时器调度; - 用
AbortController取消当前进程内任务; - 指数退避重试;
- 服务启动时把遗留 running 恢复为 queued;
- 保存顺序 Run Event。
它不能:
- 提供多节点租约;
- 保证跨节点恰好一次执行;
- 代替独立消息队列;
- 在服务进程关闭后继续计算。
它是一辆可靠的小货车,不是跨国物流集团。对当前单实例个人内容系统来说,小货车正合适。
11.1 小货车为什么换了发动机?
问:SQLite 不是一直都在吗,为什么还要专门治理运行时?
答:数据库文件相同,不代表连接语义相同。
旧兼容路径在 Node 原生 SQLite 不可用时会退回 sql.js:每个连接各自把整库加载进内存,关闭时再把整份文件写回。单连接演示很安静,两个连接先后写入时就可能上演“最后关门的人覆盖全场”。任务队列、文章保存和 AI 回调一旦并发,这不是性能小瑕疵,而是事实可能丢失。
当前运行时统一为 better-sqlite3:
它带来的不是“数据库突然分布式了”,而是更朴素也更重要的保证:所有连接面对的是同一个 SQLite 文件和真实锁,不再各自抱着一份内存副本互相蒙在鼓里。
专项并发测试会让 Worker 持有 BEGIN IMMEDIATE 写事务,主线程同时写入;第二个写者必须等待前一个提交,最后两笔数据都在。测试要验证的不是“两个函数都返回了”,而是“谁也没有把谁写没”。
问:为什么迁移也从 client 搬到 server?
答:因为 Schema 是服务端事实,不是前端的副业。
当前归属已经收敛为:
客户端不再拥有迁移和 Seed。CI 每次从空库顺序执行全部迁移,再跑架构、类型和专项回归;部署也改用锁文件驱动的 npm ci,并允许 better-sqlite3 安装原生绑定。原生依赖的安装脚本被禁掉以后,数据库通常不会因此变得更纯洁,只会变得打不开。
12. 为什么现在不只剩一个 Model Gateway?
问:领域服务直接调用 DeepSeek 不行吗?
答:能跑,但很快会出现一种经典架构:阅读助手有一套请求代码,写作助手复制一套,第三个 AI 功能再复制一套;半年后大家都声称自己那套才是“公共实现”。
当前代码把模型边界拆成三层:
ai-provider 不知道什么是文章、阅读助手或写作分身,它只负责供应商协议和可实例化的资源策略基元;业务含义留在上层。
写作侧的 Model Gateway 负责:
generateText/generateStructured门面;- Prompt 与 Trace Context;
- JSON 对象提取;
- 一次通用结构化修复;
- 写作 usage 和错误映射。
阅读助手则保留自己的公开文章校验、访客身份、响应缓存和阅读额度,不再充当全项目的“兼职模型基础设施部”。
问:阅读和写作共享 API Key,为什么额度还要分开?
答:共享供应商账号不等于共享业务预算。
公开阅读请求可能突然变多,后台长文生成又可能一次消耗大量 token。如果它们共用一个内存计数器,读者多翻译几次,作者就会在编辑器里收到“今天先别写了”。这个产品联动很有戏剧性,但没有产品价值。
因此当前两侧分别拥有:
- 分钟 / 每日请求上限;
- 用户 / 访客并发;
- 总任务并发;
- 每日 token 预算;
- 独立错误码。
写作配置优先读取 WRITING_AI_*,未单独配置的供应商凭据和通用参数可以回退到 CONTENT_AI_*;写作模型默认仍是 deepseek-v4-pro,阅读模型默认是 deepseek-v4-flash。
问:现在能无缝切换任意供应商吗?
答:还不能。边界已经中性化,当前实际传输实现仍是 DeepSeek SSE。把插座从墙里独立出来,不代表抽屉里已经备齐全世界的转换头。
13. “模型结构化输出无效”后来怎样治理?
问:模型返回了 JSON,为什么全文候选仍可能失败?
答:JSON 合法只说明括号对上了,不说明 FlowDocument 合法。
当前修复分三层:
领域修复明确要求:
heading.level只能是 1~4;- 新文本块只能是 paragraph、heading、quote;
- 未知标题块转 heading,其他未知文本块转 paragraph;
- 原文字不得丢失或改写;
- media、table、code、list、details、equation 等复杂块必须原样保留。
修复失败就失败,不会用“清洗成空文档”这种方式假装成功。空白文章很干净,但通常不是用户想要的那种干净。
14. Writing Persona 怎样生成新版本?
问:用户点“生成分身版本”后,底层发生什么?
答:这是一个 writing_persona_generation 父任务。它固定来源修订,顺序生成已启用的组件,最后原子激活。Style 始终生成,Learning 和 Memory 由能力开关决定。
关键边界:
- 来源必须仍是已发布文章;
- 私有 / 口令来源必须经过显式部署开关;
- 生成期间来源变化会返回冲突;
- 任一已启用组件失败,旧 Persona Version 继续 active;
- 所有已启用组件的候选都成功后才切换;
- 用户以后只选择 Persona,不手工拼组件 ID。
15. Style Profile 到底学了什么?
问:风格学习是不是微调模型?
答:不是。当前是生成可版本化、可追溯的结构化画像。
画像主要包含:
- 风格摘要;
- language、syntax、paragraph、structure、narrative、format 等维度;
- 带权重的风格规则;
- 代表性表达模式;
- 不确定项;
- 来源证据;
- learner / Prompt 版本。
来源会固定到文章不可变版本和 content hash。文章后来改了,旧画像不会偷偷“穿越”到新正文。
问:为什么必须有 evidence?
答:因为“作者常用时间线结构”和“模型觉得他应该喜欢时间线”不是同一件事。
画像可以不完美,但不能完全失忆自己从哪里得出的结论。
16. Personal Memory 为什么有两层?
问:既然 Memory 按 Persona 隔离,为什么还存在用户级 Memory Space?
答:为了复用昂贵的来源抽取,同时保持使用边界隔离。
当前分为:
- 用户级抽取层:按用户维护来源版本和 Memory Observation,可供多个 Persona 复用;
- Persona 级投影层:只用该 Persona 选择的来源,生成独立 Memory Snapshot。
抽取结果可以复用,active Memory 不能串台。后台可以省电,分身不能借错人生。
问:系统怎么保证“不能串台”不是一句团队口号?
答:服务层和数据库层各上一把锁。
服务层现在遵守三条硬规则:
- 写作请求没有
writingPersonaId,就不加载 Style、Learning 或 Memory; - Memory 检索必须同时匹配用户、Persona 和被固定的 Snapshot;
- Learning 的读取、重建和规则查询都必须带 Persona,普通通用写作不会悄悄写一条全局偏好。
数据库迁移 0014_persona_component_isolation.sql 还会:
- 清空
personal_memory_spaces.active_snapshot_id,用户级 Space 只保留来源与抽取缓存职责; - 把旧的全局 active Memory / Learning 投影降为 superseded;
- 用触发器拒绝新增或改回
persona_id IS NULL的 Memory Snapshot、Learning Snapshot、Rule 和 Observation。
同时,客户端和 Admin HTTP 已移除全局 /admin/writing-memory/*、/admin/writing-learning* 管理路径。现在的入口是 /admin/writing-personas,不是在角落里再藏一套“临时全局人格”。架构里的临时后门,往往比正式大门活得更久,所以干脆把门框也拆了。
17. Personal Memory 保存的是什么?
问:它是不是把整篇文章塞进一个向量库?
答:不是。
当前 Memory 会把来源文章版本转换为结构化 Observation,再投影为 Memory Item。条目保留:
- memory key 和类型;
- 事实陈述;
- 时间及精度;
- 置信度;
- 敏感度;
- supported / conflicting / restricted 等状态;
- 对应来源文章版本、块位置和证据 hash;
- 补强、冲突、替代等关系;
- 可检索文本。
Memory 的职责是保存“有出处的个人事实”,不是保存另一份全文,也不是让模型给作者编一本未经授权的传记。
18. Memory 怎样自动进入一次写作?
问:用户写作时需要手工勾选 Memory 吗?
答:不需要。写作分身默认启用,系统会自动完成 Memory 检索和使用;用户只需要关心写作。
当前检索不是向量检索,主要使用:
- SQLite FTS(存在时);
- 词项匹配;
- 置信度;
- 证据数量;
- 条数和字符预算。
它还会排除仅由当前文章自己提供证据的记忆,避免“拿正在写的内容证明正在写的内容”,形成文学版循环引用。
19. Grounding 怎样阻止模型编经历?
问:Memory 已经提供给模型,为什么还要 Grounding?
答:因为模型看过证据,不代表一定只写证据。
当前流程是:
第一人称身份、经历、时间、地点、数量和结果,必须由当前输入或检索到的 Memory 支持。
生成完成后,系统还会记录:
- 提供了多少条 Memory;
- 实际使用了多少条;
- 使用了哪些 memory key;
- Grounding 是否通过;
- Memory 来源文章。
界面默认只显示“自动参考 N 篇 · 使用 Memory N 条”,需要核对时再展开。日常写作不应该被一份审计报告挡住,但审计报告必须找得到。
20. Learning Snapshot 是怎样来的?
问:用户接受一次建议,系统会立刻修改人格吗?
答:不会。一次接受可能代表喜欢,也可能代表今天赶着下班。
当前 Learning Projection 只把接受和拒绝先保存为不可变 Observation,再在生成新 Persona Version 时重建。
当前激活门槛包括至少 3 次正向、至少 4 个来源 Observation、置信度不低于 0.7,并经过确定性评估。
问:人工编辑也会成为修订偏好吗?
答:人工编辑会进入文章版本和 manual_edit Observation,但当前 Learning Projection 真正用于生成规则的数据,仍主要是与 Suggestion 关联的 accepted / rejected 决策。
“人工改了哪里”和“为什么这样改”之间还隔着一层语义。系统没有假装自己已经读心,这一点值得保留。
21. 自动参考文章从哪里来?
问:当前还需要用户手工挑参考文章吗?
答:写作助手主路径已经改成自动选择。
服务端会把以下信息组合成查询:
- 当前文章标题、摘要、分类和标签;
- 写作指令;
- 目标文本;
- 邻近上下文。
然后从其他已发布文章中按 metadata + lexical 规则排序,最多选择 3 篇。
当前没有向量召回。算法很朴素,但可解释、可测试,而且不会在半夜突然声称“这两篇文章在灵魂深处相似”。
22. 一次真实写作请求的完整链路
问:用户在聊天框输入“结合我的 Android 经历重写全文”后发生什么?
答:
本次 Run 会固定:
- 文章 base version 和 render hash;
- Writing Persona Version;
- Style Profile Version;
- Learning Snapshot;
- Personal Memory Snapshot;
- 自动参考文章版本;
- Writing Context Snapshot;
- Prompt / provider / model 摘要。
所以历史解释不需要拿“今天最新的 Persona”去猜“昨天生成时用了什么”。
23. 接受建议时发生什么?
问:点击接受只是前端把内容塞进编辑器吗?
答:不是。接受由服务端再次校验并创建新版本。
拒绝时只更新 Suggestion 状态并写 Learning Observation,正文不动。拒绝不是失败,它是系统终于听见了一次“不”。
24. 为什么版本分 v1.r2 和 v2.r0?
问:局部修改和全文改写为什么版本语义不同?
答:
- 局部人工修改、选区或块级建议,形成当前主版本的新 Revision;
- 全文
replace_document接受后创建新 Major Version; - 恢复历史会追加
restored版本,不修改旧行。
候选在接受前不创建正式版本,否则每次试写都会把版本列表变成模型的草稿箱。
25. 旧建议怎样避免覆盖新正文?
问:模型生成期间用户继续修改,怎么办?
答:生成时固定基线,接受时再次检查。
并发保护包括:
| 基线 | 作用 |
|---|---|
baseVersionId | 当前 head 是否仍是生成时版本 |
baseRenderHash | 当前物化正文是否变化 |
expectedRevisionId | 精确保护目标块修订 |
idempotencyKey | 防止重复点击制造重复事务 |
任一关键基线过期,接受返回 409,Suggestion 标记为 conflicted,不会自动“聪明合并”。
自动合并听起来体贴,事实冲突时通常只是把两个错误搅匀。
26. 全文改写怎样保护图片、表格和代码?
问:候选通过 FlowDocument 校验就安全了吗?
答:不够。结构合法不等于业务安全。
当前全文候选只允许模型自由重组文本内容。复杂块会比较:
- 数量;
- 稳定块 ID;
- 顺序;
- 稳定序列化内容。
media、table、code、list、callout、details、columns、bookmark、file、embed、equation 等复杂块被改变,就拒绝候选。
服务端内部的 section 候选更严格:必须保留原块数量、顺序、ID 和 type,文本块只改 richText,复杂块原样返回。
27. 私有文章为什么需要单独开关?
问:管理员能看私有文章,不就能把它发给模型吗?
答:不能这样推断。
“有权在本站阅读”和“同意发给外部模型供应商”是两种权限。
当前规则:
- 来源必须是 published;
- public 来源默认可用;
- private / password 来源需要
WRITING_ALLOW_PRIVATE_MODEL_SOURCES; - Memory 使用私有来源还需要
WRITING_MEMORY_PRIVATE_SOURCES_ENABLED; - 服务端在任务开始时重新检查状态和可见性;
- 客户端选择不构成最终授权。
权限不能靠复选框的乐观主义。
28. 功能开关之间有什么依赖?
问:当前有哪些主要开关?
答:
WRITING_PERSONA_ENABLED=trueWRITING_ALLOW_PRIVATE_MODEL_SOURCESWRITING_MEMORY_PRIVATE_SOURCES_ENABLEDWRITING_AI_MODEL其中:
WRITING_PERSONA_ENABLED是唯一的写作分身功能总开关,默认开启;- Versioning、Style、Assistant、Learning、Memory、Memory Usage、Memory Auto Refresh 和 Orchestrator 随总开关统一启停;
- Memory Usage 仍要求模型配置完整,否则不会发起模型调用;
WRITING_ALLOW_PRIVATE_MODEL_SOURCES与WRITING_MEMORY_PRIVATE_SOURCES_ENABLED是数据出境安全授权,不属于普通功能开关,因此继续独立且默认关闭。
模型资源配置已经单独形成 WRITING_AI_* 命名空间,包括 ENABLED、PROVIDER、API_KEY、BASE_URL、并发、频率、每日 token 预算、超时和重试等参数。未设置的通用供应商参数可以回退到 CONTENT_AI_*,但运行时计数器不会回退成阅读助手那一只。
设置 WRITING_PERSONA_ENABLED=false 后,文章手工编辑、保存、发布、导出仍应正常。AI 是副驾驶,不是点火系统。
29. 当前总体架构应该怎么画?
问:怎样画一张不夸大现状的架构图?
答:
图里没有一个“万能 Agent 大脑”,因为代码里也没有。真正的控制中心仍是明确的领域服务、资源边界和数据库事务。图很大,但至少每个方框都真有文件,不是拿虚线框出来融资。
30. 各模块在演讲中怎么一句话解释?
31. 为什么现在还没有通用 Agent Loop?
问:能力已经这么多,为什么不上 LangGraph 或通用工具循环?
答:因为当前主要任务仍能用显式流程可靠完成。
这些流程分支有限,失败语义明确。引入通用 Agent Loop 会增加隐藏控制流,却暂时没有带来同等收益。
未来 Orchestrator 可以决定“下一步调用哪个稳定命令”,但不能:
- 绕过 FlowDocument;
- 绕过权限;
- 绕过版本基线;
- 自己接受 Suggestion;
- 自己恢复版本;
- 自己发布文章。
Agent 可以负责路线规划,不能顺手把道路产权也办到自己名下。
32. Personal Mind 为什么还没有进入当前架构?
问:有了 Personal Memory,为什么还需要 Personal Mind?
答:两者解决的问题不同。
- Memory 是有证据的事实层:做过什么、经历过什么、什么时候发生;
- Mind 更接近长期观点、价值判断、认知模型和当前立场。
当前项目已经能安全使用个人事实,但尚未建立足够稳定的观点冲突、时效、反思和修订协议。把观点过早写成“长期人格”,比记错一个年份更难纠正。
所以当前顺序是合理的:先把 Memory 做成有证据、可冲突、可撤回的事实系统,再讨论 Mind。先学会记笔记,再考虑写哲学。
33. 系统失败时怎样降级?
问:模型失败会影响普通编辑吗?
答:不会。AI Run / Task 失败,正文保持不变。
问:结构修复失败呢?
答:Run 失败,不保存可接受 Suggestion。
问:Grounding 两次都不过呢?
答:Run 失败,不允许候选进入接受阶段。
问:接受时版本冲突呢?
答:Suggestion 变成 conflicted,正文不变。
问:服务重启呢?
答:遗留 running Task 恢复为 queued,并记录 recovered_after_restart。
问:版本、head 或搜索索引写到一半失败呢?
答:SQLite 事务整体回滚。
这些降级有一个共同点:宁可少生成一次,也不偷偷多改一笔。
34. 如果 Agent 做错了,怎样排查?
问:最短排查链是什么?
答:
依次问:
- 谁接受了这条建议?
- 接受时的 base version 和 hash 是什么?
- operations 改了哪些块?
- 生成时使用哪个 Persona Version?
- 提供和实际使用了哪些 Memory?
- 自动参考了哪些文章版本?
- 结构化修复或 Grounding 是否发生过?
- Run 是否重试、取消或从重启中恢复?
把“AI 怎么突然这样写”拆成可查询事实,情绪会下降,定位速度会上升。
35. 常见现场质疑怎么回答?
问:这不就是工作流,不是 Agent 吗?
答:
当前执行层主要是显式工作流,这是刻意的阶段选择。系统先把上下文、模型、任务、建议、版本和权限做成可独立验证的能力,再让未来 Orchestrator 编排它们。Agent 这个名字不能拿来给自由文本接口镀金。
问:为什么不用大模型一次完成所有事情?
答:一次大 Prompt 无法替代来源版本、隐私策略、任务恢复、输出校验、建议决策、并发冲突和数据库事务。它只是把系统复杂度塞进一段更难调试的文字里。
问:为什么不用 Git 保存文章版本?
答:产品需要领域版本:块身份、Run、Message、Suggestion、接受者、幂等键和在线事务。Git 擅长文件历史,不负责理解“这条建议为什么形成 v2.r0”。
问:SQLite 撑得住吗?
答:当前单实例、个人内容系统和受控并发下可以,而且现在不是靠祈祷撑住。
运行时已经统一到 better-sqlite3,文件库启用 WAL、外键和 5 秒 busy timeout;并发测试覆盖两个真实写者争抢事务锁后都成功提交。迁移、Seed 和运行时也都归服务端所有。
但这不等于 SQLite 被授予了“永不迁移”勋章。真正的升级信号包括:持续锁竞争、长事务阻塞、数据库体量与备份窗口失控、需要多节点 Worker 租约,或者写入吞吐开始由单机磁盘决定。表多不是罪证,写入模型失配才是。
问:项目接入 CI 了吗?
答:仓库侧已经接入统一门禁。
npm run ci:validate 会创建临时空库,执行全部迁移,再运行架构检查、客户端类型检查、服务端健康检查、共享层检查、旧来源检查,以及写作 Agent、阅读助手和搜索回归。GitHub 镜像有现成 Workflow;主远端是 Codeup,因此云效 Flow 还需要在控制台做一次仓库关联并执行同一命令。
CI 不是用来证明代码永远没问题,它负责让同一种低级错误不要每周轮流复活。这个岗位不浪漫,但很省咖啡。
36. 下一步最合理的演进顺序
问:现在最该做多 Agent,还是补现有闭环?
答:更稳妥的顺序是:
数据库运行时、AI 资源边界和 Persona 隔离这三个高风险地基已经收紧;下一步先把门禁真正挂到 Codeup 提交事件,再继续降低大文件的变更半径。admin-routes 已拆出分身路由,assistant-sessions 已拆出 Prompt,但它们还没有小到可以放进口袋里。
原因很简单:写作分身的长期价值来自“越来越理解作者”,而不是“工具调用次数越来越多”;工程稳定性也来自边界持续变窄,而不是文件名越来越有气势。
37. 十分钟架构演讲怎么讲?
问:只有十分钟,主线怎么安排?
答:可以分五段。
第一分钟,讲产品:
这是住在博客编辑器里的个人写作分身。它从用户认可的已发布文章生成风格、修订偏好和个人记忆,但模型只能提交候选。
第二到三分钟,讲第一个转折:
选区 AI 只需要“文字进、文字出”。一旦要回答谁改了什么、能否恢复、能否学习,就必须升级成 FlowDocument 操作和不可变版本。
第四到六分钟,讲核心链路:
写作请求固定 Persona Version、自动参考和 Memory,经过 Run / Task、结构校验和 Grounding 后保存 pending Suggestion。接受时唯一提交命令原子更新版本、head 和索引。
第七到八分钟,讲个性化:
Persona 下 Style、Learning、Memory 平行版本化。Memory 抽取可在用户级复用,但 active Snapshot 按 Persona 隔离。
最后两分钟,讲边界:
当前没有通用自主 Agent。模型传输可以共享,但阅读和写作额度分开;SQLite 用 WAL 保证单机并发写入,迁移和 CI 从服务端事实开始。未来 Orchestrator 只编排稳定命令,不拥有正文、权限和发布权。
收尾可以说:
个人写作分身真正的难题,不是让模型多写几段话,而是让每一次 AI 参与都可解释、可拒绝、可恢复,并且最终仍然属于作者。
38. 常见追问速答
问:AI 会自动发布文章吗?
答:不会。
问:AI 会直接覆盖正文吗?
答:不会。先生成 pending Suggestion,接受后才进入版本事务。
问:聊天框默认改哪里?
答:默认全文;从编辑器区块入口打开时自动块级。
问:选区 AI 走什么路径?
答:新版能力开启时走隐藏 Session + Run + Task + Suggestion;关闭时降级到旧 editor-assistant。
问:小节修改已经有 UI 吗?
答:服务端协议支持,当前主 UI 没有开放入口。
问:参考文章需要手选吗?
答:不需要。服务端自动召回最多 3 篇相关已发布文章。
问:Memory 需要手工维护吗?
答:不需要逐条维护。用户只选择 Persona 的已发布来源文章;提取、投影、检索和使用自动进行。
问:不选择写作分身时,会不会偷偷使用某个全局 Memory?
答:不会。不选择 Persona 时,Style、Learning 和 Memory 都为空;全局 active Snapshot 和管理 API 已退出运行路径。
问:怎么知道用了几条 Memory?
答:助手消息会显示使用数量,展开可查看来源解释。
问:风格学习是微调吗?
答:不是,是有版本、来源和证据的结构化画像。
问:Learning 会在一次接受后立刻改变当前分身吗?
答:不会。Observation 先积累,生成新 Persona Version 时才重建和激活 Snapshot。
问:现在有向量数据库吗?
答:没有。参考文章用 metadata + lexical;Memory 用 SQLite FTS / 词项排序。
问:现在有 Personal Mind 吗?
答:没有。
问:现在有通用多 Agent 吗?
答:没有。当前是显式领域服务和持久任务工作流。
问:阅读助手会抢光写作助手额度吗?
答:不会共用运行时额度。两者可以回退使用同一组供应商凭据,但频率、并发和每日 token 预算分别计算。
问:SQLite 还有 `sql.js` 降级吗?
答:没有。当前统一使用 better-sqlite3,文件库启用 WAL 和 busy timeout。
问:模型能修改图片和表格吗?
答:当前自动改写主要面向文本块,复杂块必须原样保留。
39. 术语表
40. 代码事实索引
问:讲完以后去哪里核对?
答:
- 能力开关:
server/src/modules/writing-agent/writing-config.js - Writing Persona:
server/src/modules/writing-persona/writing-persona.service.js - 写作模型门面:
server/src/modules/ai-gateway/model-gateway.js - 写作资源策略:
server/src/modules/ai-gateway/writing-resource-policy.js - 中性模型 Provider:
server/src/modules/ai-provider - 公开阅读额度与缓存:
server/src/modules/reading-assistant/reading-assistant.service.js - AI Run:
server/src/modules/ai-runs/ai-runs.service.js - TaskRunner:
server/src/modules/task-runner/task-runner.service.js - 自动参考文章:
server/src/modules/content-context/content-context.service.js - Style Profile:
server/src/modules/style/style.service.js - Learning Projection:
server/src/modules/learning/learning.service.js - Personal Memory:
server/src/modules/personal-memory - 会话和 Suggestion:
server/src/modules/assistant-sessions/assistant-sessions.service.js - 助手 Prompt:
server/src/modules/assistant-sessions/assistant-prompt.js - 当前正文提交:
server/src/modules/posts/posts.service.js - 不可变版本:
server/src/modules/versioning/versioning.service.js - FlowDocument 操作与 Diff:
shared/src/content/document/flow-document-operations.js - Admin API 总入口:
server/src/http/admin-routes.js - Writing Persona Admin API:
server/src/http/admin-writing-persona-routes.js - SQLite 运行时:
server/src/db/sqlite-runtime.js - 数据库迁移与 Seed:
server/scripts/db-migrate.mjs、server/scripts/db-seed.mjs - CI 统一入口:
scripts/run-ci.mjs、.github/workflows/ci.yml - 分身管理 UI:
client/components/admin/writing-agent/WritingAgentHome.js - 写作助手 UI:
client/components/admin/assistant/WritingAssistantPanel.js - 编辑页入口:
client/pages/admin/posts/[id]/edit.js - 基础迁移:
server/db/migrations/0010_writing_agent_foundation.sql - Task / Learning 迁移:
server/db/migrations/0011_writing_agent_completion.sql - Personal Memory 迁移:
server/db/migrations/0012_personal_memory.sql - Writing Persona 迁移:
server/db/migrations/0013_writing_personas.sql - Persona 强隔离迁移:
server/db/migrations/0014_persona_component_isolation.sql - 写作 Agent 测试:
tests/feature/20260715/writing-agent-foundation.test.mjs - Personal Memory 测试:
tests/feature/20260715/personal-memory.test.mjs - Writing Persona 测试:
tests/feature/20260716/writing-persona.test.mjs - AI 资源边界测试:
tests/feature/20260716/ai-resource-boundary.test.mjs - SQLite 并发测试:
tests/feature/20260716/sqlite-runtime-concurrency.test.mjs
进一步阅读:
- 项目架构
- 文章数据架构
- 编辑器架构
- 个人写作分身 Agent 技术说明
- 端侧分层架构
- 前台长文阅读渲染架构
- OpenClaw 架构演进问答
41. 最后的架构判断
问:这套架构最值得保留的是什么?
答:不是某个模型名,也不是某一版 Prompt,而是这些边界:
- FlowDocument 是正文唯一主协议;
- 客户端不直接写 SQLite,也不持有模型密钥;
- 模型只生成候选,不拥有正文写入权;
- 用户接受后才进入统一内容提交命令;
- 版本、块修订、事件、head 和索引保持事务一致;
- Persona 下 Style、Learning、Memory 平行且独立版本化;
- Memory 抽取可以复用,Persona Snapshot 必须隔离;
- 没有 Persona 就没有 Style、Learning 和 Memory 上下文,不存在全局人格兜底;
- 自动参考和 Memory 使用都固定到一次 Run;
- 结构化输出、复杂块和个人事实分别有校验护栏;
- 阅读与写作只共享中性 Provider,不共享运行时额度;
- SQLite 文件连接统一使用
better-sqlite3 + WAL,不回退到整库覆盖写回; - 数据库迁移和 Seed 归服务端,客户端不拥有 Schema;
npm run ci:validate从空库迁移开始执行统一门禁;- Run 是业务事实,Task 是执行事实;
- 原始版本和行为事实不可覆盖,投影可以重算;
- 未来 Orchestrator 只编排稳定能力,不成为第二套内容系统。
只要这些边界不丢,模型可以换,TaskRunner 可以升级,检索可以演进,Learning 算法也可以重算。
这就是当前项目的核心选择:
先让 AI 的每一次参与都能被系统负责,再逐步让它拥有更多能力。