归档存储结构补齐:collection+分条化写入/读取API #166

Merged
Yushu merged 1 commit from feat/156-archive-collection-split into main 2026-08-19 05:53:33 +00:00
Member

Closes #156

实现内容

issue #156(ADR-0009"归档拆分",issue #108 grill Q6 拍板"现在实现")实现期核实:arise_archive_collection 自代码写下第一天起(2026-07-08 引入)就只是占位名字,从未被创建、写入、召回过——不是"episodic 与 archive 混同一 collection 需要拆分",而是"archive 这条存储路径要从零建"。ADR-0009"背景"节描述的"归档器把 ~60 条原文拼成单 blob 整块召回"是立项前研究对照的旧 dxkuma 实现缺陷,不是本仓库自己的历史状态。

新增 archive.py 模块(仿 events.py/skill.py 先例):

  • ArchiveCandidate(写入前)/ArchiveEntry(已落盘)——刻意不带 sensitive/source_ctx 字段,理由:产出方结构上永远没有 LLM 分类信号可用(触发条件本身就是 LLM 调用失败),"按 chat_id(+ 知情 sender 私聊)收窄、不存在非敏感→全局可见分支"是这个 collection 的结构性不变量,不是某条记录的可选属性。用一个恒为 True 的字段反而是死列反模式。
  • ArchiveStoragePortwrite_archive(chat_id, entries: list[tuple[ArchiveCandidate, list[float]]]) -> list[ArchiveEntry](对齐 search_events/resolve_similar_skill 的元组配对先例)+ get_archive_entries(chat_id) -> list[ArchiveEntry](对齐 get_knowledge(chat_id) 先例)
  • PersistentStorage(Qdrant 实现)+ SentLog(内存测试实现)两侧补齐,ensure_vector_schema 新增第 4 个 collection
  • 分条粒度严格 1:1(1 条 ArchiveCandidate = 1 个独立 Qdrant point),不做任何跨条聚合

范围调整

本票只交付存储结构(collection + 分条化写入/读取 API),不接入三因子/四因子召回的打分/排序逻辑——在没有真实产出方之前接召回是没有产出方可验证的死代码。真正触发写入的产出方(delta 压缩连续失败→整批转存低置信度记录的降级路径,ADR-0009 2026-07-08 更新节设计、2026-07-30 更新节确认"没有实现",需要新增跨重启失败计数机制)拆到独立的 issue #165。CONTEXT.md/design.md/ADR-0009 已同步标注这一范围调整。

两轴 review 修复

get_archive_entries 单次 scroll(limit=1000) 会在 chat 归档超过 1000 条时静默截断,与 ArchiveStoragePort docstring 承诺的"取回该 chat 已归档的全部条目"矛盾(不像 list_skills()get_skill(id) 式的补偿读法,archive 是唯一读接口)——已改成基于 next_page_offset 的真分页,并补了 1001 条条目的回归测试验证。

测试

全部 1948 个测试通过(含 14 条新增的 archive 存储测试,双参数化覆盖 SentLog/PersistentStorage 两侧实现);ruff check/lint-imports/ty check 干净。新增逻辑(chat_id 隔离、id 唯一性、1:1 分条化、分页读取)均做过 mutation testing 确认能被现有测试捕获。

test_debounce_e2e.py 里一条撤回通知相关用例在全量跑时出现过一次失败,单独重跑和第二次全量重跑均通过——是与本票改动无关的既有时序型 flaky 测试,不是回归。)

Closes #156 ## 实现内容 issue #156(ADR-0009"归档拆分",issue #108 grill Q6 拍板"现在实现")实现期核实:`arise_archive_collection` 自代码写下第一天起(2026-07-08 引入)就只是占位名字,从未被创建、写入、召回过——不是"episodic 与 archive 混同一 collection 需要拆分",而是"archive 这条存储路径要从零建"。ADR-0009"背景"节描述的"归档器把 ~60 条原文拼成单 blob 整块召回"是立项前研究对照的旧 dxkuma 实现缺陷,不是本仓库自己的历史状态。 新增 `archive.py` 模块(仿 `events.py`/`skill.py` 先例): - `ArchiveCandidate`(写入前)/`ArchiveEntry`(已落盘)——刻意不带 `sensitive`/`source_ctx` 字段,理由:产出方结构上永远没有 LLM 分类信号可用(触发条件本身就是 LLM 调用失败),"按 chat_id(+ 知情 sender 私聊)收窄、不存在非敏感→全局可见分支"是这个 collection 的结构性不变量,不是某条记录的可选属性。用一个恒为 `True` 的字段反而是死列反模式。 - `ArchiveStoragePort`:`write_archive(chat_id, entries: list[tuple[ArchiveCandidate, list[float]]]) -> list[ArchiveEntry]`(对齐 `search_events`/`resolve_similar_skill` 的元组配对先例)+ `get_archive_entries(chat_id) -> list[ArchiveEntry]`(对齐 `get_knowledge(chat_id)` 先例) - `PersistentStorage`(Qdrant 实现)+ `SentLog`(内存测试实现)两侧补齐,`ensure_vector_schema` 新增第 4 个 collection - 分条粒度严格 1:1(1 条 `ArchiveCandidate` = 1 个独立 Qdrant point),不做任何跨条聚合 ## 范围调整 本票只交付存储结构(collection + 分条化写入/读取 API),**不接入三因子/四因子召回的打分/排序逻辑**——在没有真实产出方之前接召回是没有产出方可验证的死代码。真正触发写入的产出方(delta 压缩连续失败→整批转存低置信度记录的降级路径,ADR-0009 2026-07-08 更新节设计、2026-07-30 更新节确认"没有实现",需要新增跨重启失败计数机制)拆到独立的 [issue #165](https://code.srcz.one/ProjectKuma/arise/issues/165)。CONTEXT.md/design.md/ADR-0009 已同步标注这一范围调整。 ## 两轴 review 修复 `get_archive_entries` 单次 `scroll(limit=1000)` 会在 chat 归档超过 1000 条时静默截断,与 `ArchiveStoragePort` docstring 承诺的"取回该 chat 已归档的全部条目"矛盾(不像 `list_skills()` 有 `get_skill(id)` 式的补偿读法,archive 是唯一读接口)——已改成基于 `next_page_offset` 的真分页,并补了 1001 条条目的回归测试验证。 ## 测试 全部 1948 个测试通过(含 14 条新增的 archive 存储测试,双参数化覆盖 `SentLog`/`PersistentStorage` 两侧实现);`ruff check`/`lint-imports`/`ty check` 干净。新增逻辑(chat_id 隔离、id 唯一性、1:1 分条化、分页读取)均做过 mutation testing 确认能被现有测试捕获。 (`test_debounce_e2e.py` 里一条撤回通知相关用例在全量跑时出现过一次失败,单独重跑和第二次全量重跑均通过——是与本票改动无关的既有时序型 flaky 测试,不是回归。)
issue #156(ADR-0009"归档拆分",issue #108 grill Q6 拍板):实现期核实
archive collection 从代码写下第一天起就只是占位名字,从未被创建/写入/
召回过——不是"episodic 与 archive 混一起需要拆分",而是"archive 这条
存储路径要从零建"。

本票范围收窄为只建存储结构:新模块 archive.py(ArchiveCandidate/
ArchiveEntry + ArchiveStoragePort Protocol,刻意不带 sensitive/source_ctx
字段——产出方结构上永远没有 LLM 分类信号可用,"按 chat_id 收窄、不存在
非敏感→全局可见分支"是这个 collection 的结构性不变量)+ PersistentStorage/
SentLog 两侧实现 + ensure_vector_schema 接入第4个 collection。分条粒度
严格 1:1,不做任何跨条聚合。不接入三因子/四因子召回打分逻辑。

真正触发写入的产出方(delta压缩连续失败→整批转存降级路径,ADR-0009
07-08节设计、07-30节确认"没有实现")已拆到独立的 issue #165。

两轴 review 额外修复:get_archive_entries 单次 scroll(limit=1000) 会在
chat 归档超过1000条时静默截断,与 docstring 承诺的"全部条目"矛盾,改成
真分页。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Yushu merged commit 75a595e215 into main 2026-08-19 05:53:33 +00:00
Yushu deleted branch feat/156-archive-collection-split 2026-08-19 05:53:33 +00:00
Sign in to join this conversation.
No description provided.