7.6 KiB
7.6 KiB
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=themefact_key=market_themefact_value_text=A股fact_value_norm=a_share
示例二:项目标题叫“开票助手”,描述里出现“面向财务运营,自动生成增值税发票草稿”。
可能抽出:
fact_key=business_domainfact_value_norm=invoicingfact_key=aliasesfact_value_text=开票系统
Async Extraction Flow
语义事实抽取链建议统一为:
- 项目、信号、文档、功能目录、配置快照等源对象完成事务提交。
- 提交后只入队一个
semantic fact extraction事件,不在主事务里直接跑 LLM。 - worker 读取事件,加载最小源上下文与证据片段。
- LLM 只负责结构化抽取事实,不负责最终回答。
- 服务端做 normalize、去重、merge、失效旧 facts。
- 抽取结果写入
semantic_facts,并把 run 状态写入semantic_fact_extraction_runs。 - 后续内部查询命中该对象时,优先从
semantic_facts读取。
Query Read Path
方案 2 与方案 1 的衔接方式是固定的:
Query Route Gateway决定本轮走内部查询。Operation Contract先锁定回答合同与对象 mentions。Target Resolver在 project / feature / config / doc 等候选间做受控仲裁。Query DSL Plan再基于已解析目标产出query_type/entity_type/filters/answer_contract。Bounded Executor先查semantic_facts。- 若 facts 命中且置信度满足阈值,直接形成 existence/count/list/status 的证据基础。
- 若 facts 不足,再回退到受控文本检索、图谱检索或配置快照读取。
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
首轮建议:
- 对现有
projects做一次租户内 backfill - 对与项目强绑定的
signals / docs / feature catalog做增量补抽 - 在运行稳定后,再扩到
config / run / integration snapshot
计划改动文件清单(基线确认后)
方案 2 数据模型与迁移
backend/alembic/versions/<new>_add_semantic_facts.pybackend/app/models/semantic_fact.pybackend/app/models/semantic_fact_extraction_run.pybackend/app/models/__init__.py
方案 2 服务与异步链路
backend/app/services/semantic_fact_service/service.pybackend/app/services/semantic_fact_service/mixin_extract.pybackend/app/services/semantic_fact_service/mixin_query.pybackend/app/services/project_knowledge_graph_service/mixin_event_queue.pybackend/app/services/project_knowledge_graph_service/service.pybackend/app/services/adaptive_rag_service/mixin_answer_retrieval.pybackend/app/services/adaptive_rag_service/mixin_retrieval_router_db.pybackend/app/services/adaptive_rag_service/mixin_fast_answer_project_main.py
方案 2 回归测试
backend/tests/test_project_knowledge_graph_event_queue_contract.pybackend/tests/test_adaptive_rag_mainline_contract.pybackend/tests/test_ai_pmo_ingress_mainline.pybackend/tests/test_runtime_structured_semantics_contract.pybackend/tests/test_semantic_fact_service.py
本轮文档基线
AGENTS.mdCLAUDE.mddocs/architecture/RUNTIME_GUARDRAILS.mddocs/architecture/DATAFLOW.mddocs/architecture/ARCHITECTURE_INVENTORY.mddocs/superpowers/specs/2026-04-15-internal-query-dsl-plan-design.mddocs/superpowers/specs/2026-04-15-semantic-facts-read-model-design.md