dsh-data-domain-knowledge 0.1.0-alpha.9 → 0.1.1-alpha.2

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 (438) hide show
  1. package/README.md +118 -567
  2. package/cordis.patch.yml +5 -418
  3. package/eval/README.md +19 -8
  4. package/eval/benchmark-extraction-format.mjs +20 -0
  5. package/eval/evaluate-retrieval.mjs +17 -8
  6. package/eval/queries.example.jsonl +1 -0
  7. package/lib/client.js +4181 -3239
  8. package/lib/contract-envelope.d.ts +91 -0
  9. package/lib/contract-envelope.js +106 -0
  10. package/lib/contract-layout.d.ts +130 -0
  11. package/lib/contract-layout.js +138 -0
  12. package/lib/contract-refs.d.ts +39 -0
  13. package/lib/contract-refs.js +9 -0
  14. package/lib/contract-version.d.ts +311 -0
  15. package/lib/contract-version.js +270 -0
  16. package/lib/contract-wire.d.ts +147 -0
  17. package/lib/contract-wire.js +99 -0
  18. package/lib/contracts.d.ts +12 -0
  19. package/lib/contracts.js +12 -0
  20. package/lib/doc-page.js +2 -2
  21. package/lib/pages.js +3329 -1296
  22. package/lib/types/agent-behavior-rules.d.ts +10 -0
  23. package/lib/types/agent-behavior-rules.d.ts.map +1 -0
  24. package/lib/types/agent-behavior-rules.js +28 -0
  25. package/lib/types/agent-behavior-rules.js.map +1 -0
  26. package/lib/types/axis-registry.d.ts +37 -5
  27. package/lib/types/axis-registry.d.ts.map +1 -1
  28. package/lib/types/axis-registry.js +234 -26
  29. package/lib/types/axis-registry.js.map +1 -1
  30. package/lib/types/card-organize.d.ts +27 -12
  31. package/lib/types/card-organize.d.ts.map +1 -1
  32. package/lib/types/card-organize.js +58 -73
  33. package/lib/types/card-organize.js.map +1 -1
  34. package/lib/types/compiler/embedding-identity.d.ts +3 -0
  35. package/lib/types/compiler/embedding-identity.d.ts.map +1 -0
  36. package/lib/types/compiler/embedding-identity.js +24 -0
  37. package/lib/types/compiler/embedding-identity.js.map +1 -0
  38. package/lib/types/compiler/snapshot.d.ts +16 -0
  39. package/lib/types/compiler/snapshot.d.ts.map +1 -0
  40. package/lib/types/compiler/snapshot.js +2 -0
  41. package/lib/types/compiler/snapshot.js.map +1 -0
  42. package/lib/types/config-schema.d.ts +6 -3
  43. package/lib/types/config-schema.d.ts.map +1 -1
  44. package/lib/types/config-schema.js +6 -2
  45. package/lib/types/config-schema.js.map +1 -1
  46. package/lib/types/convert/host.d.ts +6 -5
  47. package/lib/types/convert/host.d.ts.map +1 -1
  48. package/lib/types/convert/host.js +58 -18
  49. package/lib/types/convert/host.js.map +1 -1
  50. package/lib/types/convert/ingest.d.ts +10 -1
  51. package/lib/types/convert/ingest.d.ts.map +1 -1
  52. package/lib/types/convert/ingest.js +68 -9
  53. package/lib/types/convert/ingest.js.map +1 -1
  54. package/lib/types/derive-contract.d.ts +3 -0
  55. package/lib/types/derive-contract.d.ts.map +1 -0
  56. package/lib/types/derive-contract.js +15 -0
  57. package/lib/types/derive-contract.js.map +1 -0
  58. package/lib/types/diagnosis/cards.d.ts +7 -3
  59. package/lib/types/diagnosis/cards.d.ts.map +1 -1
  60. package/lib/types/diagnosis/cards.js +13 -8
  61. package/lib/types/diagnosis/cards.js.map +1 -1
  62. package/lib/types/diagnosis/run.d.ts +4 -13
  63. package/lib/types/diagnosis/run.d.ts.map +1 -1
  64. package/lib/types/diagnosis/run.js +26 -16
  65. package/lib/types/diagnosis/run.js.map +1 -1
  66. package/lib/types/diagnosis/schema.js +2 -2
  67. package/lib/types/diagnosis/schema.js.map +1 -1
  68. package/lib/types/diagnosis/seed-source.d.ts +1 -1
  69. package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
  70. package/lib/types/diagnosis/seed-source.js +5 -4
  71. package/lib/types/diagnosis/seed-source.js.map +1 -1
  72. package/lib/types/diagnosis/seed.d.ts +4 -4
  73. package/lib/types/diagnosis/seed.d.ts.map +1 -1
  74. package/lib/types/diagnosis/seed.js +6 -8
  75. package/lib/types/diagnosis/seed.js.map +1 -1
  76. package/lib/types/diagnosis/store.d.ts +24 -18
  77. package/lib/types/diagnosis/store.d.ts.map +1 -1
  78. package/lib/types/diagnosis/store.js +130 -65
  79. package/lib/types/diagnosis/store.js.map +1 -1
  80. package/lib/types/diagnosis/types.d.ts +47 -15
  81. package/lib/types/diagnosis/types.d.ts.map +1 -1
  82. package/lib/types/diagnosis/types.js.map +1 -1
  83. package/lib/types/environment/dsh-python.d.ts +77 -0
  84. package/lib/types/environment/dsh-python.d.ts.map +1 -0
  85. package/lib/types/environment/dsh-python.js +139 -0
  86. package/lib/types/environment/dsh-python.js.map +1 -0
  87. package/lib/types/environment/install.d.ts +15 -0
  88. package/lib/types/environment/install.d.ts.map +1 -0
  89. package/lib/types/environment/install.js +297 -0
  90. package/lib/types/environment/install.js.map +1 -0
  91. package/lib/types/environment/manifest.d.ts +208 -0
  92. package/lib/types/environment/manifest.d.ts.map +1 -0
  93. package/lib/types/environment/manifest.js +26 -0
  94. package/lib/types/environment/manifest.js.map +1 -0
  95. package/lib/types/environment/model-catalog.d.ts +223 -0
  96. package/lib/types/environment/model-catalog.d.ts.map +1 -0
  97. package/lib/types/environment/model-catalog.js +129 -0
  98. package/lib/types/environment/model-catalog.js.map +1 -0
  99. package/lib/types/environment/process.d.ts +10 -0
  100. package/lib/types/environment/process.d.ts.map +1 -0
  101. package/lib/types/environment/process.js +54 -0
  102. package/lib/types/environment/process.js.map +1 -0
  103. package/lib/types/environment/selection.d.ts +9 -0
  104. package/lib/types/environment/selection.d.ts.map +1 -0
  105. package/lib/types/environment/selection.js +29 -0
  106. package/lib/types/environment/selection.js.map +1 -0
  107. package/lib/types/environment/service.d.ts +64 -0
  108. package/lib/types/environment/service.d.ts.map +1 -0
  109. package/lib/types/environment/service.js +424 -0
  110. package/lib/types/environment/service.js.map +1 -0
  111. package/lib/types/environment/types.d.ts +56 -0
  112. package/lib/types/environment/types.d.ts.map +1 -0
  113. package/lib/types/environment/types.js +2 -0
  114. package/lib/types/environment/types.js.map +1 -0
  115. package/lib/types/events.d.ts +76 -0
  116. package/lib/types/events.d.ts.map +1 -0
  117. package/lib/types/events.js +52 -0
  118. package/lib/types/events.js.map +1 -0
  119. package/lib/types/extract-contract.d.ts +3 -0
  120. package/lib/types/extract-contract.d.ts.map +1 -0
  121. package/lib/types/extract-contract.js +41 -0
  122. package/lib/types/extract-contract.js.map +1 -0
  123. package/lib/types/extract-model.d.ts +5 -0
  124. package/lib/types/extract-model.d.ts.map +1 -1
  125. package/lib/types/extract-model.js.map +1 -1
  126. package/lib/types/extract-table-contract.d.ts +3 -0
  127. package/lib/types/extract-table-contract.d.ts.map +1 -0
  128. package/lib/types/extract-table-contract.js +27 -0
  129. package/lib/types/extract-table-contract.js.map +1 -0
  130. package/lib/types/extract-table.d.ts +8 -0
  131. package/lib/types/extract-table.d.ts.map +1 -0
  132. package/lib/types/extract-table.js +158 -0
  133. package/lib/types/extract-table.js.map +1 -0
  134. package/lib/types/extraction-runs.d.ts +30 -6
  135. package/lib/types/extraction-runs.d.ts.map +1 -1
  136. package/lib/types/extraction-runs.js +69 -8
  137. package/lib/types/extraction-runs.js.map +1 -1
  138. package/lib/types/fs-atomic.d.ts +0 -7
  139. package/lib/types/fs-atomic.d.ts.map +1 -1
  140. package/lib/types/fs-atomic.js +17 -2
  141. package/lib/types/fs-atomic.js.map +1 -1
  142. package/lib/types/images.d.ts +26 -0
  143. package/lib/types/images.d.ts.map +1 -0
  144. package/lib/types/images.js +196 -0
  145. package/lib/types/images.js.map +1 -0
  146. package/lib/types/index.d.ts +2 -2
  147. package/lib/types/index.d.ts.map +1 -1
  148. package/lib/types/index.js +598 -209
  149. package/lib/types/index.js.map +1 -1
  150. package/lib/types/intent/engine.d.ts +12 -4
  151. package/lib/types/intent/engine.d.ts.map +1 -1
  152. package/lib/types/intent/engine.js +43 -5
  153. package/lib/types/intent/engine.js.map +1 -1
  154. package/lib/types/intent/host.d.ts +5 -8
  155. package/lib/types/intent/host.d.ts.map +1 -1
  156. package/lib/types/intent/host.js +56 -11
  157. package/lib/types/intent/host.js.map +1 -1
  158. package/lib/types/intent/scope.d.ts +25 -0
  159. package/lib/types/intent/scope.d.ts.map +1 -0
  160. package/lib/types/intent/scope.js +116 -0
  161. package/lib/types/intent/scope.js.map +1 -0
  162. package/lib/types/intent/tools.d.ts +24 -20
  163. package/lib/types/intent/tools.d.ts.map +1 -1
  164. package/lib/types/intent/tools.js +65 -23
  165. package/lib/types/intent/tools.js.map +1 -1
  166. package/lib/types/intent/types.d.ts +11 -4
  167. package/lib/types/intent/types.d.ts.map +1 -1
  168. package/lib/types/knowledge-bases.d.ts +11 -5
  169. package/lib/types/knowledge-bases.d.ts.map +1 -1
  170. package/lib/types/knowledge-bases.js +52 -20
  171. package/lib/types/knowledge-bases.js.map +1 -1
  172. package/lib/types/knowledge-contract.d.ts +32 -0
  173. package/lib/types/knowledge-contract.d.ts.map +1 -0
  174. package/lib/types/knowledge-contract.js +116 -0
  175. package/lib/types/knowledge-contract.js.map +1 -0
  176. package/lib/types/knowledge.d.ts +31 -9
  177. package/lib/types/knowledge.d.ts.map +1 -1
  178. package/lib/types/knowledge.js +137 -60
  179. package/lib/types/knowledge.js.map +1 -1
  180. package/lib/types/layout.d.ts +10 -29
  181. package/lib/types/layout.d.ts.map +1 -1
  182. package/lib/types/layout.js +17 -36
  183. package/lib/types/layout.js.map +1 -1
  184. package/lib/types/materialize-snapshot.d.ts +49 -0
  185. package/lib/types/materialize-snapshot.d.ts.map +1 -0
  186. package/lib/types/materialize-snapshot.js +2 -0
  187. package/lib/types/materialize-snapshot.js.map +1 -0
  188. package/lib/types/migrate.d.ts +11 -0
  189. package/lib/types/migrate.d.ts.map +1 -1
  190. package/lib/types/migrate.js +83 -2
  191. package/lib/types/migrate.js.map +1 -1
  192. package/lib/types/model-text.d.ts +58 -1
  193. package/lib/types/model-text.d.ts.map +1 -1
  194. package/lib/types/model-text.js +85 -30
  195. package/lib/types/model-text.js.map +1 -1
  196. package/lib/types/oplog.d.ts.map +1 -1
  197. package/lib/types/oplog.js +7 -0
  198. package/lib/types/oplog.js.map +1 -1
  199. package/lib/types/organize-contract.d.ts +18 -0
  200. package/lib/types/organize-contract.d.ts.map +1 -0
  201. package/lib/types/organize-contract.js +82 -0
  202. package/lib/types/organize-contract.js.map +1 -0
  203. package/lib/types/pipeline/engine.d.ts +66 -0
  204. package/lib/types/pipeline/engine.d.ts.map +1 -0
  205. package/lib/types/pipeline/engine.js +352 -0
  206. package/lib/types/pipeline/engine.js.map +1 -0
  207. package/lib/types/pipeline/store.d.ts +73 -0
  208. package/lib/types/pipeline/store.d.ts.map +1 -0
  209. package/lib/types/pipeline/store.js +143 -0
  210. package/lib/types/pipeline/store.js.map +1 -0
  211. package/lib/types/pipeline/types.d.ts +102 -0
  212. package/lib/types/pipeline/types.d.ts.map +1 -0
  213. package/lib/types/pipeline/types.js +5 -0
  214. package/lib/types/pipeline/types.js.map +1 -0
  215. package/lib/types/present.d.ts +2 -0
  216. package/lib/types/present.d.ts.map +1 -1
  217. package/lib/types/present.js +9 -4
  218. package/lib/types/present.js.map +1 -1
  219. package/lib/types/preset-ids.d.ts +4 -0
  220. package/lib/types/preset-ids.d.ts.map +1 -0
  221. package/lib/types/preset-ids.js +6 -0
  222. package/lib/types/preset-ids.js.map +1 -0
  223. package/lib/types/query-analysis.d.ts +13 -1
  224. package/lib/types/query-analysis.d.ts.map +1 -1
  225. package/lib/types/query-analysis.js +62 -6
  226. package/lib/types/query-analysis.js.map +1 -1
  227. package/lib/types/recall-rank.d.ts +3 -3
  228. package/lib/types/recall-rank.d.ts.map +1 -1
  229. package/lib/types/recall-rank.js +4 -4
  230. package/lib/types/recall-rank.js.map +1 -1
  231. package/lib/types/retention.d.ts +39 -0
  232. package/lib/types/retention.d.ts.map +1 -0
  233. package/lib/types/retention.js +42 -0
  234. package/lib/types/retention.js.map +1 -0
  235. package/lib/types/retrieval/vector-index.d.ts +32 -0
  236. package/lib/types/retrieval/vector-index.d.ts.map +1 -0
  237. package/lib/types/retrieval/vector-index.js +15 -0
  238. package/lib/types/retrieval/vector-index.js.map +1 -0
  239. package/lib/types/retrieval/visibility.d.ts +6 -0
  240. package/lib/types/retrieval/visibility.d.ts.map +1 -0
  241. package/lib/types/retrieval/visibility.js +11 -0
  242. package/lib/types/retrieval/visibility.js.map +1 -0
  243. package/lib/types/runtime.d.ts +272 -27
  244. package/lib/types/runtime.d.ts.map +1 -1
  245. package/lib/types/runtime.js +2751 -537
  246. package/lib/types/runtime.js.map +1 -1
  247. package/lib/types/settings-schema.d.ts +17 -15
  248. package/lib/types/settings-schema.d.ts.map +1 -1
  249. package/lib/types/settings-schema.js +50 -47
  250. package/lib/types/settings-schema.js.map +1 -1
  251. package/lib/types/settings-volatile.d.ts +4 -4
  252. package/lib/types/settings-volatile.js +4 -4
  253. package/lib/types/source-evidence.d.ts +14 -0
  254. package/lib/types/source-evidence.d.ts.map +1 -0
  255. package/lib/types/source-evidence.js +36 -0
  256. package/lib/types/source-evidence.js.map +1 -0
  257. package/lib/types/store/axis-references.d.ts +5 -0
  258. package/lib/types/store/axis-references.d.ts.map +1 -0
  259. package/lib/types/store/axis-references.js +106 -0
  260. package/lib/types/store/axis-references.js.map +1 -0
  261. package/lib/types/store/derived.d.ts +12 -1
  262. package/lib/types/store/derived.d.ts.map +1 -1
  263. package/lib/types/store/derived.js +71 -12
  264. package/lib/types/store/derived.js.map +1 -1
  265. package/lib/types/store/knowledge-db.d.ts +31 -1
  266. package/lib/types/store/knowledge-db.d.ts.map +1 -1
  267. package/lib/types/store/knowledge-db.js +252 -18
  268. package/lib/types/store/knowledge-db.js.map +1 -1
  269. package/lib/types/store/migrate.d.ts.map +1 -1
  270. package/lib/types/store/migrate.js +17 -6
  271. package/lib/types/store/migrate.js.map +1 -1
  272. package/lib/types/trajectory-types.d.ts +172 -0
  273. package/lib/types/trajectory-types.d.ts.map +1 -0
  274. package/lib/types/trajectory-types.js +36 -0
  275. package/lib/types/trajectory-types.js.map +1 -0
  276. package/lib/types/types.d.ts +59 -27
  277. package/lib/types/types.d.ts.map +1 -1
  278. package/lib/types/types.js +6 -15
  279. package/lib/types/types.js.map +1 -1
  280. package/lib/types/wiki-finalize.d.ts +16 -0
  281. package/lib/types/wiki-finalize.d.ts.map +1 -1
  282. package/lib/types/wiki-finalize.js +26 -0
  283. package/lib/types/wiki-finalize.js.map +1 -1
  284. package/lib/types/wiki-materialize.d.ts +4 -0
  285. package/lib/types/wiki-materialize.d.ts.map +1 -1
  286. package/lib/types/wiki-materialize.js +21 -7
  287. package/lib/types/wiki-materialize.js.map +1 -1
  288. package/lib/types/wiki-trial-types.d.ts +87 -0
  289. package/lib/types/wiki-trial-types.d.ts.map +1 -1
  290. package/lib/types/wiki-trial-types.js +0 -16
  291. package/lib/types/wiki-trial-types.js.map +1 -1
  292. package/lib/types/wiki-trial.d.ts +40 -16
  293. package/lib/types/wiki-trial.d.ts.map +1 -1
  294. package/lib/types/wiki-trial.js +337 -224
  295. package/lib/types/wiki-trial.js.map +1 -1
  296. package/package.json +29 -20
  297. package/python/prepare-models.py +19 -0
  298. package/python/requirements.txt +8 -6
  299. package/scripts/doctor.mjs +13 -19
  300. package/scripts/environment.mjs +29 -0
  301. package/scripts/setup-embedding.sh +15 -5
  302. package/src/agent-behavior-rules.ts +37 -0
  303. package/src/axis-registry.ts +234 -27
  304. package/src/card-organize.ts +69 -75
  305. package/src/client/AgentBehaviorRulesCard.tsx +65 -0
  306. package/src/client/AttributionEventGuide.tsx +41 -39
  307. package/src/client/AxisRegistrySection.tsx +204 -164
  308. package/src/client/CompileDialogs.tsx +44 -29
  309. package/src/client/DeleteDocumentDialog.tsx +36 -5
  310. package/src/client/DirectoryPicker.tsx +0 -10
  311. package/src/client/EnvironmentSetup.tsx +243 -0
  312. package/src/client/ExpansionGraph.tsx +13 -7
  313. package/src/client/ExtractionTasksPage.tsx +578 -0
  314. package/src/client/GuideArchitectureCanvas.tsx +470 -0
  315. package/src/client/ImportFilesDialog.tsx +2 -13
  316. package/src/client/IntentLogic.tsx +2 -2
  317. package/src/client/IntentTab.tsx +118 -47
  318. package/src/client/KnowledgeBaseTab.tsx +277 -197
  319. package/src/client/KnowledgeLayerSection.tsx +24 -6
  320. package/src/client/KnowledgePanel.tsx +58 -93
  321. package/src/client/KnowledgeSettingsDrawer.tsx +90 -0
  322. package/src/client/ListPager.tsx +15 -5
  323. package/src/client/MaterializeTrajectoryView.tsx +124 -0
  324. package/src/client/NodeSettings.tsx +210 -89
  325. package/src/client/NormalizeTrajectoryView.tsx +136 -0
  326. package/src/client/PipelineNotice.tsx +26 -0
  327. package/src/client/PreviewSections.tsx +118 -51
  328. package/src/client/RecallPanel.tsx +89 -85
  329. package/src/client/TabGuide.tsx +159 -184
  330. package/src/client/TrialReportView.tsx +391 -214
  331. package/src/client/TypeDefinitionDrawer.tsx +150 -23
  332. package/src/client/WikiCardPreview.tsx +62 -16
  333. package/src/client/WikiFlowPanel.tsx +4 -4
  334. package/src/client/arch/NodeLogicPanel.tsx +3 -4
  335. package/src/client/arch/model.ts +6 -9
  336. package/src/client/architecture.tsx +23 -33
  337. package/src/client/attribution/AttributionGraph.tsx +10 -6
  338. package/src/client/attribution/AttributionTab.tsx +136 -84
  339. package/src/client/attribution/CaseView.tsx +3 -1
  340. package/src/client/attribution/EventView.tsx +108 -0
  341. package/src/client/attribution/RunView.tsx +2 -2
  342. package/src/client/attribution/StageChrome.tsx +34 -3
  343. package/src/client/attribution/ViewSwitch.tsx +20 -5
  344. package/src/client/attribution/graph-options.ts +47 -39
  345. package/src/client/attribution/model.ts +17 -10
  346. package/src/client/config-scope.ts +3 -3
  347. package/src/client/graph/palette.ts +165 -0
  348. package/src/client/index.ts +49 -42
  349. package/src/client/pages.css +459 -135
  350. package/src/client/pages.tsx +37 -9
  351. package/src/client/problem-domains.ts +1 -1
  352. package/src/client/session-remote.ts +9 -0
  353. package/src/client/styles.css +205 -62
  354. package/src/client/wiki-trial.ts +44 -3
  355. package/src/compiler/embedding-identity.ts +21 -0
  356. package/src/compiler/snapshot.ts +17 -0
  357. package/src/config-schema.ts +8 -3
  358. package/src/convert/host.ts +56 -21
  359. package/src/convert/ingest.ts +74 -10
  360. package/src/derive-contract.ts +14 -0
  361. package/src/diagnosis/cards.ts +13 -8
  362. package/src/diagnosis/run.ts +27 -17
  363. package/src/diagnosis/schema.ts +2 -2
  364. package/src/diagnosis/seed-source.ts +5 -4
  365. package/src/diagnosis/seed.ts +6 -8
  366. package/src/diagnosis/store.ts +138 -69
  367. package/src/diagnosis/types.ts +34 -15
  368. package/src/environment/dsh-python.ts +156 -0
  369. package/src/environment/install.ts +244 -0
  370. package/src/environment/manifest.ts +28 -0
  371. package/src/environment/model-catalog.ts +147 -0
  372. package/src/environment/process.ts +54 -0
  373. package/src/environment/selection.ts +29 -0
  374. package/src/environment/service.ts +348 -0
  375. package/src/environment/types.ts +45 -0
  376. package/src/events.ts +108 -0
  377. package/src/extract-contract.ts +42 -0
  378. package/src/extract-model.ts +10 -1
  379. package/src/extract-table-contract.ts +26 -0
  380. package/src/extract-table.ts +132 -0
  381. package/src/extraction-runs.ts +70 -11
  382. package/src/fs-atomic.ts +19 -2
  383. package/src/images.ts +203 -0
  384. package/src/index.ts +553 -205
  385. package/src/intent/README.md +8 -6
  386. package/src/intent/engine.ts +44 -6
  387. package/src/intent/host.ts +59 -18
  388. package/src/intent/scope.ts +125 -0
  389. package/src/intent/tools.ts +70 -28
  390. package/src/intent/types.ts +11 -4
  391. package/src/knowledge-bases.ts +56 -20
  392. package/src/knowledge-contract.ts +114 -0
  393. package/src/knowledge.ts +138 -69
  394. package/src/layout.ts +17 -37
  395. package/src/materialize-snapshot.ts +46 -0
  396. package/src/migrate.ts +75 -2
  397. package/src/model-text.ts +109 -4
  398. package/src/oplog.ts +7 -0
  399. package/src/organize-contract.ts +83 -0
  400. package/src/pipeline/engine.ts +323 -0
  401. package/src/pipeline/store.ts +147 -0
  402. package/src/pipeline/types.ts +93 -0
  403. package/src/present.ts +10 -4
  404. package/src/preset-ids.ts +6 -0
  405. package/src/python/embedding_worker.py +66 -2
  406. package/src/query-analysis.ts +68 -8
  407. package/src/recall-rank.ts +5 -5
  408. package/src/retention.ts +76 -0
  409. package/src/retrieval/vector-index.ts +45 -0
  410. package/src/retrieval/visibility.ts +14 -0
  411. package/src/runtime.ts +2708 -567
  412. package/src/settings-schema.ts +11 -8
  413. package/src/settings-volatile.ts +4 -4
  414. package/src/source-evidence.ts +37 -0
  415. package/src/store/axis-references.ts +96 -0
  416. package/src/store/derived.ts +70 -12
  417. package/src/store/knowledge-db.ts +248 -17
  418. package/src/store/migrate.ts +16 -6
  419. package/src/trajectory-types.ts +142 -0
  420. package/src/types.ts +61 -35
  421. package/src/wiki-finalize.ts +28 -0
  422. package/src/wiki-materialize.ts +25 -6
  423. package/src/wiki-trial-types.ts +83 -1
  424. package/src/wiki-trial.ts +357 -185
  425. package/lib/types/intent/preset-tools.d.ts +0 -37
  426. package/lib/types/intent/preset-tools.d.ts.map +0 -1
  427. package/lib/types/intent/preset-tools.js +0 -45
  428. package/lib/types/intent/preset-tools.js.map +0 -1
  429. package/preset/tita-data-agent/agent.cordis.yml +0 -394
  430. package/preset/tita-data-agent/preset.yml +0 -3
  431. package/scripts/install-agent-preset.mjs +0 -92
  432. package/scripts/sync-agent-preset.mjs +0 -188
  433. package/src/client/AgentPresetCard.tsx +0 -247
  434. package/src/client/agent-preset.ts +0 -121
  435. package/src/client/convert/ConvertPanel.tsx +0 -583
  436. package/src/client/convert/styles.css +0 -138
  437. package/src/client/convert/styles.ts +0 -49
  438. package/src/intent/preset-tools.ts +0 -48
package/src/wiki-trial.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { normalizeKnowledgeExtraction } from './knowledge-contract.js'
1
2
  /**
2
3
  * Wiki 抽取试跑(0063):**调一次模型**按新方案判一遍,不落盘。
3
4
  *
@@ -29,10 +30,11 @@
29
30
  * 顺序里有一条硬规矩:**先把"知识是什么"搞清楚,再决定"这些知识怎么组织成 Wiki Card"。**
30
31
  * Wiki Card 是组织结果,不是知识合并结果。
31
32
  *
32
- * ## 只调一次
33
+ * ## 主抽取只调一次
33
34
  *
34
- * 七步是给人 review 的,不是给模型分的工。一次调用输入全文,输出一个 JSON:
35
+ * 七步是给人 review 的,不是给模型分的工。主抽取一次输入全文,输出一个 JSON:
35
36
  * `subject` / `knowledge` / `objects` / `actions` / `scenes` / `dropped`。
37
+ * 编译时若坐标不能直接命中,系统会把有候选的坐标合并成一次受约束的补判请求;没有候选时不补判。
36
38
  * 分块只在文档塞不进上下文时才做。
37
39
  *
38
40
  * ## 对象 × 动作矩阵
@@ -53,16 +55,18 @@ import type { ModelCall } from './extract-model.js'
53
55
  // 类型定义在 `wiki-trial-types.ts`:客户端也 import 那一份,所以只有一份定义。
54
56
  // 这里再导出一次,让 `./wiki-trial.js` 仍然是一个完整的入口。
55
57
  export type {
56
- TrialActor, TrialPhase, TrialSource, TrialKnowledge, TrialAxis, TrialPlacement,
58
+ TrialActor, TrialPhase, TrialSource, TrialKnowledge, TrialEvent, TrialEventStepKind, TrialAxis, TrialPlacement,
57
59
  TrialWikiCard, TrialMatrixGroup, TrialMatrixCell, TrialMatrixLoose, TrialMatrix,
58
- TrialStepItem, TrialStep, TrialCheck, TrialAudit, TrialReport,
60
+ TrialStepItem, TrialStep, TrialCheck, TrialAudit, TrialModelCallTrace, TrialReport,
61
+ TrialTokenUsage,
59
62
  } from './wiki-trial-types.js'
60
63
  import type {
61
- TrialActor, TrialPhase, TrialSource, TrialKnowledge, TrialAxis,
64
+ TrialActor, TrialPhase, TrialSource, TrialKnowledge, TrialEvent, TrialEventStepKind, TrialAxis,
62
65
  TrialWikiCard, TrialMatrixGroup, TrialMatrixCell, TrialMatrixLoose, TrialMatrix,
63
- TrialStepItem, TrialStep, TrialCheck, TrialReport,
66
+ TrialStepItem, TrialStep, TrialCheck, TrialModelCallTrace, TrialReport, TrialTokenUsage,
64
67
  } from './wiki-trial-types.js'
65
- import { AXIS_CONTRACT_LINES } from './axis-contract.js'
68
+ import { buildTrialSystem } from './extract-contract.js'
69
+ import { decodeExtractionTable } from './extract-table.js'
66
70
 
67
71
  /**
68
72
  * 抽取契约(0063 §5.6)。**这是契约,不是要抄进代码的字符串。**
@@ -74,103 +78,9 @@ import { AXIS_CONTRACT_LINES } from './axis-contract.js'
74
78
  * 2. 三个分量**可以全空** —— §九 明写"允许 Knowledge 没有领域坐标"。
75
79
  * 文档 §5.6 里"每条至少挂一个 object 或一个 action"与它冲突。
76
80
  */
77
- /**
78
- * 把骨架允许的小节拼成提示词里那一条。
79
- *
80
- * §5.3.2 说「`section` 的合法取值来自类型定义」—— 那就应当**下发**给它,帮助模型
81
- * 使用一致的小节名。不下发的话,那句「合法取值来自类型定义」只是一句写在文档里的愿望。
82
- */
83
- function sectionRule(sections: string[] | undefined): string[] {
84
- const legal = (sections ?? []).map(section => section.trim()).filter(Boolean)
85
- if (legal.length === 0) {
86
- return ['- section 是这条知识在卡片里的位置。拿不准就填「字段」。']
87
- }
88
- return [
89
- `- section 只能填这几个之一:${legal.join(' / ')},或者「字段」。`,
90
- '- 拿不准就填「字段」。**不要自创小节名** —— 卡片是按骨架校验的,编出来的名字对不上。',
91
- ]
92
- }
93
81
 
94
- /**
95
- * 抽取契约的 system 段。**骨架的小节随知识库变化,所以它是拼出来的,不是常量。**
96
- *
97
- * 编译链路用 `buildTrialSystem(profileSections)`;试跑面板用默认实例(不下发小节清单),
98
- * 因为那一层没有「本库骨架」这个概念。
99
- */
100
- export function buildTrialSystem(sections?: string[]): string {
101
- return trialSystemBody(sectionRule(sections))
102
- }
103
-
104
- const trialSystemBody = (positionRule: string[]): string => [
105
- '你在把一份业务文档拆成知识库要用的内容。知识以 Knowledge 为单位:',
106
- '一条可独立检索、验证或执行的最小知识单元。',
107
- '',
108
- '只回一个 JSON 对象,不要散文,不要代码围栏。',
109
- '',
110
- '输出形状:',
111
- '{',
112
- ' "subject": "这份文档讲的业务主体",',
113
- ' "knowledge": [',
114
- ' { "id": "k1",',
115
- ' "text": "一条可独立检索、验证或执行的最小知识单元",',
116
- ' "object": "对象名或 null", "action": "动作名或 null", "scene": "场景名或 null",',
117
- ' "section": "步骤", "order": 1,',
118
- ' "sources": [ { "quote": "原文里原样复制的一段", "heading_path": ["一级", "二级"] } ] }',
119
- ' ],',
120
- ' "objects": [ { "mention": "原文中的对象叫法", "definition": "一句话说清它指什么、边界在哪", "evidence": "支持它的原文短引文", "existing": "已有对象的名字(没有就省略这个键)" } ],',
121
- ' "actions": [ { "mention": "原文中的动作叫法", "definition": "同上", "evidence": "支持它的原文短引文", "existing": "…" } ],',
122
- ' "scenes": [ { "mention": "原文中的场景叫法", "definition": "同上", "evidence": "支持它的原文短引文", "existing": "…" } ],',
123
- ' "dropped": [ { "quote": "原文片段", "why": "为什么丢掉" } ]',
124
- '}',
125
- '',
126
- '# 知识:一条只讲一个单元',
127
- '- text 是一条能脱离上下文独立阅读的最小知识单元。事实要能判断真假,操作步骤要能被执行。',
128
- '- 一段原文里有几个独立单元就写几条。不要压成一条,也不要拆碎。',
129
- ' 不该拆:「SPM 由 spm_b、spm_c、spm_d 三部分构成」是一个完整事实。',
130
- ' 该拆:「spm_c 是楼层 ID」「楼层拖拽后 ID 会变化」「取数不能写死楼层 ID」是三条。',
131
- '- 不许推理出原文没有的事实。只做压缩、归一化、结构化。不许编原文没有的步骤。',
132
- '- 不要写「该字段」「上述」这种指代 —— 读者看不到上下文。',
133
- '',
134
- ...AXIS_CONTRACT_LINES,
135
- '',
136
- '- 一条 Knowledge 只挂一个 object、一个 action、一个 scene。那是它的家。',
137
- ' 别的卡片可以引用它 —— 那是卡片的事,不是坐标的事,不要为它多挂几个坐标。',
138
- '- 三个都留 null 是允许的。它不进矩阵、不成卡,但仍然可被全库命中。',
139
- ' 不要为了让它成卡而编一个坐标。',
140
- '- 不要造"动作实例"。「通天塔spm取数」不是动作,它是坐标(通天塔 × 取数 × SPM)推出来的卡名。',
141
- '',
142
- '# 坐标:定义与证据',
143
- '- 每个 object / action / scene 都写 `definition`:一句话说清**它指什么、边界在哪**。',
144
- ' 它是下一次抽取的参考 —— 名字只能让人认出写法相同的东西,定义才能认出「换了说法但同一个」。',
145
- ' 写「通天塔坑位click:通天塔投放位上的一次点击上报」这种能判断的句子,不要写「一个对象」这种空话。',
146
- '- 每个 object / action / scene 都写 `evidence`:从原文复制支持该坐标及其定义的最短片段。没有证据就不要抽这个坐标。',
147
- '- `definition` 只能总结原文和已有坐标定义能支持的范围,不要补造原文没有的属性、状态或因果。',
148
- '',
149
- '# 坐标:先看用户消息里的「已有坐标」',
150
- '- 你讲的东西如果就是清单里的某一个(哪怕原文用的是另一个说法),就写**它的名字**,',
151
- ' 并把 `existing` 写成那个名字。别造一个近似的名字 —— 那会让库里长出两个只在写法上不同的实体。',
152
- '- 清单里没有的才写新名字(`existing` 留空)。拿不准就写新名字:解析那一步会兜底。',
153
- '- 清单是**参考**,不是限制:库里没有的东西照样可以抽出来。',
154
- '',
155
- '# 来源:原样,别改写',
156
- '- quote 必须一个字都不改,从原文原样复制。',
157
- '- 挑最短的必要片段,不要整段照抄。',
158
- '- 一段原文支撑多条知识时,每条都要各自写上它的 quote。',
159
- '- heading_path 是从外到内的标题路径,只作定位线索。',
160
- '',
161
- '# 位置:这条知识在卡里排哪',
162
- ...positionRule,
163
- '- order 是同一小节内的第几位,从 1 开始。',
164
- '- 不要写卡片正文,也不要写导语、备注、警告。你只给"哪条知识放在哪个小节、排第几"。',
165
- '',
166
- '# 别把排版当结构',
167
- '- 原文里的小节标题只是作者写给"人顺着读"的排版,不是知识边界。',
168
- '- 一个标题里可以装几件事,一件事也可以跨几个标题。不要按标题切。',
169
- '',
170
- '- 不含知识的片段放 dropped,写清 why。不许悄悄丢。',
171
- '',
172
- '每条 quote 必须从原文原样复制,一个字都不要改。没有 quote 的判断一律不要。',
173
- ].join('\n')
82
+ /** 抽取契约的 system 段搬到了 `extract-contract.ts`(说明页要显示它,而这里进不了浏览器)。 */
83
+ export { buildTrialSystem } from './extract-contract.js'
174
84
 
175
85
  /** 默认的 system 段(不下发骨架小节)。试跑面板用它。 */
176
86
  export const TRIAL_SYSTEM = buildTrialSystem()
@@ -191,13 +101,14 @@ export function buildTrialPrompt(input: {
191
101
  axes?: AxisPromptBlock
192
102
  /** 这一库的坐标约定,人写的(0068)。 */
193
103
  note?: string
104
+ outputFormat?: 'table' | 'json'
194
105
  }): string {
195
106
  const sections: string[] = ['# 文档', `文件名:${input.title}`, '', input.text.trim(), '']
196
107
  const block = renderAxisBlock(input.axes)
197
108
  if (block !== '') sections.push(block, '')
198
109
  const note = (input.note ?? '').trim()
199
110
  if (note !== '') sections.push('# 本库的坐标约定(人写的,优先听它)', note, '')
200
- sections.push('# 输出', '按 system 里说的形状,回一个 JSON 对象。')
111
+ sections.push('# 输出', `按 system 中的版本协议返回${input.outputFormat === 'json' ? ' JSON 对象' : ' TOON 表格'},不要混用格式。`)
201
112
  return sections.join('\n')
202
113
  }
203
114
 
@@ -266,6 +177,8 @@ function renderAxisBlock(block: AxisPromptBlock | undefined): string {
266
177
  /** 从可能带 ``` 围栏的文本里取出 JSON。与 `extract-model.ts` 同构。 */
267
178
  function parseJsonObject(raw: string): unknown {
268
179
  const trimmed = raw.trim()
180
+ // Parse the transport first: code fences inside JSON string values are source data.
181
+ try { return JSON.parse(trimmed) } catch { /* tolerate an outer Markdown wrapper */ }
269
182
  const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/)
270
183
  const body = (fenced ? fenced[1] : trimmed).trim()
271
184
  try { return JSON.parse(body) } catch { /* 退一步取首尾大括号 */ }
@@ -294,7 +207,7 @@ function plainRecord(value: unknown): Record<string, unknown> {
294
207
  * 被误报为「清单外」并置空,最终整批 Knowledge 都变成「无坐标」。先把它们补回
295
208
  * 抽取清单,再走同一套 Entity Resolution,既不猜坐标,也不绕过注册表。
296
209
  */
297
- function axisMentionsFromKnowledge(raw: unknown): Record<AxisKind, Array<{ mention: string }>> {
210
+ function axisMentionsFromItems(raw: unknown): Record<AxisKind, Array<{ mention: string }>> {
298
211
  const out: Record<AxisKind, Array<{ mention: string }>> = { object: [], action: [], scene: [] }
299
212
  if (!Array.isArray(raw)) return out
300
213
  const fields: Array<{ kind: AxisKind; keys: string[] }> = [
@@ -315,6 +228,14 @@ function axisMentionsFromKnowledge(raw: unknown): Record<AxisKind, Array<{ menti
315
228
  return out
316
229
  }
317
230
 
231
+ function axisMentionsFromKnowledge(raw: unknown): Record<AxisKind, Array<{ mention: string }>> {
232
+ return axisMentionsFromItems(raw)
233
+ }
234
+
235
+ function axisMentionsFromEvents(raw: unknown): Record<AxisKind, Array<{ mention: string }>> {
236
+ return axisMentionsFromItems(raw)
237
+ }
238
+
318
239
  /** 顶层轴清单优先,Knowledge 坐标只补缺项;readAxes 会按归一化名称去重。 */
319
240
  function mergeAxisMentions(raw: unknown, supplements: Array<{ mention: string }>): unknown[] {
320
241
  return [...(Array.isArray(raw) ? raw : []), ...supplements]
@@ -604,6 +525,9 @@ function readKnowledge(raw: unknown, options: ReadKnowledgeOptions): { knowledge
604
525
  const order = typeof rawOrder === 'number' && Number.isFinite(rawOrder) ? Math.trunc(rawOrder) : 1
605
526
  knowledge.push({
606
527
  id,
528
+ kind: record.kind === 'procedure' ? 'procedure' : 'fact',
529
+ ...(record.scope_identity === true ? { scopeIdentity: true } : {}),
530
+ ...(record.data && typeof record.data === 'object' ? { data: record.data as TrialKnowledge['data'] } : {}),
607
531
  text,
608
532
  object,
609
533
  action,
@@ -617,6 +541,136 @@ function readKnowledge(raw: unknown, options: ReadKnowledgeOptions): { knowledge
617
541
  return { knowledge, problems }
618
542
  }
619
543
 
544
+ interface ReadEventOptions extends ReadKnowledgeOptions {}
545
+
546
+ function readTrialSources(raw: unknown, options: Pick<ReadEventOptions, 'text' | 'documentId'>): TrialSource[] {
547
+ if (!Array.isArray(raw)) return []
548
+ const sources: TrialSource[] = []
549
+ for (const item of raw) {
550
+ if (item === null || typeof item !== 'object') continue
551
+ const source = item as Record<string, unknown>
552
+ const quote = str(source.quote)
553
+ if (!quote) continue
554
+ const located = locateQuote(options.text, quote)
555
+ sources.push({
556
+ documentId: options.documentId,
557
+ quote,
558
+ ...(located.locator ? { locator: located.locator } : {}),
559
+ headingPath: strList(source.heading_path),
560
+ found: located.found,
561
+ })
562
+ }
563
+ return sources
564
+ }
565
+
566
+ /** 读取一次具体事件;缺失字段保留为空,不用模型补猜。 */
567
+ function readEvents(raw: unknown, options: ReadEventOptions): { events: TrialEvent[]; problems: string[] } {
568
+ if (!Array.isArray(raw)) return { events: [], problems: [] }
569
+ const objectMap = new Map(options.objects.flatMap(axis => [axis.name, ...axis.aliases].map(name => [normalizeAxisName(name), axis.name] as const)))
570
+ const actionMap = new Map(options.actions.flatMap(axis => [axis.name, ...axis.aliases].map(name => [normalizeAxisName(name), axis.name] as const)))
571
+ const sceneMap = new Map(options.scenes.flatMap(axis => [axis.name, ...axis.aliases].map(name => [normalizeAxisName(name), axis.name] as const)))
572
+ const events: TrialEvent[] = []
573
+ const problems: string[] = []
574
+ const ids = new Set<string>()
575
+ for (const entry of raw) {
576
+ if (entry === null || typeof entry !== 'object') continue
577
+ const record = entry as Record<string, unknown>
578
+ const id = str(record.id) || `e${events.length + 1}`
579
+ if (ids.has(id)) {
580
+ problems.push(`Event id「${id}」重复,后续条目已跳过`)
581
+ continue
582
+ }
583
+ ids.add(id)
584
+ const title = str(record.title)
585
+ const summary = str(record.summary) || title
586
+ if (!title && !summary) {
587
+ problems.push(`Event「${id}」没有标题或摘要,已跳过`)
588
+ continue
589
+ }
590
+ const coordinate = plainRecord(record.coordinate)
591
+ const resolve = (value: unknown, map: Map<string, string>, axis: string): string | null => {
592
+ const mention = str(value)
593
+ if (!mention) return null
594
+ const canonical = map.get(normalizeAxisName(mention))
595
+ if (canonical) return canonical
596
+ problems.push(`Event「${id}」的${axis}「${mention}」没有进入轴清单,已置空`)
597
+ return null
598
+ }
599
+ const object = resolve(coordinate.object_mention ?? coordinate.object, objectMap, '对象')
600
+ const action = resolve(coordinate.action_mention ?? coordinate.action, actionMap, '动作')
601
+ const scene = resolve(coordinate.scene_mention ?? coordinate.scene, sceneMap, '场景')
602
+
603
+ const timeline: TrialEvent['timeline'] = []
604
+ if (Array.isArray(record.timeline)) {
605
+ for (const [index, rawStep] of record.timeline.entries()) {
606
+ if (rawStep === null || typeof rawStep !== 'object') continue
607
+ const step = rawStep as Record<string, unknown>
608
+ const description = str(step.description)
609
+ if (!description) continue
610
+ const candidateType = str(step.type)
611
+ const type: TrialEventStepKind = ['observation', 'check', 'action', 'result', 'conclusion'].includes(candidateType)
612
+ ? candidateType as TrialEventStepKind
613
+ : 'observation'
614
+ const sources = readTrialSources(step.sources, options)
615
+ if (!sources.some(source => source.found)) problems.push(`Event「${id}」时间线第 ${index + 1} 项没有可核验的原文引文`)
616
+ timeline.push({
617
+ id: str(step.id) || `${id}.t${index + 1}`,
618
+ type,
619
+ description,
620
+ result: str(step.result) || null,
621
+ at: str(step.at) || null,
622
+ sources,
623
+ })
624
+ }
625
+ }
626
+ const sources = readTrialSources(record.sources, options)
627
+ const evidence = [...sources, ...timeline.flatMap(step => step.sources)]
628
+ if (!evidence.some(source => source.found)) {
629
+ problems.push(`Event「${id}」没有可核验的原文引文,已留在抽取轨迹但不进入 Event 真源`)
630
+ continue
631
+ }
632
+
633
+ const rawRootCause = record.root_cause
634
+ const root = typeof rawRootCause === 'string' ? { text: rawRootCause, status: 'suspected' } : plainRecord(rawRootCause)
635
+ const rootCause = str(root.text) || null
636
+ const rootStatus = str(root.status)
637
+ const rootCauseStatus: TrialEvent['rootCauseStatus'] = rootCause && (rootStatus === 'confirmed' || rootStatus === 'suspected')
638
+ ? rootStatus
639
+ : null
640
+ const missing = strList(record.missing)
641
+ const addMissing = (label: string, missingValue: unknown): void => {
642
+ if (!str(missingValue) && !missing.includes(label)) missing.push(label)
643
+ }
644
+ const occurredAt = str(record.occurred_at) || null
645
+ const symptom = str(record.symptom) || null
646
+ const outcome = str(record.outcome) || null
647
+ addMissing('发生时间', occurredAt)
648
+ addMissing('问题现象', symptom)
649
+ addMissing('根因', rootCause)
650
+ addMissing('处理结果', outcome)
651
+ addMissing('问题坐标', object || action || scene)
652
+ events.push({
653
+ id,
654
+ externalId: str(record.external_id) || null,
655
+ title: title || summary,
656
+ summary,
657
+ object,
658
+ action,
659
+ scene,
660
+ occurredAt,
661
+ symptom,
662
+ timeline,
663
+ rootCause,
664
+ rootCauseStatus,
665
+ outcome,
666
+ missing,
667
+ sources,
668
+ noCoordinate: !object && !action && !scene,
669
+ })
670
+ }
671
+ return { events, problems }
672
+ }
673
+
620
674
  // ------------------------------------------------------------ 本地推导
621
675
 
622
676
  /**
@@ -631,27 +685,39 @@ function readKnowledge(raw: unknown, options: ReadKnowledgeOptions): { knowledge
631
685
  * **没有"要不要建卡"这个问题。** 一个坐标 + 至少一条知识,就天然要建。
632
686
  * 三个分量全空的知识不成卡 —— 它没有坐标。
633
687
  */
634
- export function materialize(knowledge: TrialKnowledge[]): TrialWikiCard[] {
688
+ export function materialize(knowledge: TrialKnowledge[], events: TrialEvent[] = []): TrialWikiCard[] {
635
689
  const groups = new Map<string, TrialWikiCard>()
636
- for (const item of knowledge) {
637
- if (item.noCoordinate) continue
638
- const key = `${item.object ?? ''}\u0001${item.action ?? ''}\u0001${item.scene ?? ''}`
690
+ const groupOf = (object: string | null, action: string | null, scene: string | null): TrialWikiCard => {
691
+ const key = `${object ?? ''}\u0001${action ?? ''}\u0001${scene ?? ''}`
639
692
  let card = groups.get(key)
640
693
  if (!card) {
641
694
  card = {
642
- object: item.object,
643
- action: item.action,
644
- scene: item.scene,
645
- name: cardNameFor(item.object, item.action, item.scene),
695
+ object,
696
+ action,
697
+ scene,
698
+ name: cardNameFor(object, action, scene),
646
699
  placements: [],
700
+ events: [],
647
701
  }
648
702
  groups.set(key, card)
649
703
  }
704
+ return card
705
+ }
706
+ for (const item of knowledge) {
707
+ if (item.noCoordinate) continue
708
+ const card = groupOf(item.object, item.action, item.scene)
650
709
  card.placements.push({ item: item.id, section: item.section, order: item.order })
651
710
  }
711
+ for (const event of events) {
712
+ if (event.noCoordinate) continue
713
+ const key = `${event.object ?? ''}\u0001${event.action ?? ''}\u0001${event.scene ?? ''}`
714
+ // A WikiCard is created by Knowledge. Event only adds a reference to an existing knowledge group.
715
+ groups.get(key)?.events.push(event.id)
716
+ }
652
717
  for (const card of groups.values()) {
653
718
  card.placements.sort((left, right) =>
654
719
  left.section.localeCompare(right.section) || left.order - right.order || left.item.localeCompare(right.item))
720
+ card.events.sort((left, right) => left.localeCompare(right))
655
721
  }
656
722
  return [...groups.values()]
657
723
  }
@@ -671,15 +737,15 @@ export function materialize(knowledge: TrialKnowledge[]): TrialWikiCard[] {
671
737
  * 组是**最小的可点单位**:点一个数字要看到的就是那一组的知识。
672
738
  */
673
739
  export function buildMatrix(
674
- objects: string[], actions: string[], knowledge: TrialKnowledge[], wikiCards: TrialWikiCard[],
740
+ objects: string[], actions: string[], knowledge: TrialKnowledge[], wikiCards: TrialWikiCard[], events: TrialEvent[] = [],
675
741
  ): TrialMatrix {
676
742
  /** 一组一组的桶。键是 `object\u0001action\u0001scene`。 */
677
- const buckets = new Map<string, { object: string | null; action: string | null; scene: string; knowledge: number; items: string[] }>()
743
+ const buckets = new Map<string, { object: string | null; action: string | null; scene: string; knowledge: number; events: number; items: string[]; eventItems: string[] }>()
678
744
  const bucketOf = (object: string | null, action: string | null, scene: string) => {
679
745
  const key = `${object ?? '\u0000'}\u0001${action ?? '\u0000'}\u0001${scene}`
680
746
  const hit = buckets.get(key)
681
747
  if (hit) return hit
682
- const created = { object, action, scene, knowledge: 0, items: [] as string[] }
748
+ const created = { object, action, scene, knowledge: 0, events: 0, items: [] as string[], eventItems: [] as string[] }
683
749
  buckets.set(key, created)
684
750
  return created
685
751
  }
@@ -690,6 +756,12 @@ export function buildMatrix(
690
756
  bucket.knowledge += 1
691
757
  bucket.items.push(item.id)
692
758
  }
759
+ for (const event of events) {
760
+ if (event.noCoordinate) continue
761
+ const bucket = bucketOf(event.object, event.action, event.scene ?? '')
762
+ bucket.events += 1
763
+ bucket.eventItems.push(event.id)
764
+ }
693
765
 
694
766
  // 卡名叠进它自己那一组。一个 `(object, action, scene)` 至多一张卡,所以是赋值不是追加。
695
767
  const cardOf = new Map<string, string>()
@@ -704,8 +776,10 @@ export function buildMatrix(
704
776
  out.push({
705
777
  scene: bucket.scene,
706
778
  knowledge: bucket.knowledge,
779
+ events: bucket.events,
707
780
  card: cardOf.get(`${object ?? '\u0000'}\u0001${action ?? '\u0000'}\u0001${bucket.scene}`) ?? '',
708
781
  items: bucket.items,
782
+ eventItems: bucket.eventItems,
709
783
  })
710
784
  }
711
785
  // 场景名的阅读顺序:先空场景(它是「没有细分」的那一组),再按名字。
@@ -718,7 +792,7 @@ export function buildMatrix(
718
792
  for (const action of actions) {
719
793
  const groups = groupsFor(object, action)
720
794
  if (groups.length === 0) continue
721
- cells.push({ object, action, groups, knowledge: groups.reduce((sum, group) => sum + group.knowledge, 0) })
795
+ cells.push({ object, action, groups, knowledge: groups.reduce((sum, group) => sum + group.knowledge, 0), events: groups.reduce((sum, group) => sum + group.events, 0) })
722
796
  }
723
797
  }
724
798
 
@@ -726,12 +800,12 @@ export function buildMatrix(
726
800
  const noAction: TrialMatrixLoose[] = []
727
801
  for (const object of objects) {
728
802
  const groups = groupsFor(object, null)
729
- if (groups.length > 0) noAction.push({ name: object, groups, knowledge: groups.reduce((sum, group) => sum + group.knowledge, 0) })
803
+ if (groups.length > 0) noAction.push({ name: object, groups, knowledge: groups.reduce((sum, group) => sum + group.knowledge, 0), events: groups.reduce((sum, group) => sum + group.events, 0) })
730
804
  }
731
805
  const noObject: TrialMatrixLoose[] = []
732
806
  for (const action of actions) {
733
807
  const groups = groupsFor(null, action)
734
- if (groups.length > 0) noObject.push({ name: action, groups, knowledge: groups.reduce((sum, group) => sum + group.knowledge, 0) })
808
+ if (groups.length > 0) noObject.push({ name: action, groups, knowledge: groups.reduce((sum, group) => sum + group.knowledge, 0), events: groups.reduce((sum, group) => sum + group.events, 0) })
735
809
  }
736
810
 
737
811
  return {
@@ -741,6 +815,7 @@ export function buildMatrix(
741
815
  noAction,
742
816
  noObject,
743
817
  noCoordinate: knowledge.filter(item => item.noCoordinate).length,
818
+ noCoordinateEvents: events.filter(item => item.noCoordinate).length,
744
819
  withKnowledge: cells.filter(cell => cell.knowledge > 0).length,
745
820
  filled: cells.length,
746
821
  slots: objects.length * actions.length,
@@ -789,7 +864,7 @@ function findTwins(knowledge: TrialKnowledge[]): Array<{ key: string; ids: strin
789
864
  * 每一步给两样:`rows`(按什么判、缺什么)和 `items`(**逐条判出的实际值**)。
790
865
  * 试跑面板是给人逐步 review 的,只有统计没有值就没法 review。
791
866
  *
792
- * 第 2、3 步里如实写明单文档试跑缺什么:归一缺库内注册表,同义与冲突缺第二次模型调用。
867
+ * 第 2、3 步里如实写明能力边界:轴归一需要库内注册表,候选歧义可在编译时补判;知识同义与冲突仍不做模型裁决。
793
868
  */
794
869
  export function buildReport(input: {
795
870
  title: string
@@ -799,18 +874,19 @@ export function buildReport(input: {
799
874
  actions: TrialAxis[]
800
875
  scenes: TrialAxis[]
801
876
  knowledge: TrialKnowledge[]
877
+ events: TrialEvent[]
802
878
  wikiCards: TrialWikiCard[]
803
879
  matrix: TrialMatrix
804
880
  problems: string[]
805
881
  dropped: Array<{ quote: string; why: string }>
806
- model: { ok: boolean; why?: string; raw?: string }
882
+ model: TrialReport['model']
807
883
  promptUser: string
808
884
  /** 这一次**真的发出去**的 system 段。面板要摊开它,不能显示一份理想版。 */
809
885
  system?: string
810
886
  /** 这一次下发了多少已有坐标(0068)。省略 = 没有下发。 */
811
887
  axesContext?: { injected: number; totals: number; truncated: number; selection?: 'full' | 'relevant' | 'head' }
812
888
  }): TrialReport {
813
- const { objects, actions, scenes, knowledge, wikiCards, matrix, dropped } = input
889
+ const { objects, actions, scenes, knowledge, events, wikiCards, matrix, dropped } = input
814
890
  const headings = scanHeadings(input.text)
815
891
  const axes = [...objects, ...actions, ...scenes]
816
892
  const aliased = axes.filter(axis => axis.aliases.length > 0)
@@ -827,8 +903,14 @@ export function buildReport(input: {
827
903
  if (!source.found) invented.push({ id: item.id, quote: source.quote })
828
904
  }
829
905
  }
906
+ for (const event of events) {
907
+ for (const source of [...event.sources, ...event.timeline.flatMap(step => step.sources)]) {
908
+ if (!source.found) invented.push({ id: event.id, quote: source.quote })
909
+ }
910
+ }
830
911
  const noSource = knowledge.filter(item => item.sources.length === 0)
831
912
  const noCoordinate = knowledge.filter(item => item.noCoordinate)
913
+ const noCoordinateEvents = events.filter(item => item.noCoordinate)
832
914
  const twins = findTwins(knowledge)
833
915
  const collisions = findCollisions(wikiCards)
834
916
  const sections = new Set(knowledge.flatMap(item => item.section))
@@ -837,30 +919,38 @@ export function buildReport(input: {
837
919
  const steps: TrialStep[] = [
838
920
  {
839
921
  id: 'extract', order: 1, name: '语义抽取', phase: '① Extract · 抽取', actor: '模型',
840
- needsHuman: noCoordinate.length > 0 || noSource.length > 0 || knowledge.length === 0,
841
- summary: `${knowledge.length} 条知识 · ${objects.length} 对象 · ${actions.length} 动作 · ${scenes.length} 场景 · 一次调用`,
922
+ needsHuman: noCoordinate.length > 0 || noSource.length > 0 || knowledge.length === 0 || events.some(event => event.missing.length > 0),
923
+ summary: `${knowledge.length} 条 Knowledge · ${events.length} 个 Event · ${objects.length} 对象 · ${actions.length} 动作 · ${scenes.length} 场景 · 一次调用`,
842
924
  rows: [
843
925
  { label: '输入', value: `${input.text.split(/\r?\n/).length} 行 / ${countChars(input.text)} 字 / ${headings.length} 个标题,全文进去,不切块` },
844
- { label: '一次调用出全部', value: '不拆成"先抽对象再抽动作再抽知识" —— 拆开要把全文重发多遍,判"这段讲什么"时手上没有原话' },
926
+ { label: '一次调用出全部', value: '同一份 Document 并行抽取 Knowledge 与 Event,再提取 Object / Action / Scene mentions' },
845
927
  { label: '这一步不解决', value: '这是不是已有知识(第 3 步)· 这些名字是不是已有实体(第 2 步)· 应该生成哪张卡(第 5 步)' },
846
928
  { label: '五条规则', value: '一条只讲一个单元 · 不推理出原文没有的事实 · 不写「该字段」这种指代 · 坐标各一个 · quote 原样复制' },
847
- { label: '没有出处', value: noSource.length > 0 ? `${noSource.length} 条知识一个 quote 都没给` : '无' },
929
+ { label: '没有出处', value: noSource.length > 0 ? `${noSource.length} 条 Knowledge 一个 quote 都没给` : 'Knowledge / Event 都有可核验出处' },
848
930
  ],
849
- items: knowledge.map(item => makeItem(
931
+ items: [
932
+ ...knowledge.map(item => makeItem(
850
933
  item.id,
851
934
  shortQuote(item.text),
852
935
  `${coordLabel(item.object, item.action, item.scene)} · ${item.sources.length} 条出处`,
853
936
  item.noCoordinate || item.sources.length === 0 ? 'warn' : undefined,
854
- )),
937
+ )),
938
+ ...events.map(event => makeItem(
939
+ `Event ${event.id}`,
940
+ event.title,
941
+ `${coordLabel(event.object, event.action, event.scene)} · ${event.timeline.length} 个时间线节点 · ${event.missing.length} 项待补`,
942
+ event.noCoordinate || event.missing.length > 0 ? 'warn' : undefined,
943
+ )),
944
+ ],
855
945
  },
856
946
  {
857
947
  id: 'entity', order: 2, name: '轴实体归一', phase: '② Resolve · 归一', actor: '系统',
858
948
  needsHuman: aliased.length > 0 || ambiguous.length > 0 || created.length > 0,
859
949
  summary: `${axes.length} 个轴实体 · 命中 ${matched.length} · 新建候选 ${created.length} · 不唯一 ${ambiguous.length}`,
860
950
  rows: [
861
- { label: '顺序', value: '归一化(大小写 / 空格 / 符号)→ 作用域内 canonical → 作用域内 alias → 全局唯一 → 新建候选 + 例外上报' },
951
+ { label: '顺序', value: '注册表直接命中 → 对有候选的歧义坐标合并一次模型补判 → 未命中时新建候选' },
862
952
  { label: '三个轴同一套', value: 'Object / Action / Scene 走同一套流程,没有哪个更特殊' },
863
- { label: '不唯一就不猜', value: '一个 mention 在多个作用域里都命中时保留原样并报出来。猜错等于把知识挂到错误的对象上,而那个错误会一路传到卡名' },
953
+ { label: '不唯一就不猜', value: '模型只在给定候选中选择;证据不足时保留歧义。猜错等于把知识挂到错误的对象上,而那个错误会一路传到卡名' },
864
954
  { label: '不机械拼前缀', value: '只有原文、章节、SourceRaw 元数据提供了作用域证据,才生成全限定名 —— 「通天塔」+ 每个对象会把「楼层」错加成「通天塔楼层」。人写的指导随已有坐标一起下发,由模型据此判断' },
865
955
  /*
866
956
  * 「这一次发了多少参考」必须露出来(0068)。模型看不到某条坐标时会以为库里没有它,
@@ -869,8 +959,10 @@ export function buildReport(input: {
869
959
  { label: '已有坐标', value: input.axesContext === undefined
870
960
  ? '这一次没有下发(没接注册表,或者库里还没有实体)—— 模型只能自己造名字'
871
961
  : `下发 ${input.axesContext.injected} / 共 ${input.axesContext.totals} 条${input.axesContext.truncated > 0 ? ` · 未下发 ${input.axesContext.truncated} 条` : ''}${input.axesContext.selection === 'relevant' ? ' · 当前文档相关候选' : input.axesContext.selection === 'full' ? ' · 全集' : ''},含名字、别名、定义与人写的指导` },
872
- { label: '模型可以指认', value: '模型说「这就是已有的某个」,代码会验(查得到 / 同类 / 没被并);验不过就退回机械解析 —— 与卡片层 existing_id 同一条纪律' },
873
- { label: '新建的是候选', value: '没命中就建一个 status=candidate 的实体,等人确认。入库在前、确认在后 —— 与卡片 review 同一条纪律' },
962
+ { label: '模型补判', value: input.model.axisResolution
963
+ ? '本次对无法直接命中的坐标发出了一次合并请求;模型只能返回候选 ID,代码会验(查得到 / 同类 / 没被并)'
964
+ : '本次坐标都可直接解析,或没有可比较的候选,因此没有额外模型请求' },
965
+ { label: '新建的是候选', value: '模型确认没有匹配项后才建 status=candidate;证据不足时保留歧义,等人确认' },
874
966
  { label: '三类绝不进别名', value: '上位概念(「通天塔spm取数」不能含「通天塔」)· 组成部分(不能含「spm取数」)· 相关但不同(不能含「通天塔埋点」)' },
875
967
  { label: '注册表', value: axes.some(axis => axis.outcome === 'no-registry')
876
968
  ? '这次没有查到库内注册表(这个部署上没有,或调用方没给)。所以只判得动文档内的写法变体'
@@ -918,20 +1010,20 @@ export function buildReport(input: {
918
1010
  },
919
1011
  {
920
1012
  id: 'materialize', order: 5, name: '按坐标成卡', phase: '③ Materialize · 物化', actor: '系统',
921
- needsHuman: collisions.length > 0 || noCoordinate.length > 0,
922
- summary: `${wikiCards.length} 张 Wiki Card · ${placements} 条知识落位 · ${noCoordinate.length} 条无坐标不成卡`,
1013
+ needsHuman: collisions.length > 0 || noCoordinate.length > 0 || events.some(event => event.noCoordinate),
1014
+ summary: `${wikiCards.length} 张 Wiki Card · ${placements} 条 Knowledge · ${events.filter(event => !event.noCoordinate).length} 个 Event`,
923
1015
  rows: [
924
1016
  { label: '三步', value: '按 (object, action, scene) 分组 → 每组物化成一张 Wiki Card → 生成自然语言标题' },
925
1017
  { label: '标题组织', value: '完整坐标写成「对象在场景下的动作」;缺少分量时退化为通顺的短标题;旧拼接标题保留为 alias' },
926
1018
  { label: '为什么不让模型做', value: '坐标是卡片主键 · 标题是确定的展示投影 · 模型判会漂移(同一坐标每次给不同标题会污染召回)' },
927
- { label: '没有"要不要建卡"', value: '一个坐标 + 至少一条知识,就天然要建。模型只回答判断类问题,机械类问题交给系统' },
928
- { label: '不成卡的那些', value: noCoordinate.length > 0 ? `${noCoordinate.length} 条知识没有坐标,不进矩阵也不成卡 —— 它们仍然可被全库命中` : '无' },
1019
+ { label: '卡片由 Knowledge 生成', value: '坐标下至少有一条 Knowledge 才物化 WikiCard;同坐标 Event 只建立引用,不单独建卡' },
1020
+ { label: '不成卡的那些', value: noCoordinate.length + events.filter(event => event.noCoordinate).length > 0 ? `${noCoordinate.length} 条 Knowledge、${events.filter(event => event.noCoordinate).length} 个 Event 缺少坐标,不归入坐标卡` : '无' },
929
1021
  ],
930
1022
  items: wikiCards.length > 0
931
1023
  ? wikiCards.map(card => makeItem(
932
1024
  coordLabel(card.object, card.action, card.scene),
933
1025
  card.name,
934
- `${card.placements.length} 条知识`,
1026
+ `${card.placements.length} 条 Knowledge · ${card.events.length} 个 Event`,
935
1027
  ))
936
1028
  : [makeItem('—', '没有成卡', '知识一条都没有坐标,或者模型没给出知识', 'warn')],
937
1029
  },
@@ -971,7 +1063,7 @@ export function buildReport(input: {
971
1063
  ],
972
1064
  items: [
973
1065
  makeItem('召回单元', `${sections.size} 个 section`, '卡片按 ## 小节切成一节一条'),
974
- makeItem('Knowledge', `${knowledge.length} 条`, '落在这些 section 上的真源(试跑不建索引)'),
1066
+ makeItem('Knowledge / Event', `${knowledge.length} / ${events.length}`, '两类并列真源;Knowledge 生成 WikiCard,Event 通过引用关联'),
975
1067
  makeItem('轴注册表', `${axes.length} 条`, 'canonical name + aliases —— 不是索引'),
976
1068
  ],
977
1069
  },
@@ -1012,6 +1104,7 @@ export function buildReport(input: {
1012
1104
  exceptions.push({ step: '作用域不确定', message: `「${axis.name}」在多个作用域里都命中,没有足够证据选一个 —— ${axis.how}` })
1013
1105
  }
1014
1106
  for (const item of noCoordinate) exceptions.push({ step: '无坐标', message: `「${shortQuote(item.text)}」三个坐标分量都空。它不进矩阵、不成卡,但仍可被全库命中 —— 要人决定补坐标还是单独归档` })
1107
+ for (const event of noCoordinateEvents) exceptions.push({ step: 'Event 无坐标', message: `Event「${shortQuote(event.title)}」没有坐标,因此没有归入 Wiki Card;Event 本身和出处仍保留` })
1015
1108
  for (const twin of twins) exceptions.push({ step: '同义候选', message: `${twin.ids.join(' / ')} 文本归一化后相同,要人确认是不是同一条知识` })
1016
1109
  for (const collision of collisions) {
1017
1110
  const where = collision.cards.map(card => coordLabel(card.object, card.action, card.scene)).join(' / ')
@@ -1029,6 +1122,7 @@ export function buildReport(input: {
1029
1122
  actions,
1030
1123
  scenes,
1031
1124
  knowledge,
1125
+ events,
1032
1126
  wikiCards,
1033
1127
  matrix,
1034
1128
  checks,
@@ -1046,16 +1140,17 @@ function emptyReport(input: {
1046
1140
  title: string; text: string; subject: string; promptUser: string
1047
1141
  system?: string
1048
1142
  axesContext?: { injected: number; totals: number; truncated: number; selection?: 'full' | 'relevant' | 'head' }
1049
- model: { ok: boolean; why?: string; raw?: string }
1143
+ model: TrialReport['model']
1050
1144
  }): TrialReport {
1051
1145
  const objects: TrialAxis[] = []
1052
1146
  const actions: TrialAxis[] = []
1053
1147
  const scenes: TrialAxis[] = []
1054
1148
  const knowledge: TrialKnowledge[] = []
1149
+ const events: TrialEvent[] = []
1055
1150
  const wikiCards: TrialWikiCard[] = []
1056
1151
  return buildReport({
1057
1152
  title: input.title, text: input.text, subject: input.subject,
1058
- objects, actions, scenes, knowledge, wikiCards, dropped: [],
1153
+ objects, actions, scenes, knowledge, events, wikiCards, dropped: [],
1059
1154
  matrix: buildMatrix([], [], [], []),
1060
1155
  problems: [],
1061
1156
  model: input.model, promptUser: input.promptUser,
@@ -1087,6 +1182,11 @@ export async function runWikiTrial(input: {
1087
1182
  * 能新建候选);不给就退化成文档内的机械归一,报告里写 `no-registry`。
1088
1183
  */
1089
1184
  resolveAxis?: TrialAxisResolver
1185
+ /**
1186
+ * 编译链路的批量解析入口。先由注册表直接解析,再把仍有候选的 mention 合并成一次模型补判。
1187
+ * 试跑面板保留 `resolveAxis` 的单条解析路径。
1188
+ */
1189
+ resolveAxisSet?: TrialAxisSetResolver
1090
1190
  /**
1091
1191
  * 已有坐标,**下发进用户提示词**(0068)。
1092
1192
  *
@@ -1108,14 +1208,20 @@ export async function runWikiTrial(input: {
1108
1208
  * 试跑面板不传,用默认值。
1109
1209
  */
1110
1210
  sessionId?: string
1211
+ /** 实际模型选择。凭据不进入轨迹。 */
1212
+ modelRequest?: Omit<NonNullable<TrialReport['model']['request']>, 'sessionId'>
1111
1213
  call?: ModelCall
1112
1214
  callWhy?: string
1215
+ outputFormat?: 'table' | 'json'
1216
+ onDelta?: (text: string) => void
1113
1217
  }): Promise<TrialReport> {
1114
1218
  const subject = (input.subject ?? '').trim()
1115
1219
  const docId = (input.docId ?? '').trim()
1116
1220
  // 提示词先组装好再走分支:模型不可用时它照样要回给界面 —— 用户要看的正是"本来会发出去什么"。
1117
- const promptUser = buildTrialPrompt({ title: input.title, text: input.text, ...(input.axes === undefined ? {} : { axes: input.axes }), ...(input.note ? { note: input.note } : {}) })
1118
- const system = buildTrialSystem(input.sections)
1221
+ const promptUser = buildTrialPrompt({ title: input.title, text: input.text, ...(input.axes === undefined ? {} : { axes: input.axes }), ...(input.note ? { note: input.note } : {}), outputFormat: input.outputFormat })
1222
+ const system = buildTrialSystem(input.sections, input.outputFormat)
1223
+ const sessionId = input.sessionId ?? 'knowledge:trial'
1224
+ const request = { ...input.modelRequest, sessionId }
1119
1225
  const axesContext = input.axes === undefined ? undefined : {
1120
1226
  injected: (['object', 'action', 'scene'] as AxisKind[]).reduce((sum, kind) => sum + (input.axes!.entries[kind]?.length ?? 0), 0),
1121
1227
  totals: (['object', 'action', 'scene'] as AxisKind[]).reduce((sum, kind) => sum + (input.axes!.totals[kind] ?? 0), 0),
@@ -1127,22 +1233,37 @@ export async function runWikiTrial(input: {
1127
1233
  return emptyReport({
1128
1234
  title: input.title, text: input.text, subject: subject || input.title, promptUser, system,
1129
1235
  axesContext,
1130
- model: { ok: false, why: input.callWhy ?? '模型不可用' },
1236
+ model: { ok: false, why: input.callWhy ?? '模型不可用', request },
1131
1237
  })
1132
1238
  }
1133
1239
 
1134
1240
  let raw = ''
1241
+ let usage: TrialTokenUsage | undefined
1135
1242
  try {
1136
- raw = await input.call({ system, prompt: promptUser, sessionId: input.sessionId ?? 'knowledge:trial' })
1243
+ raw = await input.call({ system, prompt: promptUser, sessionId, onUsage: value => { usage = value }, onDelta: text => { raw += text; input.onDelta?.(text) } })
1137
1244
  } catch (cause) {
1138
1245
  return emptyReport({
1139
1246
  title: input.title, text: input.text, subject: subject || input.title, promptUser, system,
1140
1247
  axesContext,
1141
- model: { ok: false, why: `调用模型失败:${cause instanceof Error ? cause.message : String(cause)}` },
1248
+ model: {
1249
+ ok: false,
1250
+ why: `调用模型失败:${cause instanceof Error ? cause.message : String(cause)}`,
1251
+ request,
1252
+ ...(raw ? { response: raw } : {}),
1253
+ ...(usage ? { usage } : {}),
1254
+ },
1142
1255
  })
1143
1256
  }
1144
1257
 
1145
- const parsed = parseJsonObject(raw)
1258
+ let parsed: unknown
1259
+ let outputFormat: 'table' | 'json' = 'json'
1260
+ let parseError = ''
1261
+ const body = raw.trim().replace(/^```(?:toon|text)?\s*\n/, '').replace(/\n```\s*$/, '')
1262
+ try {
1263
+ if (/^format:\s*knowledge-table-v[12]\s*$/m.test(body)) { parsed = decodeExtractionTable(body); outputFormat = 'table' }
1264
+ else parsed = parseJsonObject(raw)
1265
+ if (parsed) parsed = normalizeKnowledgeExtraction(parsed)
1266
+ } catch (cause) { parseError = cause instanceof Error ? cause.message : String(cause) }
1146
1267
  const record = parsed !== null && typeof parsed === 'object' ? parsed as Record<string, unknown> : undefined
1147
1268
  if (!record) {
1148
1269
  return emptyReport({
@@ -1150,7 +1271,10 @@ export async function runWikiTrial(input: {
1150
1271
  axesContext,
1151
1272
  model: {
1152
1273
  ok: false,
1153
- why: '模型的回答里没有 JSON',
1274
+ why: parseError || '模型返回不是有效的抽取表格或兼容 JSON',
1275
+ request,
1276
+ response: raw,
1277
+ ...(usage ? { usage } : {}),
1154
1278
  // 把模型实际说的那一段带上:只报「不是 JSON」的话,人不知道该去改什么。
1155
1279
  ...(raw.trim() ? { raw: raw.trim().replace(/\s+/g, ' ').slice(0, 160) } : {}),
1156
1280
  },
@@ -1164,9 +1288,10 @@ export async function runWikiTrial(input: {
1164
1288
  */
1165
1289
  const mentions = plainRecord(record.axis_mentions)
1166
1290
  const knowledgeAxes = axisMentionsFromKnowledge(record.knowledge)
1167
- const objectRead = readAxes(mergeAxisMentions(mentions.objects ?? record.objects, knowledgeAxes.object), 'object')
1168
- const actionRead = readAxes(mergeAxisMentions(mentions.actions ?? record.actions, knowledgeAxes.action), 'action')
1169
- const sceneRead = readAxes(mergeAxisMentions(mentions.scenes ?? record.scenes, knowledgeAxes.scene), 'scene')
1291
+ const eventAxes = axisMentionsFromEvents(record.events)
1292
+ const objectRead = readAxes(mergeAxisMentions(mentions.objects ?? record.objects, [...knowledgeAxes.object, ...eventAxes.object]), 'object')
1293
+ const actionRead = readAxes(mergeAxisMentions(mentions.actions ?? record.actions, [...knowledgeAxes.action, ...eventAxes.action]), 'action')
1294
+ const sceneRead = readAxes(mergeAxisMentions(mentions.scenes ?? record.scenes, [...knowledgeAxes.scene, ...eventAxes.scene]), 'scene')
1170
1295
 
1171
1296
 
1172
1297
  /**
@@ -1179,18 +1304,39 @@ export async function runWikiTrial(input: {
1179
1304
  * (`坑位click` → `通天塔坑位click`),而 Knowledge 的坐标写的是 mention。
1180
1305
  * 顺序反了,那条知识就会因为「坐标不在清单里」被当成编造的坐标丢掉。
1181
1306
  */
1182
- const objects = await resolveAxes('object', objectRead.axes, input.resolveAxis)
1183
- const actions = await resolveAxes('action', actionRead.axes, input.resolveAxis)
1184
- const scenes = await resolveAxes('scene', sceneRead.axes, input.resolveAxis)
1307
+ let objects: TrialAxis[]
1308
+ let actions: TrialAxis[]
1309
+ let scenes: TrialAxis[]
1310
+ let axisResolution: TrialModelCallTrace | undefined
1311
+ if (input.resolveAxisSet) {
1312
+ const resolved = await input.resolveAxisSet({
1313
+ objects: objectRead.axes,
1314
+ actions: actionRead.axes,
1315
+ scenes: sceneRead.axes,
1316
+ })
1317
+ objects = resolved.objects
1318
+ actions = resolved.actions
1319
+ scenes = resolved.scenes
1320
+ axisResolution = resolved.trace
1321
+ } else {
1322
+ objects = await resolveAxes('object', objectRead.axes, input.resolveAxis)
1323
+ actions = await resolveAxes('action', actionRead.axes, input.resolveAxis)
1324
+ scenes = await resolveAxes('scene', sceneRead.axes, input.resolveAxis)
1325
+ }
1185
1326
  const knowledgeRead = readKnowledge(record.knowledge, {
1186
1327
  text: input.text,
1187
1328
  // 有真文档就用它的 id:来源要指得回那份文档,而不是一个标题字符串。
1188
1329
  documentId: docId || input.title,
1189
1330
  objects, actions, scenes,
1190
1331
  })
1332
+ const eventRead = readEvents(record.events, {
1333
+ text: input.text,
1334
+ documentId: docId || input.title,
1335
+ objects, actions, scenes,
1336
+ })
1191
1337
  // `dropped` 在新契约里收在 `audit` 下 —— 它是审计,不是知识模型的一部分(§5.5)。
1192
1338
  const dropped = readDropped(plainRecord(record.audit).dropped ?? record.dropped)
1193
- const wikiCards = materialize(knowledgeRead.knowledge)
1339
+ const wikiCards = materialize(knowledgeRead.knowledge, eventRead.events)
1194
1340
  const modelSubject = str(record.subject) || subject || input.title
1195
1341
 
1196
1342
  return buildReport({
@@ -1199,13 +1345,25 @@ export async function runWikiTrial(input: {
1199
1345
  actions,
1200
1346
  scenes,
1201
1347
  knowledge: knowledgeRead.knowledge,
1348
+ events: eventRead.events,
1202
1349
  wikiCards,
1203
- matrix: buildMatrix(objects.map(axis => axis.name), actions.map(axis => axis.name), knowledgeRead.knowledge, wikiCards),
1204
- problems: [...objectRead.problems, ...actionRead.problems, ...sceneRead.problems, ...knowledgeRead.problems],
1350
+ matrix: buildMatrix(objects.map(axis => axis.name), actions.map(axis => axis.name), knowledgeRead.knowledge, wikiCards, eventRead.events),
1351
+ problems: [...objectRead.problems, ...actionRead.problems, ...sceneRead.problems, ...knowledgeRead.problems, ...eventRead.problems],
1205
1352
  dropped,
1206
- model: knowledgeRead.knowledge.length > 0
1207
- ? { ok: true }
1208
- : { ok: false, why: '模型没有给出任何知识' },
1353
+ model: {
1354
+ ok: knowledgeRead.knowledge.length > 0 || eventRead.events.length > 0,
1355
+ parsed: Array.isArray(record.knowledge) && Array.isArray(record.events ?? [])
1356
+ && knowledgeRead.knowledge.length === record.knowledge.length
1357
+ && eventRead.events.length === (Array.isArray(record.events) ? record.events.length : 0)
1358
+ && knowledgeRead.knowledge.every(item => item.sources.length > 0 && item.sources.every(source => source.found))
1359
+ && eventRead.events.every(item => [...item.sources, ...item.timeline.flatMap(step => step.sources)].every(source => source.found)),
1360
+ outputFormat,
1361
+ ...(!(knowledgeRead.knowledge.length > 0 || eventRead.events.length > 0) ? { why: '模型没有给出 Knowledge 或 Event' } : {}),
1362
+ request,
1363
+ response: raw,
1364
+ ...(usage ? { usage } : {}),
1365
+ ...(axisResolution ? { axisResolution } : {}),
1366
+ },
1209
1367
  promptUser, system,
1210
1368
  ...(axesContext === undefined ? {} : { axesContext }),
1211
1369
  })
@@ -1227,6 +1385,18 @@ export type TrialAxisResolver = (
1227
1385
  extra?: { preferMention?: string; description?: string },
1228
1386
  ) => Promise<AxisResolution | null>
1229
1387
 
1388
+ export interface TrialAxisSet {
1389
+ objects: TrialAxis[]
1390
+ actions: TrialAxis[]
1391
+ scenes: TrialAxis[]
1392
+ }
1393
+
1394
+ export interface TrialAxisSetResolution extends TrialAxisSet {
1395
+ trace?: TrialModelCallTrace
1396
+ }
1397
+
1398
+ export type TrialAxisSetResolver = (axes: TrialAxisSet) => Promise<TrialAxisSetResolution>
1399
+
1230
1400
  /** 命中方式 → 给人看的一句话。 */
1231
1401
  function describeHit(resolution: AxisResolution): string {
1232
1402
  switch (resolution.how) {
@@ -1263,33 +1433,35 @@ async function resolveAxes(
1263
1433
  })
1264
1434
  // `null` = 这个部署上没有注册表。那不是「没命中」,是「这一次没查过」。
1265
1435
  if (resolution === null) return axes
1266
- if ((resolution.outcome === 'matched' || resolution.outcome === 'created') && resolution.entity) {
1267
- const entity = resolution.entity
1268
- out.push({
1269
- name: entity.name,
1270
- id: entity.id,
1271
- // 原来的 mention 进别名:它可能只是这个实体的一种写法。
1272
- aliases: [...new Set([...axis.aliases, ...(entity.name === axis.name ? [] : [axis.name])])],
1273
- outcome: resolution.outcome === 'created' ? 'new' : 'matched',
1274
- how: describeHit(resolution),
1275
- // 报告里显示的是**库里那份**(模型这次给的可能没被采纳 —— 人写的定义不许覆盖)。
1276
- ...(entity.description ? { definition: entity.description } : axis.definition ? { definition: axis.definition } : {}),
1277
- ...(axis.evidence ? { evidence: axis.evidence } : {}),
1278
- })
1279
- continue
1436
+ out.push(applyTrialAxisResolution(axis, resolution))
1437
+ }
1438
+ return out
1439
+ }
1440
+
1441
+ /** 将注册表结论变成报告里的坐标。供批量补判复用同一套字段处理。 */
1442
+ export function applyTrialAxisResolution(axis: TrialAxis, resolution: AxisResolution | null): TrialAxis {
1443
+ if (resolution === null) return { ...axis, outcome: 'no-registry', how: '只做了文档内的机械归一' }
1444
+ if ((resolution.outcome === 'matched' || resolution.outcome === 'created') && resolution.entity) {
1445
+ const entity = resolution.entity
1446
+ return {
1447
+ name: entity.name,
1448
+ id: entity.id,
1449
+ aliases: [...new Set([...axis.aliases, ...(entity.name === axis.name ? [] : [axis.name])])],
1450
+ outcome: resolution.outcome === 'created' ? 'new' : 'matched',
1451
+ how: describeHit(resolution),
1452
+ ...(entity.description ? { definition: entity.description } : axis.definition ? { definition: axis.definition } : {}),
1453
+ ...(axis.evidence ? { evidence: axis.evidence } : {}),
1280
1454
  }
1281
- if (resolution.outcome === 'ambiguous') {
1282
- const names = (resolution.candidates ?? []).map(candidate => candidate.name)
1283
- out.push({
1284
- ...axis,
1285
- outcome: 'ambiguous',
1286
- how: names.length > 0 ? `不唯一,候选:${names.join('、')}` : '不唯一,但注册表里没有候选',
1287
- })
1288
- continue
1455
+ }
1456
+ if (resolution.outcome === 'ambiguous') {
1457
+ const names = (resolution.candidates ?? []).map(candidate => candidate.name)
1458
+ return {
1459
+ ...axis,
1460
+ outcome: 'ambiguous',
1461
+ how: names.length > 0 ? `不唯一,候选:${names.join('、')}` : '不唯一,但注册表里没有候选',
1289
1462
  }
1290
- out.push({ ...axis, outcome: 'no-registry', how: '只做了文档内的机械归一' })
1291
1463
  }
1292
- return out
1464
+ return { ...axis, outcome: 'ambiguous', how: '坐标为空,无法解析' }
1293
1465
  }
1294
1466
 
1295
1467
  /** 读 `dropped`。它是**审计轨迹**(§5.5),不进知识模型。 */
@@ -1308,10 +1480,10 @@ function readDropped(raw: unknown): Array<{ quote: string; why: string }> {
1308
1480
 
1309
1481
  /** 给界面用的一句话规模描述。矩阵的规模由界面自己从 `matrix` 取。 */
1310
1482
  export function trialSummary(report: TrialReport): string {
1311
- const pieces = [
1483
+ const pieces = [
1312
1484
  `${report.lines} 行`, `${report.chars} 字`,
1313
1485
  `${report.objects.length} 对象`, `${report.actions.length} 动作`, `${report.scenes.length} 场景`,
1314
- `${report.knowledge.length} 知识`, `${report.wikiCards.length} Wiki Card`,
1486
+ `${report.knowledge.length} Knowledge`, `${report.events.length} Event`, `${report.wikiCards.length} Wiki Card`,
1315
1487
  ]
1316
1488
  const dropped = report.audit.dropped.length
1317
1489
  if (dropped > 0) pieces.push(`丢弃 ${dropped} 段`)