diff --git a/.gitignore b/.gitignore
index d8686c6..5e1649f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,5 @@ pnpm-debug.log*
*.tsbuildinfo
next-env.d.ts
.gstack/
+
+e2e/thread-chat/shots/**
diff --git a/docs/memory/01-survey.md b/docs/memory/01-survey.md
new file mode 100644
index 0000000..a8df16f
--- /dev/null
+++ b/docs/memory/01-survey.md
@@ -0,0 +1,384 @@
+# Chatbot / Agent 记忆全景调研报告
+
+> **版本提示(2026-07-24)**:本文是阶段 1 的历史研究基线。快速变化的方案以阶段 2 源码快照为准,尤其是 Mem0 当前 OSS 主干已从本文描述的 ADD/UPDATE/DELETE/NOOP 管线演进为 V3 ADD-only + memory linking,Letta 的推荐入口也已从 V1 server 转向 Agent SDK。详见 [`02-deep-dives/`](./02-deep-dives/)。
+
+> 阶段 1 产出 · 2026-07 · 深度调研流程:5 个检索角度并行 → 24 个来源 → 抽取 114 条候选结论 → 对 25 条关键结论做 3 票对抗性核实(24 确认 / 1 证伪)。
+>
+> **证据分级标记**(贯穿全文):
+> - ✅ **已核实** —— 经 3 票对抗性验证、原文逐字比对通过;
+> - ⚠️ **有保留** —— 已核实但来源单一或为厂商自报数据,采信需打折;
+> - ❓ **待核验** —— 来自社区博客/厂商文档的一手抽取,未进入本轮验证名单,将在阶段 2(逐方案深入)中核验;
+> - ❌ **已证伪** —— 对抗验证中被否决的说法,供透明披露。
+
+---
+
+## 目录
+
+1. [为什么 Chatbot 需要「记忆」,记忆和 RAG 有什么区别](#1-为什么需要记忆)
+2. [学术分类学:给「记忆程度」找到骨架](#2-学术分类学)
+3. [记忆程度阶梯:L0–L7 分级与对应实现方法(本报告核心)](#3-记忆程度阶梯)
+4. [代表方案全景:逐个画像](#4-代表方案全景)
+5. [统一架构对比表](#5-统一架构对比表)
+6. [批判性发现:基准之战、公认短板与安全风险](#6-批判性发现)
+7. [关键论文与文献清单](#7-关键论文清单)
+8. [本项目落地路线图(总纲)](#8-本项目落地路线图)
+9. [遗留问题与阶段 2 核验清单](#9-遗留问题与阶段-2-核验清单)
+
+---
+
+## 1. 为什么需要记忆
+
+一个没有记忆机制的 LLM 应用,每次请求都只能依赖「塞进上下文窗口的内容」。上下文窗口是有限的、无状态的、按 token 计费的——这带来三类问题:
+
+1. **长对话撑爆窗口**:对话越长,历史越多,最终截断或报错;
+2. **跨会话失忆**:用户昨天说过「我对坚果过敏」,今天新开会话模型一无所知;
+3. **成本与延迟随历史线性增长**:每轮都把全部历史重发一遍,token 费用和首字延迟持续累加。
+
+学术界对记忆的定位(✅):多篇综述将记忆视为 LLM Agent **自我进化与长期交互的核心组件**(arXiv:2404.13501,ACM TOIS),是支撑长程推理、持续适应与有效交互的基石能力(arXiv:2512.13564)。
+
+**记忆 ≠ RAG**(❓,多来源一致):向量检索式 RAG 只是记忆实现的一个子集。真正的 Agent 记忆系统与纯 RAG 的本质区别在于**写入路径**——记忆需要同时具备**写入策略、检索策略和更新策略**,而 RAG 通常是只读检索。换句话说:RAG 回答「如何把已有知识读出来」,记忆还要回答「哪些交互值得写进去、写成什么形态、过时了怎么办」。
+
+一条被两篇独立综述逐字确认的写入原则(✅):
+
+> "Storing every interaction verbatim is tempting and almost always wrong."(逐字存储所有交互很诱人,但几乎总是错的)—— arXiv:2603.07670
+>
+> "Forgetting is not a bug; it is a feature——essential for robustness, privacy, and efficiency."(遗忘不是缺陷而是特性)—— 同上
+
+---
+
+## 2. 学术分类学
+
+「记忆做到什么程度」不是一个社区拍脑袋的问题——2024–2026 年间至少五篇系统性综述给出了互补的分类框架(✅,每篇的分类维度均经原文逐字核验):
+
+| 综述 | 分类维度 | 对本报告的贡献 |
+| --- | --- | --- |
+| **arXiv:2404.13501**(人大/华为诺亚,ACM TOIS,同行评审)| 记忆来源(试验内 in-trial / 跨试验 cross-trial / 外部知识)× 形式(文本/参数化)× 操作(读 reading / 写 writing / 管理 management)| 「单会话 vs 跨会话」层级的直接学术对应 |
+| **arXiv:2504.15965**《From Human Memory to AI Memory》| 对象(个人 vs 系统)× 形式(参数化 vs 非参数化)× 时间(短期 vs 长期)三维八象限;并把人类记忆操作映射到 AI:编码、存储、检索、巩固、再巩固、反思、遗忘 | 「更新/冲突/遗忘」对比维度的统一表头;短期记忆被明确定义为「当前会话内临时维持的上下文」 |
+| **arXiv:2512.13564**《Memory in the Age of AI Agents》(47 位作者)| Forms(token 级 / 参数化 / 潜在状态)× Functions(事实性 / 经验性 / 工作记忆)× Dynamics(形成 / 演化整合遗忘 / 检索)| 明确「RAG 只是记忆的子集」;配套 200+ 论文索引仓库 |
+| **arXiv:2602.06052**(60 位作者)| 记忆基质(内部/外部)× 认知机制(情景/语义/感觉/工作/程序性)× 记忆主体(Agent 中心 vs 用户中心)| 「Agent 的记忆」和「关于用户的记忆」是两件事——本项目主要做后者 |
+| **arXiv:2603.07670**《Memory for Autonomous LLM Agents》| 时间尺度(工作/情景/语义/程序性)× 表示基底(上下文文本/向量库/结构化存储/可执行仓库)× 控制策略(启发式/提示自控/RL 学习式)| 与本报告 L0–L7 阶梯最接近的层级体系 |
+
+### 认知四分法(贯穿所有层级的横向维度)
+
+「情景 vs 语义 vs 程序性记忆」的学术源头是 **CoALA**(arXiv:2309.02427,TMLR 2024,同行评审),已被 LangChain/LangMem 文档、Cognee 及后续综述沿用为标准参照(✅):
+
+- **工作记忆(working)**:当前决策周期内的活跃信息 ≈ 当前上下文窗口内容;
+- **情景记忆(episodic)**:过往经历/轨迹 ≈「用户在 5 月 3 日的会话里说过 X」;
+- **语义记忆(semantic)**:关于世界与自身的去情境化知识 ≈「用户对坚果过敏」(不关心是哪次说的);
+- **程序性记忆(procedural)**:隐式存于 LLM 权重 + 显式写在 Agent 代码/系统提示中的「怎么做」知识 ≈「回答该用户时始终用中文、代码示例用 TypeScript」。
+
+CoALA 还把读写机制形式化为(✅):**检索动作把长期记忆读入工作记忆,学习动作把信息写入长期记忆**——这是后文对比所有方案「写入时机/检索注入方式」的统一分析框架。
+
+> 注意(✅,验证者附注):各家分类学是**正交维度**切分,不是严格的「程度递进」。下一章的 L0–L7 阶梯是本报告为落地做的层级映射,映射关系已尽量对齐上表文献。
+
+---
+
+## 3. 记忆程度阶梯
+
+这是本报告的核心:把「记忆可以做到什么程度」整理为 8 级递进阶梯。每一级给出定义、社区通行实现方法、代表方案与工程复杂度。**每一级都包含前面所有级的能力**;实践中通常组合多级,而非只选一级。
+
+### L0 · 无记忆(stateless)
+
+- **定义**:每次请求独立,不携带任何历史。
+- **实现**:什么都不做。单轮工具型应用(翻译、改写)常刻意停在这一级。
+- **成本/复杂度**:零。
+
+### L1 · 单会话上下文窗口管理(截断 / 滑动窗口)
+
+- **定义**:把当前会话的消息历史随请求重发;超出窗口预算时做截断。对应学术上的**工作记忆**、arXiv:2504.15965 的「短期记忆」(✅)。
+- **实现方法**:
+ - **全量重发**:历史不长时最简单,也是 AI SDK `streamText({ messages })` 的默认形态;
+ - **滑动窗口**:只保留最近 N 条消息或最近 K tokens;
+ - **头尾保留**:保留 system prompt + 最早几条(任务设定)+ 最近若干条,砍中间;
+ - **重要性截断**:按消息类型加权(保留工具结果摘要、丢弃寒暄)。
+- **代表**:所有聊天框架的默认行为;LangChain 早期的 `ConversationBufferWindowMemory` 即此级的封装。
+- **本项目现状**:`app/api/chat/route.ts` 目前就是全量重发,**尚无任何窗口管理**。
+- **成本/复杂度**:极低。纯确定性代码,无额外 LLM 调用。
+- **失效模式**(❓):上下文膨胀——历史一长,成本涨、注意力稀释、最终截断丢信息。
+
+### L2 · 会话内摘要压缩(summarization / compaction)
+
+- **定义**:会话过长时,用 LLM 把较早的消息压缩成摘要,替换原文继续对话。仍是会话内行为,对应「巩固 consolidation」操作的最初级形态(✅,2504.15965 将 consolidation 定义为「将短期记忆转化为长期记忆」)。
+- **实现方法**:
+ - **阈值触发滚动摘要**:token 数超过阈值 → 把最早 50% 压成一段摘要,作为一条 system/assistant 消息放回队首(LangChain `ConversationSummaryBufferMemory`、Claude Code 的 compaction、OpenAI Agents SDK 的 session summarization 均为此思路);
+ - **分层摘要**:摘要的摘要,适合超长会话;
+ - **选择性保留**:摘要时显式保留实体、数字、决定等高价值信息。
+- **理论上限警示**(⚠️,来自 schema-grounded 论文 arXiv:2604.27906,vendor 相邻但论证独立成立):由数据处理不等式 I(A;Z) ≤ I(A;X)——**写入时无法预知未来查询**,摘要必然丢失低显著性细节(精确数值、被否决的选项、否定式陈述、时间戳)。摘要是有损压缩,不能当作可靠的事实存储。
+- **成本/复杂度**:低。每次压缩一次 LLM 调用;难点在触发时机与「摘要丢了关键细节」的兜底。
+
+### L3 · 跨会话持久化(thread persistence)
+
+- **定义**:把完整消息历史落库,刷新页面、换设备、隔天回来还能继续这个会话。对应学术上的 in-trial → cross-trial 边界(✅,2404.13501)。
+- **注意**:这一级持久化的是**聊天历史**,还不是「记忆」——换一个新会话,模型依然不认识你(❓,db0 等多来源一致强调此区分)。Vercel AI SDK 本身只提供消息持久化与钩子(`prepareStep`、`onFinish`),**没有内置的跨会话长期记忆原语**(❓)。
+- **实现方法**:
+ - 消息整体存 JSONB(**本项目已实现**:`lib/db/schema.ts` 的 `threads`/`messages`,`messages.content` 存完整 `UIMessage`);
+ - 规范化三表设计(chats / messages / parts,每种 part 类型专用前缀列)——Vercel 官方示例 `vercel-labs/ai-sdk-persistence-db` 的做法,但该仓库已于 2026-06 归档,仅作架构参考(❓)。
+- **本项目现状**:✅ 已完成(`RemoteThreadListAdapter` + `ThreadHistoryAdapter` + Postgres)。**本项目的记忆工程从 L3 之上开始。**
+- **成本/复杂度**:低-中(本项目已付讫)。
+
+### L4 · RAG 向量检索式记忆(retrieval memory)
+
+- **定义**:把历史消息/文档切块、嵌入为向量存入向量库;回答前按语义相似度检索 top-k 注入提示。综述确认这是**当前 Agent 外部记忆的主导实现**(✅,2602.06052 原文:"The dominant implementation of external memory in the current work of agents or LLMs")。
+- **标准实现**(✅):记忆条目与查询嵌入到共享向量空间 → 近似最近邻 top-k → 片段追加进 prompt 作为 grounding 上下文。
+- **实现变体**:
+ - **对会话历史做 RAG**:把旧消息按轮次/块嵌入,新问题来时检索相关旧轮次(情景记忆的向量化);
+ - **对知识库做 RAG**:AI SDK 官方 RAG Chatbot 指南的路线(❓,但为官方一手文档)——技术栈与本项目完全一致(Next.js App Router + AI SDK + Drizzle + Postgres/pgvector):
+ - 写入:模型通过 `addResource` **工具自主决定写入时机**(工具描述指示「用户主动提供知识时无需确认即写入」);
+ - 检索:Drizzle `cosineDistance` 算余弦相似度、过滤 > 0.5、取 top-4,embedding 列建 **HNSW 索引**(`vector_cosine_ops`);
+ - 注入:**按需工具检索**(`getInformation` 工具 + `stopWhen` 5 步上限),而非每轮自动前置注入;
+ - 分块刻意极简(按句号切分、无重叠),生产需自行增强。
+ - **检索排序不只看相似度**(❓,db0 实践文章):语义相似度 ×0.7 + 时近性 ×0.2 + 使用频度 ×0.1 的加权公式是社区常见做法(Generative Agents 的 recency × importance × relevance 三因子是其源头)。
+- **能力边界**(⚠️,schema-grounded 论文核心论点,✅ 核实其原文):**嵌入捕捉主题相关性,但不是谓词求值器**——对精确事实查找、状态跟踪、聚合、关系查询、否定/排除查询,纯向量检索「在构造上就不够用」(高相似 ≠ 事实存在)。向量记忆擅长「找回大概相关的内容」,不擅长「精确回答用户现在住哪」。
+- **成本/复杂度**:中。需要 embedding 调用、pgvector 扩展、索引运维;无写入管线时仍接近只读 RAG。
+
+### L5 · 结构化事实 / 实体记忆(extraction + fact store)
+
+- **定义**:不再存原文块,而是用 LLM 从对话中**抽取离散事实**(「用户住在杭州」「项目截止日是 8/1」),存为结构化条目,并有显式的**更新/冲突/去重**管线。这是「记忆系统区别于 RAG」的分水岭——有了真正的写入策略。
+- **代表实现——Mem0 的两阶段管线**(✅,论文原文 + 第三方代码走读双重核实):
+ 1. **抽取阶段**:LLM 从新消息对(结合会话摘要与近期上下文)抽取显著事实;
+ 2. **更新阶段**:LLM 本身(非独立分类器)把候选事实与向量检索到的 top-s 相似既有记忆比较,决定 **ADD / UPDATE / DELETE / NOOP** 四操作之一——一个管线同时完成写入、增补、冲突消解与去重。
+- **图增强变体**(✅):Mem0^g 将记忆存为有向带标签图 G=(V,E,L)(节点=实体、边=关系、标签=语义类型),实体抽取 + 关系三元组两阶段写入,检索结合实体导航与语义三元组匹配;Zep/Graphiti 则把事实建模为**带有效时间窗口的图边**——新旧冲突时旧边标记失效但保留历史,支持「时间点正确性」查询,且查询路径不调 LLM(❓,厂商文档,P95 检索延迟约 300ms 为自报)。
+- **量化收益**(⚠️,Mem0 论文自报,数字经逐项核验、算术自洽,但为 vendor 自评且存在竞品争议,详见第 6 章):相比 full-context 全量历史,p95 端到端延迟降低 91%(约 1.44s vs 17.1s)、token 成本节省超 90%(约 1,764 vs 26,031 tokens)——**代价是准确率略降**(LOCOMO J 分 66.88% vs full-context 的 72.90%)。即抽取式记忆的本质是**用少量准确率换一个数量级的成本/延迟优势**。
+- **抽取的另一条路**(❓):确定性规则匹配抽取(零 LLM 调用、近零延迟,但只覆盖约 60–70% 场景)vs LLM 抽取(每轮 +200–500ms、$0.001–0.01)——db0 实践文章的实测取舍。
+- **成本/复杂度**:中-高。每轮(或缓冲后)额外 1–3 次 LLM 调用;需要设计事实 schema、作用域(user/session/agent)、冲突策略。**这一级是社区公认收益/复杂度比最高的一级,也是各方案分化最大的一级。**
+
+### L6 · 用户画像与偏好记忆(profile memory / 程序性记忆)
+
+- **定义**:把零散事实进一步**聚合成稳定的用户画像**(结构化档案)或**行为规则**(程序性记忆),每轮对话直接注入,不走检索。对应 2602.06052 的「用户中心记忆」与 2404.13501 的「个人记忆用于个性化」(✅ 综述层面)。
+- **实现方法**:
+ - **层级化画像**(❓,Memobase 路线,"Memory for User, not Agent"):结构化 topic/sub-topic 档案(`basic_info.name`、`interest.games`、`psychological.goals`),**写入不在热路径**——新消息先进缓冲区,累计约 1024 tokens 或闲置约 1 小时才触发 LLM 抽取 flush;读取只是把预计算好的画像打包成字符串插入 prompt,在线延迟可 <100ms(自报);
+ - **固定槽位画像**:ChatGPT memory 的「Saved memories」形态——离散记忆条目 + 从历史中隐式学到的偏好(官方披露有限,待阶段 2 核验,❓);
+ - **程序性记忆 / 自更新系统提示**(❓,LangMem 路线):LangMem 在 LangGraph 持久化 store 之上支持情景/语义/程序性三类记忆,其中**程序性记忆允许 Agent 更新自己的系统提示指令**——「用户偏好简洁回答」不是存起来等检索,而是直接改写到下轮 system prompt;Letta 的 core memory blocks(persona/human 块常驻上下文、由 Agent 自编辑)同属此思路。
+- **与 L5 的关系**:L5 是「事实数据库」,L6 是「每轮必带的压缩视图」。典型组合:画像常驻注入(L6)+ 长尾事实按需检索(L4/L5)。
+- **成本/复杂度**:中。画像抽取可离线/缓冲进行,不增加在线延迟;难点是画像 schema 设计与「何时该进画像、何时留在事实库」的分层。
+
+### L7 · 自我反思与记忆整合(reflection / consolidation / forgetting)
+
+- **定义**:记忆系统定期「回顾自己的记忆」——把低层记忆综合成高层洞见(反思)、合并去重(整合)、按时间/价值衰减修剪(遗忘)。这是目前最高级也最不成熟的一级。
+- **奠基设计——Generative Agents**(✅,arXiv:2304.03442):memory stream 以自然语言记录全部经历(带时间戳与重要性分数),检索按 recency × importance × relevance 加权;**反思机制**将低层记忆随时间综合为高层结论;消融实验证明去掉反思后,智能体行为在 48 模拟小时内从连贯的多日规划退化为重复的无上下文响应(❓ 该消融细节来自抽取,方向与 ✅ 核实的「反思整合是该论文核心贡献」一致)。
+- **遗忘的实现例证**(✅):MemoryBank(AAAI 2024)按 **Ebbinghaus 遗忘曲线**对记忆做时间衰减——被引用越少、越久远的记忆越先淡出。
+- **记忆演化**(❓,A-Mem,arXiv:2502.12110):受卡片盒笔记法(Zettelkasten)启发——每条新记忆写入时 LLM 生成结构化笔记(上下文描述/关键词/标签),并自动触发两个操作:**链接生成**(嵌入检索候选 + LLM 判断建链)与**记忆演化**(LLM 可回头更新既有记忆的描述与标签);自报每次记忆操作仅约 1,200 tokens(对比 MemGPT 基线约 16,900)。
+- **反思的量化收益**(❓):Reflexion 在 HumanEval 上 91% pass@1(无反思 GPT-4 基线 80%)。
+- **成本/复杂度**:高。需要后台任务(cron/队列)、多轮 LLM 调用、且效果难评估。社区共识是**先把 L4–L6 做扎实再考虑 L7**;但轻量版(定期把事实表去重合并一次)值得早做。
+
+### 阶梯总览
+
+| 级别 | 名称 | 记忆类型(CoALA) | 额外 LLM 调用 | 存储 | 本项目现状 |
+| --- | --- | --- | --- | --- | --- |
+| L0 | 无记忆 | — | 无 | 无 | — |
+| L1 | 窗口管理 | 工作记忆 | 无 | 无 | ❌ 未做(全量重发) |
+| L2 | 会话内摘要 | 工作记忆→巩固 | 压缩时 1 次 | 会话内 | ❌ 未做 |
+| L3 | 跨会话持久化 | (聊天历史,非记忆) | 无 | Postgres | ✅ 已完成 |
+| L4 | 向量检索记忆 | 情景记忆 | embedding | pgvector | ❌ 未做 |
+| L5 | 结构化事实记忆 | 语义记忆 | 每轮/缓冲 1–3 次 | 事实表(±向量/图) | ❌ 未做 |
+| L6 | 画像/偏好记忆 | 语义+程序性 | 离线/缓冲 | 画像表 | ❌ 未做 |
+| L7 | 反思/整合/遗忘 | 元记忆操作 | 后台批量 | 同上 | ❌ 未做 |
+
+---
+
+## 4. 代表方案全景
+
+> 本章除标注 ✅/⚠️ 的条目外,框架细节多为 ❓(社区来源一手抽取,未经对抗核验)——这正是阶段 2 要逐个深入的对象。
+
+### 4.1 MemGPT → Letta(OS 式分层记忆的鼻祖)
+
+- **论文**(✅):arXiv:2310.08560,UC Berkeley(Packer 等,团队后创立 Letta 公司)。提出**虚拟上下文管理**:借鉴操作系统分层内存(RAM/磁盘),在**主上下文(main context)、召回数据库(recall,对话历史)、归档向量存储(archival)**之间由 **LLM 自主函数调用**搬移数据(「分页」是类比,实际是工具调用)。
+- **产品形态 Letta**(❓):core memory(persona/human 块,常驻上下文、Agent 自编辑)+ recall storage(近期对话+向量搜索)+ archival storage(长期事实+语义检索);完全开源可自托管。
+- **定位**:「Agent 自己管理自己的记忆」路线的代表——把记忆决策权交给模型(提示自控策略),而非工程管线。
+- **适合**:Agent 自主性高的场景;**不适合**:想要确定性、可审计记忆行为的场景。
+
+### 4.2 Generative Agents(反思/整合路线的鼻祖)
+
+- **论文**(✅):arXiv:2304.03442(Park 等,2023-04,斯坦福 25-agent 小镇)。memory stream + recency/importance/relevance 检索 + reflection 合成高层洞见。早于 MemGPT/Mem0,是几乎所有后续记忆方案引用的上游。
+
+### 4.3 Mem0(LLM 抽取式结构化事实记忆)
+
+- **论文**(✅):arXiv:2504.19413。两阶段管线(抽取 → ADD/UPDATE/DELETE/NOOP 更新),图变体 Mem0^g;开源实现与论文一致(第三方代码走读印证)。
+- **自报成绩**(⚠️):LOCOMO 上相对 OpenAI memory +26% 相对提升(66.88% vs 52.90%);vs full-context 延迟 -91%/token -90%,但准确率低于 full-context(详见 §6.1 的基准争议)。
+- **商业化**(❓):多存储架构(向量+图+KV);三层作用域 user/session/agent;图记忆需付费 Pro;托管服务 $19/月起,核心开源。
+- **已知短板**(❓/⚠️):事实覆盖更新而非带效期版本化 → 时序推理弱(LongMemEval 49.0% vs Zep 63.8%,第三方转引);FactConsolidation 冲突消解仅 18%(⚠️,见 §6.2)。
+
+### 4.4 Zep / Graphiti(时序知识图谱)
+
+- **路线**(❓,厂商文档+多方转述一致):Graphiti 引擎把每条事实存为**带有效期窗口(valid-from / invalid-at)的图边**;新信息冲突时旧边失效但保留历史 → 支持「时间点正确性」("用户 3 月时住哪?");查询路径不调用 LLM,自报 P95 检索约 300ms。
+- **商业化**(❓):Zep 平台是 SaaS(约 $25/月起),仅 Graphiti 引擎开源。
+- **强项**(❓):时序推理(LongMemEval 63.8% vs Mem0 49.0%,转引自第三方基准);**短板**(⚠️):FactConsolidation 单跳仅 7%(见 §6.2);有社区评测称写入后需较长索引时间(❓,来自 Mem0 论文的竞品评测,存在争议)。
+
+### 4.5 LangChain / LangGraph memory 与 LangMem
+
+- **路线**(❓):LangGraph 提供持久化 checkpointer(线程内状态)+ store(跨线程记忆);LangMem 在其上实现 CoALA 三类长期记忆(情景/语义/程序性),**程序性记忆可自更新系统提示**。
+- **注意**(⚠️,Mem0 论文竞品数据,利益相关):LangMem 被测出检索延迟极高(p50 ~18s / p95 ~60s)——数字可疑且过时,阶段 2 需独立核验。
+- **对本项目的参考价值**:不引入 LangChain 依赖,但其记忆 API 设计(namespace、memory type、background vs hot-path 写入)值得抄概念。
+
+### 4.6 Cognee(知识图谱管线)
+
+- **路线**(❓):Extract-Cognify-Load(ECL)管道把数据加载为**类型化知识图谱**,多存储后端(向量 + Neo4j/FalkorDB/KuzuDB/NetworkX + 关系型元数据),可 Ollama 全离线,开源免费。
+
+### 4.7 Memobase(用户画像路线)
+
+- **路线**(❓):见 L6——层级化用户档案 + 缓冲区异步抽取 + `context()` 预计算注入;FastAPI + **Postgres** + Redis,全 Docker 化,Apache 2.0。技术栈与本项目亲和度最高的开源方案,值得阶段 2 重点读源码。
+
+### 4.8 A-Mem(记忆演化 / Zettelkasten)
+
+- **论文**(❓):arXiv:2502.12110。每条记忆是 LLM 生成的结构化笔记,写入自动触发建链 + 回头演化既有记忆;纯向量检索(all-minilm-l6-v2,k=10)。轻量、无框架依赖,适合借鉴其「写入时让 LLM 顺手整理」的思路。
+
+### 4.9 OpenAI ChatGPT memory / Anthropic Claude memory tool(产品级参照)
+
+- **ChatGPT memory**(❓,官方披露有限):Saved memories(显式条目)+ 从历史隐式学习偏好;用户可查看/删除。是 Mem0 论文对比的基线(其 LOCOMO J 分 52.90%,⚠️ 竞品评测数字)。
+- **Claude memory tool**(❓):Anthropic 提供 **client-side memory tool**——模型通过工具调用读写一个由开发者托管的记忆目录(文件式),写入时机由模型自主决定,存储与执行完全在开发者侧。这个「记忆=模型可调用的工具,存储归开发者」的模式与本项目已有的前端工具(`writeNote`)机制同构,是很自然的落地候选之一。
+- 两者的确切写入时机/存储结构均属阶段 2 核验清单(本轮无已确认一手证据)。
+
+### 4.10 自建路线(Postgres/pgvector + 提示注入)——本项目的默认路线
+
+- **官方蓝本**(❓,一手官方文档):AI SDK RAG Chatbot 指南(§L4 已详述),技术栈与本项目 100% 重合。
+- **社区实践共识**(❓,db0 等):抽取事实 + embedding 存储 + token 预算内检索 + system prompt 注入;「聊天历史」与「记忆」分表分概念。
+- **schema-grounded 主张**(⚠️→详见 §6.3):可靠的记忆应像 **system of record(记录系统)而非搜索**——结构化事实表为主、向量为辅。这与本项目 Drizzle/Postgres 技术栈天然契合,是调研得出的最重要落地启示。
+
+---
+
+## 5. 统一架构对比表
+
+对比表头来自 ✅ 核实的统一分析框架(CoALA 读写形式化 + 2404.13501 读/写/管理三操作 + 2504.15965 的巩固/反思/遗忘映射)。表内框架细节多为 ❓(阶段 2 核验)。
+
+| 维度 | MemGPT/Letta | Mem0 | Zep/Graphiti | LangMem | Memobase | A-Mem | 自建 pgvector | 自建结构化事实表 |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| **写入时机** | Agent 自主工具调用 | 每轮消息对后管线触发 | 每条消息进图管线 | hot-path 工具 或 后台任务 | 缓冲区攒够才 flush(非热路径) | 每条写入即触发 | 模型工具调用(addResource 式) | 每轮后/缓冲后管线 |
+| **抽取方式** | LLM 自决定存什么 | LLM 抽取显著事实 | LLM 抽实体/关系/时间 | LLM(可配 schema) | LLM 抽画像字段 | LLM 生成结构化笔记 | 无抽取(原文分块) | LLM 或规则抽取 |
+| **存储结构** | 上下文块+召回库+归档向量 | 向量+图+KV 混合 | 时序知识图谱 | LangGraph store | 层级画像+Postgres | 笔记+向量+链接 | pgvector 表 | 关系表(±pgvector 列) |
+| **检索/注入** | Agent 工具调用换页 | 向量 top-k 注入 | 图查询(无 LLM),亚秒 | store 检索注入 | 预计算画像整包注入 | 余弦 top-k 注入 | 相似度>阈值 top-k | SQL 谓词查询+注入 |
+| **更新/冲突** | Agent 自编辑 core memory | LLM 决策 ADD/UPDATE/DELETE/NOOP | 旧边失效+新边生效(保历史) | 依赖开发者策略 | 画像字段覆盖更新 | LLM 回头演化旧笔记 | 无(append-only) | 显式版本化/UPSERT,确定性 |
+| **遗忘** | 归档驱逐 | DELETE 操作 | 边失效(软删) | 开发者自定 | 画像修剪 | 演化中合并 | 无 | TTL/衰减列,自定 |
+| **在线延迟** | 取决于 Agent 步数 | 检索 p95 ~0.2s(自报⚠️) | 检索 p95 ~0.3–0.6s | 有争议数据(⚠️) | <100ms(预计算,自报) | 微秒级检索(自报) | 一次 embedding+SQL | 纯 SQL,最低 |
+| **额外成本** | 每轮多次工具调用 | 每轮 1–3 次 LLM | 每条消息图抽取 LLM | 每次写入 LLM | 缓冲摊薄,固定 3 次/flush | 每写入 1–2 次 LLM | 仅 embedding | 抽取 LLM(可缓冲) |
+| **开源/托管** | 全开源可自托管 | 核心开源,图记忆付费 | 仅引擎开源,平台 SaaS | 开源(MIT) | 开源(Apache 2.0) | 研究代码 | 全自有 | 全自有 |
+| **复杂度** | 高 | 中(SDK 简单,行为黑盒) | 中-高 | 中 | 低-中 | 低-中 | 低 | 中 |
+
+> ❌ **已证伪提醒**:「各主流系统在更新/冲突策略上统一采用最新事实胜出(supersession)惯例」这一说法被 3 票全数否决(0-3)。**冲突消解策略在各系统间既不统一、也普遍不可靠**(见下章)——上表「更新/冲突」一行恰是各方案区分度最高、也最需逐个核验的维度。
+
+---
+
+## 6. 批判性发现
+
+调研中最有价值的部分往往不是「大家都怎么做」,而是「哪里有争议、哪里都做不好」。
+
+### 6.1 基准之战:厂商自报数字全部需要打折
+
+- Mem0 自报 LOCOMO 成绩与第三方独立复测存在巨大差距(自报 91.6% vs 第三方复测 58–66%,❓);
+- **Zep 公开发文质疑 Mem0 论文**(❓,getzep.com 博客,双方均利益相关):称 Mem0 对 Zep 的评测配置有误(角色设置、时间戳字段、串行检索),按 Zep 自己的实现重跑 LoCoMo 得 J=75.14%(Mem0 论文只给它 65.99%),反超 Mem0;并批评 LoCoMo 基准本身太短(16k–26k tokens,塞得进现代上下文窗口)、缺知识更新类问题、有数据质量问题;
+- Mem0 的 full-context 对照仅 ~26k tokens、用 GPT-4o-mini、未开 prompt caching(✅ 验证者附注)——「抽取式优于长上下文」不能外推为普遍结论;
+- **结论**:本项目阶段 3(实验)必须自建小规模评测,不能采信任何一家的 SOTA 数字。
+
+### 6.2 公认短板:冲突消解 / 事实新鲜度(对本项目最重要的发现)
+
+- **所有已发表系统在 MemoryAgentBench 的 FactConsolidation(冲突消解)任务上显著欠佳**(⚠️,2-1 分票通过,数字经基准论文 v4 Table 3 交叉核验):Zep 单跳仅 **7.0%**、Mem0 与 Contriever 各 **18.0%**、HippoRAG-v2 54.0%、BM25 48.0%;多跳变体所有系统 ≤7%,几乎无解;
+- **确定性管线显著优于 LLM 判断**(✅,arXiv:2606.01435 受控实验):同骨干/同检索/同分块/同 top-k 下,把「让 LLM 判断哪条事实最新」换成「候选抽取 + Python `max(serial)`」确定性消解,提升 +10.8pp(67.2→78.0),且**上下文越长优势越大**(6K 时 +8pp → 262K 时 +21pp)。LLM 判断的失败模式:训练先验覆盖(无视「新者胜」规则)与长上下文下的序号比较漂移;
+- 限定(✅ 验证者附注):FC 任务是合成数据(含显式序号,利好 max 原语);在带真实时间戳的 LongMemEval knowledge-update 上确定性版本仅与 LLM 打平——**确定性聚合是「当前值冲突消解」的正确原语,但不是万能**;
+- **对本项目的直接启示**:不要指望「把所有历史扔给 LLM 让它自己搞清楚哪条是最新的」;事实要带时间戳/版本号存进结构化表,「取最新值」用 SQL `ORDER BY ... LIMIT 1` 这类确定性逻辑完成,LLM 只负责抽取。
+
+### 6.3 schema-grounded 主张:记忆应是记录系统,不是搜索
+
+(⚠️,arXiv:2604.27906,vendor 相邻论文——作者推销自家 xmemory 系统、实证数字自报;但其架构论点经 3-0 核实原文,且与 §6.2 的独立基准证据方向一致)
+
+- 核心论点:嵌入不是谓词求值器;对状态跟踪类负载(实体字段随时间变化),所有无 schema 约束的系统都积累大量错误,**在向量之上加图层也无显著改善**(Mem0 有图/无图的状态错误数几乎相同);
+- 其解法:把解释成本移到**写入路径**——schema 感知的写入管线(对象检测→字段检测→值抽取,带校验门与重试),读取变成对已验证记录的受约束查询;
+- **与本项目的契合**:这条路线的基础设施就是「一个带 schema 的关系数据库」——正是本项目已有的 Drizzle/Postgres。
+
+### 6.4 其他值得记录的发现(均 ❓,待核验)
+
+- **成本交叉点**:约 10 轮交互(~100k token 累计)后,记忆系统比长上下文更便宜(检索成本趋稳 vs 全量重发持续累加);
+- **被动召回 ≠ 主动使用**:在 LoCoMo 上接近饱和的模型,换到多会话相互依赖任务(MemoryArena)跌至 40–60%;
+- **记忆投毒(memory poisoning)**:MINJA 类攻击对 Agent 记忆注入成功率 >95%——恶意内容一旦写入记忆会作为「受信状态」持久生效,不同于一次性提示注入。落地时写入管线要有内容边界(用户可见/可删的记忆面板既是产品功能也是安全功能)。
+
+---
+
+## 7. 关键论文清单
+
+**同行评审(可放心引用)**
+- CoALA:*Cognitive Architectures for Language Agents*,arXiv:2309.02427,TMLR 2024 —— 记忆四分法与读写形式化的源头
+- *A Survey on the Memory Mechanism of LLM-based Agents*,arXiv:2404.13501,ACM TOIS —— 最常被引的记忆综述
+- MemoryBank(Ebbinghaus 遗忘曲线),AAAI 2024
+
+**奠基系统论文(arXiv,被广泛引用)**
+- MemGPT:*Towards LLMs as Operating Systems*,arXiv:2310.08560(2023-10)
+- Generative Agents:*Interactive Simulacra of Human Behavior*,arXiv:2304.03442(2023-04)
+- Mem0:arXiv:2504.19413(2025-04)
+- A-Mem:arXiv:2502.12110(2025-02)
+
+**综述(预印本,注意时效)**
+- *From Human Memory to AI Memory*,arXiv:2504.15965
+- *Memory in the Age of AI Agents: A Survey*,arXiv:2512.13564(配套论文索引:github.com/Shichun-Liu/Agent-Memory-Paper-List,200+ 篇)
+- arXiv:2602.06052(60 作者统一分类学)
+- *Memory for Autonomous LLM Agents*,arXiv:2603.07670
+
+**评测与批判**
+- MemoryAgentBench:arXiv:2507.05257
+- 确定性新鲜度消解实验:arXiv:2606.01435
+- schema-grounded 记忆主张(xmemory):arXiv:2604.27906
+- LoCoMo:Maharana et al. 2024;LongMemEval:Wu et al. 2025(arXiv:2501.13956)
+- Zep 对 Mem0 的质疑:blog.getzep.com "Lies, Damn Lies & Statistics: Is Mem0 Really SOTA in Agent Memory?"
+
+**工程一手资料**
+- AI SDK 官方 RAG Chatbot 指南:ai-sdk.dev/cookbook/guides/rag-chatbot(技术栈与本项目一致)
+- vercel-labs/ai-sdk-persistence-db(已归档,仅作参考)
+- Memobase:github.com/memodb-io/memobase
+
+---
+
+## 8. 本项目落地路线图
+
+综合调研结论,为本项目(Next.js 16 + assistant-ui + AI SDK v7 + Drizzle/Postgres + MiniMax)规划五个里程碑。**总体策略**:走「自建 Postgres 结构化事实表 + pgvector」路线——理由是 (a) §6.2/§6.3 表明结构化+确定性消解是当前证据下最可靠的路线,且与已有技术栈零摩擦;(b) 自建才能达成「学习」目标;(c) 不排除阶段 2 深入后在某一层换用开源件(如 Memobase 的画像 schema 设计)。
+
+### M0 · 已有基础(完成)
+threads/messages 持久化(L3)、前后端工具双通道(`writeNote` 前端工具机制可直接复用为「记忆工具」)、Drizzle 迁移体系。
+
+### M1 · 工作记忆管理(L1+L2)—— 低垂果实
+- 请求前按 token 预算做滑动窗口;超阈值触发滚动摘要(一次 LLM 调用),摘要落库避免重复压缩;
+- 用户体验点:超长对话不再变慢/变贵/丢头部设定。
+
+### M2 · 情景记忆检索(L4)
+- 启用 pgvector(Docker Postgres 装扩展 + Drizzle vector 列 + HNSW 索引);
+- 对历史消息按轮次嵌入;检索采用相似度×时近性加权,token 预算内注入;
+- 参照 AI SDK 官方 RAG 指南的检索参数起步(cosine >0.5、top-4),实验阶段调参;
+- 用户体验点:「我们上次聊到哪了」「你之前说过 X」跨会话可答。
+
+### M3 · 结构化事实 + 画像记忆(L5+L6)—— 核心里程碑
+- `memories` 事实表:`(id, user_id, kind, subject, value, source_message_id, valid_from, superseded_by, created_at)` + 可选 embedding 列——**带时间戳与版本链,取最新值用 SQL 而非 LLM**(§6.2 的直接应用);
+- 写入管线学 Mem0 两阶段(LLM 抽取 → 与既有记忆比对决策 ADD/UPDATE/DELETE/NOOP),但**冲突消解走确定性 supersede 链**;写入时机学 Memobase 用缓冲/`onFinish` 异步,不阻塞流式响应;
+- 画像视图:从事实表聚合出常驻注入的紧凑画像块;
+- **用户体验点(产品化重点)**:记忆面板 UI——用户可查看/编辑/删除自己的记忆(assistant-ui 组件 + 新 API 路由),记忆写入时消息旁给出「已记住 ✓」标记(复用 `useAssistantTool` 的生成式 UI 能力)。这同时是 §6.4 记忆投毒风险的产品化缓解。
+
+### M4 · 反思与遗忘(L7,可选进阶)
+- 后台定期任务:事实表去重合并、久未引用记忆衰减(Ebbinghaus 式)、从情景记忆中反思提炼语义事实(Generative Agents 式);
+- 自建 20–50 题小评测集(含知识更新类问题,弥补 LoCoMo 缺陷),横向验证 M2/M3 的效果——回应 §6.1「不可采信厂商数字」。
+
+### 关键待决技术点(进入阶段 2 前须回答)
+1. **注入挂载点**:记忆注入选在 route.ts 的 system prompt 拼装、AI SDK `prepareStep`、还是暴露为模型工具(Claude memory tool 式)?三者对流式延迟与可控性的影响需要实验;
+2. 抽取用 MiniMax 主模型还是更便宜的小模型(成本/质量权衡);
+3. embedding 供应商选择(MiniMax 是否有 embedding API,或另接一家)。
+
+---
+
+## 9. 遗留问题与阶段 2 核验清单
+
+本轮已核实证据集中于学术综述与 Mem0 论文;以下条目**只有 ❓ 级证据**,是阶段 2(`02-deep-dives/`)的任务清单,按优先级排序:
+
+| # | 深入对象 | 要回答的问题 | 优先级(对本项目) |
+| --- | --- | --- | --- |
+| 1 | **自建路线蓝本**:AI SDK RAG 指南 + Memobase 源码 | 缓冲写入、画像 schema、context() 注入的具体实现 | ★★★(M2/M3 直接依赖) |
+| 2 | **Mem0 源码** | 两阶段管线的 prompt 设计、向量库抽象、作用域实现 | ★★★(M3 写入管线参照) |
+| 3 | **Anthropic Claude memory tool** | 工具 schema、文件式存储约定、与本项目前端工具机制的兼容性 | ★★☆ |
+| 4 | **Zep/Graphiti 源码** | bi-temporal 边模型、无 LLM 查询路径 | ★★☆(时序版本化设计参照) |
+| 5 | **LangMem** | 程序性记忆(自更新系统提示)的实现与安全边界 | ★★☆ |
+| 6 | **OpenAI ChatGPT memory** | 产品行为逆向:写入时机、用户控制面 | ★☆☆(产品参照) |
+| 7 | **Letta** | core memory blocks 的自编辑协议 | ★☆☆ |
+| 8 | **评测方法**:LoCoMo/LongMemEval/MemoryAgentBench | 抽出适合本项目的小评测集设计 | ★★☆(M4 依赖) |
+
+**开放性问题**(调研流程自动生成,保留原样供阶段 2 参考):
+1. 在 Next.js + assistant-ui + AI SDK v7 技术栈中,记忆注入的最佳挂载点是什么——adapter 层叠加、`prepareStep`/middleware、还是前/后端工具?(本轮无任何已验证证据覆盖 assistant-ui 生态的记忆实践)
+2. OpenAI/Anthropic 官方记忆的实际写入时机与存储结构,以及与开源方案在同一独立基准下的公平对比?
+3. Zep 对 Mem0 的质疑孰是孰非?是否存在无利益相关方的第三方复现?
+4. 对本项目规模(单用户/小团队),自建「事实表 + LLM 抽取 + 提示注入」相比直接接 Mem0/Zep 托管,在工程成本/延迟/可控性上的实测权衡?
+
+---
+
+*报告完。阶段 2 已完成:见 [逐方案深入](./02-deep-dives/)。*
diff --git a/docs/memory/02-deep-dives/01-self-built-postgres.md b/docs/memory/02-deep-dives/01-self-built-postgres.md
new file mode 100644
index 0000000..3981bf6
--- /dev/null
+++ b/docs/memory/02-deep-dives/01-self-built-postgres.md
@@ -0,0 +1,130 @@
+# 自建 Postgres 路线:本项目的候选蓝本
+
+## 为什么它是首选
+
+当前项目已经具备这条路线最贵的基础设施:
+
+- [`lib/db/schema.ts`](../../../lib/db/schema.ts) 已使用 Drizzle/Postgres,并有 `vector(1536)` 与 HNSW 索引;
+- [`lib/ai/embeddings.ts`](https://github.com/hifizz/thread-chatbot/blob/main/lib/ai/embeddings.ts) 已封装 OpenAI-compatible embedding;
+- [`lib/chat/retrieve.ts`](https://github.com/hifizz/thread-chatbot/blob/main/lib/chat/retrieve.ts) 已实现 cosine 检索;
+- [`app/api/chat/route.ts`](../../../app/api/chat/route.ts) 已有用户身份、服务端 system prompt、`prepareStep`、`onFinish` 和 `after()`;
+- 分支聊天树已经整体持久化,但它保存的是会话状态,不是跨树共享的用户事实。
+
+因此“自建”不是从零实现向量数据库,而是给现有数据层增加正确的记忆语义。
+
+## 现有 RAG 可以复用什么
+
+附件 RAG 的链路是:
+
+```text
+PDF pages
+ -> chunkPages(size=1000, overlap=150)
+ -> embedMany
+ -> attachment_chunks + HNSW
+ -> embedQuery
+ -> cosine top-k
+ -> 带页码片段注入消息
+```
+
+可复用:embedding provider、批量写入、HNSW 建索引方式、相似度查询和失败降级。
+
+不可直接复用:
+
+- `attachment_chunks` 以 `attachment_id` 分区,记忆必须以 `user_id` 为第一隔离边界;
+- 文档块是 append/replace 内容,事实需要来源、置信度、有效时间、撤销和用户编辑;
+- `gt(similarity, 0)` 对文档兜底可以接受,对用户记忆召回过宽;
+- `EMBEDDING_DIMENSIONS=1536` 是数据库契约,更换模型不能只改环境变量;
+- 向量近邻不能可靠完成“用户现在住哪里”这类当前值查询。
+
+## 目标数据模型
+
+建议阶段 3 先验证四层,不一次性做完整产品表:
+
+```text
+memory_events
+ id, user_id, source_tree_id, source_message_id
+ observed_at, payload, extraction_version
+
+memory_facts
+ id, user_id, subject, predicate, value_json
+ valid_from, valid_to, status, confidence
+ source_event_id, supersedes_id, created_at, updated_at
+
+memory_profiles
+ user_id, profile_json, version, refreshed_at
+
+memory_embeddings (可选)
+ fact_id, embedding, searchable_text
+```
+
+关键不变量:
+
+1. 所有查询必须显式带 `user_id`;
+2. 事实来源不可丢,用户能追溯到树和消息;
+3. 当前值由 `status/valid_to/supersedes_id` 和 SQL 决定,不让 LLM 临场猜;
+4. 删除默认先做可审计的失效,隐私删除再做物理清理;
+5. embedding 是事实的索引,不是事实本体。
+
+## 写入路径
+
+```text
+assistant 流完成
+ -> 记录待处理 event(幂等键 = source_message_id + extractor_version)
+ -> 后台/缓冲任务读取若干新 turn
+ -> LLM 输出受 schema 约束的候选事实
+ -> 确定性校验(作用域、敏感字段、枚举、时间)
+ -> 按 (user, subject, predicate) 查当前值
+ -> ADD / supersede / ignore
+ -> 异步生成 embedding 与刷新 profile
+```
+
+`onFinish` 当前承担计费,不能把昂贵抽取直接塞进去。`after()` 只保证请求后的工作有机会完成,不是持久队列;阶段 3 可以用它做最小实验,生产实现要有持久状态、重试和幂等。
+
+写入不应默认记住 assistant 的所有回答。第一版只抽取用户明确陈述和明确“请记住”的内容;assistant 生成的计划另设 `kind`,避免把模型幻觉升级成用户事实。
+
+## 读取与注入路径
+
+每次请求建议只做一次读:
+
+1. SQL 获取紧凑画像和与问题中明确实体匹配的当前事实;
+2. 若问题需要长尾回忆,再对 active facts/events 做向量召回;
+3. 按固定 token/字符预算打包;
+4. 拼入 [`app/api/chat/route.ts`](../../../app/api/chat/route.ts) 的服务端 `system`。
+
+`prepareStep` 适合“模型在本轮调用记忆工具后,需要让下一步看到最新记忆”的场景。若记忆只在请求开始前读取,直接构造 `system` 更简单,也避免每个 tool step 重复查库。AI SDK v7 源码显示 `prepareStep` 返回的 `instructions/messages` 会传递到后续步骤,实施时必须避免重复追加同一 memory block。
+
+建议注入格式:
+
+```xml
+
+ 仅在与当前问题相关时使用;不得把记忆当作用户本轮明确陈述。
+ ...
+ ...
+ ...
+
+```
+
+## 用户控制面
+
+最小可用 UI 必须同时交付:
+
+- 查看记忆、来源与最近更新时间;
+- 编辑当前值并标记为用户确认;
+- 删除单条/清空全部;
+- 关闭自动记忆或使用临时聊天;
+- 在回答旁解释“本次使用了哪些记忆”。
+
+没有控制面就不应默认上线自动写入,因为错误记忆和投毒会跨会话持续生效。
+
+## 阶段 3 的最小切片
+
+先只做 `preferred_language`、`dietary_restriction`、`current_project` 三类 predicate:
+
+- 结构固定,容易验证冲突;
+- 能覆盖偏好、敏感约束和短期状态;
+- 不需要一开始就做通用 ontology;
+- 可直接比较“全上下文 / 纯向量 / 结构化事实”三条路线。
+
+## 判定
+
+**进入实验。** 技术栈匹配、数据主权最好,也最符合阶段 1 对确定性新鲜度消解的结论。主要风险不在数据库,而在抽取质量、后台可靠性、隐私控制和 schema 演进。
diff --git a/docs/memory/02-deep-dives/02-mem0.md b/docs/memory/02-deep-dives/02-mem0.md
new file mode 100644
index 0000000..e01af7c
--- /dev/null
+++ b/docs/memory/02-deep-dives/02-mem0.md
@@ -0,0 +1,80 @@
+# Mem0:从四操作管线到 V3 ADD-only
+
+> 源码快照:`mem0ai/mem0@d6d89c987bddf580870db14c69db974edfc5263c`(2026-07-23)。重点文件:[`mem0/memory/main.py`](https://github.com/mem0ai/mem0/blob/d6d89c987bddf580870db14c69db974edfc5263c/mem0/memory/main.py)、[`mem0/configs/prompts.py`](https://github.com/mem0ai/mem0/blob/d6d89c987bddf580870db14c69db974edfc5263c/mem0/configs/prompts.py)。
+
+## 设计动机
+
+Mem0 把“从对话里挑出值得长期保存的信息”包装成一个通用 memory API,并用 `user_id`、`agent_id`、`run_id` 做作用域。调用者可选择:
+
+- `infer=True`:由 LLM 抽取记忆;
+- `infer=False`:把非 system 消息原文直接存为记忆;
+- `memory_type="procedural_memory"`:生成代理执行历史的过程性摘要。
+
+## 必须修正的旧认识
+
+阶段 1 根据论文和旧源码把 Mem0 描述为:
+
+```text
+事实抽取 -> 检索相似旧记忆 -> LLM 决定 ADD/UPDATE/DELETE/NONE
+```
+
+当前 OSS 主干仍保留 `DEFAULT_UPDATE_MEMORY_PROMPT`,但 `Memory._add_to_vector_store()` 的主路径已经标成 `V3 PHASED BATCH PIPELINE`,实际采用 **ADD-only + memory linking**。不能仅看到旧 prompt 就断言运行时仍走四操作管线。
+
+## 当前源码调用链
+
+`Memory.add()` 的通用记忆路径最终进入 `_add_to_vector_store()`:
+
+1. 从 history DB 取同一 session 最近 10 条消息;
+2. `parse_messages()` 把新消息串成抽取输入;
+3. 对输入生成 query embedding,在当前 `user_id/agent_id/run_id` 范围内检索 top 10;
+4. 把真实 UUID 映射为短整数 ID,降低模型伪造 ID 的概率;
+5. 使用 `ADDITIVE_EXTRACTION_PROMPT` 做一次 JSON 抽取;
+6. prompt 明确规定唯一操作是 ADD,旧记忆仅用于去重和 `linked_memory_ids`;
+7. 批量 embedding 新记忆;失败时退回逐条 embedding;
+8. 用文本 MD5 对既有结果与本批次做精确去重;
+9. 批量写向量条目、history,并抽取实体建立“实体 -> memory ids”关联;
+10. 保存原消息,供后续抽取读取最近上下文。
+
+V3 prompt 还要求:
+
+- 同时从 user 与 assistant 消息抽取,但正确标注来源;
+- 用 observation date 把相对时间转为绝对时间;
+- 记忆保持自包含、保留专名和精确数字;
+- 发生偏好变化或矛盾时,新增一条并链接旧记忆,而不是覆盖旧值。
+
+## 数据与检索
+
+向量条目的 payload 至少包含 `data`、`hash`、`created_at`、`updated_at`、`text_lemmatized` 和作用域字段。代码同时维护 SQLite history,并引入 BM25 词形化、多信号排序与 entity boost;具体 vector store、LLM、embedder、reranker 都经 factory 配置。
+
+这带来良好的适配性,但也意味着语义取决于多个可替换组件,不能把单一 benchmark 数字当作固定产品能力。
+
+## 优点
+
+- API 面小,作用域清晰,provider/vector store 可替换;
+- V3 单次抽取与批量 embedding 比逐事实多轮判断更省调用;
+- 旧记忆 ID 映射、hash 去重、history、实体关联都有明确工程防线;
+- ADD-only 保留演化轨迹,避免 LLM 直接破坏历史。
+
+## 风险
+
+- ADD-only 把“冲突消解”推迟到读取或后处理;当前值仍不能只靠相似检索得到;
+- prompt 采用“有疑问就抽取”,更偏召回率,可能制造大量低价值记忆;
+- assistant 推荐也会成为记忆,若不分 source/kind,模型输出可能被再次当作用户事实;
+- OSS 的 temporal `timestamp` 参数在代码中明确报“platform-only”,不能假设开源版已提供完整时间语义;
+- hash 只消除字面重复,语义重复仍依赖 LLM 与检索。
+
+## 对本项目的可复用点
+
+- 作用域字段必须在所有写/读路径中保持不变;
+- 给模型短 ID,执行时再映射真实主键;
+- 抽取失败与“没有事实”必须是两种状态,便于重试;
+- 先批量 embedding,失败再逐条降级;
+- 保留原始 event/history,派生事实可以重算。
+
+## 不直接接入的原因
+
+本项目核心需求是“当前用户事实可解释、可编辑、可确定性更新”。Mem0 V3 更像高召回的记忆事件层,而不是强 schema 的记录系统。直接接入仍需额外实现当前值、权限、来源和 UI,收益不足以抵消一层外部抽象。
+
+## 判定
+
+**借鉴,不接入。** 阶段 3 可把 Mem0 V3 的“单次 ADD-only 抽取”作为候选 B,与“schema 候选 + 确定性 supersede”候选 A 对比。
diff --git a/docs/memory/02-deep-dives/03-anthropic-memory-tool.md b/docs/memory/02-deep-dives/03-anthropic-memory-tool.md
new file mode 100644
index 0000000..53341f4
--- /dev/null
+++ b/docs/memory/02-deep-dives/03-anthropic-memory-tool.md
@@ -0,0 +1,82 @@
+# Anthropic memory tool:文件协议与 JIT 读取
+
+> 文档快照:2026-07-24。官方入口:[Memory tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool)。工具 `memory_20250818` 已 GA,但部分 SDK helper 仍位于 beta namespace。
+
+## 设计动机
+
+Anthropic 没有替开发者托管记忆。模型只发出文件操作请求,应用在自己控制的存储上执行,再以 `tool_result` 返回。`/memories` 是逻辑前缀,可以映射到本地目录、对象存储或数据库。
+
+核心思路不是“每轮把所有记忆塞进 prompt”,而是:
+
+```text
+任务开始 -> view /memories
+ -> view 相关文件/行
+ -> 使用信息
+ -> create/edit/rename/delete 持久化新进展
+```
+
+这是 just-in-time context retrieval,尤其适合长程任务和跨 session 恢复。
+
+## 工具协议
+
+请求只需声明:
+
+```json
+{ "type": "memory_20250818", "name": "memory" }
+```
+
+应用必须实现六个命令:
+
+| command | 关键参数 | 语义 |
+| --- | --- | --- |
+| `view` | `path`, `view_range?` | 列目录或按 1-based 行号读文件 |
+| `create` | `path`, `file_text` | 创建文件;官方 handler 对已存在路径报错 |
+| `str_replace` | `path`, `old_str`, `new_str?` | 仅在旧文本唯一出现时替换 |
+| `insert` | `path`, `insert_line`, `insert_text` | 在指定行后插入,0 表示文件头 |
+| `delete` | `path` | 删除文件/目录,但不能删除根目录 |
+| `rename` | `old_path`, `new_path` | 移动或改名,不覆盖已有目标 |
+
+Python/TypeScript SDK 提供本地文件 helper;生产实现仍应替换为每用户隔离的持久存储。
+
+## 为什么 `str_replace` 值得借鉴
+
+它要求 `old_str` 唯一匹配,否则拒绝操作。这相当于一种面向文本的乐观并发控制:模型必须先读到精确旧值,才能修改。相比“整文件覆盖”,它减少误删与基于过期视图覆盖新内容的风险。
+
+对结构化事实表,可映射成:
+
+```text
+update memory_facts
+set value = :new, version = version + 1
+where id = :id and version = :expected_version
+```
+
+## 安全边界
+
+官方文档明确把安全责任交给应用:
+
+- 所有路径必须位于 `/memories`;
+- canonicalize 后再次验证根目录;
+- 拒绝 `../`、`..\\` 和 URL-encoded traversal;
+- 限制文件大小、单次 `view` 返回量与文件寿命;
+- 不把敏感数据交给模型自行决定保存;
+- 错误用 `tool_result.is_error=true` 返回,不能静默成功。
+
+多租户实现还必须把逻辑路径绑定到已鉴权 `userId`,绝不能让模型输入决定真实租户目录。
+
+## 与当前项目的映射
+
+项目已有 AI SDK tool loop 和 `prepareStep`,可以用普通 `tool()` 模拟相同协议;但原生 Anthropic schema 只适用于 Claude provider。当前产品支持多模型,因此若采用,应定义 provider-neutral 的 `viewMemory/updateMemory` 工具,而不是把 `memory_20250818` 写死到主链路。
+
+此外,Thread Chat 的主要对象是“关于用户的事实”,不是“agent 自己维护的项目文件”。对用户画像而言,文件缺少 predicate、来源和有效时间,不应成为主存储。
+
+## 可复用点
+
+- 存储完全由应用控制;
+- 先读后写、精确替换、显式错误;
+- 按需读取而非全量常驻;
+- memory 与 compaction 分工:前者跨 session,后者压缩当前会话;
+- 目录/文件可直接成为用户可查看的审计界面。
+
+## 判定
+
+**借鉴工具协议,不照搬文件模型。** 若阶段 4 增加“模型主动管理记忆”,应把可写动作限制为候选提议或带版本条件的 CRUD,并继续以 Postgres 事实表作为记录系统。
diff --git a/docs/memory/02-deep-dives/04-graphiti.md b/docs/memory/02-deep-dives/04-graphiti.md
new file mode 100644
index 0000000..c5064a0
--- /dev/null
+++ b/docs/memory/02-deep-dives/04-graphiti.md
@@ -0,0 +1,100 @@
+# Graphiti / Zep:时序 Context Graph
+
+> 源码快照:`getzep/graphiti@3bb2d0bba56f8e22311574c045452c420a012f49`(2026-07-23)。重点文件:[`graphiti_core/graphiti.py`](https://github.com/getzep/graphiti/blob/3bb2d0bba56f8e22311574c045452c420a012f49/graphiti_core/graphiti.py)、[`graphiti_core/edges.py`](https://github.com/getzep/graphiti/blob/3bb2d0bba56f8e22311574c045452c420a012f49/graphiti_core/edges.py)。
+
+## Graphiti 与 Zep 不是同一个交付物
+
+- **Graphiti**:开源 Python 时序图引擎;调用者自建用户、消息、图数据库与运维。
+- **Zep**:托管 context infrastructure,使用其生产图引擎并提供 SDK、治理和运维能力。
+
+不能把 Zep 的托管延迟或规模声明直接归因于自托管 Graphiti。
+
+## 数据模型
+
+Graphiti 的核心对象:
+
+- `EpisodicNode`:原始输入,承担 provenance;
+- `EntityNode`:人、地点、项目等实体;
+- `EntityEdge`:实体之间的事实/关系;
+- community/saga:更高层组织与连续 episode。
+
+`EntityEdge` 源码字段直接体现 bi-temporal 设计:
+
+```text
+fact, fact_embedding, episodes
+created_at, expired_at
+valid_at, invalid_at
+reference_time
+source_node_uuid, target_node_uuid, group_id
+```
+
+`valid_at/invalid_at` 描述事实何时在现实中成立;`created_at/expired_at` 描述系统何时知道或使其失效;`episodes` 保留来源。
+
+## 写入调用链
+
+`Graphiti.add_episode()` 及其内部方法大致执行:
+
+1. 保存带 `group_id`、`source_description`、`reference_time` 的 episode;
+2. 读取前序 episodes 作为上下文;
+3. LLM 结构化抽取 entity nodes;
+4. `resolve_extracted_nodes()` 与图中实体去重;
+5. LLM 抽取 relation edges 与属性;
+6. `resolve_extracted_edges()` 处理重复与矛盾,返回 resolved、invalidated、new 三组边;
+7. 为事实生成 embedding;
+8. 批量保存 episode、entity、关系与 provenance edges;
+9. 可选更新 community/saga 摘要。
+
+源码提供 bulk 路径并使用 semaphore 控制并发。官方 README 明确说明 structured output 质量决定 ingestion 稳定性,小模型或仅“声称兼容 JSON schema”的 provider 可能失败。
+
+## 读取路径
+
+Graphiti 不是在查询时让 LLM 总结整张图,而是组合:
+
+- fact/entity embedding 的语义检索;
+- 全文/BM25;
+- 图距离或节点邻接;
+- 预设 search recipes 和 reranking。
+
+检索结果是事实边/实体,LLM 只消费返回的 context。查询本身可以不调用 LLM,但写入已经付出多次 LLM 和 embedding 成本。
+
+## 优点
+
+- 时间语义、provenance 和历史保留是一等字段;
+- 同一实体的演化比扁平向量条目更自然;
+- hybrid retrieval 能回答关系和多跳问题;
+- prescribed ontology 可用 Pydantic 约束实体/边类型。
+
+## 成本与风险
+
+- 需要 Python 服务和 Neo4j/FalkorDB/Neptune;当前项目只有 Postgres;
+- 一个 episode 的写入包含多次结构化抽取、去重、关系消解和 embedding;
+- `group_id` 是图分区,不自动等于应用的鉴权边界;
+- LLM 仍参与实体/边消解,“自动失效”不等于永远正确;
+- 项目当前需要的主要是少量用户当前值,图建模收益尚未覆盖运维复杂度。
+
+## 对本项目的可复用点
+
+不引入图数据库也应借用四个字段:
+
+```text
+valid_from / valid_to # 现实有效时间
+recorded_at # 系统观察时间
+source_event_id # provenance
+supersedes_id # 演化链
+```
+
+还应把“原始 episode”和“派生 fact”分开:抽取模型升级后可从 episode 重算事实,而不丢原始证据。
+
+## 何时再考虑 Graphiti
+
+满足以下至少两项再评估:
+
+- 需要跨用户/组织实体关系;
+- 多跳关系查询是主要产品能力;
+- 需要回答历史时点真值;
+- 单用户事实量已让关系表查询变得笨重;
+- 团队能承担独立 Python/graph 服务。
+
+## 判定
+
+**暂缓。** 学习其 bi-temporal 与 provenance 设计,但第一版用 Postgres 版本化事实表实现相同的关键不变量。
diff --git a/docs/memory/02-deep-dives/05-langmem.md b/docs/memory/02-deep-dives/05-langmem.md
new file mode 100644
index 0000000..20895d5
--- /dev/null
+++ b/docs/memory/02-deep-dives/05-langmem.md
@@ -0,0 +1,92 @@
+# LangMem:hot path 工具、后台 manager 与程序性记忆
+
+> 源码快照:`langchain-ai/langmem@a2d580946465137c89162e67dc0b18108bd4850c`(2026-07-15)。重点文件:[`knowledge/tools.py`](https://github.com/langchain-ai/langmem/blob/a2d580946465137c89162e67dc0b18108bd4850c/src/langmem/knowledge/tools.py)、[`knowledge/extraction.py`](https://github.com/langchain-ai/langmem/blob/a2d580946465137c89162e67dc0b18108bd4850c/src/langmem/knowledge/extraction.py)、[`prompts/optimization.py`](https://github.com/langchain-ai/langmem/blob/a2d580946465137c89162e67dc0b18108bd4850c/src/langmem/prompts/optimization.py)。
+
+## 三组能力
+
+LangMem 不是单一“记忆数据库”,而是构建在 LangGraph store 之上的三组 primitive:
+
+1. hot path 的 `manage_memory` / `search_memory` 工具;
+2. background 的 `MemoryManager` 抽取与整合;
+3. 根据轨迹和反馈更新系统提示的 prompt optimizer。
+
+## Hot path 工具
+
+`create_manage_memory_tool()` 生成一个结构化工具:
+
+```text
+content: str | 自定义 Pydantic schema
+action: create | update | delete
+id: UUID(update/delete 必填)
+```
+
+实现本身很薄:
+
+- namespace 通过模板和 runtime config 解析,可做 per-user 分区;
+- delete 调 `store.delete/adelete`;
+- create 生成 UUID;
+- create/update 都调用 `store.put/aput`,value 为 `{"content": ...}`;
+- 默认说明要求模型主动保存偏好、显式记忆请求、工作上下文,并修正过期 memory。
+
+因此“一致性”主要依赖模型是否调用正确 action;BaseStore 只提供存取,不自动理解冲突。
+
+## Background MemoryManager
+
+`MemoryManager` 使用 Trustcall 的 schema extraction:
+
+- 输入:新消息、可选 existing memories、`max_steps`;
+- 默认支持 insert/update,delete 默认关闭;
+- `_prepare_messages()` 把轨迹和“extract/contextualize、compare/update、synthesize”说明组成 memory subroutine;
+- `create_extractor()` 接收自定义 schemas 和已有文档 ID;
+- 每步可并行产生多个 tool calls;第二步起增加 `Done` 工具;
+- 外部传入的 memory ID 被保留,update 继续使用同一 ID;
+- 返回的是变更后的 memory objects,`create_memory_store_manager()` 再负责持久化。
+
+这比“让聊天主模型自己顺便记住”更易测试,因为 memory manager 是独立纯函数式边界。
+
+## 程序性记忆
+
+`create_prompt_optimizer()` 支持:
+
+- `prompt_memory`:单次调用提取成功模式;
+- `metaprompt`:多轮分析/更新;
+- `gradient`:反思与应用更新分开,调用最多。
+
+它实际上会改写 agent 的行为 prompt。此能力风险高于事实记忆:错误或恶意反馈可能长期改变系统行为,必须版本化、评测并审批,不能直接让终端用户对生产 system prompt 写入。
+
+## 优点
+
+- 工具、抽取 manager、存储分层清楚;
+- schema 可由应用定义;
+- namespace 明确,PostgresStore 可用于生产;
+- background manager 可以独立回放与评测;
+- delete 默认关闭是合理保守值。
+
+## 与本项目的摩擦
+
+- Python + LangGraph/Trustcall 生态,与当前 TypeScript + AI SDK v7 不同;
+- 引入它仍需部署 Python 服务或重写桥接;
+- current project 已有 AI SDK `tool()`、Zod schema、Drizzle 和 `prepareStep`,核心 primitive 可原生实现;
+- prompt optimizer 不应成为第一版用户记忆的一部分。
+
+## 可移植设计
+
+在本项目中可以复刻,而无需引入依赖:
+
+```text
+Zod CandidateFact[]
+ <- 独立 generateText/object 输出
+ <- new events + selected current facts
+
+deterministic reducer
+ -> insert/supersede/ignore
+
+Drizzle transaction
+ -> event + facts + extraction run
+```
+
+模型只提议候选,reducer 决定允许的状态迁移;这比 hot-path 工具直接 CRUD 更符合当前需求。
+
+## 判定
+
+**借鉴 background manager,不接入 LangMem。** 程序性记忆等事实记忆和评测体系稳定后再单独立项。
diff --git a/docs/memory/02-deep-dives/06-memobase.md b/docs/memory/02-deep-dives/06-memobase.md
new file mode 100644
index 0000000..5eacf34
--- /dev/null
+++ b/docs/memory/02-deep-dives/06-memobase.md
@@ -0,0 +1,101 @@
+# Memobase:缓冲写入、画像与事件时间线
+
+> 源码/文档快照:`memodb-io/memobase@358c16bbc6d687937d79bc2f984a11c3be8da901`(2026-01-11)。入口:[仓库 README](https://github.com/memodb-io/memobase/tree/358c16bbc6d687937d79bc2f984a11c3be8da901)。
+
+## 设计动机
+
+Memobase 的口号是 “Memory for User, not Agent”。它不让每条聊天立刻进入 agentic tool loop,而是维护:
+
+- 用户 blob buffer;
+- topic/sub-topic 结构化 profile;
+- 带时间的 event timeline;
+- 把画像与事件打包进 prompt 的 `context()`。
+
+它解决的是在线产品的稳定个性化,而不是让 agent 自主维护任意知识。
+
+## 调用链
+
+公开 API 展示的路径:
+
+```text
+u.insert(ChatBlob(messages))
+ -> blob 进入该用户 buffer
+ -> 达到阈值 / 闲置超时 / 手动 u.flush()
+ -> 后台工作流批量抽取与更新
+ -> profile(topic, sub_topic, content) + events
+
+u.context(max_token_size, prefer_topics)
+ -> SQL 读取预计算结果
+ -> 按预算打包 user background + latest events
+ -> 注入聊天 prompt
+```
+
+默认异步 flush;README 给出的典型触发值是约 1024 tokens 或闲置约 1 小时,也允许在会话结束时手动 flush。处理后的原始 blob 默认可删除,是否留存由配置控制。
+
+## 为什么 buffer 很重要
+
+每轮都抽取会造成:
+
+- LLM 固定开销被短消息放大;
+- 同一话题被多次拆成碎片;
+- 写入延迟落在用户等待路径;
+- 多个并发 turn 更容易发生旧结果覆盖新结果。
+
+缓冲让模型一次看到一个较完整的局部情境,成本按多轮摊薄。代价是 eventual consistency:刚说完的信息可能暂时没有进入 profile。
+
+## 画像 schema
+
+Profile 以 `topic/sub_topic/content` 组织,例如:
+
+```text
+basic_info.name
+interest.games
+work.title
+psychological.goals
+```
+
+这比无 schema 的向量条目更适合:
+
+- 直接构造 system prompt;
+- 产品 UI 按分类展示;
+- SQL 过滤或分析;
+- 限制哪些信息允许被记住。
+
+但开放式 topic 仍会漂移。项目落地时应先固定少量 predicate,不直接复制大而全的人格画像分类。
+
+## 读取语义
+
+常规 `profile()` 是预计算结果,读取便宜;`context()` 负责 token budget、topic 偏好和事件打包。README 同时说明较新 context 搜索可能调用 embedding、耗时高于单纯 profile 读取。两者不应混称为同一延迟。
+
+## 可复用点
+
+- 写入脱离热路径;
+- buffer 以 token 和 idle time 双阈值触发;
+- 画像与事件分层:稳定属性常驻,长尾经历按需;
+- 注入 API 自带预算,而不是把整张画像无限拼接;
+- profile schema 同时服务模型和用户控制面。
+
+## 不直接接入的原因
+
+- 需要独立 FastAPI/Postgres/Redis 服务或托管 API;
+- 本项目已经有 Postgres、鉴权和 embedding,双写另一套用户系统会增加一致性成本;
+- 厂商自报的 LOCOMO/延迟不能替代本项目数据上的验证;
+- 泛化画像类别可能收集超出产品必要范围的敏感信息。
+
+## 本项目落地映射
+
+建议复制其节奏,而不是 SDK:
+
+```text
+memory_events(status=pending)
+ -> 每用户 token_count >= N 或 oldest_event_age >= T
+ -> extraction_run
+ -> facts transaction
+ -> profile refresh
+```
+
+阶段 3 先测试 `N=4~8 turns` 和会话结束 flush;不要先锁死 1024 tokens/1 小时,这些数值没有本项目证据。
+
+## 判定
+
+**重点借鉴。** 在所有外部方案中,它与“用户画像 + 低在线延迟 + 自有 Postgres”的产品目标最接近。
diff --git a/docs/memory/02-deep-dives/07-letta.md b/docs/memory/02-deep-dives/07-letta.md
new file mode 100644
index 0000000..93e7b6e
--- /dev/null
+++ b/docs/memory/02-deep-dives/07-letta.md
@@ -0,0 +1,70 @@
+# Letta / MemGPT:常驻 memory blocks 与后台 agent
+
+> 生态快照:2026-07-24。`letta-ai/letta` 主仓库已明确标注为 legacy V1 server;新项目推荐 Letta Agent SDK。用于通用记忆的实验性封装快照为 `letta-ai/ai-memory-sdk@4494e00410469082bf298b8b03b7c9f93e244f14`。
+
+## 设计动机
+
+MemGPT/Letta 的核心比喻是“LLM 操作系统”:上下文窗口像有限内存,agent 通过工具主动编辑常驻内容,并把更大历史放入可检索的外部存储。
+
+当前可见的层次:
+
+- **memory blocks**:常驻上下文的带 label 文本块,如 `human`、`persona`、`preferences`;
+- **files**:较大的只读/可开关资料;
+- **archival memory**:可写、语义检索的长尾 passages;
+- **external RAG**:通过自定义工具/MCP 访问。
+
+Letta 文档对旧 V1 的建议是:重要且小的内容放 blocks,较弱的情景记忆放 archival。
+
+## AI Memory SDK 的实现模型
+
+实验性 Memory SDK 没有把主聊天模型换成 Letta,而是创建一个“subconscious agent”:
+
+```text
+subject_id
+ -> 一个 Letta memory agent
+ -> 多个 labeled blocks
+
+主应用先读取 blocks -> 拼进自己的 system prompt
+主应用完成对话 -> 批量 add_messages()
+memory agent 异步处理 -> 更新 blocks
+可选 skip_vector_storage=false -> 写 archival passages
+```
+
+一个 subject 可以是用户、项目或团队。SDK 建议每次批量发送 5–10 条消息或只在消息被挤出上下文时处理,以降低 agent 调用成本。
+
+## 与早期 MemGPT 的变化
+
+不能再按 2023 年论文假设当前产品 API:
+
+- V1 server/SDK 仍存在,但官方主仓库已称 legacy;
+- 新入口是 Agent SDK、Letta Code/App Server;
+- memory filesystem、git-backed context、sleeptime/dreaming 等能力仍在快速演进;
+- 因此源码级集成必须钉版本,不能只依据论文概念。
+
+## 优点
+
+- memory block 是“每轮必带压缩视图”的直接实现;
+- block label/description 给模型明确写入边界;
+- blocks 可跨 agent 共享;
+- 主 agent 与后台 memory agent 分离,避免阻塞用户响应;
+- archival 层补足 block 容量限制。
+
+## 风险
+
+- 记忆 agent 本身是有状态 runtime,接入不只是加一个数据库 SDK;
+- block 自编辑仍由 LLM 决定,缺少强 schema 当前值约束;
+- 一用户一 agent 带来生命周期、成本、删除和并发管理;
+- 官方推荐入口变化快,V1、Agent SDK、Memory SDK、Code SDK 容易混用;
+- 当前项目已有自己的 tree state、streaming、tools 与计费,替换运行时侵入面很大。
+
+## 可复用点
+
+- 用多个短 block,而不是一个无限增长的“用户简介”;
+- label + description 同时约束写入和注入语义;
+- 画像常驻、长尾事件检索;
+- 后台 worker/agent 处理 memory,不占主聊天延迟;
+- 对记忆变更做版本化,借鉴 git-backed context 的可回滚思想。
+
+## 判定
+
+**不接入。** 其 memory blocks 是有价值的产品抽象,但本项目可以用 `memory_profiles.profile_json` 与固定 sections 更轻量地实现。
diff --git a/docs/memory/02-deep-dives/08-chatgpt-memory.md b/docs/memory/02-deep-dives/08-chatgpt-memory.md
new file mode 100644
index 0000000..5633700
--- /dev/null
+++ b/docs/memory/02-deep-dives/08-chatgpt-memory.md
@@ -0,0 +1,68 @@
+# ChatGPT memory:闭源产品行为参考
+
+> 证据边界:ChatGPT memory 是闭源产品,本篇只记录 OpenAI 官方公开行为,不声称知道内部表结构、向量库或 prompt。官方来源:[Memory FAQ](https://help.openai.com/en/articles/8590148-memory-faq)、[Dreaming: Better memory for a more helpful ChatGPT](https://openai.com/index/chatgpt-memory-dreaming/)。
+
+## 2026 年的产品模型
+
+旧系统包含两条路径:
+
+- saved memories:用户明确要求记住,或聊天中触发保存;
+- reference chat history:从历史聊天中提取上下文。
+
+OpenAI 2026 年披露的新架构以 **dreaming** 为基础:后台过程持续综合过去对话,更新一个可用于新对话的 memory state。官方强调它会随时间修正状态,例如把“将去新加坡”演化成“2026 年 7 月去过新加坡”。
+
+## 能确认的行为
+
+- memory 是跨聊天的个性化上下文;
+- 后台 synthesis 不要求用户每次明确说“记住”;
+- memory summary 只展示高层视图,不保证列出所有内部上下文;
+- 用户能刷新 summary、纠正信息、要求不要再提及;
+- memory sources 可解释一次回答使用了哪些来源;
+- Temporary Chat 不使用/不写入 memory;
+- 删除聊天不等于删除独立保存的 memory;
+- legacy saved memories 可单独管理,删除日志可能为安全/调试保留最多 30 天。
+
+## 产品设计上真正值得学的部分
+
+### 1. 记忆必须有可见表面
+
+用户要能知道系统记住了什么、为什么使用、如何纠正。只在后端静默注入会把错误个性化变成不可诊断行为。
+
+### 2. 来源与记忆分离
+
+删除来源聊天和删除派生 memory 是不同动作,但 UI 必须解释清楚。对本项目意味着 `source_message_id` 不能省略。
+
+### 3. 时间会主动改变语义
+
+事实不只在新消息到达时变化。计划、地点、短期目标会自然过期,需要 `valid_from/valid_to` 或周期性 consolidation。
+
+### 4. summary 不是完整记录
+
+用户可见 summary 是控制面,不一定是数据库真相。若本项目也采用聚合画像,应同时允许展开来源事实,避免只提供不可核验的总括。
+
+## 不应推断的内容
+
+官方没有公开:
+
+- 具体 embedding、数据库或检索 top-k;
+- dreaming 的模型、触发频率、prompt 与成本;
+- 冲突消解算法;
+- memory summary 与实际注入 context 是否一一对应;
+- 敏感信息分类器的完整规则。
+
+因此不能把“ChatGPT 看起来能记住”翻译成某个可复制的源码架构。
+
+## 对本项目的最小 UX 要求
+
+在自动记忆默认开启前,至少提供:
+
+1. memory summary/列表;
+2. 每条来源与时间;
+3. 编辑、删除、清空;
+4. 临时聊天或关闭自动记忆;
+5. 回答级“使用了哪些记忆”;
+6. 明确区分“删除对话”和“删除派生记忆”。
+
+## 判定
+
+**重点借鉴控制面与后台持续综合,不把闭源行为当作实现证据。**
diff --git a/docs/memory/02-deep-dives/09-evaluation.md b/docs/memory/02-deep-dives/09-evaluation.md
new file mode 100644
index 0000000..e39a3e7
--- /dev/null
+++ b/docs/memory/02-deep-dives/09-evaluation.md
@@ -0,0 +1,130 @@
+# 评测方案:从公开 benchmark 到本项目最小实验
+
+## 为什么不能直接排名选型
+
+公开分数常同时改变 memory backend、生成模型、judge、top-k、prompt、数据清洗版本与 full-context 上限。厂商复测还可能只公布有利配置。阶段 3 的目标不是复现“SOTA”,而是回答本项目的工程问题:
+
+- 应该记什么?
+- 新事实如何替换旧事实?
+- 读取是否把正确证据送给模型?
+- 延迟、token 与错误写入是否可接受?
+
+## 可用公开集
+
+| 数据集 | 主要能力 | 适合用途 | 不足 |
+| --- | --- | --- | --- |
+| [LoCoMo](https://github.com/snap-research/locomo/tree/3eb6f2c585f5e1699204e3c3bdf7adc5c28cb376) | 多 session 事实、时间、多跳 | 兼容业界结果、长对话回放 | 仅 10 段对话;judge 与配置敏感 |
+| [LongMemEval](https://github.com/xiaowu0162/LongMemEval/tree/9e0b455f4ef0e2ab8f2e582289761153549043fc) | extraction、多 session、knowledge update、时间、拒答 | 当前值与新鲜度 | 合成/编排成分较高 |
+| [LongMemEval-V2](https://github.com/xiaowu0162/LongMemEval-V2) | agent 经验、环境定制 | 后续程序性记忆 | 超出第一版用户事实范围 |
+
+第一轮不要跑完整排行榜。先抽 20–50 题,并加入本项目自己的中文/英文 fixture。
+
+## 必须分层计分
+
+端到端“答案对不对”无法定位问题。每个样本至少记录:
+
+1. **write precision/recall**:该写的事实是否写入,不该写的是否被记住;
+2. **state accuracy**:当前值、旧值、有效时间是否正确;
+3. **retrieval recall@k**:正确证据是否进入候选;
+4. **context precision**:注入的无关/矛盾记忆比例;
+5. **answer correctness**:模型最终答案;
+6. **abstention**:没有证据时是否承认不知道;
+7. **privacy/control**:删除后是否停止召回、跨用户是否绝不泄露;
+8. **cost/latency**:写入调用数、tokens、读 p50/p95、增加的输入 tokens。
+
+## 本项目 fixture 格式
+
+建议把测试用例版本化为 JSON:
+
+```json
+{
+ "id": "knowledge-update-city-zh",
+ "userId": "u-a",
+ "events": [
+ { "at": "2026-01-01", "text": "我现在住在杭州。" },
+ { "at": "2026-06-01", "text": "我搬到新加坡了。" }
+ ],
+ "query": { "at": "2026-07-01", "text": "我现在住哪里?" },
+ "expected": {
+ "answerFacts": ["current_location=Singapore"],
+ "inactiveFacts": ["current_location=Hangzhou"],
+ "mustNotContain": ["Hangzhou is current"]
+ }
+}
+```
+
+同一 fixture 必须可喂给三个 backend,避免每种方案使用不同数据加工。
+
+## 第一轮 30 题组成
+
+| 类别 | 数量 | 示例 |
+| --- | ---: | --- |
+| 稳定偏好 | 5 | 语言、格式、饮食 |
+| 当前值更新 | 6 | 城市、项目、职位变化 |
+| 时间与过期 | 4 | 旅行结束、临时计划 |
+| 多 session 组合 | 4 | 分散透露的人/项目关系 |
+| 否定与撤回 | 3 | “我不再…”、“忘掉…” |
+| 不应记忆 | 3 | 寒暄、assistant 幻觉、一次性敏感信息 |
+| 删除与隔离 | 3 | 删除后不召回、u-a/u-b 隔离 |
+| 拒答 | 2 | 从未提供的信息 |
+
+至少一半中文,全部包含稳定 ID 和绝对时间。
+
+## 对比组
+
+在相同聊天模型、embedding、答案 prompt、预算下比较:
+
+- A0:只给当前 session;
+- A1:全历史;
+- A2:历史 turn 向量 top-k;
+- A3:Mem0 风格 ADD-only 记忆 + vector;
+- A4:结构化 fact + 确定性 supersede;
+- A5:A4 + 紧凑 profile + 长尾 vector。
+
+若无法锁定模型采样,至少把原始抽取 JSON、检索结果和最终 prompt 保存,允许人工复核。
+
+## 成功门槛
+
+第一版不是追求最高 QA 分,而是满足上线底线:
+
+- 跨用户泄露:0;
+- 删除后召回:0;
+- current-value 冲突准确率:≥95%;
+- 不应记忆 precision:≥95%;
+- retrieval recall@6:≥90%;
+- 在线读路径 p95:目标 <150ms(不含主模型);
+- 注入预算:默认不超过主上下文的 10%;
+- 写入失败不影响聊天成功,且可重试、无重复事实。
+
+这些是阶段 3 的初始工程门槛,不是已经测得的结果。
+
+## 实验顺序
+
+1. 先做纯函数 reducer:candidate facts -> state;
+2. 再比较抽取 prompt/schema;
+3. 再接 Postgres 与向量召回;
+4. 最后接 `route.ts` 测真实流式延迟;
+5. 通过后才设计 memory UI 和自动写入默认值。
+
+## 需要记录的反例
+
+每次失败都归入一个稳定 taxonomy:
+
+```text
+WRITE_MISSED
+WRITE_FALSE_POSITIVE
+ATTRIBUTION_ERROR
+TEMPORAL_NORMALIZATION_ERROR
+CONFLICT_RESOLUTION_ERROR
+RETRIEVAL_MISS
+CONTEXT_DISTRACTION
+ANSWER_IGNORED_EVIDENCE
+DELETION_LEAK
+TENANT_LEAK
+```
+
+只看总分会掩盖安全性失败;taxonomy 才能指导下一轮修改。
+
+## 阶段 3 的明确输入
+
+阶段 2 推荐从 A4 开始,实现三个 predicate 的 30 题 fixture;A3 用同一数据作为对照。若 A4 没有在 current-value、删除和隔离上明显胜出,就不进入通用 schema 和 UI 开发。
diff --git a/docs/memory/02-deep-dives/README.md b/docs/memory/02-deep-dives/README.md
new file mode 100644
index 0000000..ca5f5ca
--- /dev/null
+++ b/docs/memory/02-deep-dives/README.md
@@ -0,0 +1,57 @@
+# 阶段 2:逐方案深入
+
+> 研究快照:2026-07-24。本文档集完成 PR [#2](https://github.com/hifizz/thread-chatbot/pull/2) 中“每个代表方案一篇:设计动机、架构、源码级实现细节”的任务。外部项目仍在快速变化,引用的主干快照与关键文件均在各篇中标明。
+
+## 结论先行
+
+本项目不应直接引入某个通用记忆框架作为核心依赖。优先进入实验的是:
+
+1. 以现有 Postgres、Drizzle、pgvector 为底座,自建“结构化事实 + 事件时间线 + 可选向量召回”;
+2. 写入采用 Memobase 的缓冲思想,但事实冲突由 schema 和确定性版本规则处理;
+3. 读取采用“紧凑画像常驻 + 精确事实查询 + 长尾语义召回”的分层组合;
+4. 借鉴 ChatGPT 的可审计控制面和 Anthropic memory tool 的受限 CRUD 协议;
+5. 暂不引入 Graphiti、LangMem、Letta 或 Mem0 运行时;它们分别带来图数据库、Python/LangGraph、外部 agent runtime 或不可控写入语义。
+
+这只是阶段 2 的架构决策,不等于已经实现产品记忆。数据表、写入任务、注入与 UI 属于阶段 3/4。
+
+## 文档导航
+
+| 文档 | 重点 | 对本项目的判定 |
+| --- | --- | --- |
+| [01-self-built-postgres.md](./01-self-built-postgres.md) | 基于当前源码的目标架构与挂载点 | **进入实验** |
+| [02-mem0.md](./02-mem0.md) | V3 ADD-only 管线、检索、作用域 | 借鉴,不接入 |
+| [03-anthropic-memory-tool.md](./03-anthropic-memory-tool.md) | 文件式 JIT 记忆协议与安全边界 | 借鉴工具协议 |
+| [04-graphiti.md](./04-graphiti.md) | bi-temporal 图、写入与混合检索 | 暂缓 |
+| [05-langmem.md](./05-langmem.md) | hot path 工具、后台 manager、提示优化 | 借鉴 manager |
+| [06-memobase.md](./06-memobase.md) | 缓冲写入、画像、事件、context 注入 | **重点借鉴** |
+| [07-letta.md](./07-letta.md) | core blocks、archival memory、sleeptime | 不接入 |
+| [08-chatgpt-memory.md](./08-chatgpt-memory.md) | 产品行为、后台 synthesis、用户控制面 | **重点借鉴 UX** |
+| [09-evaluation.md](./09-evaluation.md) | 评测边界与阶段 3 最小实验集 | **阶段 3 输入** |
+
+## 横向比较
+
+| 方案 | 主存储形态 | 写入控制者 | 冲突/时间 | 读取路径 | 接入摩擦 |
+| --- | --- | --- | --- | --- | --- |
+| 自建 Postgres | 关系表 + 可选 vector | 应用管线 | schema + 版本链 | SQL + vector | 低 |
+| Mem0 V3 | 向量条目 + links + history | 单次 LLM 抽取 | 新记忆关联旧记忆 | 多信号检索 | 中 |
+| Anthropic tool | 客户端文件/自定义存储 | Claude 工具调用 | 文件编辑语义 | 按需 `view` | 中;模型绑定 |
+| Graphiti | 实体/事实边/episode 图 | 多阶段 LLM 管线 | `valid_at`/`invalid_at` | 语义 + BM25 + 图 | 高 |
+| LangMem | LangGraph BaseStore | agent 工具或 manager | LLM update/delete | store search/注入 | 高;Python |
+| Memobase | 画像 + 事件 + buffer | 后台固定工作流 | 画像更新 + timeline | `context()` | 中 |
+| Letta | core blocks + archival | 主 agent/后台 agent | block 自编辑 | 常驻 + 检索 | 高;runtime 替换 |
+| ChatGPT | 未公开 | 后台 synthesis | 持续重写、时间演化 | 产品内部 | 不可接入 |
+
+## 研究方法与证据边界
+
+- 优先读官方仓库、官方文档与当前项目源码,而不是二手博客。
+- “源码级”表示定位到数据模型、入口函数、关键 prompt 或 tool schema;不把厂商性能数字当作独立证据。
+- 闭源产品只能做行为层分析。`08-chatgpt-memory.md` 明确区分官方披露与推断,不伪造内部实现。
+- 每篇都包含“可复用点 / 不可照搬点 / 对本项目结论”,保证调研能直接进入实验设计。
+
+## 阶段 2 验收
+
+- [x] 覆盖 PR #2/`01-survey.md` 阶段 2 清单中的代表方案
+- [x] 对快速变化的 Mem0、Letta 做版本修正
+- [x] 给出本项目当前源码的注入、写入和存储挂载点
+- [x] 给出阶段 3 的可执行实验集与成功门槛
+- [x] 更新总入口的阶段状态与阅读导航
diff --git a/docs/memory/README.md b/docs/memory/README.md
new file mode 100644
index 0000000..23d0dc0
--- /dev/null
+++ b/docs/memory/README.md
@@ -0,0 +1,31 @@
+# Chatbot 记忆体系化工程 · 总入口
+
+本目录承载「为本项目引入记忆能力」的完整过程:**从零开始 → 全景调研 → 逐方案深入 → 动手实验 → 产品落地 → 经验总结**。目标有两个:
+
+1. **学习**:搞清楚 Chatbot / Agent 到底是如何实现记忆的(分几层、每层怎么做、社区有哪些代表方案、各自的取舍)。
+2. **落地**:把合适的方案在本项目(Next.js 16 + assistant-ui + AI SDK v7 + Drizzle/Postgres)中实现出来,让用户真实体验到。
+
+## 目录结构与阶段
+
+| 阶段 | 目录/文件 | 状态 | 说明 |
+| --- | --- | --- | --- |
+| 0. 总纲 | `README.md`(本文件) | ✅ | 路线图与导航 |
+| 1. 全景调研 | `01-survey.md` | ✅ | 记忆分层分类学 + 社区方案全景 + 对比 + 路线图(本阶段产出) |
+| 2. 逐方案深入 | [`02-deep-dives/`](./02-deep-dives/) | ✅ | 8 个代表方案 + 评测设计:设计动机、架构、源码级实现细节与本项目判定 |
+| 2.5 成品架构研究 | [`research/`](./research/) | ✅ | A 托管服务 / B 混合内核 / C 事件图:共享类型、安全契约、Mermaid 图与落地计划 |
+| 3. 实验 | `03-experiments/` | ⬜ | 用最小可复现实验验证关键设计点(抽取质量、检索命中、延迟、成本) |
+| 4. 落地实现 | `04-implementation/` | ⬜ | 本项目的记忆功能设计文档、分期实施记录 |
+| 5. 总结 | `05-retrospective.md` | ⬜ | 实践结果、踩坑、经验与最佳实践 |
+
+## 阅读顺序
+
+从零开始的读者按 1 → 2 → 3 → 4 → 5 顺序读即可;只关心「本项目怎么做」的读者可先读 [`01-survey.md`](./01-survey.md) 的最后一章(落地路线图),再读[阶段 2 结论](./02-deep-dives/#结论先行)和[自建 Postgres 蓝本](./02-deep-dives/01-self-built-postgres.md)。
+
+## 当前进度
+
+- [x] 阶段 1:全景调研报告
+- [x] 阶段 2:逐方案深入(自建 Postgres / Mem0 / Anthropic memory tool / Graphiti / LangMem / Memobase / Letta / ChatGPT memory / 评测)
+- [x] 成品架构研究:A / B / C 三套可比较落地方案
+- [ ] 阶段 3:关键实验
+- [ ] 阶段 4:分期落地
+- [ ] 阶段 5:总结复盘
diff --git a/docs/memory/research/00-shared-product-model.md b/docs/memory/research/00-shared-product-model.md
new file mode 100644
index 0000000..2402f63
--- /dev/null
+++ b/docs/memory/research/00-shared-product-model.md
@@ -0,0 +1,402 @@
+# 三种方案共用的产品模型与安全契约
+
+## 1. 先定义边界,再选择存储
+
+Memory 服务、Postgres 和知识图谱回答的是“如何保存与检索”;产品还必须先回答:
+
+- 什么对象具有权威性;
+- 谁可以修改;
+- 哪些修改必须确认;
+- 冲突时谁覆盖谁;
+- 删除后哪些派生数据必须一起消失;
+- 模型看到的内容如何与普通指令隔离。
+
+以下契约应当独立于 A/B/C。切换后端时,前端控制面、Project 语义和安全规则不应一起重写。
+
+## 2. 领域对象
+
+```ts
+type ID = string
+type ISODateTime = string
+
+interface Project {
+ id: ID
+ ownerUserId: ID
+ title: string
+ status: "active" | "archived"
+ createdAt: ISODateTime
+ updatedAt: ISODateTime
+}
+
+interface ProjectTreeBinding {
+ projectId: ID
+ treeId: ID
+ isPrimary: boolean
+ createdAt: ISODateTime
+}
+
+interface ProjectGoal {
+ id: ID
+ projectId: ID
+ objective: string
+ successCriteria: string[]
+ constraints: string[]
+ version: number
+ updatedBy: "user" | "system"
+ updatedAt: ISODateTime
+}
+```
+
+当前实现可以在创建 tree 时一并创建 Project 和绑定记录。不要继续让 `tree_id` 同时承担 Project 身份,否则未来一 Project 多树时,目标和记忆都需要迁移主键。
+
+## 3. 记忆作用域与类型
+
+```ts
+type MemoryScope =
+ | { type: "user"; userId: ID }
+ | { type: "project"; userId: ID; projectId: ID }
+
+type MemoryKind =
+ | "preference"
+ | "instruction"
+ | "fact"
+ | "decision"
+ | "constraint"
+ | "glossary"
+ | "open_question"
+
+type MemoryAuthority = "explicit_user" | "confirmed_user" | "inferred"
+type MemoryStatus = "active" | "superseded" | "deleted"
+type Sensitivity = "normal" | "sensitive" | "prohibited"
+
+interface MemorySource {
+ treeId: ID
+ threadId: ID
+ messageId: ID
+ quote?: string
+}
+
+interface MemoryItem {
+ id: ID
+ scope: MemoryScope
+ kind: MemoryKind
+ key: string
+ value: unknown
+ searchableText: string
+ authority: MemoryAuthority
+ sensitivity: Sensitivity
+ status: MemoryStatus
+ confidence?: number
+ source: MemorySource
+ supersedesId?: ID
+ validFrom?: ISODateTime
+ validTo?: ISODateTime
+ version: number
+ createdAt: ISODateTime
+ updatedAt: ISODateTime
+}
+```
+
+`key` 提供确定性覆盖语义。例如:
+
+```text
+user / instruction / output_language
+project / decision / memory_architecture
+project / glossary / thread
+```
+
+同一 scope、kind、key 可以有历史版本,但只能有一个 active 当前值。
+
+## 4. 候选、确认和拒绝
+
+模型不能直接创建 `MemoryItem`。它只能输出候选:
+
+```ts
+type CandidateDecision =
+ | "auto_accept"
+ | "needs_confirmation"
+ | "reject"
+
+interface MemoryCandidate {
+ id: ID
+ proposed: Omit<
+ MemoryItem,
+ "id" | "status" | "version" | "createdAt" | "updatedAt"
+ >
+ extractedFrom: MemorySource[]
+ extractorVersion: string
+ rationaleCode:
+ | "explicit_remember_request"
+ | "stable_preference"
+ | "project_decision"
+ | "project_constraint"
+ | "possible_sensitive_data"
+ | "assistant_only_claim"
+ | "ephemeral_statement"
+ decision: CandidateDecision
+ status: "pending" | "accepted" | "rejected" | "expired"
+ createdAt: ISODateTime
+}
+```
+
+推荐决策表:
+
+| 内容 | 默认结果 |
+| --- | --- |
+| 用户明确说“记住……”且不敏感 | `auto_accept` |
+| 普通 Project 决策,有用户原话来源 | `auto_accept` |
+| 自动推断的稳定偏好 | `needs_confirmation` 或低权威生效 |
+| 用户全局 instruction 的新增/修改 | `needs_confirmation` |
+| 健康、财务、身份凭证、精确位置等敏感信息 | `needs_confirmation` 或 `reject` |
+| 只有 assistant 说过、用户未确认 | `reject` |
+| 寒暄、一次性请求、临时上下文 | `reject` |
+
+“低权威生效”不能覆盖用户明确设置;它只能在没有更高优先级值时作为建议。
+
+## 5. Thread 总结
+
+当前领域模型中的稳定节点是 `Thread`,不是 UI 列,也不是单条消息:
+
+```ts
+interface ThreadSummary {
+ id: ID
+ projectId: ID
+ treeId: ID
+ threadId: ID
+ title: string
+ summary: string
+ keyPoints: string[]
+ decisions: string[]
+ openQuestions: string[]
+ sourceMessageIds: ID[]
+ inputRevision: string
+ generation: number
+ state: "draft" | "locked" | "stale"
+ editedBy: "ai" | "user"
+ createdAt: ISODateTime
+ updatedAt: ISODateTime
+}
+```
+
+规则:
+
+1. Thread 消息稳定后异步生成;
+2. `inputRevision` 是参与总结的消息 ID、内容版本和生成器版本的 hash;
+3. 新消息到达后,AI 草稿变成 `stale` 并可自动重算;
+4. 用户编辑后设置 `locked`,系统不得静默覆盖;
+5. locked 总结有新消息时只提示“有更新可合并”,由用户决定;
+6. 总结不是事实本体,不自动写入全局记忆;
+7. 从总结发现的决策只能生成 `MemoryCandidate`,继续走确认策略。
+
+## 6. 思维导图
+
+思维导图是只读派生视图:
+
+```ts
+interface MindMapNode {
+ id: string
+ kind: "goal" | "thread" | "decision" | "question"
+ label: string
+ sourceRef: {
+ projectId: ID
+ treeId?: ID
+ threadId?: ID
+ memoryId?: ID
+ }
+}
+
+interface MindMapEdge {
+ from: string
+ to: string
+ kind: "contains" | "branches_to" | "supports" | "blocks" | "answers"
+}
+
+interface MindMapView {
+ projectId: ID
+ sourceRevision: string
+ nodes: MindMapNode[]
+ edges: MindMapEdge[]
+ generatedAt: ISODateTime
+}
+```
+
+输入只包括:
+
+- Project 目标、成功标准和约束;
+- 当前 tree 拓扑;
+- Thread 标题与总结;
+- 已生效的 Project 决策、约束和 open question。
+
+不直接输入所有原始消息,也不读取用户全局偏好作为导图内容。全局偏好只影响导图的语言和展示格式。
+
+```mermaid
+flowchart TD
+ Goal["Project Goal"] --> Build["Mind Map Builder"]
+ Tree["Thread Tree Topology"] --> Build
+ Summary["Thread Summaries"] --> Build
+ PM["Active Project Memories"] --> Build
+ Build --> Validate["验证 sourceRef 与节点数量"]
+ Validate --> Cache["按 sourceRevision 缓存"]
+ Cache --> View["只读思维导图"]
+```
+
+思维导图缓存删除或过期都不会损失信息;任何反向修改都必须通过目标编辑或记忆确认 UI 完成。
+
+## 7. 九个产品安全问题如何解决
+
+### 7.1 什么内容允许被记住
+
+使用 allowlist,而不是让模型自由发明类别。首批允许:
+
+- 用户全局:语言、输出格式、稳定的交互偏好;
+- Project:术语、决策、约束、已确认事实、开放问题;
+- 禁止默认收集:密码、API key、支付信息、精确证件、无必要的健康和身份数据。
+
+抽取器输出未知 `kind/key` 时直接拒绝,不自动扩展 schema。
+
+### 7.2 assistant 幻觉能否写入
+
+不能。`source` 必须能追溯到用户消息,或用户对 assistant 产物的明确确认。assistant 生成的计划、总结和 Artifact 都是派生内容,只能产生候选。
+
+### 7.3 敏感信息是否需要确认
+
+敏感分类先由确定性检测和模型分类双重判断。默认做三挡:
+
+- normal:按写入策略处理;
+- sensitive:明确说明内容和用途后确认;
+- prohibited:不创建候选,日志也不得保留明文。
+
+### 7.4 用户 ID 如何绑定
+
+`userId` 只能从服务端 session 获取,不能接受模型、客户端 body 或 Memory 服务返回值指定。每条 Project 查询必须同时验证 `owner_user_id`。
+
+```text
+authenticated session
+ -> resolve ownerUserId
+ -> authorize projectId
+ -> derive provider namespace
+```
+
+### 7.5 什么场景读取记忆
+
+不做“每轮全量读取”。Context Compiler 先分类:
+
+- 全局 instruction:每轮读取少量 active 值;
+- Project goal/constraint:Project 对话每轮读取;
+- 精确 key:按实体和 key 查;
+- 模糊历史:只有当前问题需要回忆时才语义检索;
+- Thread summary:只加载当前 lineage 和明确引用的分支。
+
+### 7.6 如何安全注入 system prompt
+
+记忆被当作带来源的数据,不是更高权限指令:
+
+```xml
+
+ 记忆可能过期;与用户当前消息冲突时,以当前消息为准。
+ ...
+ ...
+ ...
+
+```
+
+实现要求:
+
+- 服务端构造,不接受客户端 system role;
+- 严格 token/字符预算;
+- 对来源、作用域和状态二次过滤;
+- 不执行记忆文本中包含的工具调用或“忽略系统规则”等指令;
+- 记录本轮实际注入的 memory IDs,支持解释和审计。
+
+### 7.7 用户如何查看、编辑和删除
+
+控制面至少包括:
+
+- 全局设置页;
+- Project 的 Goal 与 Memory 面板;
+- Thread 总结的编辑、锁定、重新生成;
+- 每条记忆的来源跳转、状态、最后更新时间;
+- 删除单条、清空 Project、关闭自动记忆;
+- 当前回答“使用了哪些记忆”的解释入口。
+
+编辑必须使用 `version` 做乐观并发控制,不能整对象无条件覆盖。
+
+### 7.8 删除后是否真的停止召回
+
+删除是跨存储工作流:
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant API as Memory API
+ participant DB as Canonical Store
+ participant IDX as Memory/Vector Index
+ participant C as Context Cache
+ U->>API: 删除 memoryId
+ API->>DB: 标记 deleted + 写 deletion job
+ DB-->>API: 提交成功
+ API-->>U: 已从在线读取路径移除
+ API->>IDX: 删除外部/向量条目
+ IDX-->>API: 删除确认
+ API->>C: 失效相关缓存
+ API->>DB: deletion job = verified
+```
+
+第一笔事务完成后,所有在线查询必须过滤 deleted,因此即使外部索引删除暂时失败,也不能再注入。后台任务持续重试并核验供应商删除结果。
+
+### 7.9 错误记忆如何追溯
+
+保存:
+
+- 来源 message IDs 与用户原话片段;
+- extractor/model/prompt 版本;
+- 候选决策原因;
+- 每次接受、编辑、覆盖和删除的 actor;
+- 每次回答实际使用的 memory IDs;
+- 外部 provider ID 映射和删除状态。
+
+审计日志不应该保存 prohibited 敏感明文。
+
+## 8. 共用接口
+
+```ts
+interface MemoryEngine {
+ propose(input: {
+ scope: MemoryScope
+ messages: Array<{ id: ID; role: "user" | "assistant"; text: string }>
+ }): Promise
+
+ index(items: MemoryItem[]): Promise
+
+ search(input: {
+ scope: MemoryScope
+ query: string
+ kinds?: MemoryKind[]
+ limit: number
+ }): Promise>
+
+ remove(memoryIds: ID[]): Promise
+}
+
+interface ContextCompiler {
+ compile(input: {
+ userId: ID
+ projectId: ID
+ treeId: ID
+ threadId: ID
+ userMessage: string
+ budgetTokens: number
+ }): Promise
+}
+
+interface CompiledMemoryContext {
+ globalSettings: MemoryItem[]
+ projectGoal: ProjectGoal
+ projectMemories: MemoryItem[]
+ threadSummaries: ThreadSummary[]
+ usedMemoryIds: ID[]
+ renderedText: string
+}
+```
+
+方案差异主要体现在 `MemoryEngine` 背后和 `MemoryItem` 的真相来源,不应泄漏到聊天 UI。
diff --git a/docs/memory/research/01-option-a-managed-memory.md b/docs/memory/research/01-option-a-managed-memory.md
new file mode 100644
index 0000000..95cde94
--- /dev/null
+++ b/docs/memory/research/01-option-a-managed-memory.md
@@ -0,0 +1,438 @@
+# 方案 A:以成熟 Memory 服务为主要记忆后端
+
+## 1. 方案定义
+
+方案 A 选择 Mem0 一类成熟 Memory 服务/库作为长期记忆的主要存储和检索系统。应用自己的 Postgres 仍然保存 Project、目标、tree 绑定和 Thread 总结,因为这些对象不是通用 Memory API 可以替代的。
+
+```mermaid
+flowchart LR
+ UI["Thread Chat UI"] --> API["Next.js API"]
+ API --> PG["App Postgres
Project / Goal / Tree / Summary"]
+ API --> MEM["Managed Memory
Mem0 Platform / self-hosted"]
+ MEM --> V["Memory records / vector / history"]
+ API --> CC["Context Compiler"]
+ PG --> CC
+ MEM --> CC
+ CC --> LLM["Chat Model"]
+```
+
+这里的“服务为主”指:
+
+- 用户全局偏好和 Project 事实/决策以 provider memory ID 为主要记录;
+- 应用只保存 provider 映射、审计和必要的本地投影;
+- 检索排序、相似度、记忆压缩和供应商内部更新语义由 provider 决定。
+
+## 2. Mem0 的位置与当前实现语义
+
+Mem0 提供 `user_id`、`agent_id`、`run_id` 作用域,以及 add/search/update/delete 一类 API。根据本项目阶段 2 使用的源码快照:
+
+[`mem0ai/mem0@d6d89c987bddf580870db14c69db974edfc5263c`](https://github.com/mem0ai/mem0/tree/d6d89c987bddf580870db14c69db974edfc5263c)
+
+OSS 主路径已经从旧的 ADD/UPDATE/DELETE/NONE 管线转为 V3 ADD-only + memory linking。完整源码笔记见[Mem0 深入](../02-deep-dives/02-mem0.md)。
+
+### Mem0 V3 写入图
+
+```mermaid
+flowchart TD
+ M["新消息"] --> H["读取同 session 最近消息"]
+ H --> Q["对输入生成 query embedding"]
+ Q --> S["在 user / agent / run scope
检索 top memories"]
+ S --> IDs["真实 UUID 映射为短 ID"]
+ IDs --> E["LLM additive extraction"]
+ E --> N{"有新增记忆?"}
+ N -- 否 --> Done["只保存消息历史"]
+ N -- 是 --> BE["批量生成 embedding"]
+ BE --> D["文本 hash 精确去重"]
+ D --> W["写 memory + history"]
+ W --> L["建立 linked_memory_ids / entity links"]
+ L --> Done
+```
+
+它适合作为高召回的语义记忆层,但 ADD-only 意味着“杭州 → 新加坡”可能同时存在。若产品必须稳定返回当前值,应用仍需:
+
+- 在 metadata 中保存 key、authority、valid time;
+- 或维护本地当前值投影;
+- 或在读取时增加确定性 reducer。
+
+这也是方案 A 最重要的边界:即使采用成熟服务,Project 产品语义仍然不能全部外包。
+
+## 3. 作用域映射
+
+推荐把 provider namespace 视为服务端派生值:
+
+```ts
+interface ProviderScope {
+ userId: string
+ agentId: string
+ runId?: string
+}
+
+function toProviderScope(
+ scope:
+ | { type: "user"; userId: string }
+ | { type: "project"; userId: string; projectId: string }
+): ProviderScope {
+ return scope.type === "user"
+ ? {
+ userId: scope.userId,
+ agentId: "global-preferences",
+ }
+ : {
+ userId: scope.userId,
+ agentId: `project:${scope.projectId}`,
+ }
+}
+```
+
+不能让客户端直接传 `agentId`。服务端先验证 Project owner,再生成 provider scope。
+
+Thread ID 不建议直接作为一级 provider namespace。Thread 总结本身已经是派生知识;如果每个 Thread 都建独立记忆空间,跨分支检索会变得困难。可以把 `treeId/threadId/messageId` 放进 metadata,供过滤、来源跳转和删除。
+
+## 4. 本地数据模型
+
+即使 provider 是主要记忆后端,本地仍需要以下表:
+
+```ts
+interface ManagedMemoryRef {
+ id: string
+ ownerUserId: string
+ projectId?: string
+ provider: "mem0"
+ providerMemoryId: string
+ kind:
+ | "preference"
+ | "instruction"
+ | "fact"
+ | "decision"
+ | "constraint"
+ | "glossary"
+ | "open_question"
+ key: string
+ authority: "explicit_user" | "confirmed_user" | "inferred"
+ sensitivity: "normal" | "sensitive"
+ sourceTreeId: string
+ sourceThreadId: string
+ sourceMessageId: string
+ providerState: "active" | "delete_pending" | "deleted" | "error"
+ lastSyncedAt: string
+ createdAt: string
+ updatedAt: string
+}
+```
+
+另外还要有:
+
+- `projects`
+- `project_tree_bindings`
+- `project_goals`
+- `thread_summaries`
+- `memory_candidates`
+- `memory_audit_events`
+- `memory_provider_jobs`
+
+本地 ref 不是完整真相副本,但必须足够完成:
+
+- 身份授权;
+- 来源展示;
+- provider 删除失败时阻止在线召回;
+- 查询某 Project 有哪些 provider IDs;
+- 迁移供应商。
+
+## 5. Provider Adapter
+
+应用不能在 route 中直接散落 Mem0 SDK 调用:
+
+```ts
+interface ManagedMemoryProvider {
+ add(input: {
+ scope: ProviderScope
+ messages: Array<{
+ id: string
+ role: "user" | "assistant"
+ content: string
+ }>
+ metadata: Record
+ infer: boolean
+ }): Promise>
+
+ search(input: {
+ scope: ProviderScope
+ query: string
+ limit: number
+ filters?: Record
+ }): Promise<
+ Array<{
+ providerMemoryId: string
+ text: string
+ score: number
+ metadata: Record
+ }>
+ >
+
+ update(input: {
+ providerMemoryId: string
+ text: string
+ metadata: Record
+ }): Promise
+
+ remove(providerMemoryId: string): Promise
+
+ removeScope(scope: ProviderScope): Promise
+}
+```
+
+生产实现还需要:
+
+- timeout 和 retry;
+- 限流与熔断;
+- provider request ID 日志;
+- SDK/API 版本固定;
+- 请求和响应 schema 验证;
+- 内容长度限制;
+- 禁止把 API key 或 prohibited 内容发给 provider。
+
+## 6. 写入流程
+
+成熟产品不应该在聊天响应前同步等待 Memory 服务。
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant CHAT as Chat API
+ participant DB as App Postgres
+ participant Q as Durable Worker
+ participant P as Policy Engine
+ participant M as Mem0
+
+ U->>CHAT: 发送消息
+ CHAT->>DB: 保存消息 + memory extraction job
+ CHAT-->>U: 流式回答
+ Q->>DB: 领取 job(幂等键)
+ Q->>M: add(messages, provider scope)
+ M-->>Q: extracted memories
+ Q->>P: 分类 kind/key/authority/sensitivity
+ P-->>Q: accept / confirm / reject
+ alt 自动生效
+ Q->>DB: 保存 ManagedMemoryRef + audit
+ else 需要确认
+ Q->>DB: 保存 MemoryCandidate
+ DB-->>U: 控制面显示待确认
+ else 拒绝
+ Q->>DB: 只保存不含敏感明文的拒绝原因
+ end
+```
+
+这里存在一个现实问题:Mem0 可能已经写入内容,应用 Policy Engine 才决定拒绝。为避免 prohibited 内容短暂进入外部服务,有两种实现:
+
+1. **先本地预分类,再调用 Mem0 infer**:推荐;敏感/禁止内容在出站前拦截;
+2. 调用自有抽取器生成候选,确认后用 `infer=false` 写 Mem0:控制更强,但方案已经向 B 靠近。
+
+方案 A 若要保持安全,至少必须有出站前的 deterministic + model risk classifier。
+
+## 7. 读取流程
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant API as Chat API
+ participant PG as App Postgres
+ participant M as Mem0
+ participant C as Context Compiler
+ participant L as Chat Model
+
+ U->>API: Project 内提问
+ API->>PG: 校验 owner + 读 goal/summary/settings
+ API->>M: search(user/project scope, query)
+ M-->>API: provider memories
+ API->>PG: 过滤本地 delete_pending/deleted refs
+ API->>C: 合并、去重、优先级和预算
+ C-->>API: rendered memory context + used IDs
+ API->>L: system + messages
+ L-->>U: 流式回答
+ API->>PG: 记录本轮 used memory IDs
+```
+
+### Provider 故障降级
+
+如果 Mem0 search 超时:
+
+- 聊天继续成功;
+- 仍注入本地全局设置、Project 目标和 Thread 总结;
+- 不使用过期的任意 provider cache,除非 cache 中保存了 scope 和删除 epoch;
+- 记录 degraded 状态,但不向用户伪装“已使用完整记忆”。
+
+## 8. 冲突处理
+
+方案 A 有三个可选层级:
+
+### A1. 完全相信 provider
+
+优点是简单,缺点是无法保证当前值。只适合开放式回忆,不适合 Project instruction 和关键决策。
+
+### A2. Provider memory + metadata key
+
+应用为每条返回结果补充 `key/valid_from/status`,查询后按 key 选择最新有效值。需要本地 ref 或 provider 支持强 metadata filtering。
+
+### A3. Provider 保存全文,本地保存 current projection
+
+所有语义历史在 Mem0;本地表保存每个 scope/kind/key 的当前 provider ID。这是方案 A 中最成熟的实现,但已经引入小型关系型真相层。
+
+建议至少采用 A3,否则无法可靠实现全局输出语言和 Project 覆盖规则。
+
+## 9. 用户编辑
+
+用户编辑“输出语言 = 中文”时,不应该只更新一段自然语言:
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant API as Settings API
+ participant DB as App Postgres
+ participant M as Mem0
+
+ U->>API: 更新 output_language,expectedVersion=3
+ API->>DB: 校验 version,写本地 current projection
+ DB-->>API: version=4 + provider sync job
+ API-->>U: 更新成功
+ API->>M: update/add provider memory
+ M-->>API: provider ID
+ API->>DB: 更新 ref;旧 ID 标为 delete_pending
+ API->>M: 删除旧 provider memory
+```
+
+用户体验不能被 provider 同步阻塞。本地 projection 已更新后,Context Compiler 应立即使用新值,同时后台完成 provider 对齐。
+
+## 10. 删除语义
+
+```mermaid
+stateDiagram-v2
+ [*] --> active
+ active --> delete_pending: 用户删除/清空
+ delete_pending --> deleted: provider 删除已核验
+ delete_pending --> delete_pending: 重试失败
+ deleted --> [*]
+```
+
+规则:
+
+1. 本地 ref 进入 `delete_pending` 后立即从读取路径排除;
+2. provider 删除使用持久 job 重试;
+3. 清空 Project 时按本地 ref 枚举并删除,不能只依赖一次不透明的 provider `delete_all`;
+4. provider 返回成功后再次 search 验证,或使用官方删除状态接口;
+5. 相关 Context cache 和思维导图 cache 一并失效;
+6. 原始聊天消息是否删除是独立产品操作,不能暗中级联。
+
+## 11. Thread 总结和思维导图
+
+这两部分不交给 Mem0:
+
+```mermaid
+flowchart TD
+ Msg["Thread messages"] --> Sum["Summary Worker"]
+ Sum --> TS["App Postgres: ThreadSummary"]
+ TS --> MM["Mind Map Builder"]
+ Goal["Project Goal"] --> MM
+ Tree["Tree topology"] --> MM
+ PM["Mem0 project memories
经本地 ref 过滤"] --> MM
+ MM --> View["Read-only Mind Map"]
+```
+
+原因:
+
+- 总结的锁定、stale 和用户编辑状态是本产品语义;
+- 思维导图必须保留 `threadId/sourceRef`;
+- provider 的相似检索不能替代真实 tree 拓扑。
+
+## 12. 建议模块边界
+
+```text
+lib/memory/
+ contracts.ts
+ policy.ts
+ context-compiler.ts
+ provider.ts
+ providers/mem0.ts
+ provider-scope.ts
+ audit.ts
+
+lib/projects/
+ repository.ts
+ goal.ts
+ tree-bindings.ts
+ thread-summary.ts
+ mind-map.ts
+
+app/api/
+ projects/[projectId]/goal/route.ts
+ projects/[projectId]/memories/route.ts
+ projects/[projectId]/mind-map/route.ts
+ threads/[threadId]/summary/route.ts
+ user/memory-settings/route.ts
+```
+
+数据库写入、provider 调用和 UI 类型不要共享一个巨大 `memory.ts` 文件。
+
+## 13. 分阶段落地
+
+### A-1:Project 身份与权威配置
+
+- 新增 `projects` 和 `project_tree_bindings`;
+- 新增 `project_goals`;
+- 用户全局 instruction 和 Project override 先保存在本地;
+- Context Compiler 先只处理确定性设置和目标。
+
+### A-2:接入 provider
+
+- 实现 `ManagedMemoryProvider`;
+- 服务端作用域映射;
+- durable extraction/sync jobs;
+- 本地 provider refs 与审计;
+- 超时降级。
+
+### A-3:控制面与删除核验
+
+- 全局设置和 Project Memory 面板;
+- 候选确认;
+- 来源跳转;
+- 删除状态和清空;
+- provider deletion reconciliation。
+
+### A-4:Thread 总结和思维导图
+
+- Thread summary revision/lock;
+- 只读 mind map;
+- cache key 使用 goal、tree、summary、memory revisions。
+
+### A-5:供应商退出演练
+
+- 导出所有 provider memories;
+- 用本地 ref 校验数量和 scope;
+- 导入替代 provider;
+- 随机抽样验证删除项没有复活;
+- 在 staging 完成一次真实切换。
+
+没有退出演练,“provider 可替换”只是文档声明。
+
+## 14. 适用与不适用
+
+选择 A 的前提:
+
+- 供应商在本项目 fixture 上通过准确性和删除测试;
+- 可以接受数据处理和成本模型;
+- provider SLA 满足要求;
+- 团队愿意让语义检索和记忆演进部分依赖供应商;
+- 已完成出口和删除核验。
+
+不应选择 A 的信号:
+
+- 大量记忆必须作为确定性的业务状态;
+- provider ADD-only/更新语义造成当前值错误;
+- 必须对每次写入提供完整本地证据链;
+- 敏感数据不能发送到外部服务;
+- provider 故障会让产品核心工作流不可用。
+
+## 15. 结论
+
+方案 A 并不是“接一个 SDK 就完成记忆”。成熟版本仍需本地 Project 模型、Policy Engine、provider ref、审计、删除 job、Context Compiler、Thread 总结和思维导图。
+
+它真正节省的是通用的抽取、Embedding、索引和语义检索基础设施。若这些能力在统一评测中稳定可靠,而且供应商边界可接受,A 是合理的产品选择;否则它会变成一套难以调试的第二真相来源。
diff --git a/docs/memory/research/02-option-b-hybrid-kernel.md b/docs/memory/research/02-option-b-hybrid-kernel.md
new file mode 100644
index 0000000..0222978
--- /dev/null
+++ b/docs/memory/research/02-option-b-hybrid-kernel.md
@@ -0,0 +1,766 @@
+# 方案 B:关系型记忆内核 + 可插拔 Memory 引擎
+
+## 1. 方案定义
+
+方案 B 把产品必须准确理解的内容保存在本地 Postgres,把通用的候选抽取、Embedding、语义召回和聚类放在可替换的 `MemoryEngine` 后面。
+
+```mermaid
+flowchart TB
+ UI["Thread Chat / Settings / Project UI"] --> API["Next.js API"]
+ API --> Kernel["Memory Kernel"]
+ Kernel --> PG["Postgres Canonical Store"]
+ Kernel --> Policy["Policy + Deterministic Reducer"]
+ Kernel --> Engine["Pluggable Memory Engine"]
+ Engine --> Hosted["Managed Service
可选"]
+ Engine --> Local["Local Extractor / pgvector
可选"]
+ PG --> Compiler["Context Compiler"]
+ Engine --> Compiler
+ Compiler --> Model["Chat Model"]
+```
+
+权威状态包括:
+
+- 用户全局设置;
+- Project 目标;
+- 已确认的 Project 事实、决策、约束和术语;
+- 事实的当前版本、来源、确认和删除状态;
+- Thread 总结及其锁定状态。
+
+Memory Engine 的结果只能是候选或索引命中,不能直接覆盖权威状态。
+
+## 2. 为什么称为“内核”
+
+Memory Kernel 是一组稳定的领域规则,而不是某个 LLM prompt:
+
+```ts
+interface MemoryKernel {
+ propose(input: ExtractionInput): Promise
+ decide(candidate: MemoryCandidate): PolicyDecision
+ apply(command: MemoryCommand): Promise
+ delete(command: DeleteMemoryCommand): Promise
+ compileContext(input: CompileContextInput): Promise
+}
+```
+
+它保证:
+
+1. 所有作用域都从服务端身份派生;
+2. 模型输出必须通过 schema;
+3. 同一 scope/kind/key 只有一个 active 当前值;
+4. 高权威值不被低权威候选覆盖;
+5. 删除项不会因外部索引延迟再次出现;
+6. 每次状态变化都有来源和审计。
+
+这些规则不依赖使用 Mem0、其他服务还是本地模型。
+
+## 3. 数据模型
+
+### 3.1 Project 与 tree
+
+```text
+projects
+ id
+ owner_user_id
+ title
+ status
+ created_at
+ updated_at
+
+project_tree_bindings
+ project_id
+ tree_id
+ is_primary
+ created_at
+
+project_goals
+ id
+ project_id
+ objective
+ success_criteria_json
+ constraints_json
+ version
+ updated_by
+ updated_at
+```
+
+现有 `branch_trees` 继续保存完整 `ThreadTreeState`。第一阶段一棵 tree 创建一个 Project;未来新增 tree 只增加 binding,不迁移目标和记忆。
+
+### 3.2 权威记忆
+
+```text
+memory_items
+ id
+ owner_user_id
+ scope_type user | project
+ project_id nullable
+ kind
+ key
+ value_json
+ searchable_text
+ authority
+ sensitivity
+ status
+ confidence
+ source_tree_id
+ source_thread_id
+ source_message_id
+ source_quote
+ supersedes_id
+ valid_from
+ valid_to
+ version
+ created_at
+ updated_at
+ deleted_at
+```
+
+约束:
+
+```text
+scope_type = user => project_id IS NULL
+scope_type = project => project_id IS NOT NULL
+```
+
+active current-value 唯一性用 partial unique index 表达:
+
+```sql
+unique (
+ owner_user_id,
+ scope_type,
+ coalesce(project_id, ''),
+ kind,
+ key
+)
+where status = 'active'
+```
+
+具体迁移需要按 PostgreSQL/Drizzle 支持方式实现;这里表达的是数据库不变量,不是可直接执行的最终 migration。
+
+### 3.3 候选、事件和索引
+
+```text
+memory_candidates
+ id
+ owner_user_id
+ project_id
+ proposed_json
+ source_message_ids_json
+ extractor_version
+ rationale_code
+ policy_decision
+ status
+ expires_at
+ created_at
+
+memory_audit_events
+ id
+ memory_id
+ action proposed | accepted | edited | superseded | deleted
+ actor_type user | system | worker
+ actor_id
+ before_json
+ after_json
+ source_message_id
+ created_at
+
+memory_index_entries
+ memory_id
+ provider
+ external_id
+ embedding_model
+ index_version
+ status pending | ready | delete_pending | deleted | error
+ last_error
+ updated_at
+```
+
+### 3.4 Thread 总结
+
+```text
+thread_summaries
+ id
+ project_id
+ tree_id
+ thread_id
+ title
+ summary
+ key_points_json
+ decisions_json
+ open_questions_json
+ source_message_ids_json
+ input_revision
+ generation
+ state draft | locked | stale
+ edited_by
+ created_at
+ updated_at
+```
+
+`(project_id, tree_id, thread_id)` 唯一。
+
+## 4. 写入架构
+
+### 4.1 消息进入系统
+
+聊天成功与记忆写入解耦:
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant CHAT as Chat API
+ participant DB as Postgres
+ participant W as Memory Worker
+ participant E as Memory Engine
+ participant P as Policy Engine
+ participant R as Deterministic Reducer
+
+ U->>CHAT: 发送消息
+ CHAT->>DB: 保存消息 + outbox event
+ CHAT-->>U: 流式回答
+ W->>DB: 领取未处理 event
+ W->>E: propose(messages, scope)
+ E-->>W: schema-valid candidates
+ W->>P: classify risk + authority + action
+ P-->>W: accept / confirm / reject
+ alt 自动接受
+ W->>R: apply candidate
+ R->>DB: 事务写 active/superseded + audit + index job
+ else 等待确认
+ W->>DB: 保存 pending candidate
+ else 拒绝
+ W->>DB: 保存最小拒绝审计
+ end
+```
+
+### 4.2 Outbox
+
+不要依赖 Next.js 请求结束后的内存任务作为生产队列。聊天事务内同时写:
+
+```ts
+interface MemoryOutboxEvent {
+ id: string
+ type:
+ | "messages_appended"
+ | "memory_index_requested"
+ | "memory_delete_requested"
+ | "thread_summary_requested"
+ | "mind_map_invalidated"
+ aggregateId: string
+ payload: unknown
+ idempotencyKey: string
+ attempts: number
+ availableAt: string
+ processedAt?: string
+}
+```
+
+`idempotencyKey` 示例:
+
+```text
+extract:{projectId}:{threadId}:{lastMessageId}:{extractorVersion}
+index:{memoryId}:{version}:{indexVersion}
+summary:{threadId}:{inputRevision}:{summaryVersion}
+delete-index:{memoryId}:{indexVersion}
+```
+
+Worker 可以由独立进程、平台队列或定时领取器实现;领域层只依赖持久 outbox 契约。
+
+## 5. 确定性 Reducer
+
+LLM 只提出候选,Reducer 决定状态:
+
+```ts
+type ReduceAction =
+ | { type: "ADD" }
+ | { type: "SUPERSEDE"; currentMemoryId: string }
+ | { type: "IGNORE"; reason: string }
+ | { type: "REQUIRE_CONFIRMATION"; reason: string }
+
+function decideMutation(
+ current: MemoryItem | null,
+ candidate: MemoryCandidate
+): ReduceAction {
+ // 伪代码:最终规则需要单元测试覆盖
+ if (candidate.proposed.sensitivity === "prohibited") {
+ return { type: "IGNORE", reason: "prohibited" }
+ }
+ if (candidate.proposed.authority === "inferred" && current?.authority) {
+ return {
+ type: "REQUIRE_CONFIRMATION",
+ reason: "inferred-cannot-overwrite-authoritative",
+ }
+ }
+ if (!current) return { type: "ADD" }
+ if (deepEqual(current.value, candidate.proposed.value)) {
+ return { type: "IGNORE", reason: "same-current-value" }
+ }
+ return { type: "SUPERSEDE", currentMemoryId: current.id }
+}
+```
+
+事务更新:
+
+```mermaid
+flowchart TD
+ C["候选"] --> Load["SELECT active by scope/kind/key FOR UPDATE"]
+ Load --> Same{"值相同?"}
+ Same -- 是 --> Ignore["记录 ignore audit"]
+ Same -- 否 --> Auth{"允许覆盖当前 authority?"}
+ Auth -- 否 --> Confirm["等待用户确认"]
+ Auth -- 是 --> Old["旧项 status=superseded
valid_to=now"]
+ Old --> New["插入新 active
supersedes_id=old.id"]
+ New --> Audit["写 audit + outbox"]
+```
+
+## 6. 全局设置与 Project override
+
+指令类记忆不走语义 top-k,而是精确读取:
+
+```ts
+interface EffectiveSetting {
+ key: string
+ value: T
+ source: "current_turn" | "project" | "user_global" | "inferred"
+ memoryId?: string
+}
+```
+
+解析算法:
+
+```text
+1. 当前消息是否明确设置本轮要求;
+2. Project scope 是否有 active instruction/key;
+3. user scope 是否有 active instruction/key;
+4. 是否存在 inferred preference;
+5. 否则使用产品默认值。
+```
+
+示例:
+
+```text
+全局:output_language = zh-CN
+Project:deliverable_language = en-US
+当前消息:这次请先用中文解释
+
+聊天解释:中文
+Project 最终交付物:英文
+```
+
+这里需要区分 `output_language` 和 `deliverable_language`,不能让模型把自然语言冲突临场解释成同一个 key。
+
+## 7. Project 目标
+
+Project Goal 是独立权威对象,不作为普通向量记忆:
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant UI as Goal Editor
+ participant API as Project API
+ participant DB as Postgres
+ participant INV as Invalidation Worker
+
+ U->>UI: 编辑目标/成功标准/约束
+ UI->>API: PUT expectedVersion
+ API->>DB: UPDATE WHERE version=expectedVersion
+ alt 版本匹配
+ DB-->>API: new version
+ API->>INV: 失效 context/mind-map cache
+ API-->>U: 保存成功
+ else 版本冲突
+ DB-->>API: current goal
+ API-->>U: 显示冲突,禁止静默覆盖
+ end
+```
+
+AI 可以调用 `proposeGoalChange`,但工具结果只能创建候选,不直接修改 Goal。
+
+## 8. Thread 总结
+
+### 8.1 生成触发
+
+建议触发条件:
+
+- assistant 回复完成且 Thread 有新增稳定消息;
+- Thread 一段时间无新消息;
+- 用户切出 Thread;
+- 用户主动点击重新生成。
+
+不要每个 token 或每条短消息都重算。
+
+```mermaid
+sequenceDiagram
+ participant CHAT as Chat Store
+ participant O as Outbox
+ participant S as Summary Worker
+ participant DB as Postgres
+ participant UI as Thread UI
+
+ CHAT->>O: thread_summary_requested(inputRevision)
+ S->>O: 领取最新 revision
+ S->>DB: 读取 Thread 消息 + 继承边界
+ S->>S: 生成 summary/key points/decisions/questions
+ S->>DB: compare inputRevision
+ alt 当前是 AI draft 且 revision 未变化
+ S->>DB: upsert draft summary
+ DB-->>UI: 展示新总结
+ else 用户已 locked
+ S->>DB: 保存 update suggestion,不覆盖正文
+ else revision 已过期
+ S->>O: 放弃并让更新事件重试
+ end
+```
+
+### 8.2 锁定规则
+
+| 当前状态 | 新消息到达 | Worker 行为 |
+| --- | --- | --- |
+| draft | 是 | 标 stale,重算后替换 |
+| stale | 是 | 合并到最新 revision,旧 job 放弃 |
+| locked | 是 | 不覆盖;生成“有更新”提示或候选 diff |
+| locked | 用户点重新生成 | 创建新 draft preview,确认后替换 |
+
+### 8.3 总结与记忆的关系
+
+总结中的 `decisions` 不是正式 Project 决策。Summary Worker 可以发出候选:
+
+```text
+ThreadSummary.decisions
+ -> MemoryCandidate(kind=decision, scope=project)
+ -> policy/confirmation
+ -> active Project memory
+```
+
+这条单向边界可以防止总结幻觉污染长期行为。
+
+## 9. 读取与 Context Compiler
+
+```mermaid
+flowchart TD
+ Req["userId/projectId/treeId/threadId/query"] --> Auth["Auth + Project ownership"]
+ Auth --> Global["精确读全局 settings"]
+ Auth --> Goal["读 Project goal"]
+ Auth --> Project["精确读 Project active memories"]
+ Auth --> Lineage["读当前 Thread lineage summaries"]
+ Req --> Need{"需要模糊历史?"}
+ Need -- 是 --> Search["MemoryEngine.search"]
+ Need -- 否 --> Merge
+ Global --> Merge["优先级 + 去重 + 来源 + token budget"]
+ Goal --> Merge
+ Project --> Merge
+ Lineage --> Merge
+ Search --> Merge
+ Merge --> Render["结构化 memory_context"]
+ Render --> Prompt["server-owned system"]
+```
+
+### 9.1 是否需要语义检索
+
+第一版可以使用规则 + 小模型分类:
+
+- “我们之前为什么……”:需要;
+- “另一个分支讨论了什么……”:需要;
+- “按照项目约束继续……”:只需精确 Project memory;
+- 普通概念问题:可能不需要;
+- 当前 Thread 已包含全部相关消息:不需要重复召回。
+
+分类失败时可以少召回,不应默认把全 Project 历史塞满 prompt。
+
+### 9.2 注入预算
+
+示例预算不是固定最终值:
+
+```text
+Project goal + hard constraints 20%
+Effective settings 10%
+Current lineage summaries 30%
+Exact project memories 20%
+Semantic long-tail recall 20%
+```
+
+某类为空时预算可以让给其他类。每条内容必须保留 ID/source,便于回答后解释。
+
+## 10. Memory Engine 的可插拔位置
+
+```ts
+interface MemoryEngine {
+ propose(input: {
+ scope: MemoryScope
+ messages: Array<{ id: string; role: "user" | "assistant"; text: string }>
+ }): Promise
+
+ index(items: MemoryItem[]): Promise
+
+ search(input: {
+ scope: MemoryScope
+ query: string
+ kinds?: MemoryKind[]
+ limit: number
+ }): Promise>
+
+ remove(memoryIds: string[]): Promise
+}
+```
+
+可以有三种实现:
+
+```text
+LocalStructuredEngine
+ propose: 自有 schema extraction
+ index/search: pgvector
+
+Mem0Engine
+ propose/search: Mem0
+ index: 写 provider,并保留 canonical memoryId metadata
+
+NoSemanticEngine
+ propose: 规则或模型
+ search: 空结果
+```
+
+聊天和 UI 只依赖 `MemoryKernel`,不直接知道 provider。
+
+## 11. 删除与一致性
+
+Canonical Store 是读取闸门:
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant API as Memory API
+ participant DB as Canonical Store
+ participant W as Index Worker
+ participant E as Memory Engine
+ participant Cache as Context/MindMap Cache
+
+ U->>API: 删除 memoryId, expectedVersion
+ API->>DB: 事务标 deleted + audit + outbox
+ DB-->>API: committed
+ API-->>U: 删除成功
+ Note over API,DB: 从此所有读取立即过滤 deleted
+ W->>E: remove(memoryId)
+ alt 成功
+ E-->>W: removed
+ W->>DB: index status=deleted
+ else 失败
+ E-->>W: error
+ W->>DB: retry with backoff
+ end
+ W->>Cache: invalidate revisions
+```
+
+如果索引返回已删除 ID,Context Compiler 必须回查 canonical active 状态并丢弃。这条防线也处理索引延迟和 provider 数据复活。
+
+## 12. 思维导图
+
+思维导图不调用 Memory Engine 自由生成结构。先由应用构造受限输入,再允许模型补充边标签:
+
+```mermaid
+flowchart LR
+ G["Goal + criteria + constraints"] --> D["Deterministic Draft"]
+ T["Thread tree topology"] --> D
+ S["Active/locked summaries"] --> D
+ M["Active project decisions/questions"] --> D
+ D --> L["可选 LLM:压缩 label / 判断 supports/blocks"]
+ L --> V["Schema validation"]
+ V --> R["Read-only MindMapView"]
+```
+
+确定性草稿保证:
+
+- 每个 Thread 至少一个 node;
+- `branches_to` 来自真实 parent/children;
+- 所有 node 都有 `sourceRef`;
+- 模型不能生成不存在的 threadId/memoryId;
+- 超过节点上限时按 subtree 做分层折叠。
+
+缓存键:
+
+```text
+hash(
+ goal.version,
+ tree.updatedAt,
+ threadSummary generations,
+ projectMemory revision
+)
+```
+
+## 13. 建议 API
+
+```text
+POST /api/projects
+GET /api/projects/:projectId
+PUT /api/projects/:projectId/goal
+
+GET /api/user/memories
+POST /api/user/memories
+PATCH /api/user/memories/:memoryId
+DELETE /api/user/memories/:memoryId
+
+GET /api/projects/:projectId/memories
+POST /api/projects/:projectId/memories
+PATCH /api/projects/:projectId/memories/:memoryId
+DELETE /api/projects/:projectId/memories/:memoryId
+
+GET /api/projects/:projectId/memory-candidates
+POST /api/memory-candidates/:candidateId/accept
+POST /api/memory-candidates/:candidateId/reject
+
+GET /api/projects/:projectId/threads/:threadId/summary
+PATCH /api/projects/:projectId/threads/:threadId/summary
+POST /api/projects/:projectId/threads/:threadId/summary/regenerate
+
+GET /api/projects/:projectId/mind-map
+```
+
+所有 project route 都从 session 取 userId 并验证 owner,不能只按不可猜 UUID 判断授权。
+
+## 14. 建议代码边界
+
+```text
+lib/memory/
+ contracts.ts
+ repository.ts
+ policy.ts
+ reducer.ts
+ context-compiler.ts
+ prompt-renderer.ts
+ audit.ts
+ outbox.ts
+ engines/
+ engine.ts
+ local.ts
+ mem0.ts
+
+lib/projects/
+ repository.ts
+ goals.ts
+ tree-bindings.ts
+ thread-summaries.ts
+ mind-map.ts
+
+app/api/projects/
+app/api/user/memories/
+app/api/memory-candidates/
+
+constants/
+ memory.ts
+ memory-policy.ts
+```
+
+`constants/` 中保存 token 预算、允许 kind、敏感策略和重试上限;不要把 magic strings 分散到 route 和 worker。
+
+## 15. 分阶段落地
+
+### B-1:Project 身份与 Context Compiler 骨架
+
+- 新增 Project 和 tree binding;
+- tree 创建时自动创建 Project;
+- 目标编辑和版本控制;
+- 全局 output language 与 Project override;
+- chat request 带 `projectId/treeId/threadId`,服务端重新授权;
+- 编译并记录 `CompiledMemoryContext`,暂不自动抽取。
+
+### B-2:权威记忆 CRUD
+
+- memory_items 和 audit;
+- Project Memory 控制面;
+- 来源跳转;
+- optimistic concurrency;
+- 删除与清空;
+- 只允许用户显式创建。
+
+### B-3:候选抽取与 Reducer
+
+- outbox/worker;
+- schema extraction;
+- policy table;
+- auto/confirm/reject;
+- current-value supersede;
+- 失败重试和幂等。
+
+### B-4:Thread 总结
+
+- revision hash;
+- draft/stale/locked;
+- 用户编辑;
+- decision candidates;
+- lineage summary 注入。
+
+### B-5:语义引擎
+
+- 实现统一 `MemoryEngine`;
+- 先接一个 provider 或本地 index;
+- canonical recheck;
+- semantic recall gating;
+- provider failure 降级;
+- 删除 reconciliation。
+
+### B-6:只读思维导图
+
+- deterministic tree nodes;
+- LLM 可选压缩;
+- schema validation;
+- revision cache;
+- sourceRef 跳转。
+
+### B-7:产品化
+
+- 自动记忆开关与临时聊天;
+- 敏感确认;
+- “本次使用的记忆”解释;
+- 数据导出和清空;
+- 监控抽取误报、删除积压和 provider 错误;
+- 回归 fixture 进入 CI。
+
+## 16. 运维指标
+
+```text
+memory_candidate_accept_rate
+memory_false_write_rate
+memory_supersede_conflict_rate
+memory_extraction_job_lag
+memory_index_job_lag
+memory_index_delete_backlog
+memory_context_compile_p50/p95
+memory_context_tokens
+thread_summary_stale_count
+thread_summary_locked_update_count
+provider_error_rate
+```
+
+安全告警:
+
+- tenant scope mismatch 必须为 0;
+- deleted memory 被 Context Compiler 选中必须为 0;
+- prohibited 内容出站必须为 0;
+- assistant-only claim 自动生效必须为 0。
+
+## 17. 优点与代价
+
+优点:
+
+- 产品真相、删除和优先级可验证;
+- Memory 服务可以替换或移除;
+- 全局设置、目标、总结和思维导图具有清晰边界;
+- 外部服务故障不会破坏权威状态;
+- 可以逐步演进到事件图,而不推翻 API。
+
+代价:
+
+- 需要维护 schema、Reducer、outbox 和控制面;
+- provider 与 canonical store 存在索引一致性工作;
+- 团队必须持续维护记忆 taxonomy;
+- 抽取质量仍需要自己的 fixture 和观测。
+
+## 18. 结论
+
+方案 B 的核心不是“自己实现一切”,而是明确区分:
+
+```text
+产品必须准确拥有的状态 -> 自己保存
+通用的语义能力 -> 成熟引擎
+可重新生成的展示 -> 派生视图
+```
+
+对于 Project 驱动、需要全局设置覆盖、目标、Thread 总结和用户控制面的成熟产品,这是当前最平衡的方案。
diff --git a/docs/memory/research/03-option-c-event-graph.md b/docs/memory/research/03-option-c-event-graph.md
new file mode 100644
index 0000000..7af09ce
--- /dev/null
+++ b/docs/memory/research/03-option-c-event-graph.md
@@ -0,0 +1,648 @@
+# 方案 C:事件溯源 + 时序知识图谱
+
+## 1. 方案定义
+
+方案 C 不直接把“当前记忆”作为唯一真相,而是把所有有意义的变化先写入不可变事件日志,再投影出:
+
+- 当前用户设置;
+- 当前 Project 目标;
+- 当前 Project 事实和决策;
+- 时间版本;
+- Thread 总结;
+- 实体和关系图。
+
+```mermaid
+flowchart TB
+ CMD["Commands
用户操作 / AI 候选 / 系统任务"] --> ES["Append-only Event Store"]
+ ES --> P1["Current State Projection"]
+ ES --> P2["Audit Timeline Projection"]
+ ES --> P3["Temporal Knowledge Graph"]
+ ES --> P4["Search Index"]
+ P1 --> CC["Context Compiler"]
+ P3 --> CC
+ P4 --> CC
+ CC --> LLM["Chat Model"]
+ P1 --> UI["Settings / Goal / Memory UI"]
+ P2 --> UI
+ P3 --> MM["Mind Map / Relationship View"]
+```
+
+事件是事实记录;投影可以删除后重建。图不是聊天消息的简单向量化,而是从事件中形成有时间语义的 entity/relation。
+
+## 2. 适用问题
+
+方案 C 擅长回答:
+
+- 某个 Project 决策经历了哪些变化?
+- 这个结论基于哪些分支和证据?
+- 哪些约束阻塞了哪些目标?
+- 用户偏好从何时开始生效?
+- 某次回答在当时看到了哪些版本的记忆?
+- 删除、编辑或修正是由谁、在什么时间发生的?
+
+如果产品只需要“默认用中文”和“记住当前项目”,事件图会明显过度设计。只有关系、时间和可追溯推理成为核心产品能力时,复杂度才有回报。
+
+## 3. 三层真相
+
+```text
+Level 1: Event Store
+ 不可变的业务事件,是最终审计真相
+
+Level 2: Projections
+ 为读取优化的当前状态、历史、搜索和图
+
+Level 3: Derived Views
+ Prompt context、Thread 总结、思维导图
+```
+
+删除是特殊情况:审计事件可以保留“发生过删除”,但 prohibited 或隐私删除的原文必须从 payload、投影和索引中物理清除,事件只留下不含内容的 tombstone。
+
+## 4. 事件模型
+
+```ts
+type MemoryEventType =
+ | "project_created"
+ | "tree_bound_to_project"
+ | "project_goal_set"
+ | "project_goal_revised"
+ | "memory_candidate_proposed"
+ | "memory_candidate_accepted"
+ | "memory_candidate_rejected"
+ | "memory_added"
+ | "memory_superseded"
+ | "memory_deleted"
+ | "thread_summary_generated"
+ | "thread_summary_edited"
+ | "thread_summary_locked"
+ | "thread_summary_marked_stale"
+
+interface DomainEvent {
+ eventId: string
+ eventType: MemoryEventType
+ aggregateType: "user_memory" | "project" | "project_memory" | "thread_summary"
+ aggregateId: string
+ ownerUserId: string
+ projectId?: string
+ aggregateVersion: number
+ occurredAt: string
+ actor: {
+ type: "user" | "system" | "worker"
+ id?: string
+ }
+ correlationId: string
+ causationId?: string
+ payload: T
+ metadata: {
+ schemaVersion: number
+ modelId?: string
+ extractorVersion?: string
+ sourceTreeId?: string
+ sourceThreadId?: string
+ sourceMessageId?: string
+ }
+}
+```
+
+`aggregateVersion` 用于乐观并发。写事件时要求:
+
+```sql
+expected_version = current aggregate version
+```
+
+不匹配就返回冲突,调用方重新加载状态后再决定。
+
+## 5. Command 与 Event 分离
+
+外部输入先成为命令:
+
+```ts
+type MemoryCommand =
+ | {
+ type: "AcceptMemoryCandidate"
+ candidateId: string
+ expectedVersion: number
+ }
+ | {
+ type: "ReviseProjectGoal"
+ projectId: string
+ objective: string
+ successCriteria: string[]
+ constraints: string[]
+ expectedVersion: number
+ }
+ | {
+ type: "DeleteMemory"
+ memoryId: string
+ expectedVersion: number
+ reason: "user_request" | "privacy" | "correction"
+ }
+ | {
+ type: "LockThreadSummary"
+ summaryId: string
+ expectedVersion: number
+ }
+```
+
+Command Handler 完成身份验证、业务校验和 policy 后,才产生事件:
+
+```mermaid
+flowchart LR
+ Input["HTTP / Worker / Tool"] --> Command["Typed Command"]
+ Command --> Auth["Auth + Scope"]
+ Auth --> Load["Load Aggregate"]
+ Load --> Rules["Domain Rules"]
+ Rules --> Events["0..n Domain Events"]
+ Events --> Append["Atomic Append"]
+ Append --> Dispatch["Projection / Worker"]
+```
+
+LLM 永远不能直接 append 任意事件。它只能产生 `ProposeMemoryCandidate` 命令,事件类型和 payload 由应用生成。
+
+## 6. Event Store
+
+最小 PostgreSQL 表:
+
+```text
+memory_domain_events
+ sequence_id bigserial,全局排序
+ event_id uuid unique
+ aggregate_type
+ aggregate_id
+ owner_user_id
+ project_id
+ aggregate_version
+ event_type
+ occurred_at
+ actor_json
+ payload_json
+ metadata_json
+ correlation_id
+ causation_id
+
+unique (aggregate_id, aggregate_version)
+index (owner_user_id, sequence_id)
+index (project_id, sequence_id)
+```
+
+另有:
+
+```text
+projection_checkpoints
+ projection_name
+ last_sequence_id
+ status
+ updated_at
+
+projection_dead_letters
+ event_id
+ projection_name
+ error
+ attempts
+ next_retry_at
+```
+
+Event Store 不是普通日志表:
+
+- 只允许 append;
+- schema version 必须显式;
+- 事件发布与 append 在同一事务,使用 outbox/顺序轮询;
+- 事件 payload 需要数据保留和隐私策略;
+- 备份、恢复和 projection rebuild 必须演练。
+
+## 7. 当前状态投影
+
+为在线查询维护关系型 projection:
+
+```text
+current_user_settings
+current_project_goals
+current_project_memories
+current_thread_summaries
+```
+
+投影器示例:
+
+```ts
+function projectMemory(
+ state: MemoryProjection | null,
+ event: DomainEvent
+): MemoryProjection | null {
+ switch (event.eventType) {
+ case "memory_added":
+ return fromAddedEvent(event)
+ case "memory_superseded":
+ return applySupersede(state, event)
+ case "memory_deleted":
+ return state ? { ...state, status: "deleted" } : null
+ default:
+ return state
+ }
+}
+```
+
+投影必须幂等:
+
+- 记录最后处理的 `sequence_id`;
+- 同一 event 重放不会重复产生记录;
+- 新版本投影使用新 projection name,从头重建并切换;
+- 不能让在线请求依赖每次重放全部事件。
+
+## 8. 时序知识图谱
+
+### 8.1 图模型
+
+```ts
+type EntityKind =
+ | "user"
+ | "project"
+ | "goal"
+ | "thread"
+ | "decision"
+ | "constraint"
+ | "concept"
+ | "artifact"
+
+interface TemporalNode {
+ id: string
+ kind: EntityKind
+ canonicalKey: string
+ properties: Record
+ validFrom: string
+ validTo?: string
+ sourceEventIds: string[]
+}
+
+interface TemporalEdge {
+ id: string
+ fromNodeId: string
+ toNodeId: string
+ relation:
+ | "HAS_GOAL"
+ | "HAS_THREAD"
+ | "BRANCHES_TO"
+ | "DECIDED"
+ | "CONSTRAINS"
+ | "SUPPORTS"
+ | "BLOCKS"
+ | "ANSWERS"
+ | "SUPERSEDES"
+ validFrom: string
+ validTo?: string
+ sourceEventIds: string[]
+ confidence: number
+}
+```
+
+### 8.2 双时间
+
+成熟时间模型可以区分:
+
+- `valid time`:事实在业务世界何时有效;
+- `transaction time`:系统何时知道/记录它。
+
+```text
+用户 7 月 10 日说:
+“我从 7 月 1 日起,Project 输出改用英文。”
+
+valid_from = 7 月 1 日
+recorded_at = 7 月 10 日
+```
+
+若产品没有“回到历史时点回答”的需求,可以先只在事件层保留 `occurred_at` 和 memory `valid_from/valid_to`,不急于引入完整双时间数据库。
+
+### 8.3 图写入
+
+```mermaid
+sequenceDiagram
+ participant ES as Event Store
+ participant GP as Graph Projector
+ participant R as Entity Resolver
+ participant G as Graph Store
+ participant DLQ as Dead Letter
+
+ ES->>GP: memory/goal/summary event
+ GP->>R: 提取受限 entity/relation candidates
+ R->>G: 查 canonicalKey / aliases
+ G-->>R: existing nodes
+ R-->>GP: resolved IDs + confidence
+ alt 达到阈值或确定性关系
+ GP->>G: upsert temporal nodes/edges
+ GP->>ES: checkpoint
+ else 不确定
+ GP->>DLQ: 保存待确认候选
+ end
+```
+
+`Project HAS_THREAD`、`Thread BRANCHES_TO Thread` 来自确定性 tree 数据,不经过 LLM。`Decision SUPPORTS Goal` 等语义边才允许模型提议。
+
+## 9. 写入时序
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant CHAT as Chat API
+ participant ES as Event Store
+ participant EX as Extraction Worker
+ participant P as Policy
+ participant CH as Command Handler
+ participant CP as Current Projection
+ participant GP as Graph Projection
+
+ U->>CHAT: 发送消息
+ CHAT->>ES: append messages_appended reference
+ CHAT-->>U: 流式回答
+ EX->>ES: 读取新消息事件
+ EX->>P: 提取并分类 candidate
+ P->>CH: Accept/RequestConfirmation/Reject command
+ CH->>ES: append candidate + memory events
+ ES-->>CP: 顺序投影当前状态
+ ES-->>GP: 顺序投影实体/关系
+```
+
+如果 projection 落后,在线读取可能暂时看不到刚写事件。解决方式:
+
+- 用户显式编辑返回后,把新 aggregate version 放进 read-your-writes token;
+- API 在投影未追上 token 时短暂等待或从 aggregate events 局部 fold;
+- 后台自动候选允许 eventual consistency。
+
+## 10. 读取时序
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant API as Chat API
+ participant CP as Current Projection
+ participant G as Temporal Graph
+ participant S as Search Index
+ participant C as Context Compiler
+ participant L as Chat Model
+
+ U->>API: Project 内提问
+ API->>CP: goal/settings/current memories/summaries
+ API->>API: 判断是否需要历史/关系推理
+ par 需要关系
+ API->>G: bounded graph query
+ G-->>API: nodes/edges + sourceEventIds
+ and 需要语义
+ API->>S: semantic search
+ S-->>API: memory/event IDs
+ end
+ API->>CP: 回查 active/deleted 状态
+ API->>C: 合并、优先级、预算、来源
+ C->>L: memory context
+ L-->>U: 流式回答
+```
+
+图查询必须 bounded:
+
+- 最大 hop;
+- 最大节点/边数;
+- 允许的 relation;
+- Project scope;
+- 时间范围;
+- 每条结果必须能回到 sourceEvent。
+
+不能让模型生成任意 Cypher/SQL 直接访问全库。
+
+## 11. Thread 总结
+
+Thread 总结仍是派生知识,但在方案 C 中它的所有变化都形成事件:
+
+```mermaid
+stateDiagram-v2
+ [*] --> Draft: thread_summary_generated
+ Draft --> Stale: thread_summary_marked_stale
+ Stale --> Draft: thread_summary_generated
+ Draft --> Locked: thread_summary_locked
+ Locked --> Locked: 新消息只产生 update suggestion
+ Locked --> Draft: 用户确认 regenerate
+```
+
+`thread_summary_edited` 保存用户编辑后的完整新版本或受保护的 diff。不要只保存自然语言“用户修改了总结”,否则 projection 无法重建。
+
+## 12. 思维导图
+
+方案 C 最容易生成关系丰富的思维导图,但仍然必须区分:
+
+```text
+真实 tree 关系 -> BRANCHES_TO,确定性
+Project Goal 结构 -> HAS_GOAL / CONSTRAINS,确定性
+确认的 Project 记忆 -> DECIDED / glossary,权威
+LLM 推断关系 -> SUPPORTS / BLOCKS,带 confidence
+```
+
+```mermaid
+flowchart TD
+ Q["MindMapQuery(projectId, asOf?)"] --> G["读取 bounded subgraph"]
+ G --> Filter["过滤 invalid/deleted/低 confidence"]
+ Filter --> Fold["按 Thread subtree 折叠"]
+ Fold --> Label["生成短 label"]
+ Label --> Validate["sourceRef + DAG/size 校验"]
+ Validate --> View["Read-only MindMapView"]
+```
+
+当前产品不需要 `asOf` 历史导图,可以不暴露 UI,但事件结构允许未来增加。
+
+## 13. 删除与隐私
+
+事件不可变与隐私删除存在天然张力。不能用“事件溯源”作为永不删除用户数据的借口。
+
+推荐 crypto-shredding + redaction:
+
+1. 敏感 payload 使用每用户/Project data key 加密;
+2. 普通删除追加 `memory_deleted`,projection 立即移除;
+3. 隐私删除同时清除 projection、search、graph、cache;
+4. 对原事件 payload 做受控 redaction,或销毁专属 data key;
+5. 保留不含原文的 tombstone:event ID、删除时间、原因代码;
+6. 重建 projection 时,redacted 事件只能产生 deleted 状态,不能恢复内容。
+
+```mermaid
+sequenceDiagram
+ actor U as 用户
+ participant API as Privacy API
+ participant ES as Event Store
+ participant CP as Projections
+ participant G as Graph/Search
+ participant K as Key Store
+
+ U->>API: 隐私删除 memory
+ API->>ES: append memory_deleted tombstone
+ API->>CP: remove current/history content
+ API->>G: remove nodes/edges/index
+ API->>K: destroy scoped content key / redact payload
+ API->>ES: mark redaction verified
+ API-->>U: 删除完成
+```
+
+这比 B 的删除复杂得多,必须在选 C 前验证法规和审计需求是否真的需要事件不可变性。
+
+## 14. 存储选择
+
+### C1. PostgreSQL 事件 + PostgreSQL 投影 + 图表
+
+先用关系表存 node/edge:
+
+```text
+knowledge_nodes
+knowledge_edges
+```
+
+优点是单一数据库、事务和备份简单;有限 hop 可以递归 CTE。适合作为 C 的最小成熟起点。
+
+### C2. PostgreSQL 事件 + 专用 Graph Store
+
+Postgres 是 event source,Neo4j/其他图存储只是 projection。
+
+优点是复杂 traversal 和图工具丰富;缺点是:
+
+- 双系统一致性;
+- 删除核验;
+- 备份和重建;
+- 新运维能力;
+- scope 查询安全。
+
+没有实际图查询压力前,不建议先引入专用图数据库。
+
+## 15. 建议代码边界
+
+```text
+lib/events/
+ contracts.ts
+ event-store.ts
+ command-bus.ts
+ aggregate.ts
+ projector.ts
+ checkpoints.ts
+
+lib/memory/
+ commands.ts
+ events.ts
+ aggregate.ts
+ current-projection.ts
+ context-compiler.ts
+ policy.ts
+
+lib/knowledge-graph/
+ contracts.ts
+ projector.ts
+ entity-resolver.ts
+ repository.ts
+ query.ts
+ mind-map.ts
+
+lib/projects/
+ commands.ts
+ events.ts
+ aggregate.ts
+
+workers/
+ event-projector.ts
+ memory-extractor.ts
+ graph-projector.ts
+ search-projector.ts
+```
+
+各 aggregate 和 projection 分文件,避免把所有 event switch 堆进单文件。
+
+## 16. 分阶段落地
+
+### C-1:事件基础设施
+
+- Event Store、aggregate version 和 append transaction;
+- Project/tree/goal 事件;
+- checkpoint 和 dead letter;
+- projection rebuild 工具;
+- 备份恢复演练。
+
+### C-2:当前状态 projection
+
+- user settings;
+- project goal;
+- project memories;
+- Context Compiler 只读 projection;
+- read-your-writes token。
+
+### C-3:候选与审计
+
+- extraction events;
+- policy command handler;
+- accept/reject/supersede/delete;
+- 完整 correlation/causation;
+- 控制面时间线。
+
+### C-4:Thread summary events
+
+- revision、draft/stale/locked;
+- summary projection;
+- Project 决策候选;
+- rebuild 验证。
+
+### C-5:关系图 projection
+
+- 先投影确定性 Project/Thread/Goal 边;
+- 再引入受限 entity resolver;
+- confidence 和人工确认;
+- bounded graph query;
+- 性能与 scope 安全测试。
+
+### C-6:思维导图
+
+- 从图 projection 读取;
+- subtree folding;
+- sourceRef;
+- 只读缓存;
+- 可选历史 `asOf`。
+
+### C-7:隐私与灾备
+
+- payload encryption/redaction;
+- graph/search deletion reconciliation;
+- projection 全量重建;
+- provider/graph store 丢失恢复;
+- deletion 不复活测试。
+
+## 17. 测试重点
+
+除了方案 B 的共同 fixture,C 必须额外测试:
+
+- 相同 aggregate 并发写只接受一个 version;
+- 事件重复投递不重复投影;
+- 任意 checkpoint 重启后结果一致;
+- 从空 projection 全量重建结果一致;
+- 旧 schema event 经过 upcaster 后可读取;
+- graph edge 都有 sourceEventIds;
+- 删除后重建不会复活;
+- owner/project scope 不会跨图遍历;
+- projection lag 可观测且不会无限增长;
+- redacted event 不含可恢复明文。
+
+## 18. 优点与代价
+
+优点:
+
+- 最完整的演化历史和审计;
+- 当前状态、历史状态和关系视图边界清楚;
+- 投影可按新需求重建;
+- 非常适合未来的因果、时间和关系分析;
+- 思维导图可以直接利用真实关系。
+
+代价:
+
+- Command/Event/Projection 心智负担高;
+- eventual consistency 和 read-your-writes 更难;
+- schema 演进、重放和死信需要长期维护;
+- 隐私删除复杂;
+- 图抽取和 entity resolution 仍会出错;
+- 对当前个人 Project 需求可能是显著过度设计。
+
+## 19. 结论
+
+方案 C 应当由真实查询需求触发,而不是由“成熟产品”四个字触发。以下信号同时出现时值得选择:
+
+- 用户经常跨大量分支追踪决策演化;
+- 产品需要时间点回放或完整审计;
+- 关系推理和图导航成为核心体验;
+- 数据量和查询已经证明关系表 + 简单版本链难以维护;
+- 团队具备事件溯源和图投影的长期运维能力。
+
+如果这些信号尚未出现,方案 B 的 audit event + version chain 已经覆盖大部分成熟产品需要,并保留向 C 演进的路径。
diff --git a/docs/memory/research/04-decision-and-delivery.md b/docs/memory/research/04-decision-and-delivery.md
new file mode 100644
index 0000000..af4af54
--- /dev/null
+++ b/docs/memory/research/04-decision-and-delivery.md
@@ -0,0 +1,393 @@
+# A / B / C 决策方法与统一落地计划
+
+## 1. 不用“成熟度”代替选择标准
+
+三个方案都可以做成成熟产品,区别是复杂度放在哪里:
+
+```text
+A:复杂度主要交给供应商,应用承担集成、一致性和退出成本
+B:复杂度主要在本地域模型,语义能力可外包
+C:复杂度主要在事件、投影、时间和关系基础设施
+```
+
+最终选择需要回答:
+
+- 哪些状态绝不能依赖 LLM 或供应商临场判断?
+- 产品是否真的需要复杂时间和图关系查询?
+- 团队是否愿意长期运维事件投影或外部服务一致性?
+- 删除、导出和供应商退出能否验证?
+- 哪个方案在本项目数据上得到更好的正确性与总拥有成本?
+
+## 2. 必须先交付的共同基础
+
+以下工作不是某个方案特有,因此可以在最终选型前完成:
+
+```mermaid
+flowchart TD
+ P["Project identity
project_id 与 tree_id 分离"] --> G["Project Goal"]
+ P --> S["User Global Settings"]
+ P --> TS["Thread Summary contract"]
+ G --> C["Context Compiler contract"]
+ S --> C
+ TS --> C
+ C --> E["Evaluation fixtures"]
+ E --> A["接 A adapter"]
+ E --> B["接 B kernel"]
+ E --> C2["接 C projection prototype"]
+```
+
+共同基础包括:
+
+1. `projects` 和 `project_tree_bindings`;
+2. Goal 的主目标、成功标准、约束和版本;
+3. 全局 instruction 与 Project override;
+4. 服务端 `ContextCompiler` 接口;
+5. ThreadSummary 的 draft/stale/locked 契约;
+6. 统一 MemoryCandidate、MemoryItem 和 source 类型;
+7. 固定 fixture 与观测格式。
+
+这部分不会迫使团队选择某个 provider,也不会提前引入图数据库。
+
+## 3. 统一对比矩阵
+
+### 3.1 功能与语义
+
+| 维度 | A:托管服务 | B:混合内核 | C:事件图 |
+| --- | --- | --- | --- |
+| 用户全局设置 | 需要本地 current projection | 原生关系表 | 当前状态 projection |
+| Project override | 需要本地规则 | 原生规则 | Command + projection |
+| Project Goal | 本地对象 | 本地对象 | Goal aggregate |
+| Project 事实/决策 | provider 为主,本地 refs | 本地权威 | event + projection |
+| Thread 总结 | 本地派生 | 本地派生 | event + projection |
+| 思维导图 | 本地 tree + provider memories | 本地 tree + summaries + memories | graph projection |
+| 当前值冲突 | 依赖 provider + 本地补充 | deterministic reducer | event fold |
+| 时间历史 | provider 能力决定 | version chain | 原生强项 |
+| 多跳关系 | 弱—中 | 弱—中 | 强 |
+| 用户控制面 | 仍需自建 | 自建 | 自建 |
+
+### 3.2 工程与运维
+
+| 维度 | A | B | C |
+| --- | ---: | ---: | ---: |
+| 领域代码量 | 中 | 高 | 最高 |
+| 基础设施代码量 | 低 | 中 | 最高 |
+| 外部依赖 | 高 | 可选 | 图存储可选 |
+| 故障模式数量 | 中 | 中 | 高 |
+| 数据迁移难度 | provider 导出决定 | 低—中 | 事件 schema 演进高 |
+| 删除工作流 | 跨系统 | canonical + index | event + projection + graph |
+| 本地可调试性 | 中 | 高 | 中;需要事件工具 |
+| 重建能力 | provider 决定 | 可重建索引 | 可重建全部 projection |
+| 团队学习成本 | 中 | 中 | 高 |
+
+### 3.3 产品风险
+
+| 风险 | A | B | C |
+| --- | --- | --- | --- |
+| 供应商锁定 | 高 | 低 | 低 |
+| LLM 候选污染权威状态 | 中—高 | 低 | 低 |
+| 架构过度设计 | 低—中 | 中 | 高 |
+| 关系需求增长后受限 | 高 | 中 | 低 |
+| 隐私删除实现错误 | 中 | 中 | 高 |
+| 在线读延迟 | provider 网络影响 | 可本地优先 | projection 快,图查询需控 |
+
+## 4. 建议加权评分
+
+权重必须由产品目标决定。针对当前已确认的“个人 Project + 全局设置 + 目标 + Thread 总结 + 只读思维导图”,可以先使用:
+
+| 维度 | 权重 |
+| --- | ---: |
+| 当前值、覆盖和删除的确定性 | 25% |
+| 用户控制、来源和可解释性 | 20% |
+| 与 Project/Thread 模型贴合 | 20% |
+| 长期维护和可替换性 | 15% |
+| 语义召回能力 | 10% |
+| 时间/关系扩展能力 | 10% |
+
+初始架构评估,不是实测结果:
+
+| 方案 | 确定性 | 控制面 | 领域贴合 | 维护/替换 | 语义能力 | 时间/关系 | 加权 |
+| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
+| A | 3 | 3 | 3 | 2 | 5 | 3 | 3.05 |
+| B | 5 | 5 | 5 | 4 | 4 | 3 | 4.50 |
+| C | 5 | 5 | 4 | 2 | 4 | 5 | 4.25 |
+
+分值是待验证假设。团队如果把复杂关系查询的权重提高,C 会超过 B;如果把内部研发投入权重降到最低且 provider 通过全部测试,A 会更有吸引力。
+
+## 5. 统一测试对象
+
+### 5.1 Fixture 类型
+
+```ts
+interface MemoryEvaluationCase {
+ id: string
+ ownerUserId: string
+ projectId: string
+ initialGlobalSettings?: Record
+ projectGoal?: {
+ objective: string
+ successCriteria: string[]
+ constraints: string[]
+ }
+ events: Array<{
+ at: string
+ treeId: string
+ threadId: string
+ role: "user" | "assistant"
+ text: string
+ }>
+ operation?:
+ | { type: "edit"; key: string; value: unknown }
+ | { type: "delete"; key: string }
+ | { type: "lock_summary"; threadId: string }
+ query: {
+ at: string
+ treeId: string
+ threadId: string
+ text: string
+ }
+ expected: {
+ effectiveSettings?: Record
+ activeMemories?: Array<{ kind: string; key: string; value: unknown }>
+ inactiveKeys?: string[]
+ requiredSourceMessageIds?: string[]
+ forbiddenText?: string[]
+ }
+}
+```
+
+同一个 fixture adapter 分别驱动 A/B/C,不能为不同方案修改输入。
+
+### 5.2 必测场景
+
+| 类别 | 场景 |
+| --- | --- |
+| 全局设置 | 全局中文,Project 英文,当前 turn 临时中文 |
+| 当前值 | 城市、当前项目、输出格式发生更新 |
+| authority | inferred 值不得覆盖 explicit 值 |
+| assistant 幻觉 | assistant 建议不得自动成为用户事实 |
+| Project 决策 | 一个分支作出决策,另一个分支能够引用 |
+| Goal | AI 提议修改目标但未确认,目标不变化 |
+| Thread 总结 | draft 自动更新;locked 不被覆盖 |
+| 删除 | 删除后在线读取、索引、缓存都不再返回 |
+| 隔离 | user A 与 user B、Project A 与 Project B 不串数据 |
+| 思维导图 | 只出现真实 thread/sourceRef,不生成幽灵节点 |
+| 拒答 | 无来源时承认不知道 |
+| 故障 | provider/worker/graph 超时不阻断聊天 |
+
+## 6. 统一指标
+
+### 6.1 正确性
+
+```text
+candidate precision / recall
+auto-accept precision
+current-value accuracy
+authority precedence accuracy
+retrieval recall@k
+context precision
+answer correctness
+abstention accuracy
+thread-summary factuality
+mind-map source coverage
+```
+
+### 6.2 安全
+
+硬门槛:
+
+```text
+跨用户泄露 = 0
+跨 Project 泄露 = 0
+deleted memory 在线召回 = 0
+assistant-only claim 自动生效 = 0
+prohibited 内容发送到 provider = 0
+无 sourceRef 的导图事实节点 = 0
+```
+
+### 6.3 性能与成本
+
+分别记录:
+
+- Context Compiler p50/p95;
+- provider search p50/p95;
+- graph query p50/p95;
+- 每轮增加的 prompt tokens;
+- 每个 turn 的抽取和 embedding 成本;
+- background job lag;
+- provider 月度成本;
+- 工程维护和 on-call 时间。
+
+不应只比较 API 账单。方案 C 的人员成本可能远高于存储成本,方案 A 的供应商退出成本也必须计入。
+
+## 7. 验收门槛
+
+可以沿用并扩展[阶段 2 评测方案](../02-deep-dives/09-evaluation.md):
+
+```text
+current-value 冲突准确率 >= 95%
+auto-accept precision >= 98%
+retrieval recall@6 >= 90%
+Thread summary 关键事实支持率 >= 95%
+Mind map source coverage = 100%
+在线 Context Compiler p95 < 150ms(不含主模型)
+默认记忆注入预算 <= 主上下文 10%
+聊天成功不依赖后台写入成功 = 100%
+```
+
+对于 A,`<150ms` 可能受外部网络影响,应同时测冷缓存与 provider 降级。对于 C,图查询必须限制 hop 和结果数,不能用无界 traversal 达成召回率。
+
+## 8. 推荐的实施顺序
+
+即使最终选择 A 或 C,也建议按以下顺序落地:
+
+```mermaid
+flowchart LR
+ D1["1. Project 与 tree 身份分离"] --> D2["2. Goal / Settings / Override"]
+ D2 --> D3["3. Context Compiler 契约"]
+ D3 --> D4["4. 显式 Memory CRUD 与审计"]
+ D4 --> D5["5. 删除 / 清空 / 导出"]
+ D5 --> D6["6. Candidate / Policy / Worker"]
+ D6 --> D7["7. Thread Summary"]
+ D6 --> A["A Provider Adapter"]
+ D6 --> B["B Canonical Index"]
+ D6 --> C["C Event Projection Prototype"]
+ D7 --> M["8. Read-only Mind Map"]
+ A --> V["9. 对比、故障与删除演练"]
+ B --> V
+ C --> V
+ M --> V
+```
+
+## 9. 当前仓库的落地点
+
+### 9.1 现有对象
+
+```text
+branch_trees.state
+ -> 完整 ThreadTreeState
+ -> threads[threadId].messages
+ -> artifacts
+```
+
+现有 UI 列是 Thread 的展示,不是稳定数据作用域。Project、Summary 和 Memory 都必须绑定稳定 ID:
+
+```text
+Project -> ProjectTreeBinding -> branch_tree
+branch_tree -> ThreadTreeState.threads[threadId]
+ThreadSummary -> projectId + treeId + threadId
+MemorySource -> treeId + threadId + messageId
+```
+
+### 9.2 Chat 请求
+
+当前 thread-chat body 只有:
+
+```ts
+threadChat: { anchorText: string | null }
+```
+
+落地后客户端还需要发送必要的资源 ID,但服务端必须重新授权:
+
+```ts
+interface ThreadChatContextRef {
+ projectId: string
+ treeId: string
+ threadId: string
+ anchorText: string | null
+}
+```
+
+不能接收客户端拼好的记忆或 system prompt。服务端根据 session、Project ownership 和 Context Compiler 生成。
+
+### 9.3 原有继承上下文
+
+当前 `collectInherited()` 沿 lineage 收集父会话消息。引入记忆后仍保留:
+
+```text
+当前 Thread 原始消息
++ lineage 截断继承
++ Project/Memory Context
+```
+
+ThreadSummary 不能直接替换当前 Thread 的原始对话;它主要用于:
+
+- 跨远端分支回忆;
+- lineage 太长后的压缩;
+- Project 概览;
+- 思维导图。
+
+## 10. 迁移路径
+
+### 从 A 迁移到 B
+
+```mermaid
+flowchart LR
+ Export["导出 provider memories"] --> Map["按本地 refs 映射 scope/kind/key/source"]
+ Map --> Import["导入 canonical memory_items"]
+ Import --> Verify["数量/删除/当前值 fixture"]
+ Verify --> Dual["短期双读,canonical 优先"]
+ Dual --> Cut["关闭 provider 主存储,仅保留 engine"]
+```
+
+如果 A 没有本地 refs 和 source,迁移会非常困难,因此 refs 不是可选优化。
+
+### 从 B 演进到 C
+
+```mermaid
+flowchart LR
+ Audit["B 的 audit/outbox events"] --> Normalize["标准化 DomainEvent schema"]
+ Normalize --> ES["建立 Event Store"]
+ ES --> Rebuild["从 canonical snapshot + audit 建初始 stream"]
+ Rebuild --> Projection["新 projection"]
+ Projection --> Compare["双读比对"]
+ Compare --> Switch["切换 Event Store 为真相"]
+```
+
+B 应从第一天保留足够的审计和版本链,但不需要假装它已经是完整 event sourcing。
+
+### 从 C 简化回 B
+
+保留 current projection 作为 canonical snapshot,停止产生复杂图事件;导出 active memory、goal、summary 和 version。难点是失去历史查询能力,而不是在线功能。
+
+## 11. 决策闸门
+
+### 选择 A,必须满足
+
+- provider 在统一 fixture 上通过所有硬门槛;
+- 删除和导出已经实测;
+- provider 失败时本地设置、Goal 和 Summary 仍可工作;
+- 安全审查允许内容出站;
+- 完成一次 staging 供应商退出演练。
+
+### 选择 B,必须接受
+
+- 团队长期拥有 taxonomy、Policy 和 Reducer;
+- 要维护 durable worker 和索引 reconciliation;
+- 不能把 Memory Engine 当作黑箱真相;
+- 要持续运行自动写入误报评测。
+
+### 选择 C,必须证明
+
+- 已有明确、频繁、可量化的多跳/时间查询;
+- B 的版本链无法合理支持需求;
+- 团队能维护 event schema、projection rebuild 和 graph scope;
+- 隐私删除设计通过审查与恢复演练;
+- 基础设施成本由真实产品价值支撑。
+
+## 12. 建议结论
+
+基于目前已确认的产品边界:
+
+- A 能提供成熟语义能力,但仍需大量本地域代码,而且容易产生双真相;
+- B 把通用能力和产品权威状态拆开,最符合全局设置、Project override、Goal、Thread Summary 与可审计控制面;
+- C 对思维导图和关系演化最强,但当前只读导图可以直接从 tree + summary 生成,不足以证明引入事件图。
+
+因此建议:
+
+```text
+目标架构:B
+对照实现:A
+演进预留:C
+```
+
+这不是让团队只实现 B、不验证其他方案。应当先交付共同基础,再让 A/B 使用相同 fixture 对比;只有真实关系查询出现后,才启动 C 的 projection prototype。
diff --git a/docs/memory/research/README.md b/docs/memory/research/README.md
new file mode 100644
index 0000000..2774de1
--- /dev/null
+++ b/docs/memory/research/README.md
@@ -0,0 +1,99 @@
+# 成熟产品记忆架构:A / B / C 方案研究
+
+> 设计日期:2026-07-26。本文档集讨论的是成品级架构,不以“最快做出 MVP”为决策前提,也不把现有 PDF RAG 当作选择自建的理由。
+
+## 已确认的产品边界
+
+本轮讨论已经确定:
+
+1. 当前一棵 branch tree 对应一个 Project,但 `project_id` 与 `tree_id` 分离,未来允许一个 Project 包含多棵 tree;
+2. Project 目标采用“一个主目标 + 成功标准 + 约束”,不扩展成完整项目管理系统;
+3. 用户全局设置是默认值,Project 明确设置可以覆盖它;
+4. 每个 `Thread`(主线或分支会话)有一份 AI 生成、用户可编辑并可锁定的总结;
+5. 记忆采用分级写入:低风险内容可以自动生效,高影响或敏感内容必须确认;
+6. 思维导图是只读派生视图,不是记忆真相来源;
+7. 当前产品只支持个人 Project,不设计多人权限流。
+
+## 三个方案
+
+| 方案 | 核心主张 | 真相来源 | 最适合 |
+| --- | --- | --- | --- |
+| [A:托管 Memory 服务为主](./01-option-a-managed-memory.md) | 把抽取、存储、更新和检索交给 Mem0 一类服务 | Memory 服务 + 本地 Project 元数据 | 希望把通用记忆基础设施外包,并接受供应商语义 |
+| [B:关系型记忆内核 + 可插拔 Memory 引擎](./02-option-b-hybrid-kernel.md) | 本地保存权威状态,外部引擎只做候选抽取和语义能力 | 本地 Postgres | 要求目标、设置、决策、删除和来源保持确定性 |
+| [C:事件溯源 + 时序知识图谱](./03-option-c-event-graph.md) | 所有变化先写事件,再投影成当前状态和知识图 | 追加式事件日志 | 需要复杂时间推理、关系查询和长期演化分析 |
+
+所有方案共用的产品语义、安全边界和 TypeScript 契约见:
+
+- [00-shared-product-model.md](./00-shared-product-model.md)
+- [04-decision-and-delivery.md](./04-decision-and-delivery.md)
+
+## 共同上下文模型
+
+无论采用哪个存储方案,每次请求都不应把“所有记忆”直接塞给模型,而应先编译出有来源、预算和优先级的上下文:
+
+```mermaid
+flowchart LR
+ Turn["用户当前消息"] --> Compiler["Context Compiler"]
+ Global["用户全局设置"] --> Compiler
+ Goal["Project 目标"] --> Compiler
+ PM["Project 记忆"] --> Compiler
+ TS["当前 Thread 总结与 lineage"] --> Compiler
+ Recall["按需语义召回"] --> Compiler
+ Compiler --> Policy["优先级、权限、相关性、token 预算"]
+ Policy --> Prompt["服务端 System / Context"]
+ Prompt --> Model["聊天模型"]
+```
+
+固定优先级:
+
+```text
+用户当前消息中的明确要求
+ > Project 明确设置
+ > 用户全局明确设置
+ > 系统自动推断的偏好
+```
+
+这个优先级只解决同类指令冲突。系统安全规则永远不允许被任何记忆覆盖。
+
+## “记忆”不是一个表
+
+成熟产品至少要区分以下对象:
+
+| 对象 | 是否权威 | 是否允许模型自动修改 | 典型例子 |
+| --- | --- | --- | --- |
+| 用户全局设置 | 是 | 高影响修改必须确认 | 所有回答默认使用中文 |
+| Project 目标 | 是 | 只能提出修改建议 | 完成记忆系统设计 |
+| Project 记忆 | 是 | 按风险自动或确认 | 已决定使用 Postgres;术语定义 |
+| 记忆候选 | 否 | 是 | 从新对话抽取的可能事实 |
+| Thread 总结 | 派生知识 | 可自动生成;用户编辑后锁定 | 这个分支比较了三种存储方案 |
+| 思维导图 | 派生视图 | 只读生成 | 目标—主题—分支—结论结构 |
+| 原始消息/事件 | 证据 | 不由模型改写 | 用户原话和来源消息 |
+
+把这些内容塞进统一的 `memories` 向量集合,会丢失权限、优先级和生命周期语义。
+
+## 快速比较
+
+| 维度 | A:托管服务 | B:混合内核 | C:事件图 |
+| --- | --- | --- | --- |
+| 初始工程量 | 低—中 | 中 | 高 |
+| 长期产品控制 | 中 | 高 | 最高 |
+| 当前值/覆盖规则 | 依赖供应商 + 本地补丁 | SQL/Reducer 确定 | 事件投影确定 |
+| 时间历史 | 服务能力决定 | 版本链 | 原生强项 |
+| 关系推理 | 一般 | 有限 | 强 |
+| 删除证明 | 需要跨系统核验 | 本地事务 + 索引清理 | 墓碑事件 + 投影清理 |
+| 供应商锁定 | 高 | 低 | 低,但基础设施复杂 |
+| 运维成本 | 低—中 | 中 | 高 |
+| 与 Project 领域模型的贴合度 | 中 | 高 | 高,但可能过度设计 |
+
+## 当前倾向
+
+在不考虑现有 RAG 复用、也不以 MVP 速度为前提时,仍不能用“成熟产品”直接推出方案 C。成熟产品首先需要清晰的数据所有权、可验证行为和可演进边界,不等于选择最复杂的架构。
+
+当前倾向是 **B**:
+
+- 自己拥有用户设置、Project 目标、Project 记忆、Thread 总结和审计;
+- 通过 `MemoryEngine` 接口使用 Mem0 或其他成熟能力;
+- 外部服务故障不会让权威状态丢失;
+- 若未来确实出现多跳关系、复杂时间查询,再从事件表演进到 C。
+
+这只是待评审建议。最终选择应按 [统一验收与交付计划](./04-decision-and-delivery.md) 用相同 fixture 验证,而不是根据架构图复杂度决定。