dsh-data-domain-knowledge 0.1.0-alpha.11 → 0.1.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +64 -13
  2. package/eval/README.md +19 -8
  3. package/eval/evaluate-retrieval.mjs +17 -8
  4. package/eval/queries.example.jsonl +1 -0
  5. package/lib/client.js +2294 -2096
  6. package/lib/doc-page.js +1 -1
  7. package/lib/pages.js +2 -2
  8. package/lib/types/axis-registry.d.ts +9 -4
  9. package/lib/types/axis-registry.d.ts.map +1 -1
  10. package/lib/types/axis-registry.js +62 -23
  11. package/lib/types/axis-registry.js.map +1 -1
  12. package/lib/types/card-organize.d.ts +5 -0
  13. package/lib/types/card-organize.d.ts.map +1 -1
  14. package/lib/types/card-organize.js +15 -0
  15. package/lib/types/card-organize.js.map +1 -1
  16. package/lib/types/compiler/embedding-identity.d.ts +3 -0
  17. package/lib/types/compiler/embedding-identity.d.ts.map +1 -0
  18. package/lib/types/compiler/embedding-identity.js +24 -0
  19. package/lib/types/compiler/embedding-identity.js.map +1 -0
  20. package/lib/types/compiler/snapshot.d.ts +16 -0
  21. package/lib/types/compiler/snapshot.d.ts.map +1 -0
  22. package/lib/types/compiler/snapshot.js +2 -0
  23. package/lib/types/compiler/snapshot.js.map +1 -0
  24. package/lib/types/diagnosis/store.d.ts.map +1 -1
  25. package/lib/types/diagnosis/store.js +2 -0
  26. package/lib/types/diagnosis/store.js.map +1 -1
  27. package/lib/types/environment/install.d.ts +7 -0
  28. package/lib/types/environment/install.d.ts.map +1 -0
  29. package/lib/types/environment/install.js +172 -0
  30. package/lib/types/environment/install.js.map +1 -0
  31. package/lib/types/environment/manifest.d.ts +208 -0
  32. package/lib/types/environment/manifest.d.ts.map +1 -0
  33. package/lib/types/environment/manifest.js +26 -0
  34. package/lib/types/environment/manifest.js.map +1 -0
  35. package/lib/types/environment/model-catalog.d.ts +191 -0
  36. package/lib/types/environment/model-catalog.d.ts.map +1 -0
  37. package/lib/types/environment/model-catalog.js +91 -0
  38. package/lib/types/environment/model-catalog.js.map +1 -0
  39. package/lib/types/environment/process.d.ts +10 -0
  40. package/lib/types/environment/process.d.ts.map +1 -0
  41. package/lib/types/environment/process.js +54 -0
  42. package/lib/types/environment/process.js.map +1 -0
  43. package/lib/types/environment/selection.d.ts +9 -0
  44. package/lib/types/environment/selection.d.ts.map +1 -0
  45. package/lib/types/environment/selection.js +29 -0
  46. package/lib/types/environment/selection.js.map +1 -0
  47. package/lib/types/environment/service.d.ts +47 -0
  48. package/lib/types/environment/service.d.ts.map +1 -0
  49. package/lib/types/environment/service.js +253 -0
  50. package/lib/types/environment/service.js.map +1 -0
  51. package/lib/types/environment/types.d.ts +37 -0
  52. package/lib/types/environment/types.d.ts.map +1 -0
  53. package/lib/types/environment/types.js +2 -0
  54. package/lib/types/environment/types.js.map +1 -0
  55. package/lib/types/events.d.ts +10 -0
  56. package/lib/types/events.d.ts.map +1 -1
  57. package/lib/types/events.js +38 -1
  58. package/lib/types/events.js.map +1 -1
  59. package/lib/types/images.d.ts +26 -0
  60. package/lib/types/images.d.ts.map +1 -0
  61. package/lib/types/images.js +196 -0
  62. package/lib/types/images.js.map +1 -0
  63. package/lib/types/index.d.ts.map +1 -1
  64. package/lib/types/index.js +59 -3
  65. package/lib/types/index.js.map +1 -1
  66. package/lib/types/knowledge-bases.d.ts +3 -0
  67. package/lib/types/knowledge-bases.d.ts.map +1 -1
  68. package/lib/types/knowledge-bases.js +9 -0
  69. package/lib/types/knowledge-bases.js.map +1 -1
  70. package/lib/types/knowledge.d.ts +14 -6
  71. package/lib/types/knowledge.d.ts.map +1 -1
  72. package/lib/types/knowledge.js +56 -39
  73. package/lib/types/knowledge.js.map +1 -1
  74. package/lib/types/layout.d.ts +2 -0
  75. package/lib/types/layout.d.ts.map +1 -1
  76. package/lib/types/layout.js +2 -0
  77. package/lib/types/layout.js.map +1 -1
  78. package/lib/types/retrieval/vector-index.d.ts +31 -0
  79. package/lib/types/retrieval/vector-index.d.ts.map +1 -0
  80. package/lib/types/retrieval/vector-index.js +15 -0
  81. package/lib/types/retrieval/vector-index.js.map +1 -0
  82. package/lib/types/retrieval/visibility.d.ts +6 -0
  83. package/lib/types/retrieval/visibility.d.ts.map +1 -0
  84. package/lib/types/retrieval/visibility.js +11 -0
  85. package/lib/types/retrieval/visibility.js.map +1 -0
  86. package/lib/types/runtime.d.ts +36 -6
  87. package/lib/types/runtime.d.ts.map +1 -1
  88. package/lib/types/runtime.js +520 -190
  89. package/lib/types/runtime.js.map +1 -1
  90. package/lib/types/source-evidence.d.ts +6 -0
  91. package/lib/types/source-evidence.d.ts.map +1 -0
  92. package/lib/types/source-evidence.js +28 -0
  93. package/lib/types/source-evidence.js.map +1 -0
  94. package/lib/types/store/axis-references.d.ts +5 -0
  95. package/lib/types/store/axis-references.d.ts.map +1 -0
  96. package/lib/types/store/axis-references.js +106 -0
  97. package/lib/types/store/axis-references.js.map +1 -0
  98. package/lib/types/store/derived.d.ts +11 -0
  99. package/lib/types/store/derived.d.ts.map +1 -1
  100. package/lib/types/store/derived.js +31 -6
  101. package/lib/types/store/derived.js.map +1 -1
  102. package/lib/types/store/knowledge-db.d.ts +4 -1
  103. package/lib/types/store/knowledge-db.d.ts.map +1 -1
  104. package/lib/types/store/knowledge-db.js +15 -9
  105. package/lib/types/store/knowledge-db.js.map +1 -1
  106. package/lib/types/store/migrate.d.ts.map +1 -1
  107. package/lib/types/store/migrate.js +16 -5
  108. package/lib/types/store/migrate.js.map +1 -1
  109. package/lib/types/types.d.ts +7 -1
  110. package/lib/types/types.d.ts.map +1 -1
  111. package/lib/types/types.js +3 -4
  112. package/lib/types/types.js.map +1 -1
  113. package/package.json +3 -1
  114. package/python/prepare-models.py +19 -0
  115. package/python/requirements.txt +8 -6
  116. package/scripts/doctor.mjs +14 -2
  117. package/scripts/environment.mjs +29 -0
  118. package/scripts/setup-embedding.sh +11 -2
  119. package/src/axis-registry.ts +59 -23
  120. package/src/card-organize.ts +15 -0
  121. package/src/client/EnvironmentSetup.tsx +145 -0
  122. package/src/client/KnowledgePanel.tsx +9 -4
  123. package/src/compiler/embedding-identity.ts +21 -0
  124. package/src/compiler/snapshot.ts +17 -0
  125. package/src/diagnosis/store.ts +1 -0
  126. package/src/environment/install.ts +129 -0
  127. package/src/environment/manifest.ts +28 -0
  128. package/src/environment/model-catalog.ts +105 -0
  129. package/src/environment/process.ts +54 -0
  130. package/src/environment/selection.ts +29 -0
  131. package/src/environment/service.ts +211 -0
  132. package/src/environment/types.ts +34 -0
  133. package/src/events.ts +46 -0
  134. package/src/images.ts +203 -0
  135. package/src/index.ts +54 -3
  136. package/src/knowledge-bases.ts +9 -0
  137. package/src/knowledge.ts +56 -40
  138. package/src/layout.ts +2 -0
  139. package/src/retrieval/vector-index.ts +44 -0
  140. package/src/retrieval/visibility.ts +14 -0
  141. package/src/runtime.ts +473 -233
  142. package/src/source-evidence.ts +30 -0
  143. package/src/store/axis-references.ts +96 -0
  144. package/src/store/derived.ts +31 -6
  145. package/src/store/knowledge-db.ts +16 -9
  146. package/src/store/migrate.ts +15 -5
  147. package/src/types.ts +10 -5
package/README.md CHANGED
@@ -3,17 +3,39 @@
3
3
  本地知识库插件:一个知识库是一个目录,里面四样东西。`knowledge.db` 保存 **SourceRaw**
4
4
  (原始可追溯文档)、按主题聚合可双向链接的 wiki 卡片,以及**两者的每一个版本**;
5
5
  `derived.db` 输出可检索的 **wikiNotes**、wiki
6
- 知识图谱和向量索引,`<agentRoot>/memory/<库 id>` 保存版本快照、访问审计与反馈。默认使用本地 WeMM Python backend:从 WeMM
7
- 原生 2048 维向量按 MRL 截取 1024 维,先做 binary/Hamming 粗筛,再用 int8 点积精算余弦
8
- (这是**向量路内部的粗排 → 精算**,与 stage 4 的 cross-encoder **重排**不是一回事);
9
- 模型不可用时明确降级到 FTS,不会伪造向量结果。Markdown 中的
10
- 本地图片会与正文一起交给 WeMM,因此图片不是独立知识类型。
6
+ 知识图谱和向量索引,`<agentRoot>/memory/<库 id>` 保存操作日志、抽取轨迹、访问审计与反馈。
7
+ 默认使用本地 Python 推理:`BAAI/bge-small-zh-v1.5` 生成 512 维文本向量,
8
+ `BAAI/bge-reranker-base` 对召回候选做交叉编码重排。部署入口必须完成环境自检,
9
+ 索引必须匹配当前模型;不满足时提示准备环境或重新编译。
10
+ 需要图片语义检索时,可配置 WeMM 多模态模型与匹配维度,Markdown 中的本地图片
11
+ 会与正文一起交给模型。图片不作为独立知识类型。
11
12
 
12
13
  Tita Agent 的固定数据根目录是 `$DSH_HOME/tita-agent`(默认 `~/.dsh/tita-agent`)。
13
14
  设置面板只展示这个目录并提供「打开目录」,不允许切换根路径;知识库、意图记录、记忆、
14
15
  模型和 Python 环境都从这里派生。
15
16
 
16
- ## 承载范围(五个视图)
17
+ ## 首次运行环境准备
18
+
19
+ 向量模型和重排模型是部署必需项。打开 Tita 设置 → 环境准备 → 一键安装 / 修复。
20
+ 系统用 DSH 自带的 Python 3.12 创建专属依赖环境,不另行下载 Python;再下载固定 revision 的 `BAAI/bge-small-zh-v1.5`
21
+ (512 维)和 `BAAI/bge-reranker-base`,然后运行真实推理自检。阶段、错误和下载日志
22
+ 可查看;下载失败可以重试。未就绪时阻止编译和知识召回,来源与历史内容仍可保存、读取。
23
+ 自检不单独检查 `sentencepiece`;依赖安装失败会显示安装错误,模型真实推理失败会显示对应模型的错误。
24
+
25
+ 命令行入口:
26
+
27
+ ```bash
28
+ pnpm --filter dsh-data-domain-knowledge environment:prepare
29
+ pnpm --filter dsh-data-domain-knowledge environment:check
30
+ pnpm --filter dsh-data-domain-knowledge doctor
31
+ ```
32
+
33
+ 自检失败时命令返回非零退出码。依赖装在 Tita 专属虚拟环境中,不修改系统 Python,也不隐式运行 npm postinstall
34
+ 模型下载。已有知识在准备完成后需重新编译;宿主无需重启。自定义路径保留,使用默认
35
+ 资源时把高级设置中的路径恢复为空,并使用匹配的向量维度。
36
+ 细节见 [运行环境准备与自检](../../docs/current/16-知识运行环境准备与自检.md)。
37
+
38
+ ## 承载范围
17
39
 
18
40
  侧边栏 `TitaAgent` 面板的五个视图就是本插件的全部范围:
19
41
 
@@ -22,7 +44,7 @@ Tita Agent 的固定数据根目录是 `$DSH_HOME/tita-agent`(默认 `~/.dsh/t
22
44
  | `overview` | Agent 概览 | 各设置的挂载节点 |
23
45
  | `intent` | 意图 | `intent_record` / `intent_store_view`、`/intent/*` 路由、`titaDataAgentIntent` 服务;会话审计留痕落 `<agentRoot>/intent/` |
24
46
  | `knowledge` | 知识库 | `recall_knowledge` 等工具名、`/knowledge/*` 与 `/convert` 路由、`tita-data-agent-knowledge` / `-convert` 设置 |
25
- | `memory` | 记忆 | 抽取轨迹、轴 / Knowledge 真源、处理日志、`feedback.ndjson`、`access-audit.ndjson`,根在 `<agentRoot>/memory/<库 id>`;磁盘目录布局 |
47
+ | `memory` | 记忆 | 抽取轨迹、处理日志、`feedback.ndjson`、`access-audit.ndjson`,根在 `<agentRoot>/memory/<库 id>`;轴 / Knowledge 真源在主库 |
26
48
  | `eval` | 评测 | 问题集格式(开发期能力,不注册运行时入口) |
27
49
 
28
50
  意图的**会话审计留痕**落在 `<agentRoot>/intent/`:`intent.db` 保存当前记录与追加历史
@@ -53,7 +75,7 @@ Tita Agent 的固定数据根目录是 `$DSH_HOME/tita-agent`(默认 `~/.dsh/t
53
75
  | Knowledge | `knowledge` + `knowledge_sources` | 原子事实、坐标、来源、状态;不保存 Card 的排版 |
54
76
  | Wiki Card | `card` + `blob` + `revision` | 面向阅读和召回组装的正文;正文中的 Markdown 标题是 Section 真源 |
55
77
  | Knowledge 落位 | `card_knowledge` | 记录 Knowledge 在某张 Card 中的 Section 和顺序;这是关联索引,不是 Knowledge 属性 |
56
- | Wiki Note / FTS / Vector / Graph | 派生文件 | 可以从 Card 和 Knowledge 重新构建,不是真实知识来源 |
78
+ | Wiki Note / FTS / Vector / Graph | `derived.db` | 可以从 Card 和 Knowledge 重新构建;编译结果在同一事务发布 |
57
79
 
58
80
  ### 状态语义
59
81
 
@@ -70,7 +92,29 @@ Knowledge ID 只在一个 `knowledge.db` 内有意义。相同文本在同一个
70
92
 
71
93
  这意味着当前实现适用于“一个知识库对应一个业务范围”的模型。未来如果一个库需要混合多个租户、环境或有效期,再把 Scope 纳入判重策略。
72
94
 
73
- ### Knowledge 类型
95
+ ### 身份算法
96
+
97
+ 新 Knowledge ID 使用归一化文本的 SHA-256。归一化保留比较条件、小数、负号和标识符
98
+ 分隔符,判重同时比较正文。存量 ID 不重写;人工编辑正文保留 ID 并增加内容版本。
99
+
100
+ ### 来源更新与复核
101
+
102
+ 来源改版后保留旧 Knowledge 和原始证据版本,不自动判定失效。证据记录 `toVersion`,
103
+ 读取时返回 `currentVersion`、`evidenceStatus`、`quoteInCurrentVersion`。召回中缺少当前
104
+ 可定位证据的知识带 `requiresReview: true`;是否停用仍由人工裁决。
105
+
106
+ 引用定位不等于语义成立。组织校验能拒绝输入中没有出现的数值或比较条件,但不能证明
107
+ 任意自然语言陈述成立;召回如实返回 `claimVerification: 'unverified'`。
108
+
109
+ ### 编译发布与一致性
110
+
111
+ Notes、FTS、向量、图和 manifest 在 `derived.db` 中通过一次事务发布。召回与图查询
112
+ 读取已发布快照;失败或取消时保留上一版结果。删除状态立即过滤旧片段。抽取失败不
113
+ 保存成功指纹,模型身份、位置或本地文件版本变化会阻止旧向量复用。
114
+
115
+ 目录边界、事务和验证范围见 [知识一致性与编译快照](../../docs/current/15-知识一致性与编译快照.md)。
116
+
117
+ ### Knowledge 类型字段
74
118
 
75
119
  当前不把 `Fact` / `Rule` / `Procedure` / `Constraint` 加成 Knowledge 的正式列。类型暂作为抽取或 Card metadata 的扩展信息,只有当不同类型需要不同的召回、验证或执行策略时,才升级为结构化字段。
76
120
 
@@ -185,20 +229,22 @@ SourceRaw → Knowledge → Wiki Card → Wiki Note → FTS / Vector / Rerank
185
229
 
186
230
  ## 本地启用 WeMM
187
231
 
188
- 在 Apple Silicon 上执行一次(在插件包目录内):
232
+ 推荐使用「环境准备」面板选择 WeMM 量化版或腾讯 BF16 版。面板会安装所选模型并自动
233
+ 设置匹配的维度。也可在 Apple Silicon 上执行旧版辅助脚本(在插件包目录内):
189
234
 
190
235
  ```bash
191
236
  bash scripts/setup-embedding.sh
192
237
  ```
193
238
 
194
- 脚本会把 Python 环境放到 `~/.dsh/tita-agent/resource/python`,把量化模型放到
239
+ 脚本会用 `DSH_PYTHON`(未设置时为系统 `python3`)建立隔离依赖环境,把模型放到
195
240
  `~/.dsh/tita-agent/resource/models/WeMM-Embedding-2B-Quantized`。新配置会自动发现这两个路径;
196
- 设置位于 Tita Data Agent → Agent 概览 →「构建」节点:
241
+ 当前脚本安装的是 `ewin-reg` 社区维护的 INT4 / FP8 量化版,不是腾讯官方发行,模型卡将许可标为 `other`。
242
+ 它的输出维度为 2048。设置位于 Tita Data Agent → Agent 概览 →「构建」节点:
197
243
 
198
244
  - Provider:`WeMM Python(本地)`
199
245
  - Python:`~/.dsh/tita-agent/resource/python/bin/python`
200
246
  - Model:`~/.dsh/tita-agent/resource/models/WeMM-Embedding-2B-Quantized`
201
- - 维度:`1024`
247
+ - 维度:`2048`
202
248
  - 设备:`自动(优先 MPS)`
203
249
 
204
250
  知识库默认保存在 `~/.dsh/tita-agent/domain_knowledge/<库 id>`。点击「编译」后,
@@ -285,6 +331,11 @@ HTTP `GET /knowledge/bases` 在返回前先刷新。
285
331
  | `derived.db` | wikiNotes、知识图谱、检索索引 | 可重新编译 |
286
332
  | `assets/` | 来源图片与附件 | 丢失对应素材 |
287
333
 
334
+ 知识库提供 `images.publish` 工具作为本地图床。它接收宿主本地 `file_path` 或 Base64 图片,按
335
+ `assets/<SHA-256 前 12 位>/<文件名>` 写入当前知识库,并返回 `/knowledge/assets/<kb>/<路径>` 地址
336
+ 和 Markdown 图片引用。独立图床图片不要求来源或卡片引用,也不会被孤儿清理误删。工具会校验图片格式和 MIME,
337
+ 单张图片上限为 20 MiB;写入不需要用户审批。
338
+
288
339
  `resource/` 保存模型、Python 环境与 `knowledge-bases.json`。
289
340
  如果资源目录没有本地 Python 或模型,就使用 `python3` 或 Hugging Face 模型名。
290
341
 
package/eval/README.md CHANGED
@@ -1,19 +1,26 @@
1
1
  # Tita Knowledge Retrieval Evaluation
2
2
 
3
3
  `evaluate-retrieval.mjs` 对同一份已经编译好的 Knowledge Store,逐条运行
4
- `fts`、`vector`、`hybrid`,输出 Recall、MRR、nDCG 和 p50/p95 latency。它不自动
4
+ `fts`、`vector`、`hybrid`,输出 Recall、MRR、nDCG、弃答准确率和 p50/p95 latency。它不自动
5
5
  下载公开数据集,也不会改写知识库;公开数据集需要先转换为本插件的 qrels 格式。
6
6
 
7
7
  ## qrels 格式
8
8
 
9
9
  每行一个 JSON 对象,`relevant_unit_ids` 使用编译后的稳定 `unitId`,例如
10
10
  `metric:sales_amount`。可以附加 `include_problems` 和 `subset`,用于分组评测文本、
11
- 图片 Markdown、表格截图等场景。
11
+ 图片 Markdown、表格截图等场景。`subset` 当前保留在输入中;按子集运行时先分别生成
12
+ qrels 文件,runner 不自动输出子集汇总。
12
13
 
13
14
  ```json
14
15
  {"id":"q1","query":"销售额如何计算","relevant_unit_ids":["metric:sales_amount"],"subset":"text"}
16
+ {"id":"q2","query":"库内没有依据的问题","relevant_unit_ids":[],"subset":"abstention"}
15
17
  ```
16
18
 
19
+ 空 `relevant_unit_ids` 明确表示“不应召回”。必须提供这个字段,避免把漏标注误当作
20
+ 弃答样本。正例的 Recall、MRR、nDCG 只使用有正例的查询计算;弃答样本单独输出
21
+ `abstention.count`、`falseAnswers`(任何命中均计为误召回)和 `accuracy`。
22
+ 只有弃答样本时,正例指标为 `null`。接口失败会使评测失败,不会当作成功弃答。
23
+
17
24
  ## 本地运行
18
25
 
19
26
  先在 UI 点击一次「编译」,再运行:
@@ -21,7 +28,7 @@
21
28
  ```bash
22
29
  pnpm --filter dsh-data-domain-knowledge build
23
30
  node plugins/dsh-data-domain-knowledge/eval/evaluate-retrieval.mjs \
24
- --root "$HOME/.tita-data-agent/knowledge" \
31
+ --root "$HOME/.dsh/tita-agent/domain_knowledge/default" \
25
32
  --qrels plugins/dsh-data-domain-knowledge/eval/queries.example.jsonl
26
33
  ```
27
34
 
@@ -29,10 +36,10 @@ node plugins/dsh-data-domain-knowledge/eval/evaluate-retrieval.mjs \
29
36
 
30
37
  ```bash
31
38
  TITA_EVAL_PROVIDER=wemm-python \
32
- TITA_EVAL_PYTHON="$HOME/.tita-data-agent/embedding-venv/bin/python" \
33
- TITA_EVAL_MODEL="$HOME/.tita-data-agent/models/WeMM-Embedding-2B-Quantized" \
39
+ TITA_EVAL_PYTHON="$HOME/.dsh/tita-agent/resource/python/bin/python" \
40
+ TITA_EVAL_MODEL="$HOME/.dsh/tita-agent/resource/models/WeMM-Embedding-2B-Quantized" \
34
41
  node plugins/dsh-data-domain-knowledge/eval/evaluate-retrieval.mjs \
35
- --root "$HOME/.tita-data-agent/knowledge" \
42
+ --root "$HOME/.dsh/tita-agent/domain_knowledge/default" \
36
43
  --qrels ./queries.jsonl
37
44
  ```
38
45
 
@@ -61,12 +68,13 @@ checkpoint 编译后再跑同一份 qrels。每次比较固定知识卡片、qre
61
68
  评测,但它仍然是模型级评测,不是 Tita 端到端评测。
62
69
  3. Tita 专属 golden set:建议先人工标注 100–200 条查询,至少包含指标口径、表/字段
63
70
  语义、同义词、跨语言、图片中的 UI/图表信息、hard negative 和“不应召回”样本。
64
- 每条 query 标注 1–3 个正例 unitId;每轮发布前固定这份集合,新增样本走版本控制。
71
+ 有答案的 query 标注 1–3 个正例 unitId;不应召回的 query 使用空集合。每轮发布前
72
+ 固定这份集合,新增样本走版本控制。
65
73
 
66
74
  ## 指标与门槛
67
75
 
68
76
  主指标使用 `nDCG@10`、`Recall@10`、`MRR@10`,辅助看 `Recall@5/20`;同时记录
69
- 向量编译吞吐(units/min)、查询 p50/p95、模型常驻内存、`03_compiled` 索引大小和
77
+ 弃答准确率和误召回数。另行测量向量编译吞吐(units/min)、模型常驻内存、`derived.db` 大小和
70
78
  向量复用率。多模态集合要单独报告 text→image、image→text、text+image 三类结果,
71
79
  不能只看总平均。
72
80
 
@@ -74,3 +82,6 @@ checkpoint 编译后再跑同一份 qrels。每次比较固定知识卡片、qre
74
82
  至少应在语义改写和图片子集上带来可观增益;具体阈值在首轮 100–200 条标注跑完后再
75
83
  根据置信区间确定,不预先假设量化版一定优于 BF16。最终结果同时保存 qrels、配置、
76
84
  模型 revision、硬件和原始 JSON,确保可复现。
85
+
86
+ runner 关闭 cross-encoder 重排,比较原始召回路线。完整 Rerank 链路的质量要另行
87
+ 测量。自动化回归测试使用小型本地语料和模型替身,不构成真实模型质量门槛的验收结果。
@@ -56,8 +56,8 @@ if (!limits.length || !modes.length) usage('invalid --k or --modes')
56
56
  const lines = (await readFile(args.qrels, 'utf8')).split(/\r?\n/).map(line => line.trim()).filter(Boolean).filter(line => !line.startsWith('#'))
57
57
  const qrels = lines.map((line, index) => {
58
58
  const item = JSON.parse(line)
59
- const relevant = item.relevant_unit_ids ?? item.relevant ?? []
60
- if (!item.query || !Array.isArray(relevant) || relevant.length === 0) throw new Error(`qrels line ${index + 1} needs query and non-empty relevant_unit_ids`)
59
+ const relevant = item.relevant_unit_ids ?? item.relevant
60
+ if (!item.query || !Array.isArray(relevant)) throw new Error(`qrels line ${index + 1} needs query and relevant_unit_ids (empty means abstain)`)
61
61
  return { ...item, relevant_unit_ids: [...new Set(relevant.map(String))] }
62
62
  })
63
63
  if (!qrels.length) throw new Error('qrels is empty')
@@ -93,26 +93,35 @@ try {
93
93
  // is disabled and the raw fused ranking is measured.
94
94
  const response = await runtime.recall(qrel.query, maxLimit, qrel.include_problems === true, mode, undefined, { rerank: false })
95
95
  latencies.push(performance.now() - started)
96
+ if (!response.ok) throw new Error(`${qrel.id ?? qrel.query}: ${response.error?.code ?? 'RECALL_FAILED'}`)
96
97
  if (response.data.backend === 'fts' && mode !== 'fts') fallbackCount += 1
97
- rows.push({ hits: response.data.hits, relevant: new Set(qrel.relevant_unit_ids) })
98
+ rows.push({ hits: response.data.hits, relevant: new Set(qrel.relevant_unit_ids), subset: qrel.subset ?? 'default' })
98
99
  }
99
100
  const metrics = {}
100
101
  for (const limit of limits) {
101
102
  let recall = 0
102
103
  let mrr = 0
103
104
  let score = 0
104
- for (const row of rows) {
105
+ const positiveRows = rows.filter(row => row.relevant.size > 0)
106
+ for (const row of positiveRows) {
105
107
  const hits = row.hits.slice(0, limit)
106
108
  recall += hits.filter(hit => row.relevant.has(hit.unitId)).length / row.relevant.size
107
109
  const rank = hits.findIndex(hit => row.relevant.has(hit.unitId))
108
110
  mrr += rank < 0 ? 0 : 1 / (rank + 1)
109
111
  score += ndcg(row.hits, row.relevant, limit)
110
112
  }
111
- metrics[`recall@${limit}`] = Number((recall / rows.length).toFixed(4))
112
- metrics[`mrr@${limit}`] = Number((mrr / rows.length).toFixed(4))
113
- metrics[`ndcg@${limit}`] = Number((score / rows.length).toFixed(4))
113
+ const divisor = positiveRows.length || 1
114
+ metrics[`recall@${limit}`] = positiveRows.length ? Number((recall / divisor).toFixed(4)) : null
115
+ metrics[`mrr@${limit}`] = positiveRows.length ? Number((mrr / divisor).toFixed(4)) : null
116
+ metrics[`ndcg@${limit}`] = positiveRows.length ? Number((score / divisor).toFixed(4)) : null
117
+ }
118
+ const negativeRows = rows.filter(row => row.relevant.size === 0)
119
+ const falseAnswers = negativeRows.filter(row => row.hits.length > 0).length
120
+ output.modes[mode] = {
121
+ metrics, positiveCount: rows.length - negativeRows.length,
122
+ abstention: { count: negativeRows.length, falseAnswers, accuracy: negativeRows.length ? Number((1 - falseAnswers / negativeRows.length).toFixed(4)) : null },
123
+ latencyMs: { p50: percentile(latencies, 0.5), p95: percentile(latencies, 0.95) }, fallbackCount,
114
124
  }
115
- output.modes[mode] = { metrics, latencyMs: { p50: percentile(latencies, 0.5), p95: percentile(latencies, 0.95) }, fallbackCount }
116
125
  }
117
126
  } finally {
118
127
  runtime.close()
@@ -1,2 +1,3 @@
1
1
  {"id":"q-agent-architecture","query":"知识编译与召回的 Agent 架构","relevant_unit_ids":["concept:agent_architecture"],"subset":"text"}
2
2
  {"id":"q-ui-architecture","query":"Tita 数据 Agent 的界面和架构","relevant_unit_ids":["concept:agent_architecture","concept:ui_reference"],"subset":"text-image"}
3
+ {"id":"q-no-evidence","query":"火星香蕉的年度产量是多少","relevant_unit_ids":[],"subset":"abstention"}