Phase 2: 叙事发现引擎 — 审查与遗留问题
审查日期:2026-05-26 状态:核心功能已完成,以下为遗留改进项
审查结果
Phase 2 实现了 spec 04-engine-design.md 中 Narrative Discovery 引擎的全部 4 个子系统:
- 叙事提取 — LLM 结构化输出 + Pydantic 校验
- 语义匹配 — pgvector 三档阈值(0.85/0.6)
- 状态机 — candidate→active→fading→archived + rejected
- 合并/分裂检测 — 语义聚类 + 情绪标准差
测试覆盖:44 单元 + 10 集成 = 54 测试全部通过。
审查中已修复的问题
| # | 问题 | 修复 |
|---|---|---|
| 1 | active→fading 缺 trend 检查 | 新增 is_weakening = ewma < latest 条件 |
| 2 | 合并检测未过滤 strengthening | 新增 strength_latest > strength_ewma 过滤 |
| 3 | 缺 EWMA 计算逻辑 | orchestrator 新增 _compute_ewma_updates(),create 时初始化,update 时递推 |
| 4 | search_similar 丢失 sources 字段 | 重建 NarrativeRow 时赋值 sources=row.sources |
| 5 | ::vector 语法与 asyncpg 参数冲突 | 改用 CAST(:param AS vector) |
| 6 | Protocol 签名过时 | 同步 get_by_status、add_edge、embedding 参数 |
| 7 | 未使用 import | 清理 field、combinations、NarrativeRelationType、Sequence |
| 8 | 集成测试 pgvector 扩展未安装 | CREATE EXTENSION IF NOT EXISTS vector |
| 9 | 集成测试 event loop 不匹配 | fixture 重置 module-level 单例 |
遗留改进项(不阻塞 Phase 3)
中优先级
| # | 问题 | 影响 | 建议 |
|---|---|---|---|
| M1 | process_article 中 embedding 重复调用 | matcher.embed() 和 create_candidate 时各调一次,浪费 token 和延迟 | 将 matcher 返回的 embedding 传递给 create,避免二次调用 |
| M2 | Vector 列维度硬编码 1536 | 换 embedding 模型(如 3-large=3072)需改 ORM + 重建表 | EmbeddingProvider 已有 dimension 属性,ORM 应动态读取或使用迁移 |
| M3 | process_articles 纯串行 | 多篇文章逐条处理,高吞吐场景成瓶颈 | 改为 asyncio.gather + 幂等去重 |
低优先级
| # | 问题 | 影响 | 建议 |
|---|---|---|---|
| L1 | 单元测试多为 happy path | LLM 失败、DB 异常等 error path 未覆盖 | Phase 3 稳定后补充 error path 测试 |
| L2 | FeedReader 默认 RSS 源可能失效 | 部分源 URL 可能变更 | 从 data_sources.yaml 动态加载 feed 配置 |
| L3 | 状态机 rejected 不在 NarrativeStatus 枚举中 | 使用字符串 "rejected" 而非枚举 | 考虑加入枚举或保持当前设计(rejected 只是临时状态) |
测试矩阵
| 测试类型 | 数量 | 覆盖范围 |
|---|---|---|
| 单元 — narrative_models | 7 | Pydantic 模型校验、边界值 |
| 单元 — state_machine | 10 | 全部状态转换 + 边界条件 |
| 单元 — merge_split | 6 | cosine similarity、聚类、标准差 |
| 单元 — feed_reader | 3 | FeedSource、HTML 清理 |
| 集成 — db | 2 | pgembed + ORM CRUD |
| 集成 — narrative_engine | 8 | 完整 pipeline(mock LLM + real DB) |
| 总计 | 54 | — |