Friend 后端 Memory Continuity Gauntlet Gates:canonical 记忆流水线的六级连续性验证关卡
2026/9/15 19:42:06 网站建设 项目流程

Friend 后端 Memory Continuity Gauntlet Gates:canonical 记忆流水线的六级连续性验证关卡

【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend

导读

本文系统讲解 Friend(omi)开源仓库后端记忆系统在**记忆连续性(memory continuity)**维度上设立的一整套"关卡(Gauntlet Gates)"验证体系。它回答一个核心工程问题:当 canonical memory 从采集(capture)→ 合并(consolidate)→ 晋升(promote)→ 读取(read)整条流水线被重构、或上线新记忆版本(memory v3)时,如何用可重复、可留痕、可区分的多层测试来证明"记忆不会断、不会漏、不会串"?读完本文,你将掌握六类关卡的各自覆盖范围与边界、它们为何不能互相替代、以及如何在 CI 与本地环境实际运行它们(含全部命令与关键源码依据)。

一、背景:为什么需要"连续性关卡"

Friend 后端的记忆系统(canonical memory)不是单条 SQL 语句,而是一条跨 Firestore、向量库(Typesense)、短期晋升 worker、多种读取 surface 的持久化流水线。任何一环的回归——例如 projection 失败时回退读取了遗留格式记忆(legacy bleed)、晋升绕过了 durable outbox、archive 记忆被默认读取误曝——都会直接破坏用户记忆的连续性隐私边界

为此,仓库在 backend/docs/memory-continuity-gates.md(本文关联文档,原先是backend/AGENTS.md的一部分,因体积门槛独立成文)中定义了一套"Memory Continuity Gauntlet Gates"。文档开头特别强调了两条不可混淆的规则:

  • 绿色的 live gauntlet 并不证明 hermetic 流水线不变量成立
  • hermetic 测试并不证明部署后端的连续性成立

也就是说,这套体系刻意由互补而非互替的多个关卡构成。任何一个关卡通过,都不能替代另一个关卡;任何人在宣称"canonical-memory 上线关卡已满足"之前,都必须先读完这份文档。

二、六级关卡总览:覆盖范围与"不覆盖"边界

原文档用一张表格界定了全部关卡,这是整套体系的地图,完整继承如下:

关卡覆盖内容覆盖内容
Hermetic pipeline E2E(testing/e2e/test_canonical_memory_pipeline.py)capture→consolidate→promote→read、archive 排除在默认读取之外、surface 默认访问矩阵、projection fail-closed 且无 legacy bleed部署后的 revision 身份、生产 IAM/索引差异、真实 LLM consolidation
Listen/Pusher durable finalization gauntlet(testing/listen_pusher_stack)真实持久化会话处理转换、fanout lease、MEMORY_ENABLED解析器/围栏、围栏后的 provider leaf、以及 unset-fence 负面路径provider 输出质量、生产配置身份、inline 协议场景中被 mock 的记忆路径
Gauntlet--self-check必需文件、canonical_memory_pipelineworkflow 注册、suite/nonce wiring(在 memory-continuity-gauntlet.py 中)任何记忆写入或 HTTP 探测
Live gauntlet(memory-continuity-gauntlet.sh,需ADMIN_KEY+ 可达后端)在运行中后端上按 suite 执行结构化/v3/memories探测Gate 2 完整合成矩阵、Gate 3 生产激活
Gate 2 dev-cloud proofv3_dev_cloud_proof.py+ 已部署分支 revision)多用户合成矩阵、索引、IAM、auth、dev-cloud 回滚本地 hermetic fakes;不等于生产激活
Gate 3 production proof(按docs/rollout/memory-v3-proof-order.md顺序执行)Gate 2 GO 之后的生产特有差异 + 独立评审替代 hermetic pipeline E2E 或 gauntlet self-check

CI 只运行python3 backend/scripts/memory-continuity-gauntlet.py --self-check。Live suites 在凭据/后端不可用时如实记录NOT_RUN——绝不伪造GO

三、关卡一:Hermetic pipeline E2E——闭环流水线与默认访问矩阵

这是整套体系中最核心的"不变量证明层",位于 backend/testing/e2e/test_canonical_memory_pipeline.py。它通过 fakes(fakes.firestorefakes.redisfakes.storage)与FastAPI TestClient在进程内构建隔离后端,跑通真实业务代码路径。

3.1 capture → consolidate → promote → read 闭环

主测试test_capture_consolidate_promote_read_archive_excluded_vectors_and_delete_outbox(L223)按真实时序验证:

  1. Capture:通过POST /v1/conversations/{id}/reprocess会话入口(monkeypatch 掉process_conversation后走真实的write_canonical_extraction_memory),写入一条 short-term 记忆,断言tier == "short_term"status == "active"(L233-L237)。
  2. Consolidate/Promote:调用真实的run_canonical_short_term_maintenance(promotion worker 主函数),以脚本化 LLM(_scripted_consolidation_llm)驱动合并决策,断言promoted_count == 1tier == "long_term"graph_ready is True且生成了graph_assertion_id(L239-L255)。
  3. Vector outbox 纪律:晋升后立即检查向量存储,断言provider_id not in vectors("canonical apply 不得绕过 durable outbox"),随后显式 drain 真实 canonical outbox(_drain_canonical_outbox),断言产生vector_upsertaction、向量以用户隔离的canonical_memory_provider_id出现、metadata 携带memory_layer == "long_term"(L257-L280)。
  4. ReadMemoryService.read默认读取能看到晋升后的记忆;archive 条目与"short-term 之外的默认读取"保持正确的可见性集合。
  5. Delete 与隐私收据delete_canonical_memory删除条目后必须留下memory_deletion_receipt.v2收据且收据中不得包含 memory_id 本身(防止隐私元数据残留),向量同步清除(L334-L355)。

3.2 Fail-closed:projection 失败时不得回退泄漏 legacy 记忆

第二个测试test_universal_repository_failure_fails_closed_without_historical_bleed(L357)在 Fake Firestore 中预先种入一条遗留格式记忆(legacy-must-not-bleed),然后让MemoryService.read503 infrastructure_failure:断言 API 返回 503、错误 detail 透传、响应体中绝不出现 legacy 记忆内容。这验证了"fail-closed 优先于降级兜底"的设计。

3.3 Surface 默认访问矩阵

TestSurfaceDefaultAccessMatrix(L433)用参数化用例覆盖六种读取 surface:chat_textchat_listdeveloperagent_toolsmcpproduct_search(SURFACE_MATRIX_CASES,L423-L430)。每种 surface 都断言:archive 条目被排除、可见条目可检索;同时验证无授权 consumer 时read_decision == DENY_MEMORY(rollout 读门控拒绝)。这是"archive 不参与默认读取"这一产品不变量在全部读取入口上的矩阵式证明。

四、关卡二:Listen/Pusher durable finalization gauntlet——持久化会话处理转换

Hermetic E2E 覆盖的是记忆流水线本身,而记忆的采集源头——listen WebSocket 会话 → pusher → finalization worker 的持久化链路——由独立的 backend/testing/listen_pusher_stack 本地 gauntlet 负责。运行方式(见其 README):

backend/testing/listen_pusher_stack/run.sh --keep

4.1 真实栈 + 严格回环

该 harness 启动隔离 Redis、本地 ASGI 进程与 Firestore emulator(自动挑选独立端口),跑的是生产 listen 运行时main:app、真实 pusher router、二进制帧 101/102/103/104/201、真实 Cloud Tasks finalization handler。子进程环境白名单化:私有空HOME、无 provider 凭据、拒绝非 loopback Firestore 端点。

4.2MEMORY_ENABLED围栏与 post-fence leaf

这是该关卡与记忆连续性直接相关的关键设计:

  • 强制 Cloud Tasks 路径设置MEMORY_ENABLED=on,执行生产解析器和围栏(fence),然后证明围栏之后的 provider leaf 真实运行;
  • 负面场景 unset 该标志,证明 durable job 保持可重试、post-fence leaf 不运行、仅发出无标识符的 boundedmemory_fence诊断。

也就是说:记忆写入在 finalization 链路中被一个显式的开关围栏(fence)守护,开关关闭时宁可让任务挂起重试,也不静默写记忆。Cloud Tasks 入口只替换 provider-side leaf(LLM 摘要生成、MemoryService.ensure_canonical_mutation_ready之后的 canonical memory 工作、外部集成投递等),持久化与内存安全围栏之下全是真实代码。

4.3 十一类场景

README 列出 11 个场景(含 #9960 的 pending-finalization 恢复路径、worker 两attempt 预算耗尽后原子 dead-letter 并标记会话failed/discardedRECORDING_SESSION_MODE=enforce下操作级故障注入等)。其定位是"确定性本地故障测试",明确不测真实 Parakeet 推理、LLM/向量质量、GCS 与外部集成投递。

五、关卡三与四:Gauntlet--self-check与 Live gauntlet

这两个关卡都由统一的驱动脚本 backend/scripts/memory-continuity-gauntlet.py 实现,shell 包装 backend/scripts/memory-continuity-gauntlet.sh 只负责exec python3透传参数(脚本头部注释里写着全部用法)。

5.1--self-check:CI 的看门狗

python3 backend/scripts/memory-continuity-gauntlet.py --self-check

self_check()(L159)不做任何记忆写入或 HTTP 探测,只做静态结构性校验:

  1. 必需文件存在REQUIRED_SELF_CHECK_PATHS(L31-L37)包括驱动 py/sh、E2E 测试、backend/tests/unit/test_inv_mem_1_guard.pybackend/testing/workflow_contracts.json
  2. workflow 注册canonical_memory_pipelineworkflow 必须存在于workflow_contracts.json且注册testing/e2e/test_canonical_memory_pipeline.py(L186-L192;contract 定义见 backend/testing/workflow_contracts.json);
  3. suite/nonce wiring:驱动源码必须包含六个 suite token(capture/promote/recall/archive/surfaces/resilience)、三种 nonce kind(CAPTURE/PROMOTE/ARCHIVE)以及--self-check自身标志(L173-L184)。

这正是文档表格中"--self-check只证明文件与 wiring、不证明任何记忆行为"的来源——它是一道成本极低的 CI 闸门,防止有人在删文件/改 suite 名时悄悄破坏整套 gauntlet。

5.2 六个 suite 与 nonce 证据

六类 suite(SUITE_NAMES,L39):capturepromoterecallarchivesurfacesresilience;别名pipeline(capture→archive)与all(L40-L43)。每次运行生成三种随机 nonce(MEMGAUNTLET-{run_id}-{hex}-{KIND},L54-L57),写入被写记忆的 content 中——后续 suite 用该 nonce 作为检索 token 验证"这条记忆确实经历了 capture→promote→recall",从而把"连续性"变成可断言、可留痕的事实。

Hermetic 模式(默认,无凭据时自动回退)复用 E2E 的 fakes 与 TestClient,跑完每个 suite 的真实业务函数;surfaces在五个 surface(chat_text/chat_list/developer/agent_tools/product_search)上逐一断言 archive nonce 不出现、可见记忆出现;resilienceMemoryService.read抛 503 后断言/v3/memoriesfail-closed 且 legacy 不泄漏(L655-L686)。

5.3 Live gauntlet:运行中后端的结构化探测

# 本地起后端后: ./backend/scripts/memory-continuity-gauntlet.sh --suite capture,promote,recall MEM_GAUNTLET_API_URL=http://127.0.0.1:8080 ./backend/scripts/memory-continuity-gauntlet.sh --live

模式决策逻辑(resolve_mode,L353-L359):

  • --live强制要求 live 模式:live_env_probe(L203-L219)检查ADMIN_KEY存在、GET /health可达;
  • 未传--live时,若MEM_GAUNTLET_PREFER_LIVE=1且环境可用则走 live,否则回落 hermetic;
  • MEM_GAUNTLET_FORCE_HERMETIC=1强制 hermetic。

Live suite 只做结构化探测:用Authorization: Bearer {ADMIN_KEY}{uid}请求GET /v3/memories,仅接受200/403/503状态(L688-L695),并摘要响应体(items_countdetail)写入证据。它不做记忆写入——这正是文档说"live gauntlet 覆盖结构探测、不覆盖完整合成矩阵"的含义。当 live 不可用时,suite 记录NOT_RUN,且 manifest 的passed只有在所选 suite 中至少一个达到 GO 且无失败时才为 true(L771-L774),从机制上杜绝"全部 NOT_RUN 却被标绿"。

5.4 证据包(evidence bundle)

每次运行在backend/.harness/memory-continuity-gauntlet/{run_id}/下生成manifest.json,记录 run_id、git SHA、模式与原因、nonce、每 suite 的 status/mode/steps/failures/warnings。通过时创建latest-green符号链接并追加INDEX.md(L88-L103);超过 7 天的非 green 运行目录会被自动清理(PRUNE_ABORTED_BUNDLE_DAYS = 7)。所有证据以时间戳 + git SHA 留痕,保证"谁在哪个 revision 上、以什么模式、断言了什么"全程可追溯。

六、关卡五与六:Gate 2 dev-cloud proof 与 Gate 3 production proof

前四道关卡是工程闸门,后两道是发布决策关卡

  • Gate 2 dev-cloud proof:在 dev-cloud 上以多用户合成矩阵验证完整记忆 v3 语义,覆盖索引、IAM、auth,并具备回滚预案。它证明"这套东西在类生产基础设施上、多用户并发下也成立"——这是本地 hermetic fakes 无法提供的证据;但 dev-cloud 不等于生产激活。
  • Gate 3 production proof:仅在 Gate 2 判定 GO 之后,针对生产特有差异(真实流量、真实 IAM/索引、真实配置)做验证,并需要独立评审。它同样不能替代 hermetic pipeline E2E 或 gauntlet self-check——三道关卡各司其职。

七、实操速查与设计原则

7.1 命令速查

目的命令
CI 静态看门狗python3 backend/scripts/memory-continuity-gauntlet.py --self-check
默认全 suite(hermetic 回退)./backend/scripts/memory-continuity-gauntlet.sh
选择 suite./backend/scripts/memory-continuity-gauntlet.sh --suite capture,promote,recall
强制 live 探测MEM_GAUNTLET_API_URL=http://127.0.0.1:8080 ./backend/scripts/memory-continuity-gauntlet.sh --live
强制 hermeticMEM_GAUNTLET_FORCE_HERMETIC=1 ./backend/scripts/memory-continuity-gauntlet.sh
listen→pusher finalization gauntletbackend/testing/listen_pusher_stack/run.sh --keep
查看证据backend/.harness/memory-continuity-gauntlet/INDEX.md与各 run 的manifest.json

7.2 核心设计原则

  1. 关卡互补、绝不互替:green live gauntlet 不证明 hermetic 不变量,hermetic 测试不证明部署连续性,Gate 2 不证明生产激活——这是文档第一原则;
  2. NOT_RUN 优于假 GO:live 凭据缺失时如实记录NOT_RUN,且"全部 NOT_RUN"永远不能算通过;
  3. nonce 证据链:随机 nonce 贯穿 capture/promote/archive,把"连续性"变成可检索、可断言、可留痕的客观事实;
  4. fail-closed 纪律:projection 失败返回 503 而非降级读取 legacy;记忆围栏关闭时任务挂起重试而非静默写入;
  5. 证据可追溯:每次运行记录 run_id + git SHA + 模式 + 断言步骤,green 运行建立索引,aborted bundle 定期清理。

这套"六关卡 + 两级发布证明"的结构,为依赖长期记忆的产品提供了一个可复制、可留痕、抗回归的质量模型:任何宣称"canonical-memory 上线关卡已满足"的改动,都必须逐关对照本体系举证,缺一不可。

【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询