- 无人值守长任务设计定稿,含效率与可观测补遗 - u-relay wire 协议规格与五项评估结论 - 新能力引入评估纪律五项清单 - todo 销账与狗粮短板分析登记
6.6 KiB
6.6 KiB
跨会话召回最小闭环 · 设计(2026-10-04)
✅ 拍板通过(2026-10-04,3/3 按建议:B1 mention 唯一才启用/B2 软排序/B3 新增 scoped 方法;见 拍板集)
来源:todo.md「P1 跨会话召回最小闭环(项目级记忆+会话结束提炼进知识库,source_project 已有字段未用)」。 勘察结论(2026-10-04 现场核实,以源码为准):
- 会话内自动提炼链已存在:
knowledge_inject.rsextract_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. 目标与非目标
目标(最小闭环):
- 会话结束(AiCompleted 收敛点)自动触发提炼时,把当前会话归属项目写入产出知识的
source_project; - 知识注入(
inject_knowledge_into_prompt)按当前项目优先:同项目知识加权/前置,跨项目知识仍可召回但不淹没; - 形成闭环:会话 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=全局,行为正确);增量自然带项目 |
验收:
- 新会话提及某项目 → 完成后提炼的 candidate
source_project非空且指向该项目; - 同项目新会话注入时该知识(晋升后)排在异项目知识前;
- 无项目会话(source_project=NULL)注入行为与现状逐字一致(回归门);
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)。