Omi 产品原则与通用记忆生命周期:Capture→Act 闭环、INV-MEM 不变式与工程治理实战指南
【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend
本文以仓库根目录的 PRODUCT.md 为骨架,系统解读 Omi 的"北极星"产品原则、面向所有认证账号的通用记忆生命周期,以及如何通过product/invariants/不变式注册表把产品规则变成可机器校验的工程纪律。读完本文,你将掌握 Capture → Understand → Remember → Retrieve → Act 核心闭环的落地形态、INV-MEM-4/INV-MEM-5 的禁止性条款与守卫测试,以及"产品规则如何被 PR 检查强制引用"的整套治理机制。
一、Omi 的产品原则:一份给人和 Agent 的北极星
PRODUCT.md 开篇即说明自身定位:"Short north star for humans and agents"—— 它同时面向人类工程师与 AI Agent,是任何提议新功能或提交改变产品行为的 PR 之前的必读文档。工程规范在 AGENTS.md,已锁定的产品规则在 product/invariants/。这一分层非常关键:原则(Principles)给出方向,不变式(Invariants)给出可执行禁令,工程规范给出编码标准。
1. Memory-first:保护核心闭环
第一条原则直接定义了产品存在的理由:
Capture → Understand → Remember → Retrieve → Act
如果 Omi 未能捕获或保存记忆,其他一切都无关紧要。这一闭环贯穿整个记忆子系统:捕获(Capture)是所有新记忆的入口,理解(Understand)与维护(Maintenance)决定每条待处理记忆的去向,记住(Remember)依赖规范化存储与原子台账,检索(Retrieve)按规范谱系折叠去重,行动(Act)则由任务智能(Task Intelligence)承接。
2. Trust over cleverness:可靠优先于炫技
优先保证捕获、同步、检索的可靠性,而不是堆砌花哨功能。文档给出了两条可判定的"产品缺陷"判据:静默数据丢失(silent data loss)和双重事实来源(dual sources of truth)。这正是后文通用记忆生命周期要消灭的两种状态——历史扁平文档与规范文档并存时,必须只有一个变更权威。
3. One product mind:一个产品心智
所有界面(Chat、Agent、MCP、桌面端、移动端)都只是同一个共享产品体验的输入/输出,而非各自拥有独立历史的"多个产品"。这条原则在 INV-MEM-5 中被固化为"每个认证账号只有一套记忆策略与一套任务智能策略"。
4. Harness over heuristics:马具优于启发式
在整合我们不拥有的界面时,投资于持久化的马具(harnesses)与契约(contracts),而不是脆弱的临时自动化。仓库中可见的对应物是backend/database/下的 read boundary、outbox 等持久化抽象,以及 product/invariants/integrations.md(INV-INT-1:集成行为必须由契约与马具背书)。
5. Taste floor:品味下限
保持品牌一致性;优先删除重复路径,而不是用 feature flag 永久保留它们。这条原则直接指导了收敛 Epic 中"删除旧路径而非让新旧并存"的工程决策。
二、通用记忆生命周期:唯一准入、唯一变更权威
PRODUCT.md 的核心章节定义了一套面向每个认证账号的通用记忆生命周期,配套的可执行设计说明是 INV-MEM-4(Canonical promotion is the sole Long-term authority)与 INV-MEM-5(Universal memory and task authority)。PRODUCT.md 声明二者处于 seven-day 观察期,但从当前仓库可见,两则不变式文档的状态均已标记为locked(详见本文第四节关于注册表机制的讨论)。
2.1 短期记忆是唯一入口
所有新的记忆摄取(conversation、显式用户输入、import、API、plugin、integration)一律先进入短期记忆(Short-term),不存在任何直达长期记忆(Long-term)的通道。这一点在 memory_service.py 中体现为CanonicalMemoryBackend与HistoricalMemoryAdapter的边界设计:历史适配器"deliberately has no create/update/delete methods",物理上只读。
2.2 维护阶段:每条待处理记忆恰好一条合并路径
维护(Maintenance)给每条待处理记忆恰好一个终结路由(consolidation route),四选一:
| 路由 | 含义 |
|---|---|
| promote(晋升) | 唯一的长期记忆准入通道 |
| archive(归档) | 保留但不再参与默认检索 |
| review(复核) | 进入人工/规则复核 |
| reject(拒绝) | 作为负面样本,不再保留 |
禁止对同一待处理项施加多条路径,也禁止把短期记忆的 TTL 过期本身当作终结路由——过期项在规范 apply 记录其处置之前不得被静默隐藏。对应守卫测试可见 backend/tests/unit/test_canonical_consolidation.py(pending work 获得精确的一路由划分,且拒绝的源与近重复负面样本不可晋升)。
2.3 晋升的原子性:receipt + graph assertion
Promote 是唯一进入长期记忆的通道,且只有在一笔原子台账(ledger)交易同时记录以下内容时才会被受理:
- 服务端签发的晋升受理回执(server-authored promotion admission receipt);
- 该记忆的结构化图谱断言(structured graph assertion),按版本栅栏(version-fenced)逐记忆写入。
这排除了三类捷径:通用(generic)晋升、批量/每日(batch/daily)晋升、调用点或用户自行断言的 fast-track 晋升。代码级佐证在 backend/tests/unit/test_atomic_apply.py 与 backend/tests/unit/test_memory_apply_store.py:晋升必须原子写入 item、graph assertion、ledger 状态、操作结果与 outbox 事件;源替换与隐私墓碑复用同一 journal 边界。
2.4 默认检索:按规范谱系折叠
默认检索同时覆盖合格的短期与长期记忆,并按规范谱系(canonical lineage)折叠,使同一条逻辑记忆只出现一次。一个短期别名与其长期规范幸存者不得同时出现在默认列表或搜索结果中(守卫测试 backend/tests/unit/test_ws_m_atom_keyword_index.py)。同时,受限(restricted)记忆内容禁止发送给任何搜索、嵌入或向量提供商(backend/tests/unit/test_canonical_memory_vectors.py)。
2.5 派生视图:可重试,而非权威
关键词、向量、兼容性与共享图谱投影(projections)都是派生视图:它们的更新先提交到 outbox(带规范状态),再从权威记忆源重试。它们自身永远不被当作记忆权威。对应的 outbox 收敛语义由 backend/tests/unit/test_memory_outbox_worker.py 守卫:投影投递必须重载权威状态、修复被回收的投递、重试,并且只有收敛成功后才会确认(ack)。
三、INV-MEM-4 与 INV-MEM-5:把生命周期固化为禁令
3.1 INV-MEM-4:规范晋升是长期记忆的唯一权威
INV-MEM-4(2026-07-27 提出,当前状态 locked)的核心 Statement:
所有新的规范摄取都从短期记忆开始;一次合并决策给每条待处理项恰好一个终结路由;只有原子回执化、图谱背书的晋升才能将项准入长期记忆。默认访问折叠规范谱系,而关键词、向量、兼容性与共享图谱投影仍是可重试的派生视图。
其 MUST NOT 条款完整列出如下(择要保留原文语义):
- 不得将新捕获的 conversation、显式用户、import、API、plugin、integration 输入直接准入 Long-term 或 Archive(历史迁移/回填不是新摄取,由显式迁移策略管辖);
- 不得让待处理短期项没有合并路由,或对同一项施加多于一个 promote/archive/review/reject;
- 不得把短期 TTL 当作终结路由,也不得在规范 apply 记录处置前隐藏过期项;
- 不得在合并路由之外叠加 generic、batch/daily、调用点或用户断言的 fast-track 晋升通道;
- 提交新的短期→长期转换时,必须验证服务端签发的晋升回执,并原子地写入版本栅栏化的逐记忆图谱断言、item、ledger head/commit、操作结果与 outbox 事件;
- 默认列表或搜索结果中不得同时返回短期别名及其长期规范幸存者;
- 不得把关键词/向量/兼容性/共享图谱投影当作权威状态;
- 不得把受限记忆内容发送给搜索、嵌入或向量提供商;
- outbox 背书的投影事件在其幂等写入成功、post-write 权威栅栏协调完成前不得确认。
文档中值得注意的真实性细节:其 Guard tests 一节明确记录了一处覆盖率缺口——test_ws_i_write_convergence.py(1398 行)被提交5724a10084"converge universal memory and task authority" 删除且列表未更新,因此该文件已从守卫清单移除,以让其余守卫可被持续验证。这说明不变式文档的守卫清单不是装饰,而是会被脚本逐一核实的(见第四节)。
3.2 INV-MEM-5:通用记忆与任务权威
INV-MEM-5(2026-08-11 提出,当前状态 locked)解决的是"权威唯一性"问题:
每个认证账号使用同一套规范记忆策略与同一套任务智能策略。历史记忆与 staged-task 文档可通过受限兼容适配器保持物理可读,但 UID allowlist、store origin、客户端与 rollout 元数据永远不得选择不同的产品权威。
其 MUST NOT 关键条款:
- 不得通过固定 UID 列表授予/拒绝记忆、任务智能、目标、工作流、推荐或 Chat-first;
- 不得因为账号 enrolled/dogfood/canonical/legacy/在某个 cohort 中而路由到不同的记忆或任务业务逻辑;
- 通用规范写入启用后,不得创建或修改历史
users/{uid}/memories行(幂等的、由 lazy materialization 或账号删除拥有的 redaction/deletion 除外); - 不得把历史适配器当作 mutation、lifecycle、privacy、graph、vector 或 task 权威;
- 规范状态/墓碑/代际/就绪校验失败时,不得回退到旧写入器或无栅栏的历史读取;
- 不得因规范与历史物理记录并存而把同一逻辑记录暴露两次;
- 不得用文本相似度推断记录身份、变更归属或删除优先级;
- 现有用户使用通用记忆/任务功能前,不得要求批量历史回填、LLM 重处理或重新嵌入;
- 删除 cohort 逻辑时不得移除账号代际、所有权、幂等性、设备、隐私、主动用户选择或全局事故/成本栅栏;
- export、delete-all、账号删除、源删除、搜索、图谱、MCP、开发者工具或已发布客户端 API 都不得绕过通用权威。
代码级证据非常直观:backend/config/memory_rollout.py 中的universal_memory_capabilities(uid, *, account_generation=0)返回的是与 uid 无关的常量策略——legacy_only=False、memory_writes_enabled=True、memory_reads_enabled=True、legacy_reads_authoritative=False。文件头注释明确写道:"Memory and task product authority is universal for authenticated accounts." 遗留字段保留在内部 DTO 中仅为兼容旧调用方,且是常量,绝不从 UID 注册或持久化 rollout 状态机推导。唯一的用户可见产品开关是环境变量MEMORY_ENABLED=on|off,未设置时代码 fail-closed 到off;MEMORY_MODE与MEMORY_V3_GET_ENABLED仅作为旧版本的单次部署别名保留。
INV-MEM-5 的核心守卫测试包括:
| 测试 | 验证点 |
|---|---|
| test_universal_memory_task_authority.py | 任意认证 UID 共享同一记忆/任务决策;静态生产导入与旧写入器所有权受约束 |
| test_memory_service_parity.py | 规范与历史记录共享同一发布的 service contract |
| test_memory_mutation_contract.py | 历史变更通过规范权威物化或墓碑化,且不可复活 |
| test_backend_candidate_capture.py | 通用 Candidate 捕获具有显式 no-drop/no-duplicate 行为 |
| test_account_deletion_projection_fence.py | 删除栅栏同时覆盖两个来源与派生提供商 |
| check_app_client_openapi_compatibility.py | 已发布客户端保持方向兼容 |
四、不变式注册表:产品规则如何被机器校验
product/invariants/README.md 定义了整套治理机制,这是 PRODUCT.md "A product rule without a guard surface is taste advice, not a locked invariant" 一句的落地点。
4.1 locked 与 proposed 的真实语义
| 状态 | 含义 |
|---|---|
| locked | 机器可校验的声明:文档点名的每个 guard 都存在,命名的 guard 脚本已接入 checks manifest,PR 触碰其 path globs 时必须引用其 ID(除非显式 opt out) |
| proposed | 设计说明(design note):对判断有约束力,但对 CI 无约束力;是一个稳定的驻留状态,而非队列 |
状态不控制执行:静态 guard 之所以运行,是因为它接入了 .github/checks-manifest.yaml,与文档状态无关。locked 只控制一件事——PR body 是否必须引用该 ID。
值得注意的演进事实:注册表 README 明确指出旧的"七天浸泡期"已被废除——历史上从未有任何不变式完成过proposed → locked晋升,而有两条不变式在浸泡规则写就六天后直接"born locked"。取而代之的是 .github/scripts/check_product_invariants.py 在每个 PR 上连续审计(而非只在晋升时检查一次)。这与 PRODUCT.md 中"保持 proposed 七天不变才可锁定"的表述存在仓库内部演进时差——以当前注册表 README 与 invariant 文档的实际状态(两则均 locked)为准。
4.2 连续审计在做什么
从 check_product_invariants.py 的源码可以确认审计的三类检查:
- Guard tests 存在性:locked 不变式文档Guard tests一节点名的每个仓库路径必须存在;被点名的
.github/scripts/check_*.py必须被 checks-manifest 中的某个条目运行。若某天一个 guard 测试被删除,当日 PR 就会失败——这就是 PRODUCT.md 所说"规则必须有守卫表面"的机器化。 - 注册表自洽:每个不变式 ID 必须出现在 README 索引表中,且索引行指向的文档必须真实存在;path-glob 条目必须用反引号包裹,否则视为静默忽略并报错(fail-closed:格式漂移不得静默关闭执行)。
- Whole-tree globs 检查:若不变式的 glob 覆盖整个应用树(如
backend/**、desktop/macos/Desktop/Sources/**),强制每个 PR 引用就会让引用变成仪式。此类不变式必须让 guard 承担底线并 opt out 引用(INV-UI-1 模式)。INV-MEM-5 正是如此——其 PR rule 写明"Donotrequire naming",因为其 globs 覆盖两个桌面应用树。
4.3 PR 引用规则与失败输出
当 PR 修改的文件命中某个 locked 不变式的 path globs 时:
- 引用是廉价的:脚本提供
--suggest参数,直接打印 paste-ready 的## Product invariants affected块; - 引用的持久价值不是注意力,而是上下文路由:失败输出会附带命中不变式的 Statement 与 MUST NOT 条款,把规则送到正在编辑这些文件的人/Agent 面前;
- 对于皇冠级不变式,README 建议 PR rule 要求"声明(claim)"而非"令牌(token)",例如"说明本次变更是否保持既有权威,或属于显式迁移例外"——声明可能错误并可被评审,令牌则不能。
4.4 与 Failure-class 注册表的呼应
产品规则的"守卫表面"要求与 product/failure-classes.md 的 guard-artifact ratchet 一脉相承:90 天窗口内同一 failure class 被声明 ≥3 次却没有canonical_prevention_artifact(可复用的守卫表面)即失败。两者的共同哲学是:"意图"不是守卫,只有落地的可复用检查才算数。
五、历史数据兼容:只读适配、惰性迁移、无批量回填
PRODUCT.md 承诺"历史扁平记忆文档通过一个只读兼容适配器保持可读,用户无需批量回填即可保留既有数据",并指出完整的收敛与移除台账在 backend/docs/epics/universal_memory_task_convergence.md。该 Epic 给出了落地细节。
5.1 目标架构
authenticated surface | v Universal MemoryService <--------------------> Universal Task Intelligence | | | one policy/state machine | Candidate -> action_items | | goals/workstreams/recommendations +----------------------+----------------------+ | +---------------+----------------+ | | canonical memory repository historical storage adapter users/{uid}/memory_items users/{uid}/memories read + write authority read-only physical compatibility ledger/evidence/graph/outbox no new writes or business policy(图源:universal_memory_task_convergence.md)
历史读适配的关键决策(与 PRODUCT.md 的"不是第二个变更权威"严格对应):
- 起源定位符:
(uid, "legacy", legacy_id); - 稳定公共 ID:沿用既有 legacy ID;
- 生命周期准入:
grandfathered_long_term——一个显式的历史例外标记,而非伪造的晋升回执; - 缺失可见性/设备身份:由统一的已批准兼容策略处理,而非各调用方自行处理;
- 畸形/加密行:复用既有受保护 legacy reader 与共享畸形行策略;
- 读取时不伪造任何图谱断言或规范证据。
5.2 惰性变更(Lazy mutation)
编辑、复核、重分类、改可见性等对历史记录的变更,执行有界的逐项转换,顺序为:
- 通过受保护适配器读取并校验拥有的 legacy 文档;
- 向规范 apply 提交一个确定性的历史物化操作,尽量保留稳定公共 ID 与原始时间戳/来源;
- 原子地建立规范覆盖/抑制记录(带规范状态);
- 用规范变更逻辑应用所请求的变更;
- 通过规范 outbox 入队 provider/图谱清理;
- 仅作为幂等清理删除/脱敏物理 legacy 文档——清理失败不得让它重新可见。
删除历史记录遵循同一顺序,但提交的是规范隐私墓碑。delete-all 与账号删除重复执行有界、代际栅栏化的扫描,直到两个来源逻辑上均为空。
5.3 统一读取与显式拒绝旧游标
List/read/search 调用方使用同一个仓库;混合账号被读为两条有界索引流,并按一个确定性公共顺序合并。关键约束:旧的签名游标(signed cursor)只服务于 cutover 投影,无法表示通用混合视图,因此游标请求显式失败,直到设计并证明双源复合游标为止——这正符合"不得让工具选择第二权威"的原则。
六、工程流程:Before you build 与 Maintainer operating rule
PRODUCT.md 的"Before you build"清单(整理为可执行流程):
- 大型或模糊的功能先从 GitHub issue 开始(贡献指南见 docs/doc/developer/Contribution.mdx);
- 先查不变式注册表(product/invariants/),确认是否存在适用于你的变更的已锁定规则;
- 没有守卫表面的产品规则只是品味建议,不是锁定不变式——任何新规则必须自带守卫测试与路径 globs;
- 新规则以 proposed 设计说明驻留,行为与守卫保持七天不变后(按注册表现行机制为连续机器审计)方可锁定。
"Maintainer operating rule"则约束维护者本身:当因方向或品味拒绝一个 PR 时,要么引用一个既有不变式 ID,要么在同一周内在 product/invariants/ 打开一条 proposed 不变式。"部落式的 No(tribal 'no')变成成文法律"——拒绝的理由必须可追溯、可复核,而非口头惯例。
七、收敛 Epic 的验证门与移除证明
universal_memory_task_convergence.md 给出了十项验证门,可作为理解 PRODUCT.md 生命周期设计的验收参考:
- 跨面等价:old-only、new-only、混合用户在所有面(/v3、chat、agent、MCP、tools、developer API、Flutter、macOS)返回相同逻辑 ID/顺序/策略;
- 状态机性质:create、source replace、edit、review、visibility、archive、supersede、delete、账号重建均保持合法状态与规范优先级;
- 对抗分页:上千行、相同时间戳、非法/加密文档、冲突、交错写入产生有界读取,无重复无遗漏;
- 隐私测试:删除立即从所有读/搜/图表面消失;outbox/provider 清理在崩溃、重试、租约回收后仍存活;
- 导出预言机:每条存活逻辑记忆导出一次,墓碑内容缺席,任务派生记录遵循已批准导出策略;
- 任务 no-drop/no-duplicate:非 cohort 账号完整走通 extraction → Candidate → acceptance → action item/workstream → recommendation → feedback/outcome → Chat-first,全程不查询记忆 cohort 或图谱;
- 循环交接:规范合并的循环信号可创建 workstream Candidate,但其缺失绝不禁用任务;
- 兼容性:方向性 OpenAPI 检查与已发布 Flutter/macOS 解码 fixture 通过;
- 成本/延迟:每条返回项的 Firestore 读取、p95/p99 延迟、游标大小、vector/LLM 调用、outbox 延迟与死信保持在预 cutover 预算内;
- 回滚排练:携带通用双格式读取器的发布可以全局停止新的规范写入,而不隐藏任何历史或新规范数据。
回滚纪律同样明确:第一个通用读取器发布是回滚底线——规范写入全局化后,回滚到只读旧版会隐藏新数据,被明令禁止;安全回滚是"全局停止新规范摄取,但保留通用读取器与历史适配器"。代码收敛也不授权物理数据销毁,旧集合的删除需要另行测量证据与显式授权。
八、关键文件索引
| 主题 | 路径 |
|---|---|
| 产品北极星原则 | PRODUCT.md |
| 工程规范 | AGENTS.md |
| 不变式注册表说明 | product/invariants/README.md |
| 记忆晋升唯一权威(INV-MEM-4) | product/invariants/memory-promotion-authority.md |
| 通用记忆与任务权威(INV-MEM-5) | product/invariants/universal-memory-task-authority.md |
| 通用收敛 Epic 与实施台账 | backend/docs/epics/universal_memory_task_convergence.md |
| 记忆域模型 | backend/docs/memory/domain_model.md |
| 通用能力实现(MEMORY_ENABLED 开关) | backend/config/memory_rollout.py |
| 规范/历史/适配后端实现 | backend/utils/memory/memory_service.py |
| 不变式连续审计脚本 | .github/scripts/check_product_invariants.py |
| Failure-class 守卫表面制度 | product/failure-classes.md |
理解 Omi 的产品与工程体系,从这套"原则 — 不变式 — 守卫 — 审计"的四层治理开始:原则给方向,不变式给禁令,守卫给证据,审计给持续强制力。任何新功能提案或 PR,都应当在这四层中找到自己的位置。
【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考