暂无主题
对话与后台写入器生成的长期记忆显示在此处。常驻规则单独管理。
打开记忆设置一段对话怎么变成磁盘上的主题文件,写到一半失败会怎样,会话分叉之后靠什么认出哪些已经写过,
每次会话都注入的那一小块记忆归谁维护。
正文在 docs/reference/design/memory/overview.md,第 04 节的完整设计在 written-marker.md,这页只画机制。
别家怎么做的、我们的选择落在哪一格,在 记忆写入:八个框架横向对比。
两个写入入口、五步写入、暂存事务、夜间整理、沉默即成功的返回值契约,
以及第 05 节的常驻块派生视图:core.md是topics/core.md的渲染结果,
预算不再拒事务。
第 04 节的写入游标:跑着的仍是位置游标,分叉之后整条分支漏写, 或者只写下一个从对话中间开始的片段。要走的是在节点上打「已写」标记, 从 head 往回走到第一个有标记的为止。
name is_available initialize shutdown
extract_before_discard:五个方法运行时没有任何调用方。
两个入口,一段写入逻辑。两条路的区别只有 force 这一个开关:一个够了才写,一个剩多少写多少。
一趟里发生的事。阈值只回答"值不值得叫一次模型",不回答"一次写多少",所以跑了一天的会话要走好几趟。
Topic 规范化在清理空章节或 topic 之前,以 INVALID_TOPIC_FORMAT 拒绝实质性的无引用正文。changes、patch、编辑器和 CLI staged edit、writer shell commit 共享该校验;拒绝时保留已提交 Topic/Source 字节、Runtime 状态、revision 和 Git 历史。带引用的原生/旧格式记录及真正空 topic 保持现有规则。
实例级可信安装前回调在校验和stage准备完成后、移动已提交文件之前记录autosave或restore历史。无效编辑不会创建手动checkpoint;回调失败不会安装。有效编辑继续保留准确的编辑前快照和现有checkpoint期限。tools参数或请求正文不能提供该callable。
模型不直接改记忆库。它改的是一份临时拷贝,只有整份验过才被整体安装;验不过整份丢掉,记忆库一个字节没动。
一条会话是一张图,不是一条线。重问一句、重试一次回答、派一个子 agent,都会从中间分叉出一条新分支。 分支上说的话都该进记忆,共享前缀上说的话不该进第二遍。
get_branch 会滤掉 rewind 标记过的轮次、压缩会把摘要节点接在中间,
所以同一条线在两次读取之间也不稳定。advance_cursor 在新序号低于已存序号时抛
cursor cannot move backwards,而往回走恰恰是分叉需要的。
单调计数器和会分叉的对话是两个形状,其中一个修不成另一个。<state>/memory,
和 <state>/sessions 不相交,所以标记不会被读成一次并发的记忆写入。
会被碰到的是会话索引缓存:过期判定看 history 目录的 mtime,一次重写和一次追加一样会让它变,
别的进程重建一次索引,实测 289 个节点 4.2 MB 是 14 到 50 毫秒,而且自限。
真正会增长的是 git:会话目录每轮 git add -A 一次,
字节变过的节点都会成为新 blob,所以标记只打在这一批上,不做全量扫一遍,
否则每轮加上整个会话的重量,一生下来是平方级。
另外两条硬约束:标记不能动 updated_at(闲置看门狗就是按它判断"已处理",
动了等于把会话送回一次强制写入),也不能走"把会话记成已同步"的那条路径。
core.md是每次会话开头都注入的那一小块。它现在是
topics/core.md的渲染结果,2000 token是渲染预算而不是闸门。
改之前它是被写的正本,顶到上限的那一刻就是它最后一次改变的时刻,而且此后每一笔事务都被它拒掉。
GET /api/memory/core同时返回topics/core.md正本、根目录core.md渲染结果和考虑memory.core.inject后的有效注入内容。Core 页默认只读展示有效注入内容,并显示实际 token 数与 2000 token 预算;“完整记录”视图才进入可编辑正本。现有content/size/mtime字段继续表示正本,避免破坏旧调用方。topics/core.md时,把项目专属、过期或重复内容移入普通 Topic 或删除,只保留多数未来会话都需要的规则、偏好与身份事实。| 验收入口 | 要求 |
|---|---|
GET /api/memory/core | 在工作区锁内读取完整快照;兼容返回正本content/size/mtime,并额外返回渲染内容、有效注入内容、注入开关、token 数与预算。 |
| Memory → Core | 默认显示实际注入内容和 token 预算;关闭注入时明确显示空注入。用户可切换到完整正本并沿用现有事务保存,保存期间的新草稿不会被刷新覆盖;所有 Markdown 预览经过共享清洗。 |
| Writer prompt | 不再要求把每个重复过程写成 Topic;Core 不再接收普通项目进度。 |
| Organizer prompt | 显式整理 Core 中的项目过程、重复记录和过期状态;删除后仍由 Git 保留历史。 |
topics/core.md从 31 条、约 15305 token 整理为 3 条、约 429 token,实际注入约 425 token。重复探测、旧工具清单、项目重复检查及失效状态均已从当前视图删除,原内容仍保留在 Memory Git 历史中。自动写入阈值调整为 8000 token,检索只使用整理后的 Topic,不再直接混入 Source 原文。当前修复覆盖特殊 token 计数和保存期间继续编辑的状态边界;复核结论在验证完成后记录。记忆和 agent 运行时之间只有这一个接口。除了 name 之外每个方法都有默认实现,
所以换一套记忆系统只需要写它有事可做的那几个。下图按会话的生命周期摆开,并如实标出哪些还没接线。
<memory-context>,出口不再包一次,
包两层会把里面那层剥掉、只剩一个空壳。二是接口里没有"注册工具"这一项:
带工具的记忆系统按插件接进来,插件本来就能注册命令、技能、MCP、hook 和 agent,再开一条私道只会绕过它。
写入的返回值只有三种,观察者读的就是它。不吭声等于成功,失败要说清是哪一种:会自己好的,和再试也一样的。
节点写入标记和会话边界的活分支枚举已经实现。其余问题分别是互斥分支的语义表达、跨会话spawn关系和压缩摘要去重;这些不属于位置记录本身。
页面采用 OpenProgram 现有“顶部标题栏 + 二级栏 + 内容区”结构。现代化来自清楚的层级、统一的行高、可读的内容宽度和稳定的窄宽度行为,不增加新的数据层或装饰性仪表盘。
四个分区、上下文列表和 Topic 行使用接近的视觉权重;选中前右侧几乎全空。Core 的说明与视图切换在左栏,实际内容和 token 状态在右栏,关系不清楚。
顶部只负责页面身份与设置入口;二级栏先选择分区,再显示该分区的列表或过滤器;右侧标题区集中名称、元数据和操作,正文使用受控阅读宽度。
继续使用 Topics、Timeline、Recent、Core 四个现有入口和现有 API。没有 writer 状态条、Commitments、Scheduler 状态、统计仪表盘、新依赖或新的持久化 UI 状态。
点击左侧四个分区,可检查对应的列表与内容状态。
当前数据:0 Topics / 3 条 Core records / 2 个 Timeline 日期
对话与后台写入器生成的长期记忆显示在此处。常驻规则单独管理。
打开记忆设置时间线仍是按日期排列的派生视图,展示所选日期生成的 Markdown,不添加事件时间或变更元数据。
不得编造工具调用或结果。只报告可用工具实际返回的结果。
直接使用“你”,除非 owner 明确选择其他称呼,否则不得自行添加尊称。
不得编造工具调用或结果。报告实际观察到的结果;没有可用工具返回结果时明确说明。
直接使用“你”,除非 owner 明确选择其他称呼,否则不得自行添加尊称。
讨论记忆与其他操作工作时默认使用中文。
由完整记录生成并插入新的系统提示词。编辑完整记录即可更改此预览。
讨论记忆与其他操作工作时默认使用中文。
直接使用“你”,除非 owner 明确选择其他称呼,否则不得自行添加尊称。
不得编造工具调用或结果。报告实际观察到的结果;没有可用工具返回结果时明确说明。
小于 720px 时不把分区导航、列表和详情纵向堆叠。点击 Topic 进入详情,点击返回键恢复列表。
新的长期主题显示在此处。
讨论记忆与其他操作工作时默认使用中文。
由可编辑常驻记录生成的只读提示词。
长期决策与当前上下文。
分区导航只在列表态出现;详情态占据全部内容宽度并提供明确返回按钮。Core、Timeline 和 Recent 没有文件列表时直接进入内容态。桌面二级栏的展开状态不被窗口宽度改写。
| 区域 | 目标结构 | 避免的问题 |
|---|---|---|
| 顶部标题栏 | 页面标题与 Memory settings | 不显示无法由现有 API 准确计算的总量,也不放 writer 状态、刷新状态或重复说明 |
| 二级栏 | 分区下面的 Folder、Topic、Timeline 年/月/日、Recent 与 Core 入口全部使用同一套 36px 行高、10px 横向内边距、8px 圆角、13px 字号和选中背景。Folder 与 Timeline 年/月必须复用同一个 14×14 线性 ChevronRight SVG、路径和展开旋转规则,并使用相同的数字徽标;Timeline 只用缩进表达层级;Topics 不重复显示由 Core 分区管理的 topics/core.md | 展开控件不再使用过小的实心三角,Timeline 不再显示与 Topics 不同的计数文本 |
| 内容标题 | 名称、类型、路径、修改时间、视图与编辑操作集中在一个 76px 区域 | 删除独立 meta 横条和分散的 Core 状态 |
| 正文 | Preview/Timeline/Recent/Core 使用最大 780px 阅读面;Edit 保持全宽等宽字体编辑器 | 避免超宽文本和大片无结构空白 |
| Markdown 预览 | Topics、Timeline 与 Core 在现有 marked 和 HTML 清洗链路内解释 Memory 使用的 Obsidian Flavored Markdown:命名 [^id] 显示为连续 [1] 上标,定义集中到带分隔线的文末有序列表,支持两个空格缩进的续行与 ^[inline footnote];段末或独立行 ^block-id 只保留不可见锚点,ID 仅接受 Latin 字母、数字和连字符;围栏代码、四空格缩进代码以及反斜杠转义的脚注语法保持字面内容 | 不在阅读正文中暴露内部 evidence ID 和 block ID;不改变可编辑的磁盘原文 |
| 窄宽度 | 分区图标栏 + 列表/详情单页切换 | 不自动折叠桌面栏,不把三块上下堆叠,不允许内容区重叠 |
apps/web/components/memory/index.tsx、parts.tsx 和 memory-page.module.css 维护。Memory 固定格式的阅读转换集中在专用 Markdown helper,复用现有 marked 与 HTML 清洗,不改变编辑内容、API、writer 或磁盘格式。/memory 在桌面宽度显示固定二级栏和完整内容区;Recent 与 Core 不在中间栏重复标题、数量、视图切换或注入状态,Core 的切换和状态只在右侧内容头部出现一次;Recent 右侧标题显示当前记录数与实时 memory.recent.limit,例如 7 / 50 records,不把记录条数误写成 token 预算;顶部不把受截断的 Recent 数量表述为 Memory 总量;右侧 Preview、Timeline、Recent 与 Core 使用居中的文档面,空 Topics 使用独立空状态卡;Topics、Timeline 与 Core 的 Markdown 预览把已定义命名脚注和 inline footnote 按出现顺序显示为 [1] 上标和文末脚注列表,两个空格缩进的续行属于上一条脚注,正文不显示 evidence ID 或合法的段末 block ID,脚注链接、重复引用和未定义引用保持可预测,行内、围栏、列表围栏和四空格缩进代码中的相同文本不参与转换,反斜杠转义引用保持字面内容,任意原文字符不能与内部占位符冲突;Timeline 按年、月、日折叠且不把派生文件重建时间显示成记录时间;Core 明确区分只读 Prompt preview 与可编辑 Source records;Topics 列表不显示 core.md;小于 720px 时不得产生横向重叠,编辑器操作进入独立下一行并可换行。页面不请求或渲染 writer 状态、pending 数、Commitments 或 Scheduler 状态。memory.recent.limit,呈现为“当前记录数 / 配置上限”;Core 的 Prompt preview / Source records 切换、注入状态和 token 用量也只在右侧显示一次。Timeline 年/月/日折叠继续保留。正式 Memory 已通过结构化事务删除 survey test 项目记录;后台 writer 后续仍可按正常行为生成新 Topic。居中文档面、卡片化空状态、76px 单一标题区、780px 阅读宽度和窄窗编辑操作换行已经进入生产代码。Topics 文件行、Timeline 年/月/日入口继续复用 36px 行高、10px 横向内边距、8px 圆角和 13px 字号的侧栏行;Folder、Timeline 年和 Timeline 月也已复用同一个 14×14 线性 ChevronRight 与数字徽标,Timeline 只以缩进表达层级。Memory 专用 Preview 已把命名脚注和 inline footnote 转换成 Obsidian Reading/Publish 风格的数字上标与文末有序列表,并隐藏合法的段末 block ID;编辑器和磁盘原文保持不变。小于 720px 的完整列表/详情互斥仍不在本轮生产实现范围。5f1f06c4 实现文档面、空状态和紧凑上下文栏;1c770a4e 将路径元数据合入 76px 标题区并统一 36px/780px 尺寸;b583833d 删除不准确的顶部总量并补齐窄窗操作换行;7ef440c6 统一 Topics、Timeline、Recent 与 Core 的二级侧栏行。check-memory-status 从父提交 RED 变为 GREEN;Memory 设置、Markdown 清洗、TypeScript、production build、HTML、链接与 diff 检查通过。Memory Markdown 行为测试覆盖命名脚注、重复引用、inline footnote、两个空格续行、行内/围栏/列表围栏/四空格缩进代码字面保留、转义引用、占位冲突、block ID 隐藏与 sanitizer;默认 18100 App 的真实 Topic 显示 [1]–[4] 及带 1.–4. marker 的文末脚注列表,正文不再出现内部 ID。Memory 同时支持模型直接编辑 Topic 文件,以及模型或其他程序调用结构化接口。两种方式不互相替代;区别只在于调用方如何表达修改。所有修改最终进入同一套 Runtime 暂存、校验、派生视图、原子安装和 Git 流程。
topics/**/*.md。新脚注中的每个 Source 写成 [简短关键词](source_ref);模型只决定关键词和选择哪个已有 Source,不能决定最终链接。编辑结束后不能直接覆盖正式 Memory;必须调用 commit_edits,由 Runtime 分配和保留 ID、把 source_ref解析为归档链接、检查 Source 与 block 链接、重建 Timeline/Recent/Relations/Core,再整体安装。Web 与 CLI 的文件编辑走同样的 staged_edit 收口。memory_changes[]。内容操作是 create_record、update_record、delete_record,结构操作是 move_records。Runtime 把这些操作渲染为 Topic Markdown,然后进入与直接编辑相同的安装流程。source是不可变且必须验证的归档身份。Runtime 只根据它生成链接、检查 trust state 和恢复派生视图;显示文本不能改变引用目标。label是本条引用的局部显示文本,由同一次 Memory 写入调用生成,不是 Source 的永久标题。Memory agent 的 prompt 与工具 schema 要求它使用简短、与上下文相关的关键词,并避免 Markdown、URL、Source ID、说话人或时间模板。除必须是非空字符串这一 Markdown 结构要求外,Runtime 不判断关键词质量,不截断或改写模型返回的 label,也不为标签增加模型调用。{"content":"...","sources":[{"source":"provider/thread/message","label":"调研范围"}]}。Topic 文件最终保留标准 Markdown:[调研范围](../../sources/...#source-...)。Parser 从链接目标恢复 Source 身份,不能再把方括号内的显示文本当成 Source ID。| 参考接口 | 可复用约定 | 本项目取舍 |
|---|---|---|
| RFC 6902 JSON Patch | add/remove/replace/move分开;没有语义化 merge/split。 | 保留内容 CRUD 与结构 move 的边界,但目标使用稳定 memory_id,不暴露 Markdown 路径指针。 |
| Notion 子块 | 创建内容时同时指定插入位置;位置属于父级结构。 | create_record.destination同时描述 Topic、标题链与位置。 |
| ProseMirror 变换 | join/split 等高层意图最终分解为可验证的 transaction steps。 | 合并与拆分由模型规划成 update/create/delete/move,不增加含义不唯一的 Runtime 原语。 |
| Mem0 / Letta | 公开 Memory 写接口以记录或 block 的增删改为主。 | 沿用记录级 CRUD,并补充本项目多文件 Topic 所需的批量 move。 |
| 结构化操作 | 必要数据 | Runtime 行为 |
|---|---|---|
create_record | 最终 content、time、非空 sources[{source,label}],以及 destination:topic_path、headings、position;before/after 时再给 anchor_memory_id。模型 schema 用 undated 表示无日期;owner REST 也接受 null。 | 验证 Topic、位置和每个 Source,原样使用 label 作为显示文本,写入规范 Markdown,分配新的 block 与 evidence ID。 |
update_record | 现有 memory_id 加完整的最终 content/time/sources。 | 只修改记录内容,保留 block ID 和位置,以最终值替换该段及其 evidence definition。 |
delete_record | 现有 memory_id。 | 删除对应记录和不再使用的 evidence definition;存在悬空 block 链接时整笔拒绝。 |
move_records | 有序且非空的 memory_ids,以及与创建相同的 destination。 | 按给定顺序批量移动记录;保留 ID、内容、时间、Source 和创建顺序。可在同一章节重排,也可跨章节或 Topic 移动。 |
memory_committed 与 git_committed区分。memory_update 与 POST /api/memory/changes新增 memory_changes。现有整个 Topic 文件的 changes[path/action/content] 和旧 unified diff patch继续兼容;三种 API 表达格式每次只能选择一种,最终都调用 MemoryWorkspace.update。memory_changes[].sources[].source可引用归档中已经存在且可验证的 Source,也可引用同一请求顶层 sources 中的 new-source-*标签;后者先由 Runtime 转换为正式 Source ID。调用方不能提交 provenance、trust state 或 owner 身份。预发布的 source_refs: [string]继续接受,并以原字符串作为兼容显示名;新请求只公开 sources。destination.position只接受 start、end、before、after。before/after 的锚点必须位于目标 Topic 与标题链,且不能属于本次移动集合。批量移动后的顺序严格等于 memory_ids顺序。同一 ID 最多接受一次内容操作和一次 move;delete 与 move 不能同时指向它。update_record、若干 delete_record 和必要的 move_records表达;语义拆分由一次 update、若干 create 和必要的 move 表达。章节或文件的合并、拆分只需批量 move。Runtime 不提供 merge_records、split_record、place_records 或 reorder_records。mem_* 记录继续保留原 ID。结构操作不得拆开共享同一物理段落的多个旧 ID 或规范 block alias;需要移动时必须把该物理段落的全部 ID 按段内原顺序、连续放进同一个 move_records。INVALID_ARGUMENT 原子拒绝整个事务;相同语义仅因相对链接路径不同不视为冲突。create/update/delete输入仍由 Runtime 转换为新名称;模型 schema 和本文档只公开四个正式操作,旧别名不再扩展。memory_status、memory_get 与 memory_update,维护通过 MemoryRef 关联的长期记录;Scheduler service 不隐式双写。记录删除后不保留占位对象或 ID 转发,旧 memory_id 的当前查询返回 NOT_FOUND。外部 token capability 与非 owner 改写范围继续延期。| 验收入口 | 可观测结果 |
|---|---|
直接文件编辑后 commit_edits | [调研范围](source_ref)被规范化为显示相同关键词的真实归档链接;正式 Topic、Timeline、Recent、Relations 和 Core 同时反映模型编辑,且 parser 恢复完整 Source ID;校验失败时正式文件不变。 |
MemoryWorkspace.update(memory_changes=...) | create_record/update_record 接受 sources[{source,label}]并保留调用方提供的 label;旧 source_refs继续工作。四种操作可单独或组合执行;任一项失败时 revision 和所有 Memory 字节不变。 |
memory_update | memory_changes、整个文件 changes 和旧 unified diff 得到相同的事务与返回字段。 |
POST /api/memory/changes | 接收相同的 memory_changes,返回 revision、changed_files、block/evidence ID 与 Git 提交信息。 |
memory_changes、Topic 移动与 Timeline 派生显示;Source archive、检索身份、Preview renderer、数据库和模型调用次数不变。公共入口 RED 覆盖直接文件编辑与 MemoryWorkspace.update(memory_changes=...),受影响验证覆盖 Topic parser、结构化修改、Writer prompt、派生视图、owner REST、HTML 与文档链接。sources[{source,label}],旧 source_refs、pre-v2 provider 归档和文件名含编码字符的 Source 仍可读取和提交;同一 Source 归档在一次 Topic tree 解析中只扫描一次。Runtime 不再对 label 做语义分类、截断或回退。trusted/pending,pending 支撑的 Topic 不进入自动上下文。changes 和旧 unified diff 保持兼容。直接编辑 Topic 与结构化操作共用暂存、格式校验、空结构清理、Timeline/Recent/Core 派生、原子安装和 Git 历史。批量 move、位置与共享物理段落校验、首次结构调整时的 Recent 创建顺序保留、跨 Topic evidence 冲突拒绝均有公共入口回归测试。Scheduler 的内部 Agent 生命周期写入按 Scheduler 设计文档执行;记录删除后旧 memory_id 的当前查询返回 NOT_FOUND,不保留已删除记录或旧 ID 映射。外部 token capability 仍按本节边界延期。topics/core.md渲染;后台writer沿用默认聊天agent,并允许memory.writer.model实时覆盖;未分类异常默认不可重试。memory.backend=none既关闭提示、召回、自动写入、整理和记忆线程,也在创建工作区前拒绝全部CLI memory动词与/api/memory/*路由。writer最近成功、失败分类、retryable判定和待处理数可由工具、CLI及Web查询。一次性memory backfill已实现,只处理未被Topic引用的trusted Source、排除pending并按批复用正常writer事务;组合测试覆盖SessionDB、默认模型解析、managed tools、暂存安装、Topic、marker与watcher状态。正式工作区验收中,23个旧v1 source文件(154个frame)经origin_scope到authority_tier的只读兼容后全部解析;2条待处理消息提交为3个Topic文件、5个唯一block,6个source引用和relation目标均通过校验,两条节点marker写入成功,第二次扫描处理数为0且revision不变。该验收未执行历史backfill,因此152个未引用frame仍存在。仍未实现:越权请求hold队列、按请求方档位过滤读取结果、互斥分支语义、跨会话spawn关系和由head变化事件直接触发写入。写入失败原因码是封闭枚举,受限writer阶段看不到Source归档。