dsh-data-domain-knowledge 0.1.0-alpha.2 → 0.1.0-alpha.4

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 (180) hide show
  1. package/README.md +147 -19
  2. package/cordis.patch.yml +25 -10
  3. package/lib/client.js +6400 -3326
  4. package/lib/doc-page.js +7761 -0
  5. package/lib/types/convert/host.d.ts.map +1 -1
  6. package/lib/types/convert/host.js +63 -11
  7. package/lib/types/convert/host.js.map +1 -1
  8. package/lib/types/convert/ingest.d.ts +7 -4
  9. package/lib/types/convert/ingest.d.ts.map +1 -1
  10. package/lib/types/convert/ingest.js +52 -14
  11. package/lib/types/convert/ingest.js.map +1 -1
  12. package/lib/types/convert/types.d.ts +2 -2
  13. package/lib/types/convert/types.d.ts.map +1 -1
  14. package/lib/types/convert/types.js +2 -2
  15. package/lib/types/convert/types.js.map +1 -1
  16. package/lib/types/diagnosis/cards.d.ts +8 -7
  17. package/lib/types/diagnosis/cards.d.ts.map +1 -1
  18. package/lib/types/diagnosis/cards.js +119 -49
  19. package/lib/types/diagnosis/cards.js.map +1 -1
  20. package/lib/types/diagnosis/run.d.ts +57 -0
  21. package/lib/types/diagnosis/run.d.ts.map +1 -0
  22. package/lib/types/diagnosis/run.js +245 -0
  23. package/lib/types/diagnosis/run.js.map +1 -0
  24. package/lib/types/diagnosis/schema.d.ts +25 -2
  25. package/lib/types/diagnosis/schema.d.ts.map +1 -1
  26. package/lib/types/diagnosis/schema.js +64 -25
  27. package/lib/types/diagnosis/schema.js.map +1 -1
  28. package/lib/types/diagnosis/seed-source.d.ts +17 -2
  29. package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
  30. package/lib/types/diagnosis/seed-source.js +19 -2
  31. package/lib/types/diagnosis/seed-source.js.map +1 -1
  32. package/lib/types/diagnosis/seed.d.ts +32 -42
  33. package/lib/types/diagnosis/seed.d.ts.map +1 -1
  34. package/lib/types/diagnosis/seed.js +187 -51
  35. package/lib/types/diagnosis/seed.js.map +1 -1
  36. package/lib/types/diagnosis/store.d.ts +95 -25
  37. package/lib/types/diagnosis/store.d.ts.map +1 -1
  38. package/lib/types/diagnosis/store.js +297 -166
  39. package/lib/types/diagnosis/store.js.map +1 -1
  40. package/lib/types/diagnosis/types.d.ts +168 -16
  41. package/lib/types/diagnosis/types.d.ts.map +1 -1
  42. package/lib/types/diagnosis/types.js +1 -1
  43. package/lib/types/diagnosis/types.js.map +1 -1
  44. package/lib/types/document-page.d.ts +39 -0
  45. package/lib/types/document-page.d.ts.map +1 -0
  46. package/lib/types/document-page.js +43 -0
  47. package/lib/types/document-page.js.map +1 -0
  48. package/lib/types/extract-model.d.ts +67 -27
  49. package/lib/types/extract-model.d.ts.map +1 -1
  50. package/lib/types/extract-model.js +225 -97
  51. package/lib/types/extract-model.js.map +1 -1
  52. package/lib/types/extract.d.ts +16 -0
  53. package/lib/types/extract.d.ts.map +1 -1
  54. package/lib/types/extract.js +19 -2
  55. package/lib/types/extract.js.map +1 -1
  56. package/lib/types/fs-atomic.d.ts +11 -0
  57. package/lib/types/fs-atomic.d.ts.map +1 -0
  58. package/lib/types/fs-atomic.js +41 -0
  59. package/lib/types/fs-atomic.js.map +1 -0
  60. package/lib/types/index.d.ts.map +1 -1
  61. package/lib/types/index.js +466 -63
  62. package/lib/types/index.js.map +1 -1
  63. package/lib/types/intent/types.js +1 -1
  64. package/lib/types/intent/types.js.map +1 -1
  65. package/lib/types/knowledge-bases.d.ts +23 -12
  66. package/lib/types/knowledge-bases.d.ts.map +1 -1
  67. package/lib/types/knowledge-bases.js +37 -44
  68. package/lib/types/knowledge-bases.js.map +1 -1
  69. package/lib/types/layout.d.ts +3 -3
  70. package/lib/types/layout.d.ts.map +1 -1
  71. package/lib/types/layout.js +4 -4
  72. package/lib/types/layout.js.map +1 -1
  73. package/lib/types/migrate.d.ts +1 -1
  74. package/lib/types/migrate.js +2 -2
  75. package/lib/types/migrate.js.map +1 -1
  76. package/lib/types/model-text.d.ts +99 -0
  77. package/lib/types/model-text.d.ts.map +1 -0
  78. package/lib/types/model-text.js +103 -0
  79. package/lib/types/model-text.js.map +1 -0
  80. package/lib/types/present.d.ts +239 -0
  81. package/lib/types/present.d.ts.map +1 -0
  82. package/lib/types/present.js +255 -0
  83. package/lib/types/present.js.map +1 -0
  84. package/lib/types/recall-rank.d.ts +37 -0
  85. package/lib/types/recall-rank.d.ts.map +1 -1
  86. package/lib/types/recall-rank.js +41 -1
  87. package/lib/types/recall-rank.js.map +1 -1
  88. package/lib/types/runtime.d.ts +564 -31
  89. package/lib/types/runtime.d.ts.map +1 -1
  90. package/lib/types/runtime.js +2645 -292
  91. package/lib/types/runtime.js.map +1 -1
  92. package/lib/types/settings-schema.d.ts +4 -2
  93. package/lib/types/settings-schema.d.ts.map +1 -1
  94. package/lib/types/settings-schema.js +2 -0
  95. package/lib/types/settings-schema.js.map +1 -1
  96. package/lib/types/source-images.d.ts +118 -0
  97. package/lib/types/source-images.d.ts.map +1 -0
  98. package/lib/types/source-images.js +288 -0
  99. package/lib/types/source-images.js.map +1 -0
  100. package/lib/types/types.d.ts +356 -28
  101. package/lib/types/types.d.ts.map +1 -1
  102. package/lib/types/types.js +112 -18
  103. package/lib/types/types.js.map +1 -1
  104. package/lib/types/wiki-finalize.d.ts +140 -0
  105. package/lib/types/wiki-finalize.d.ts.map +1 -0
  106. package/lib/types/wiki-finalize.js +269 -0
  107. package/lib/types/wiki-finalize.js.map +1 -0
  108. package/lib/types/wiki-types.d.ts +10 -2
  109. package/lib/types/wiki-types.d.ts.map +1 -1
  110. package/lib/types/wiki-types.js +27 -30
  111. package/lib/types/wiki-types.js.map +1 -1
  112. package/package.json +4 -3
  113. package/preset/tita-data-agent/agent.cordis.yml +25 -10
  114. package/src/client/DirectoryPicker.tsx +12 -12
  115. package/src/client/DrawerSection.tsx +18 -0
  116. package/src/client/InfoHint.tsx +21 -8
  117. package/src/client/IntentTab.tsx +20 -26
  118. package/src/client/KnowledgeBaseTab.tsx +458 -158
  119. package/src/client/KnowledgePanel.tsx +44 -10
  120. package/src/client/KnowledgeStepRail.tsx +3 -0
  121. package/src/client/KnowledgeToolRow.tsx +331 -0
  122. package/src/client/NodeSettings.tsx +20 -22
  123. package/src/client/PreviewSections.tsx +178 -43
  124. package/src/client/RecallPanel.tsx +418 -127
  125. package/src/client/TypeDefinitionDrawer.tsx +154 -54
  126. package/src/client/WikiCardReview.tsx +266 -0
  127. package/src/client/WikiFlowCard.tsx +57 -95
  128. package/src/client/WikiTypeEditor.tsx +197 -95
  129. package/src/client/arch/AgentArchitectureGraph.tsx +30 -14
  130. package/src/client/arch/NodeLogicPanel.tsx +6 -3
  131. package/src/client/arch/model.ts +164 -26
  132. package/src/client/architecture.css +25 -10
  133. package/src/client/architecture.tsx +66 -41
  134. package/src/client/asset-images.ts +125 -0
  135. package/src/client/attribution/AttributionGraph.tsx +481 -34
  136. package/src/client/attribution/AttributionTab.tsx +501 -106
  137. package/src/client/attribution/CaseView.tsx +9 -14
  138. package/src/client/attribution/GraphStage.tsx +25 -18
  139. package/src/client/attribution/RunView.tsx +27 -9
  140. package/src/client/attribution/StageChrome.tsx +280 -140
  141. package/src/client/attribution/graph-options.ts +611 -47
  142. package/src/client/attribution/model.ts +559 -30
  143. package/src/client/convert/ConvertPanel.tsx +36 -4
  144. package/src/client/doc-page.css +134 -0
  145. package/src/client/doc-page.tsx +242 -0
  146. package/src/client/index.ts +18 -0
  147. package/src/client/panel-state.ts +105 -0
  148. package/src/client/problem-domains.ts +1 -1
  149. package/src/client/request.ts +36 -0
  150. package/src/client/styles.css +397 -54
  151. package/src/client/useFitScale.ts +15 -2
  152. package/src/client/wiki-type-groups.ts +86 -0
  153. package/src/convert/host.ts +68 -9
  154. package/src/convert/ingest.ts +61 -18
  155. package/src/convert/types.ts +2 -2
  156. package/src/diagnosis/cards.ts +120 -50
  157. package/src/diagnosis/run.ts +251 -0
  158. package/src/diagnosis/schema.ts +67 -26
  159. package/src/diagnosis/seed-source.ts +29 -4
  160. package/src/diagnosis/seed.ts +187 -85
  161. package/src/diagnosis/store.ts +310 -173
  162. package/src/diagnosis/types.ts +181 -16
  163. package/src/document-page.ts +51 -0
  164. package/src/extract-model.ts +254 -99
  165. package/src/extract.ts +35 -3
  166. package/src/fs-atomic.ts +41 -0
  167. package/src/index.ts +499 -62
  168. package/src/intent/types.ts +1 -1
  169. package/src/knowledge-bases.ts +50 -49
  170. package/src/layout.ts +4 -4
  171. package/src/migrate.ts +2 -2
  172. package/src/model-text.ts +126 -0
  173. package/src/present.ts +367 -0
  174. package/src/recall-rank.ts +57 -2
  175. package/src/runtime.ts +2802 -293
  176. package/src/settings-schema.ts +2 -0
  177. package/src/source-images.ts +326 -0
  178. package/src/types.ts +446 -45
  179. package/src/wiki-finalize.ts +310 -0
  180. package/src/wiki-types.ts +28 -32
@@ -1,60 +1,77 @@
1
1
  /**
2
- * 模型驱动的候选抽取与**实体归并**(#3b)。
2
+ * 模型驱动的**按 schema 实体抽取与归并**(#3b / 0044)。
3
3
  *
4
- * ## 为什么需要这一步,而不是继续改规则
4
+ * ## 它解决的问题
5
5
  *
6
- * Pass 0 的规则抽取用**小节标题**当实体身份(`candidateEntityId(candidate.name)`)。
7
- * 于是同一件东西只要换个标题写法,就是两个实体。这不是理论问题,是实测出来的:
6
+ * 规则路径(`extract.ts`)拿 `##` 小节标题当实体名。实测的后果有两个,都不是理论问题:
8
7
  *
9
- * - 年报里的小节叫「毛利率变动及其驱动因素」;
10
- * - 口径手册里的小节叫「毛利率(口径补充)」;
11
- * - 结果:`新建 2 / 并入 0`,两张互不相干的卡片,
12
- * 而「实体已存在就追加正文」那条分支**永远不会触发**。
8
+ * 1. **抽出来的是目录,不是知识。** 年报里的小节叫「毛利率变动及其驱动因素」,
9
+ * 口径手册里的小节叫「毛利率(口径补充)」,于是同一件东西落成两张互不相干的卡片
10
+ * (`新建 2 / 并入 0`),而「实体已存在就追加正文」那条分支**永远不会触发**。
11
+ * 想去掉括号后缀、去掉「及其驱动因素」,是语义判断,阈值化的字符串规则在第二步
12
+ * 必然出错(`应收账款周转天数` 与 `库存周转天数` 字面都带「周转天数」)。
13
+ * 2. **卡片正文是切下来的原文。** 一张 `metric` 卡片应该有「口径定义 / 计算逻辑」两个
14
+ * 必填小节,而按标题切出来的正文与类型定义毫无关系 —— 它长得像一篇文章,
15
+ * 不像一张卡片。0044 之前用户在界面上看到的「编译只是按目录截取」说的就是这件事。
13
16
  *
14
- * 想去掉括号后缀、去掉「及其驱动因素」这类尾巴,是**语义判断**:`毛利率` 与
15
- * `毛利率变动及其驱动因素` 是同一个东西,而 `应收账款周转天数` 与 `库存周转天数`
16
- * 不是 —— 两者字面都带「周转天数」。阈值化的字符串规则在第二步上必然出错,
17
- * 所以这件事必须交给模型。
17
+ * 所以这一层让模型回答三件它擅长、而规则做不好的事:
18
18
  *
19
- * ## 这一层只问一个问题
19
+ * 1. 「这份来源里有哪些**实体**」;
20
+ * 2. 「每个实体按它所属类型的 schema **该写成什么样**」—— 必填小节当骨架、必填字段当值;
21
+ * 3. 「这个实体是不是已经在下表里了」(给出已有卡片的 `existing_id`)。
20
22
  *
21
- * 它**不**让模型自由编造卡片,也不让它决定正文:正文永远是来源里的原文片段,
22
- * 模型只回答两件它擅长、而规则做不好的事 ——
23
+ * 第 3 问的**判断权在模型,决定权在这里**:模型说「是同一个」,我们就走追加;
24
+ * 说「不是」,就新建。模型给出的 `existing_id` 必须能在已有卡片里找到,
25
+ * 找不到就退回新建 —— 不能让模型凭一个不存在的 id 把内容写丢。
23
26
  *
24
- * 1. 「这份来源里有哪些**实体**」(规则只会按小节切,切出来的是标题不是实体);
25
- * 2. 「这个实体是不是已经在下表里了」(给出已有卡片的 `existing_id`)。
27
+ * ## 缺信息也抽(0044)
26
28
  *
27
- * 第 2 问是归并的关键:**判断权在模型,决定权在这里**。模型说「是同一个」,
28
- * 我们就走追加;说「不是」,就新建。模型给出的 `existing_id` 必须能在已有卡片里
29
- * 找到,找不到就退回新建 —— 不能让模型凭一个不存在的 id 把内容写丢。
29
+ * 0040 那一版要求每条候选「必须带正文」,「必须来自已登记类型」,否则整条丢掉。
30
+ * 结果是**知识进不来**:一份只写了半句口径的文档,抽出来的东西全被闸门拒了,
31
+ * 而「半句口径」本身正是最该记下来的东西 —— 后面再来一份文档就能补上。
32
+ *
33
+ * 现在只做两件事,不做拒绝:
34
+ * - 类型不在注册表里 → 退回一个已启用的兜底类型 + 一条 warning(**不丢**);
35
+ * - 正文为空 → 用 `summary` 撑起一个带必填小节骨架的最小正文(**不丢**)。
36
+ * 缺什么,由 `schema_issues` 如实标出来,人在 review 时补。
30
37
  *
31
38
  * ## 为什么要注入 `call`
32
39
  *
33
40
  * 模型调用是这一层唯一的非纯部分。把它作为参数注入,这个模块就能在没有模型的环境里
34
- * 被完整测试(见 `tests/extract-model.test.mjs`)——否则「归并对不对」这件事只能靠
35
- * 真跑一次模型来验,而那是不可重复的。
41
+ * 被完整测试(见 `tests/extract-model.test.mjs`)——否则「schema 是否真的传给了模型」
42
+ * 这件事只能靠真跑一次模型来验,而那是不可重复的。
36
43
  *
37
44
  * @module
38
45
  */
39
46
  import { candidateEntityId } from './extract.js';
40
- /** 单个实体最多给模型的正文长度:够判断语义,又不至于把上下文撑爆。 */
41
- const BODY_LIMIT = 1200;
47
+ import { templateCardFor } from './wiki-types.js';
48
+ /** 单个实体给模型的正文长度上限:够判断语义,又不至于把上下文撑爆。 */
49
+ const BODY_LIMIT = 2000;
50
+ /** 文档给模型的长度上限。 */
51
+ const DOCUMENT_LIMIT = BODY_LIMIT * 8;
52
+ /** 已有卡片目录给模型多少行。 */
53
+ const CATALOG_LIMIT = 200;
42
54
  /**
43
55
  * 系统提示。**刻意写死在这里**而不是做成设置项:它不是用户的偏好,是这个模块与模型
44
56
  * 之间的接口契约(输出必须是一段可解析的 JSON)。允许用户改它,等于允许用户把这个
45
57
  * 解析器改坏。
46
58
  */
47
59
  const SYSTEM = [
48
- 'You extract wiki entities from a business document and decide which of them already exist.',
60
+ 'You turn one business document into wiki cards for a knowledge base.',
49
61
  'Answer with a single JSON object and nothing else. No prose, no code fence.',
50
- 'Shape: {"entities":[{"name":string,"type":string,"summary":string,"body":string,"existing_id":string|null}]}',
62
+ 'Shape: {"entities":[{"name":string,"type":string,"summary":string,"content":string,"fields":{string:string},"existing_id":string|null}]}',
51
63
  'Rules:',
52
- '- "name" is the entity name as a business person would say it (e.g. 毛利率). Not a section heading.',
64
+ '- "name" is the entity name as a business person would say it (e.g. 毛利率). Never a section heading.',
53
65
  '- "type" MUST be one of the allowed type ids given in the prompt.',
54
- '- "body" must be verbatim text from the document that backs this entity. Never invent facts.',
66
+ '- "content" MUST be markdown for that type: use its required section labels as "## <label>" headings, in order.',
67
+ ' Fill each section with facts stated in the document. Copy wording, do not paraphrase loosely.',
68
+ ' If the document does not say enough for a section, still write the heading and put only what IS known.',
69
+ ' NEVER invent numbers, owners, formulas or dates. Missing information is acceptable; made-up information is not.',
70
+ '- "fields" holds the type\'s required fields whose values the document states. Omit a field the document does not answer.',
71
+ '- "summary" is one sentence saying what this entity is.',
55
72
  '- "existing_id" is the entityId of the existing card this entity is the SAME thing as, or null.',
56
73
  ' Same thing means the same business concept; a narrower or different concept is NOT the same.',
57
- '- Prefer few, real entities over many headings.',
74
+ '- Prefer few, real entities over many sections. Extract an entity even when the document only covers part of it.',
58
75
  ].join('\n');
59
76
  /** 从一串可能带 ``` 围栏的文本里取出 JSON 对象并解析。 */
60
77
  function parseJsonObject(raw) {
@@ -78,98 +95,217 @@ function parseJsonObject(raw) {
78
95
  }
79
96
  }
80
97
  }
81
- /** 把模型的回答收敛成「合法实体数组」;任何不合法的一条都丢掉并记一条 warning。 */
82
- function readEntities(raw, allowedTypes, warnings) {
83
- if (raw === null || typeof raw !== 'object') {
84
- warnings.push('模型输出不是 JSON 对象,本次抽取按空处理');
85
- return [];
86
- }
87
- const list = raw.entities;
88
- if (!Array.isArray(list)) {
89
- warnings.push('模型输出里没有 entities 数组,本次抽取按空处理');
90
- return [];
91
- }
92
- const kept = [];
93
- for (const item of list) {
94
- if (item === null || typeof item !== 'object')
95
- continue;
96
- const record = item;
97
- const name = typeof record.name === 'string' ? record.name.trim() : '';
98
- const type = typeof record.type === 'string' ? record.type.trim() : '';
99
- const body = typeof record.body === 'string' ? record.body.trim() : '';
100
- if (!name)
101
- continue;
102
- if (!allowedTypes.has(type)) {
103
- warnings.push(`模型给出的类型「${type}」不在已启用类型里,候选「${name}」被丢掉`);
104
- continue;
105
- }
106
- if (!body) {
107
- warnings.push(`候选「${name}」没有正文,被丢掉(卡片不能没有正文)`);
108
- continue;
109
- }
110
- kept.push({ ...record, name, type, body });
111
- }
112
- return kept;
113
- }
114
98
  /**
115
- * 用模型抽取实体,并把它们判归到「新建」或「追加到已有卡片」两条路上。
99
+ * 一个类型的 schema 怎么讲给模型听。
116
100
  *
117
- * 失败一律降级而不抛出:模型不可用时返回空结果 + 一条 warning,让上层继续跑规则抽取。
118
- * 「因为模型挂了所以整次编译失败」是不能接受的 —— 那条链路本来就不依赖模型。
101
+ * 这是 0044 的关键一步:**必填小节与必填字段必须出现在提示词里**,否则模型写出来的
102
+ * 「正文」只能是一段自由文本 —— 而卡片是按小节校验的。导出它,是为了让
103
+ * 「schema 到底有没有下发」这件事可以在测试里被断言,而不是只能靠读代码相信。
119
104
  */
120
- export async function extractWithModel(input) {
121
- const { source, wikiTypes, existing, call } = input;
122
- const warnings = [];
123
- const enabled = wikiTypes.filter(def => def.enabled);
124
- const allowedTypes = new Set(enabled.map(def => String(def.id)));
105
+ export function renderTypeSpec(def) {
106
+ const sections = def.sections.map(section => `${section.label}${section.required ? '(必填)' : '(可选)'}`).join(' / ');
107
+ const fields = def.fields.filter(field => field.required).map(field => `${field.name}(${field.note})`).join(';');
108
+ return [
109
+ `- ${def.id} :: ${def.label}${def.description ? ` — ${def.description}` : ''}`,
110
+ sections ? ` 小节:${sections}` : '',
111
+ fields ? ` 必填字段:${fields}` : '',
112
+ def.examples.length > 0 ? ` 例如:${def.examples.join(';')}` : '',
113
+ ` 模板:\n${indent(templateCardFor(def.sections, def.fields))}`,
114
+ ].filter(Boolean).join('\n');
115
+ }
116
+ function indent(value) {
117
+ return value.split('\n').map(line => ` ${line}`).join('\n');
118
+ }
119
+ /** 抽取用的完整提示词。导出以便测试断言「schema 真的在里面」。 */
120
+ export function buildExtractionPrompt(input) {
121
+ const { source, wikiTypes, existing } = input;
125
122
  const catalog = existing
126
- .slice(0, 200)
123
+ .slice(0, CATALOG_LIMIT)
127
124
  .map(card => `- ${card.entityId} :: ${card.title}(${card.entityType})`)
128
125
  .join('\n');
129
- const types = enabled
130
- .map(def => `- ${def.id} :: ${def.label}${def.description ? ` — ${def.description}` : ''}`)
131
- .join('\n');
132
- const prompt = [
126
+ return [
133
127
  '# Allowed wiki type ids',
134
- types || '(没有任何已启用类型)',
128
+ wikiTypes.map(renderTypeSpec).join('\n') || '(没有任何已启用类型)',
135
129
  '',
136
- '# Existing wiki cards (entityId :: title)',
130
+ '# Existing wiki cards (entityId :: title(type))',
137
131
  catalog || '(这个库还是空的)',
138
132
  '',
139
133
  '# Document',
140
134
  `title: ${source.title}`,
141
135
  `source_id: ${source.sourceId}`,
142
136
  '',
143
- source.content.slice(0, BODY_LIMIT * 8),
137
+ source.content.slice(0, DOCUMENT_LIMIT),
144
138
  ].join('\n');
139
+ }
140
+ /** 把模型的 `fields` 收敛成一个字符串到字符串的字典;非字符串值丢掉。 */
141
+ function readFields(raw) {
142
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
143
+ return {};
144
+ const result = {};
145
+ for (const [key, value] of Object.entries(raw)) {
146
+ const name = key.trim();
147
+ if (!name)
148
+ continue;
149
+ if (typeof value === 'string' && value.trim())
150
+ result[name] = value.trim();
151
+ else if (typeof value === 'number' && Number.isFinite(value))
152
+ result[name] = String(value);
153
+ }
154
+ return result;
155
+ }
156
+ /**
157
+ * 一个已启用的兜底类型。
158
+ *
159
+ * 「类型不认识」不该丢实体 —— 0040 那一版就是在这一步丢的。退回 `concept`,
160
+ * 它不在注册表里就退第一个启用的类型;一个都没有(注册表被清空)时返回 undefined,
161
+ * 那时调用方也没有别的选择。
162
+ */
163
+ function fallbackType(enabled) {
164
+ return enabled.find(def => def.id === 'concept') ?? enabled[0];
165
+ }
166
+ /**
167
+ * 把模型的一条输出收敛成候选;不合法的一条记 warning 而不是静默丢弃。
168
+ *
169
+ * 「缺信息也抽」在这里落成代码:**名字是唯一的硬要求**。类型不认识就退回兜底、
170
+ * 正文为空就用 summary 撑一个带必填小节骨架的最小正文。
171
+ */
172
+ function readEntity(item, options) {
173
+ const { enabled, warnings } = options;
174
+ if (item === null || typeof item !== 'object')
175
+ return undefined;
176
+ const record = item;
177
+ const name = typeof record.name === 'string' ? record.name.trim() : '';
178
+ if (!name)
179
+ return undefined;
180
+ const wanted = typeof record.type === 'string' ? record.type.trim() : '';
181
+ const matched = enabled.find(def => def.id === wanted);
182
+ let type = matched?.id;
183
+ if (!type) {
184
+ const fallback = fallbackType(enabled);
185
+ if (!fallback) {
186
+ warnings.push(`候选「${name}」没有任何已启用类型可用,被丢掉`);
187
+ return undefined;
188
+ }
189
+ warnings.push(`候选「${name}」的类型「${wanted || '(未给)'}」不在已启用类型里,已按「${fallback.id}」入库`);
190
+ type = fallback.id;
191
+ }
192
+ const def = enabled.find(candidate => candidate.id === type);
193
+ // 正文:优先用模型写的 `content`,兼容旧字段名 `body`。
194
+ const rawBody = typeof record.content === 'string' ? record.content.trim() : typeof record.body === 'string' ? record.body.trim() : '';
195
+ const summary = typeof record.summary === 'string' ? record.summary.trim() : '';
196
+ const content = rawBody ? rawBody.slice(0, BODY_LIMIT) : skeletonBody(def, summary);
197
+ const existingId = typeof record.existing_id === 'string' ? record.existing_id.trim() : '';
198
+ const fields = readFields(record.fields);
199
+ return {
200
+ existingId: existingId || undefined,
201
+ candidate: {
202
+ kind: type === 'concept' ? 'concept' : 'entity',
203
+ name,
204
+ type,
205
+ body: content,
206
+ ...(summary ? { summary } : {}),
207
+ ...(Object.keys(fields).length > 0 ? { fields } : {}),
208
+ aliases: [],
209
+ evidence: { sourceId: '' },
210
+ confidence: existingId ? 0.75 : 0.7,
211
+ },
212
+ };
213
+ }
214
+ /**
215
+ * 正文为空时的最小骨架。
216
+ *
217
+ * 不是「占位符」:占位符(`(必填:写什么)`)会被编译进召回单元、塞进模型上下文,
218
+ * 等于管线自己造噪声。这里只放**真实存在的信息** —— 模型给的 summary。
219
+ *
220
+ * 连 summary 都没有时返回空串。**这一层不替编译侧做丢弃决定**:它照样把候选交出去,
221
+ * 由 `runtime.compileSource` 按「正文是不是真的空」统一裁决(那是唯一保留的拒绝)。
222
+ * 在这里悄悄丢掉一条候选,会让「为什么这份文档一张卡都没抽出来」变成一个查不到答案的问题。
223
+ */
224
+ export function skeletonBody(def, summary) {
225
+ const first = def.sections[0]?.label ?? '定义';
226
+ return summary ? `## ${first}\n\n${summary}` : '';
227
+ }
228
+ /**
229
+ * 用模型抽取实体,并把它们判归到「新建」或「追加到已有卡片」两条路上。
230
+ *
231
+ * 失败一律降级而不抛出:模型不可用时返回空结果 + 一条 warning,让上层继续跑规则抽取。
232
+ * 「因为模型挂了所以整次编译失败」是不能接受的 —— 那条链路本来就不依赖模型。
233
+ */
234
+ /**
235
+ * 模型实际回了什么 —— 只留开头一小段,把 warning 变成可排查的东西。
236
+ *
237
+ * 「模型输出不是 JSON 对象」这句话本身没有下一步:是模型在讲道理、是被截断,
238
+ * 还是接缝根本没取到文字?三种情况的处理完全不同,而在实机上它们长得一模一样。
239
+ * 贴上开头 160 字符,第一次就能分辨。
240
+ */
241
+ function outputPreview(raw) {
242
+ const flat = raw.replace(/\s+/g, ' ').trim();
243
+ if (flat.length === 0)
244
+ return '(模型回了空字符串)';
245
+ return flat.length > 160 ? `${flat.slice(0, 160)}…` : flat;
246
+ }
247
+ export async function extractWithModel(input) {
248
+ const { source, wikiTypes, existing, call, sessionId } = input;
249
+ const warnings = [];
250
+ const enabled = wikiTypes.filter(def => def.enabled);
251
+ if (enabled.length === 0) {
252
+ return { fresh: [], merges: [], warnings: ['没有任何已启用的 wiki 类型,模型抽取已跳过(先在设置里登记一个类型)'] };
253
+ }
145
254
  let raw;
146
255
  try {
147
- raw = await call({ system: SYSTEM, prompt });
256
+ raw = await call({
257
+ system: SYSTEM,
258
+ prompt: buildExtractionPrompt({ source, wikiTypes: enabled, existing }),
259
+ ...(sessionId ? { sessionId } : {}),
260
+ });
148
261
  }
149
262
  catch (cause) {
150
263
  warnings.push(`模型抽取不可用:${cause instanceof Error ? cause.message : String(cause)}`);
151
264
  return { fresh: [], merges: [], warnings };
152
265
  }
266
+ const parsed = parseJsonObject(raw);
267
+ if (parsed === null || typeof parsed !== 'object') {
268
+ warnings.push(`模型输出不是 JSON 对象,本次抽取按空处理。模型实际回了:${outputPreview(raw)}`);
269
+ return { fresh: [], merges: [], warnings };
270
+ }
271
+ const list = parsed.entities;
272
+ if (!Array.isArray(list)) {
273
+ warnings.push('模型输出里没有 entities 数组,本次抽取按空处理');
274
+ return { fresh: [], merges: [], warnings };
275
+ }
153
276
  const byId = new Map(existing.map(card => [`${card.entityType}:${card.entityId}`, card]));
154
277
  const byEntityId = new Map(existing.map(card => [card.entityId, card]));
155
278
  const fresh = [];
156
279
  const merges = [];
157
280
  const claimed = new Set();
158
- for (const entity of readEntities(parseJsonObject(raw), allowedTypes, warnings)) {
159
- const name = String(entity.name);
160
- const type = String(entity.type);
161
- const body = String(entity.body).slice(0, BODY_LIMIT);
162
- const wantedId = typeof entity.existing_id === 'string' ? entity.existing_id.trim() : '';
281
+ for (const entity of list) {
282
+ const read = readEntity(entity, { enabled, warnings });
283
+ if (!read)
284
+ continue;
285
+ const { candidate, existingId } = read;
286
+ candidate.evidence = { sourceId: source.sourceId };
287
+ const name = candidate.name;
288
+ const type = candidate.type;
289
+ const body = candidate.body ?? '';
163
290
  // 模型给的 id 必须真的存在:它指着不存在的卡片时退回新建,绝不能让正文丢在空中。
164
- const target = (wantedId ? byEntityId.get(wantedId) : undefined) ?? byId.get(`${type}:${name}`);
291
+ const target = (existingId ? byEntityId.get(existingId) : undefined) ?? byId.get(`${type}:${name}`);
165
292
  if (target) {
166
293
  const key = `${target.entityType}:${target.entityId}`;
167
294
  // 同一张卡在一次抽取里被多个候选指向时只并一次,正文在这里先合起来。
168
295
  const already = merges.find(entry => entry.key === key);
169
- if (already)
296
+ if (already) {
170
297
  already.body = `${already.body}\n\n${body}`;
171
- else
172
- merges.push({ key, entityId: target.entityId, entityType: target.entityType, sourceId: source.sourceId, body });
298
+ if (!already.summary && candidate.summary)
299
+ already.summary = candidate.summary;
300
+ already.fields = { ...(already.fields ?? {}), ...(candidate.fields ?? {}) };
301
+ }
302
+ else {
303
+ merges.push({
304
+ key, entityId: target.entityId, entityType: target.entityType, sourceId: source.sourceId, body,
305
+ ...(candidate.summary ? { summary: candidate.summary } : {}),
306
+ ...(candidate.fields && Object.keys(candidate.fields).length > 0 ? { fields: candidate.fields } : {}),
307
+ });
308
+ }
173
309
  claimed.add(key);
174
310
  continue;
175
311
  }
@@ -178,15 +314,7 @@ export async function extractWithModel(input) {
178
314
  if (claimed.has(key))
179
315
  continue;
180
316
  claimed.add(key);
181
- fresh.push({
182
- kind: 'entity',
183
- name,
184
- type,
185
- body,
186
- aliases: [],
187
- evidence: { sourceId: source.sourceId },
188
- confidence: 0.7,
189
- });
317
+ fresh.push(candidate);
190
318
  }
191
319
  return { fresh, merges, warnings };
192
320
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extract-model.js","sourceRoot":"","sources":["../../src/extract-model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"extract-model.js","sourceRoot":"","sources":["../../src/extract-model.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,20 @@ export interface ExtractionCandidate {
31
31
  aliases?: string[];
32
32
  /** Markdown body that backs this candidate (used as the card body). */
33
33
  body?: string;
34
+ /**
35
+ * 一句话摘要(0044)。模型按 schema 抽取时会给出;规则路径不产它。
36
+ *
37
+ * 它有独立价值:正文还没写全时,摘要是这张卡片**唯一能说的那句话** —— 缺信息也入库
38
+ * 时,卡片至少还有它。
39
+ */
40
+ summary?: string;
41
+ /**
42
+ * 类型定义里的字段值(0044),例如 `metric` 的 `formula` / `owner`。
43
+ *
44
+ * 落在卡片 front matter 的 `metadata` 里,不占正文。模型按 schema 抽取时填;
45
+ * 规则路径不产它,所以缺字段的卡片会在 `schema_issues` 里如实标出来等人补。
46
+ */
47
+ fields?: Record<string, string>;
34
48
  /** For kind === 'relation': the other end. */
35
49
  target?: {
36
50
  name: string;
@@ -94,6 +108,8 @@ export interface NormalizedCandidates {
94
108
  entityType: SchemaType;
95
109
  sourceId: string;
96
110
  body: string;
111
+ summary?: string;
112
+ fields?: Record<string, string>;
97
113
  }>;
98
114
  /**
99
115
  * Relation candidates are never merged — they become graph edges.
@@ -1 +1 @@
1
- {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEnF;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAA;IACvC,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,0FAA0F;IAC1F,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,UAAU,CAAA;KAAE,CAAA;IAC5C,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wDAAwD;AACxD,eAAO,MAAM,UAAU,eAAe,CAAA;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,KAAK,CAAA;AAiB3C;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE9E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAItD;AAmPD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,mBAAmB,EAAE,CAyJpG;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAclF;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,mBAAmB,EAAE,CAAA;IAC5B;;;;;;OAMG;IACH,MAAM,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACxG;;;;;;;OAOG;IACH,SAAS,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACzF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,mBAAmB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,oBAAoB,CAoF9H"}
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEnF;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAA;IACvC,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,0FAA0F;IAC1F,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,8CAA8C;IAC9C,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,UAAU,CAAA;KAAE,CAAA;IAC5C,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wDAAwD;AACxD,eAAO,MAAM,UAAU,eAAe,CAAA;AAEtC;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,KAAK,CAAA;AAiB3C;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE9E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAItD;AA+PD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,mBAAmB,EAAE,CA2JpG;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAclF;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,mBAAmB,EAAE,CAAA;IAC5B;;;;;;OAMG;IACH,MAAM,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,CAAA;IAC3J;;;;;;;OAOG;IACH,SAAS,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACzF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,mBAAmB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,oBAAoB,CAwF9H"}
@@ -242,6 +242,17 @@ function entryBody(entry) {
242
242
  const value = plainRecord(entry).body;
243
243
  return typeof value === 'string' && value.trim() ? value.trim() : undefined;
244
244
  }
245
+ /**
246
+ * 声明的摘要(0044)。
247
+ *
248
+ * 有了它,一条「只知道叫什么、还写不全」的声明式候选也能落成卡片:正文由摘要撑成
249
+ * 一个带必填小节骨架的最小正文(`runtime.compileSource` 的 `skeletonBody`)。
250
+ * 没有它,声明式候选只能靠正文自重 —— 而那正是「缺信息就不入库」的老毛病。
251
+ */
252
+ function entrySummary(entry) {
253
+ const value = plainRecord(entry).summary;
254
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
255
+ }
245
256
  function entryHeading(entry) {
246
257
  const value = plainRecord(entry).heading;
247
258
  return typeof value === 'string' && value.trim() ? value.trim() : undefined;
@@ -291,11 +302,13 @@ export function extractCandidates(source, wikiTypes) {
291
302
  const aliases = stringList(plainRecord(entry).aliases);
292
303
  const heading = entryHeading(entry) ?? sectionForName(sections, name)?.heading;
293
304
  const body = entryBody(entry) ?? sectionForName(sections, name)?.markdown;
305
+ const summary = entrySummary(entry);
294
306
  declaredEntities.push({
295
307
  kind: kind === 'entity' ? 'entity' : kindFor(type), name, type,
296
308
  evidence: evidence(heading, entryQuote(entry)),
297
309
  ...(aliases.length > 0 ? { aliases } : {}),
298
310
  ...(body ? { body } : {}),
311
+ ...(summary ? { summary } : {}),
299
312
  confidence: entryConfidence(entry, 0.95),
300
313
  });
301
314
  }
@@ -522,7 +535,11 @@ export function normalizeCandidates(candidates, index) {
522
535
  continue;
523
536
  const existing = resolved.get(key);
524
537
  if (existing) {
525
- merges.push({ key, entityId: existing.entityId, entityType: existing.entityType, sourceId: candidate.evidence.sourceId, body: candidate.body ?? '' });
538
+ merges.push({
539
+ key, entityId: existing.entityId, entityType: existing.entityType, sourceId: candidate.evidence.sourceId, body: candidate.body ?? '',
540
+ ...(candidate.summary ? { summary: candidate.summary } : {}),
541
+ ...(candidate.fields && Object.keys(candidate.fields).length > 0 ? { fields: candidate.fields } : {}),
542
+ });
526
543
  continue;
527
544
  }
528
545
  fresh.push(candidate);
@@ -533,7 +550,7 @@ export function normalizeCandidates(candidates, index) {
533
550
  status: 'unreviewed',
534
551
  content: candidate.body ?? '',
535
552
  sourceRefs: [candidate.evidence.sourceId],
536
- metadata: {},
553
+ metadata: { ...(candidate.fields ?? {}) },
537
554
  updatedAt: '',
538
555
  };
539
556
  freshKeys.add(key);