PMFlow/docs/superpowers/specs/2026-04-15-semantic-facts-r...

7.6 KiB
Raw Blame History

Semantic Facts Read Model Design

Goal

为内部查询建立一层独立的“语义事实读模型”,把项目、信号、文档、配置等原始对象中的非结构化语义抽成可检索、可计数、可解释的事实条目。后续内部查询在 Query DSL Plan 之后优先查这层读模型,再决定是否回退到受控文本检索。

Why This Is Needed

方案 1 能止住“问 existence 却答 total count”这类合同漂移,但它解决不了“数据库根本没有结构化字段”的问题。

像下面这些问法,本质上都需要一个语义层:

  • 有没有 A 股项目
  • 我们系统里有没有开票系统
  • 哪些项目属于视频方向
  • 这个项目是不是偏运营工具

如果底层只有 name/title/description/status,那执行器再受控,也只能做文本猜测。要真正提准,必须提前把“项目主题、行业标签、别名、用途、对象关系”这类语义抽出来,沉淀成 facts。

Confirmed Baseline

  • 方案 2 是内部查询的第二道必经步骤。
  • 当语义事实数据可用时,内部查询必须优先走 semantic_facts。
  • semantic_facts 是只读辅助模型,不是业务写入真源。
  • facts 的生成必须异步完成,禁止在用户主链里同步抽取长耗时语义。
  • facts 必须带 confidence、evidence、extract_version 和租户边界,避免“抽出来了但说不清依据”。

Design Decision

这里不做 project_semantic_facts 这种单对象专用表,而是直接设计成跨对象通用表 semantic_facts。

原因很简单:

  • 用户真实问法不只围绕项目
  • signal / doc / config / run 也会进入内部问答
  • 一开始就用通用读模型,后续不会因为对象扩大再重做一轮 schema

Data Model

Table 1: semantic_facts

建议字段如下:

字段 说明
id 主键
tenant_id 多租户边界
entity_type project/signal/doc/config/run/...
entity_id 对应对象主键
fact_namespace 事实命名空间,如 domain/theme/capability/alias/status
fact_key 事实键,如 market_theme、industry、aliases
fact_value_text 原始值
fact_value_norm 归一化值,便于精确匹配
value_type keyword/text/number/date/enum/json
confidence 抽取置信度
evidence_source_type 证据来源对象类型
evidence_source_id 证据来源对象 id
evidence_excerpt 短证据摘要
extract_version 抽取器版本
freshness_at 事实对应的源数据时间
is_active 是否有效
created_at 创建时间
updated_at 更新时间

Table 2: semantic_fact_extraction_runs

建议补一张轻量 run/audit 表:

字段 说明
id 主键
tenant_id 多租户边界
entity_type 被抽取对象类型
entity_id 被抽取对象 id
trigger_type create/update/backfill/manual_retry
status queued/running/completed/failed
input_fingerprint 输入快照指纹,用于幂等
facts_written 本次写入事实数
failure_reason 失败原因
extract_version 抽取器版本
started_at 开始时间
finished_at 结束时间

Fact Examples

示例一:项目描述里出现“面向证券投研团队,聚焦 A 股公告解析”。

可能抽出:

  • fact_namespace=theme
  • fact_key=market_theme
  • fact_value_text=A股
  • fact_value_norm=a_share

示例二:项目标题叫“开票助手”,描述里出现“面向财务运营,自动生成增值税发票草稿”。

可能抽出:

  • fact_key=business_domain
  • fact_value_norm=invoicing
  • fact_key=aliases
  • fact_value_text=开票系统

Async Extraction Flow

语义事实抽取链建议统一为:

  1. 项目、信号、文档、功能目录、配置快照等源对象完成事务提交。
  2. 提交后只入队一个 semantic fact extraction 事件,不在主事务里直接跑 LLM。
  3. worker 读取事件,加载最小源上下文与证据片段。
  4. LLM 只负责结构化抽取事实,不负责最终回答。
  5. 服务端做 normalize、去重、merge、失效旧 facts。
  6. 抽取结果写入 semantic_facts,并把 run 状态写入 semantic_fact_extraction_runs。
  7. 后续内部查询命中该对象时,优先从 semantic_facts 读取。

Query Read Path

方案 2 与方案 1 的衔接方式是固定的:

  1. Query Route Gateway 决定本轮走内部查询。
  2. Operation Contract 先锁定回答合同与对象 mentions。
  3. Target Resolver 在 project / feature / config / doc 等候选间做受控仲裁。
  4. Query DSL Plan 再基于已解析目标产出 query_type/entity_type/filters/answer_contract。
  5. Bounded Executor 先查 semantic_facts。
  6. 若 facts 命中且置信度满足阈值,直接形成 existence/count/list/status 的证据基础。
  7. 若 facts 不足,再回退到受控文本检索、图谱检索或配置快照读取。
  8. Answer Verifier 再检查回答与证据是否一致。

这意味着后续内部查询的“真正默认路径”会变成:

Operation Contract -> Target Resolver -> DSL Plan -> facts-first -> bounded fallback -> verifier

Confidence And Evidence Rules

  • confidence 只用于排序和阈值判断,不允许把低置信猜测包装成确定性事实
  • facts 用于回答时,必须能回指 evidence_source_type/evidence_source_id/evidence_excerpt
  • 同一个 entity_type/entity_id/fact_key/fact_value_norm 要支持幂等 upsert
  • 源对象删除、重写或语义失效后,facts 必须支持失效标记,不允许永久悬挂

Guardrails

  • semantic_facts 只用于内部只读查询和解释,不作为写操作真源
  • 不允许让 LLM 在 facts 缺失时自由脑补
  • 不允许把 facts 命中为空解释成“对象不存在”,除非计划明确允许且 fallback 也已执行
  • 不允许绕过 Query DSL Plan 直接裸查 facts 表

Backfill Strategy

首轮建议:

  1. 对现有 projects 做一次租户内 backfill
  2. 对与项目强绑定的 signals / docs / feature catalog 做增量补抽
  3. 在运行稳定后,再扩到 config / run / integration snapshot

计划改动文件清单(基线确认后)

方案 2 数据模型与迁移

  • backend/alembic/versions/<new>_add_semantic_facts.py
  • backend/app/models/semantic_fact.py
  • backend/app/models/semantic_fact_extraction_run.py
  • backend/app/models/__init__.py

方案 2 服务与异步链路

  • backend/app/services/semantic_fact_service/service.py
  • backend/app/services/semantic_fact_service/mixin_extract.py
  • backend/app/services/semantic_fact_service/mixin_query.py
  • backend/app/services/project_knowledge_graph_service/mixin_event_queue.py
  • backend/app/services/project_knowledge_graph_service/service.py
  • backend/app/services/adaptive_rag_service/mixin_answer_retrieval.py
  • backend/app/services/adaptive_rag_service/mixin_retrieval_router_db.py
  • backend/app/services/adaptive_rag_service/mixin_fast_answer_project_main.py

方案 2 回归测试

  • backend/tests/test_project_knowledge_graph_event_queue_contract.py
  • backend/tests/test_adaptive_rag_mainline_contract.py
  • backend/tests/test_ai_pmo_ingress_mainline.py
  • backend/tests/test_runtime_structured_semantics_contract.py
  • backend/tests/test_semantic_fact_service.py

本轮文档基线

  • AGENTS.md
  • CLAUDE.md
  • docs/architecture/RUNTIME_GUARDRAILS.md
  • docs/architecture/DATAFLOW.md
  • docs/architecture/ARCHITECTURE_INVENTORY.md
  • docs/superpowers/specs/2026-04-15-internal-query-dsl-plan-design.md
  • docs/superpowers/specs/2026-04-15-semantic-facts-read-model-design.md