Skip to content

feat(evidence): 统一 Source → Evidence ↔ Claim 证据契约 - #104

Open
Theater-ahyeon wants to merge 1 commit into
helsome:mainfrom
Theater-ahyeon:feat/unified-evidence-contract
Open

Theater-ahyeon wants to merge 1 commit into
helsome:mainfrom
Theater-ahyeon:feat/unified-evidence-contract

Conversation

@Theater-ahyeon

Copy link
Copy Markdown

改了什么

按 issue #100 的「版本化、增量式证据契约」方向,新增统一契约的第一个增量切片:契约类型 + 三条既有证据路径的确定性投影 + bundle 组装/序列化。不改动任何现有类型、持久化格式与生产路径。

新增文件

文件 内容
packages/core/src/evidence-contract.ts EvidenceSource / EvidenceItem / EvidenceClaim / EvidenceBundle 契约类型与 folio-evidence-contract/v1 版本常量
packages/shared/src/evidence/contract.ts 四个投影函数 + buildEvidenceBundle + 序列化/守卫
packages/shared/src/evidence/contract.test.ts 13 个聚焦测试
docs/evidence-contract.md 契约文档:关系模型、身份语义、投影一览、明确约定

修改文件(各 1 行导出接线)

  • packages/core/src/index.tspackages/shared/src/evidence/index.ts

为什么要改

当前存在三套并行演化的证据抽象:Copilot 的 FinancialEvidenceEnvelope(结构化金融事实)、Deep Research 的 EvidenceRef(论点引用)、NewsItem(网页/新闻)。同一事实/来源因来自不同子系统而获得不同身份与元数据(语义碎片化)。本 PR 用一条 Source → Evidence ↔ Claim 关系链统一它们,且是投影整合而非新框架:现有类型仍是生产方的权威表示。

对应 Issue

#100(契约整合部分)。claim verifier(#13)、Source Inspector UI(#30)、检索去重(#39)的接入留作后续增量 PR。

设计要点

  • 身份确定性sourceId / evidenceId / claimId 全部由 sha256 确定性派生(截断 24 位,src_/ev_/claim_ 前缀,与现有 fe_ 风格一致);哈希输入经键排序的 stableJson 序列化,身份永不依赖对象键序。
    • sourceId 不含检索时间:同一文档/查询被再次观察仍是同一来源
    • evidenceId 含检索时间:同一事实稍后再次观察是同源新 observation,保留各自 provenance
    • claimId 由表述 + instrument 作用域派生:不同 run 的相同论点在 bundle 中合并并并集 evidenceIds —— 多对多映射由此自然成立
  • 多对多:Claim 单向持有 evidenceIds[],Evidence 不反向命名 Claim;一条证据可支撑多个论点。
  • 不伪造 URLstructured_finance 来源无公开文档,canonicalUrl 恒为空,身份由 publisher + dataset + query 承担;authority 元数据只在实际已知时填写(如监管备案)。
  • 向后兼容:投影只读输入、从不修改;已有持久化记录零迁移、保持可读。金融证据保留 metric/unit/currency/period/asOf/originalValue 语义,文档证据保留 excerpt/location 语义。
  • 显式状态:冲突/不可用是 availability 枚举值,不允许静默丢弃。

验证

环境:Bun 1.4.2 / Windows 10 (10.0.26200)

bun test packages/shared/src/evidence/contract.test.ts packages/shared/src/evidence/financial-evidence.test.ts
→ 17 passed / 0 failed(61 expect calls)

bun test packages/shared packages/core
→ 954 passed / 0 failed(89 files,3693 expect calls)

cd packages/core && bun run typecheck  → exit 0
cd packages/shared && bun run typecheck → exit 0

验收标准覆盖情况:

  • ✅ 契约文档化并存在于核心/共享代码(evidence-contract.ts + docs/evidence-contract.md
  • ✅ 三种证据来源投影到同一契约:结构化金融事实、网页/新闻、文档/备案(projectFinancialEvidence / projectNewsItems / projectTextEvidence;研究论点路径 projectEvidenceRefs 覆盖 tool 证据)
  • ✅ evidenceId/sourceId 在组装 → 序列化 → 反序列化全程稳定(round-trip 相等性测试)
  • ✅ 多对多映射(claim 合并 + 一证多 claim 测试)
  • ✅ 不为结构化金融数据伪造 URL(专门断言)
  • ✅ 已有持久化记录保持可读(投影只读 + 输入不变性测试)
  • ✅ 身份稳定性 / 多对多 / 序列化重载 / 混合证据类型的聚焦测试
  • ✅ 可复现集成示例:mixed-source integration 用例在单个 bundle 同时携带结构化金融证据、tool 论点证据、新闻摘录、监管备案摘录并验证重载一致

无可见 UI 变化

纯契约/共享层新增,不改任何 UI、IPC 或持久化行为。

已知未完成项(后续增量 PR)

  • claim verifier 接入(更新 verification / verifiedBy,契约不变)
  • Source Inspector / 引用检查器消费统一投影
  • run/retrieval 元数据写入 bundle provenance 的生产接线

新增 folio-evidence-contract/v1 统一契约(core 类型 + shared 投影),
把 FinancialEvidenceEnvelope、EvidenceRef、NewsItem 三套并行证据抽象
投影到同一条 Source → Evidence ↔ Claim 关系链:

- core/evidence-contract.ts: EvidenceSource / EvidenceItem / EvidenceClaim /
  EvidenceBundle 类型,全部 ID 确定性派生(sha256,键序无关),
  组装 → 持久化 → 重载全程稳定
- shared/evidence/contract.ts: 四个投影函数(结构化金融事实 / 研究论点 /
  新闻 / 通用文档备案)+ buildEvidenceBundle 多对多合并 + 序列化往返守卫
- 结构化金融来源不伪造 canonicalUrl;authority 元数据只在已知时填写;
  投影只读输入,现有持久化记录无需迁移
- docs/evidence-contract.md: 身份与生命周期语义文档

对应 helsome#100(契约整合部分;claim verifier / Source Inspector 接入留作
后续增量 PR)

@helsome helsome left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

方向非常值得保留,而且 #100 需要的核心形状基本已经出来了;测试报告也满足要求。当前我只卡两个会进入长期证据契约的数据语义问题,建议在 v1 落地前修掉:

  1. projectNewsItems 目前把 NewsItem.timestamp 同时写进 publishedAtretrievedAt,且直接原值写入。现有 core 明确约定 NewsItem.timestampepoch seconds,而新契约把 EvidenceSource.retrievedAt/publishedAt 定义为 epoch ms;这里会产生 1000 倍时间偏差。同时 NewsItem 只有来源时间,并没有真实 retrieval time,不能用发布时间冒充抓取时间。请让调用方显式提供 retrieval timestamp(或把未知 retrieval 表达为 unknown/optional),并在投影处做统一单位转换,补一个能抓住 seconds↔ms 的测试。

  2. projectFinancialEvidencefinancial.asOf 会优先使用 value.asOf ?? envelope.asOf,但同一 EvidenceItem 的 freshness.asOf 只写 envelope.asOf。当 metric 有更细粒度 value.asOf 时,同一条证据内部会出现两个不同的时间语义。请统一为同一个 resolved as-of,并补 value.asOf 存在、envelope asOf 缺失/不同的用例。

这两个点属于 #100 最核心的“不要让不同来源在统一层丢失/伪造时间语义”,不是要求扩大 scope。其余 Source/Evidence/Claim、确定性 ID、多对多、structured finance 不伪造 URL 的方向我认可。#105/#106 是 stacked 在本 PR 上,先把这里修正即可,不需要三个 PR 重复改。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants