Files
DevFlow/docs/02-架构设计/专项设计/跨会话召回最小闭环-设计-2026-10-04.md
lxy b994cf2c54 文档: 设计定稿与评估纪律登记
- 无人值守长任务设计定稿,含效率与可观测补遗
- u-relay wire 协议规格与五项评估结论
- 新能力引入评估纪律五项清单
- todo 销账与狗粮短板分析登记
2026-10-04 16:27:32 +08:00

6.6 KiB
Raw Permalink Blame History

跨会话召回最小闭环 · 设计(2026-10-04)

✅ 拍板通过(2026-10-04,3/3 按建议:B1 mention 唯一才启用/B2 软排序/B3 新增 scoped 方法;见 拍板集)

来源:todo.md「P1 跨会话召回最小闭环(项目级记忆+会话结束提炼进知识库,source_project 已有字段未用)」。 勘察结论(2026-10-04 现场核实,以源码为准):

  • 会话内自动提炼链已存在:knowledge_inject.rs extract_knowledge_from_conversation(user/assistant 过滤+KNOWLEDGE_EXTRACT_WINDOW 窗口+JSON 提炼+candidate 批量插入)+ EXTRACT_WATERMARKS 水位防重 + conv(cid).knowledge_extracted 哨兵 + trigger_extraction_now 手动强制。
  • source_project 字段在主仓存储层零使用:仅 idea_repo.rs 列定义/INSERT/UPDATE 贯通,idea_repo.rs:949 注释自证「source_project/source_ref 等元字段未被使用却仍随每行读出」。检索侧 search_keywords(idea_repo.rs:699)无项目过滤。
  • 注入点:knowledge_inject.rs:622 inject_knowledge_into_prompt(search_keywords(keywords, include_candidates, limit),无项目维度)。
  • 会话→项目关联:ai_conversations 表无 project_id 列(V4 仅补 archived、V33 pending_approvals、V44 origin、V45 boundary_json);项目归属当前只能靠会话内 mention/boundary_json/@项目 enrichment 间接推断。
  • candidate→published 机制已通(自动晋升 + knowledge_promote 工具 + V43 存量迁移 854 条),跨会话可见性只欠「published 参与检索注入」+「项目维度过滤」两环。

1. 目标与非目标

目标(最小闭环):

  1. 会话结束(AiCompleted 收敛点)自动触发提炼时,把当前会话归属项目写入产出知识的 source_project;
  2. 知识注入(inject_knowledge_into_prompt)按当前项目优先:同项目知识加权/前置,跨项目知识仍可召回但不淹没;
  3. 形成闭环:会话 A 的踩坑 → candidate → 晋升 published → 会话 B(同项目)注入时优先命中。

非目标(本期不做):

  • 不做会话↔项目的强绑定 schema 变更(不加 ai_conversations.project_id 列)——归属用弱推断(见 §3.1),避免迁移风险;
  • 不动 candidate 晋升策略(C3 已拍板项);
  • 不做向量检索(embedding 现状 1033 条全 NULL,另线);
  • 不做 UI 变更(Knowledge 页项目筛选留后续)。

2. 核心取舍:项目归属从哪来

会话本身不存 project_id。归属来源三选,按可靠性排序:

来源 可靠性 实现成本 结论
a. boundary_json(V45 无人值守边界)含项目路径 高(显式绑定) 低(已落库) ✅ 首选:有则用
b. 会话消息中的 @项目 enrichment / mention 记录 中(用户显式引用过) 中(解析消息) ✅ 次选:回退
c. ai_conversations 加 project_id 列 高但侵入 schema 高(迁移+全链) ❌ 本期不做

设计决策 D1(建议拍板):归属采用 a→b 两级弱推断,都不命中则 source_project = NULL(全局知识,不过滤)。这样零 schema 变更,且与「无人值守长任务」边界模型(boundary 已带项目上下文)天然对齐。

提炼函数签名扩展(knowledge_inject.rs 内,不新建文件):

async fn resolve_source_project(db, conv_id) -> Option<String>
  // 1. 读 ai_conversations.boundary_json → 解析 project_path → 反查 projects 表 path 匹配 → id
  // 2. 回退:读最近 N 条消息找 "[项目:xxx]" enrichment 标记 → 反查 → id
  // 3. 皆无 → None

候选插入处(extract_knowledge_from_conversation 内 INSERT 前)把 source_project = resolve_source_project(...) 赋上。

3. 注入侧:项目维度过滤

设计决策 D2:注入不做硬过滤(WHERE source_project = ? 会把同问题跨项目通用知识挡死),改软排序——SQL 层 search_keywords 增加可选 project_id 参数:

ORDER BY (CASE WHEN source_project = ?1 THEN 0 ELSE 1 END), reuse_count DESC, ...
  • 同项目知识排前(limit 截断时优先保留);
  • source_project IS NULL(全局知识)视为第二档但优于异项目:(CASE WHEN source_project = ?1 THEN 0 WHEN source_project IS NULL THEN 1 ELSE 2 END);
  • inject_knowledge_into_prompt 调用前同样走 resolve_source_project(此时是当前活跃会话)取 project_id 传入;
  • knowledge_search 工具(LLM 显式调用)暂不加项目参数——LLM 不知道当前 project_id,靠注入侧自动带。

接口改动面:

  • df-storage/crud/idea_repo.rs:search_keywords(&keywords, include_candidates, limit) → 增参 project_id: Option<&str>(或新方法 search_keywords_scoped,保旧签名给 MCP 等外部调用方,内部转调);
  • knowledge_inject.rs 两个调用点(:423 检索、:622 注入前置);
  • knowledge.rs 工具(tools/knowledge.rs)不动。

4. 闭环验证与防劣化

风险 对策
提炼重复(每会话产相似 candidate 洪水) 已有水位+哨兵;新增:插入前按 title 相似度(search_keywords 查同 title 命中即跳过或 reuse_count+1)——复用现有检索,零新机制
弱推断错误归属(张冠李戴) 只在高置信(boundary 显式路径)时写 source_project;mention 回退可拍板是否启用(D1-b)
注入 token 膨胀 limit 不变,软排序只改顺序不增量
存量 967 条 candidate/907 published 无 source_project 不回填(NULL=全局,行为正确);增量自然带项目

验收:

  1. 新会话提及某项目 → 完成后提炼的 candidate source_project 非空且指向该项目;
  2. 同项目新会话注入时该知识(晋升后)排在异项目知识前;
  3. 无项目会话(source_project=NULL)注入行为与现状逐字一致(回归门);
  4. cargo test -p df-storage -p df-ai 全绿 + search_keywords 新旧签名编译兼容。

5. 拍板点(3 项)

  • 拍板1(D1-b):mention 回退推断是否启用?建议启用但仅当会话中项目 mention ≥1 次且唯一(多项目 mention 不猜,置 NULL)。
  • 拍板2(D2):注入软排序 vs 硬过滤?建议软排序(理由见 §3)。
  • 拍板3:search_keywords 改签名(breaking,内部 crate)vs 新增 scoped 方法?建议新方法(df-mcp 等外部链路零影响)。

6. 工作量估算

约 1 天:resolve_source_project(2h)+ search_keywords_scoped + 两调用点接线(3h)+ 去重跳过(1h)+ 单测(2h)。