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.
- package/README.md +147 -19
- package/cordis.patch.yml +25 -10
- package/lib/client.js +6400 -3326
- package/lib/doc-page.js +7761 -0
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +63 -11
- package/lib/types/convert/host.js.map +1 -1
- package/lib/types/convert/ingest.d.ts +7 -4
- package/lib/types/convert/ingest.d.ts.map +1 -1
- package/lib/types/convert/ingest.js +52 -14
- package/lib/types/convert/ingest.js.map +1 -1
- package/lib/types/convert/types.d.ts +2 -2
- package/lib/types/convert/types.d.ts.map +1 -1
- package/lib/types/convert/types.js +2 -2
- package/lib/types/convert/types.js.map +1 -1
- package/lib/types/diagnosis/cards.d.ts +8 -7
- package/lib/types/diagnosis/cards.d.ts.map +1 -1
- package/lib/types/diagnosis/cards.js +119 -49
- package/lib/types/diagnosis/cards.js.map +1 -1
- package/lib/types/diagnosis/run.d.ts +57 -0
- package/lib/types/diagnosis/run.d.ts.map +1 -0
- package/lib/types/diagnosis/run.js +245 -0
- package/lib/types/diagnosis/run.js.map +1 -0
- package/lib/types/diagnosis/schema.d.ts +25 -2
- package/lib/types/diagnosis/schema.d.ts.map +1 -1
- package/lib/types/diagnosis/schema.js +64 -25
- package/lib/types/diagnosis/schema.js.map +1 -1
- package/lib/types/diagnosis/seed-source.d.ts +17 -2
- package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
- package/lib/types/diagnosis/seed-source.js +19 -2
- package/lib/types/diagnosis/seed-source.js.map +1 -1
- package/lib/types/diagnosis/seed.d.ts +32 -42
- package/lib/types/diagnosis/seed.d.ts.map +1 -1
- package/lib/types/diagnosis/seed.js +187 -51
- package/lib/types/diagnosis/seed.js.map +1 -1
- package/lib/types/diagnosis/store.d.ts +95 -25
- package/lib/types/diagnosis/store.d.ts.map +1 -1
- package/lib/types/diagnosis/store.js +297 -166
- package/lib/types/diagnosis/store.js.map +1 -1
- package/lib/types/diagnosis/types.d.ts +168 -16
- package/lib/types/diagnosis/types.d.ts.map +1 -1
- package/lib/types/diagnosis/types.js +1 -1
- package/lib/types/diagnosis/types.js.map +1 -1
- package/lib/types/document-page.d.ts +39 -0
- package/lib/types/document-page.d.ts.map +1 -0
- package/lib/types/document-page.js +43 -0
- package/lib/types/document-page.js.map +1 -0
- package/lib/types/extract-model.d.ts +67 -27
- package/lib/types/extract-model.d.ts.map +1 -1
- package/lib/types/extract-model.js +225 -97
- package/lib/types/extract-model.js.map +1 -1
- package/lib/types/extract.d.ts +16 -0
- package/lib/types/extract.d.ts.map +1 -1
- package/lib/types/extract.js +19 -2
- package/lib/types/extract.js.map +1 -1
- package/lib/types/fs-atomic.d.ts +11 -0
- package/lib/types/fs-atomic.d.ts.map +1 -0
- package/lib/types/fs-atomic.js +41 -0
- package/lib/types/fs-atomic.js.map +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +466 -63
- package/lib/types/index.js.map +1 -1
- package/lib/types/intent/types.js +1 -1
- package/lib/types/intent/types.js.map +1 -1
- package/lib/types/knowledge-bases.d.ts +23 -12
- package/lib/types/knowledge-bases.d.ts.map +1 -1
- package/lib/types/knowledge-bases.js +37 -44
- package/lib/types/knowledge-bases.js.map +1 -1
- package/lib/types/layout.d.ts +3 -3
- package/lib/types/layout.d.ts.map +1 -1
- package/lib/types/layout.js +4 -4
- package/lib/types/layout.js.map +1 -1
- package/lib/types/migrate.d.ts +1 -1
- package/lib/types/migrate.js +2 -2
- package/lib/types/migrate.js.map +1 -1
- package/lib/types/model-text.d.ts +99 -0
- package/lib/types/model-text.d.ts.map +1 -0
- package/lib/types/model-text.js +103 -0
- package/lib/types/model-text.js.map +1 -0
- package/lib/types/present.d.ts +239 -0
- package/lib/types/present.d.ts.map +1 -0
- package/lib/types/present.js +255 -0
- package/lib/types/present.js.map +1 -0
- package/lib/types/recall-rank.d.ts +37 -0
- package/lib/types/recall-rank.d.ts.map +1 -1
- package/lib/types/recall-rank.js +41 -1
- package/lib/types/recall-rank.js.map +1 -1
- package/lib/types/runtime.d.ts +564 -31
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +2645 -292
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/settings-schema.d.ts +4 -2
- package/lib/types/settings-schema.d.ts.map +1 -1
- package/lib/types/settings-schema.js +2 -0
- package/lib/types/settings-schema.js.map +1 -1
- package/lib/types/source-images.d.ts +118 -0
- package/lib/types/source-images.d.ts.map +1 -0
- package/lib/types/source-images.js +288 -0
- package/lib/types/source-images.js.map +1 -0
- package/lib/types/types.d.ts +356 -28
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js +112 -18
- package/lib/types/types.js.map +1 -1
- package/lib/types/wiki-finalize.d.ts +140 -0
- package/lib/types/wiki-finalize.d.ts.map +1 -0
- package/lib/types/wiki-finalize.js +269 -0
- package/lib/types/wiki-finalize.js.map +1 -0
- package/lib/types/wiki-types.d.ts +10 -2
- package/lib/types/wiki-types.d.ts.map +1 -1
- package/lib/types/wiki-types.js +27 -30
- package/lib/types/wiki-types.js.map +1 -1
- package/package.json +4 -3
- package/preset/tita-data-agent/agent.cordis.yml +25 -10
- package/src/client/DirectoryPicker.tsx +12 -12
- package/src/client/DrawerSection.tsx +18 -0
- package/src/client/InfoHint.tsx +21 -8
- package/src/client/IntentTab.tsx +20 -26
- package/src/client/KnowledgeBaseTab.tsx +458 -158
- package/src/client/KnowledgePanel.tsx +44 -10
- package/src/client/KnowledgeStepRail.tsx +3 -0
- package/src/client/KnowledgeToolRow.tsx +331 -0
- package/src/client/NodeSettings.tsx +20 -22
- package/src/client/PreviewSections.tsx +178 -43
- package/src/client/RecallPanel.tsx +418 -127
- package/src/client/TypeDefinitionDrawer.tsx +154 -54
- package/src/client/WikiCardReview.tsx +266 -0
- package/src/client/WikiFlowCard.tsx +57 -95
- package/src/client/WikiTypeEditor.tsx +197 -95
- package/src/client/arch/AgentArchitectureGraph.tsx +30 -14
- package/src/client/arch/NodeLogicPanel.tsx +6 -3
- package/src/client/arch/model.ts +164 -26
- package/src/client/architecture.css +25 -10
- package/src/client/architecture.tsx +66 -41
- package/src/client/asset-images.ts +125 -0
- package/src/client/attribution/AttributionGraph.tsx +481 -34
- package/src/client/attribution/AttributionTab.tsx +501 -106
- package/src/client/attribution/CaseView.tsx +9 -14
- package/src/client/attribution/GraphStage.tsx +25 -18
- package/src/client/attribution/RunView.tsx +27 -9
- package/src/client/attribution/StageChrome.tsx +280 -140
- package/src/client/attribution/graph-options.ts +611 -47
- package/src/client/attribution/model.ts +559 -30
- package/src/client/convert/ConvertPanel.tsx +36 -4
- package/src/client/doc-page.css +134 -0
- package/src/client/doc-page.tsx +242 -0
- package/src/client/index.ts +18 -0
- package/src/client/panel-state.ts +105 -0
- package/src/client/problem-domains.ts +1 -1
- package/src/client/request.ts +36 -0
- package/src/client/styles.css +397 -54
- package/src/client/useFitScale.ts +15 -2
- package/src/client/wiki-type-groups.ts +86 -0
- package/src/convert/host.ts +68 -9
- package/src/convert/ingest.ts +61 -18
- package/src/convert/types.ts +2 -2
- package/src/diagnosis/cards.ts +120 -50
- package/src/diagnosis/run.ts +251 -0
- package/src/diagnosis/schema.ts +67 -26
- package/src/diagnosis/seed-source.ts +29 -4
- package/src/diagnosis/seed.ts +187 -85
- package/src/diagnosis/store.ts +310 -173
- package/src/diagnosis/types.ts +181 -16
- package/src/document-page.ts +51 -0
- package/src/extract-model.ts +254 -99
- package/src/extract.ts +35 -3
- package/src/fs-atomic.ts +41 -0
- package/src/index.ts +499 -62
- package/src/intent/types.ts +1 -1
- package/src/knowledge-bases.ts +50 -49
- package/src/layout.ts +4 -4
- package/src/migrate.ts +2 -2
- package/src/model-text.ts +126 -0
- package/src/present.ts +367 -0
- package/src/recall-rank.ts +57 -2
- package/src/runtime.ts +2802 -293
- package/src/settings-schema.ts +2 -0
- package/src/source-images.ts +326 -0
- package/src/types.ts +446 -45
- package/src/wiki-finalize.ts +310 -0
- package/src/wiki-types.ts +28 -32
package/src/types.ts
CHANGED
|
@@ -7,7 +7,12 @@ export type KnowledgeStatus = 'unreviewed' | 'draft' | 'reviewed' | 'active' | '
|
|
|
7
7
|
* 注册表允许用户追加自定义分类,所以**运行时一律按 `string` 处理**
|
|
8
8
|
* (见 `SourceType`);这个字面量联合只用来标记哪些 id 是内置的。
|
|
9
9
|
*/
|
|
10
|
-
export type BuiltinSourceType =
|
|
10
|
+
export type BuiltinSourceType =
|
|
11
|
+
| 'chat_message'
|
|
12
|
+
| 'agent_chat_message'
|
|
13
|
+
| 'agent_diagnosis_case'
|
|
14
|
+
| 'document' | 'book' | 'article' | 'prd' | 'question'
|
|
15
|
+
| 'table' | 'metric' | 'data_dev_script' | 'query_case'
|
|
11
16
|
/** 来源业务分类 id。自定义分类是开放集合,所以不是字面量联合。 */
|
|
12
17
|
export type SourceType = string
|
|
13
18
|
/**
|
|
@@ -20,8 +25,8 @@ export type SourceType = string
|
|
|
20
25
|
*/
|
|
21
26
|
export type SchemaType =
|
|
22
27
|
| 'concept' | 'metric' | 'table' | 'event' | 'playbook' | 'case'
|
|
23
|
-
| 'diagnosis/symptom' | 'diagnosis/hypothesis' | 'diagnosis/check'
|
|
24
|
-
| 'diagnosis/failure-
|
|
28
|
+
| 'diagnosis/symptom' | 'diagnosis/hypothesis' | 'diagnosis/check-point'
|
|
29
|
+
| 'diagnosis/failure-reason' | 'diagnosis/case'
|
|
25
30
|
export type EmbeddingProvider = 'wemm-python' | 'ollama' | 'none'
|
|
26
31
|
export type EmbeddingDevice = 'auto' | 'mps' | 'cpu'
|
|
27
32
|
export type RerankProvider = 'bert-python' | 'none'
|
|
@@ -69,7 +74,7 @@ export interface KnowledgeConfig {
|
|
|
69
74
|
*/
|
|
70
75
|
agentRoot: string
|
|
71
76
|
/**
|
|
72
|
-
* @deprecated 0029:这是**旧布局**的知识树根(`
|
|
77
|
+
* @deprecated 0029:这是**旧布局**的知识树根(`01_source_raw` 直接躺在它
|
|
73
78
|
* 下面)。它不再可设置,也不再决定任何路径 —— 只作为一次性的迁移来源存在:
|
|
74
79
|
* 值非空且新位置还是空的时,把旧树搬进 `<agentRoot>/domain_knowledge/default`。
|
|
75
80
|
* 搬完(或本来就没有)它就不再有意义。
|
|
@@ -88,17 +93,23 @@ export interface KnowledgeConfig {
|
|
|
88
93
|
* Pass 0(候选抽取 + 归一)的总开关。
|
|
89
94
|
*
|
|
90
95
|
* 默认**开**:它是「SourceRaw → Wiki」这条链上唯一不依赖模型的一步,关掉之后
|
|
91
|
-
*
|
|
92
|
-
*
|
|
96
|
+
* 知识库就只能靠人一次次手工补卡片。关掉的场景是真实存在的
|
|
97
|
+
* (批量导入时不想让每份文档都自动长出一张卡片),所以它必须可关,
|
|
93
98
|
* 但关掉不等于删除:计数器会如实回 0,而不是假装抽过了。
|
|
94
99
|
*/
|
|
95
100
|
extractCandidates: boolean
|
|
96
101
|
/**
|
|
97
|
-
* #3b
|
|
102
|
+
* #3b:用模型做**按 schema 的实体抽取与归并**(`extract-model.ts`)。
|
|
98
103
|
*
|
|
99
|
-
* 开着时,`rebuild`
|
|
100
|
-
*
|
|
101
|
-
*
|
|
104
|
+
* 开着时,`rebuild` 把「这份来源里有哪些实体、每个实体该写成什么样的小节、
|
|
105
|
+
* 其中哪些 wiki 里已经有了」交给模型;关系候选仍由规则抽取补上,模型不可用则整轮
|
|
106
|
+
* 退回规则。所以它是**增强**而不是依赖 —— 关掉它链路照跑,只是同一实体横跨两份
|
|
107
|
+
* 文档时可能落成两张卡(实测过),而且卡片正文会退回成按小节切下来的原文片段。
|
|
108
|
+
*
|
|
109
|
+
* **0044 起默认开。** 编译的全部价值就是「按 wiki schema 提取实体」,而这件事只有
|
|
110
|
+
* 模型做得了:规则路径拿 `##` 小节标题当实体名,切出来的就是目录,不是知识。
|
|
111
|
+
* 0040 时期默认关,是因为那时候抽出来的东西要人逐条确认、成本要省着花;
|
|
112
|
+
* 现在入库在前、review 在后,默认关等于默认不编译。
|
|
102
113
|
*
|
|
103
114
|
* 触发时机跟着来源走:只有**变化的来源**才进这一轮(增量复用没变的不重跑),
|
|
104
115
|
* 所以成本与「改了多少来源」成正比,而不是与库的大小成正比。
|
|
@@ -137,6 +148,14 @@ export interface KnowledgeConfig {
|
|
|
137
148
|
vectorThreshold: number
|
|
138
149
|
/** Stage 4 rerank: how many reranked wikiNotes enter the model context. */
|
|
139
150
|
contextTopN: number
|
|
151
|
+
/**
|
|
152
|
+
* 上下文最大字符数(0050)。默认 20000,`0` = 不限。
|
|
153
|
+
*
|
|
154
|
+
* 它约束的是**组装后的上下文字符数**(命中的 note 正文 + 扩展卡片的标题/摘要/正文),
|
|
155
|
+
* 不是 `hits` 的长度:5 条 note 可能是一千字,也可能是十万字(一条 note 就是卡片的一整节)。
|
|
156
|
+
* 超出的部分落系统临时目录,上下文里留一行绝对路径,Agent 用自己的文件读取能力去取。
|
|
157
|
+
*/
|
|
158
|
+
contextMaxChars: number
|
|
140
159
|
/**
|
|
141
160
|
* Stage 4 重排后的最低分(0..1,sigmoid 之后的 cross-encoder 分)。
|
|
142
161
|
*
|
|
@@ -184,7 +203,7 @@ export type RuntimeKnowledgeConfig = Omit<KnowledgeConfig, 'rootDir' | 'agentRoo
|
|
|
184
203
|
export const DEFAULT_KNOWLEDGE_CONFIG: RuntimeKnowledgeConfig = {
|
|
185
204
|
// 智能归因的五类在这里一并默认开启:没有它们,排查思路写不进 wiki、召不回来(见 SchemaType)。
|
|
186
205
|
schemaTypes: ['concept', 'metric', 'table', 'event', 'playbook', 'case', ...DIAGNOSIS_ENTITY_TYPES],
|
|
187
|
-
sourceAppendTypes: ['chat_message', '
|
|
206
|
+
sourceAppendTypes: ['chat_message', 'agent_chat_message', 'document'],
|
|
188
207
|
allowSourceTruth: true,
|
|
189
208
|
// 0029 撤掉了 `maxSourceBytes`:单条来源**没有**字符数上限。见
|
|
190
209
|
// `runtime.ts` 的 `addSource` —— 真正的边界是转换内核的 `maxInputBytes`
|
|
@@ -192,9 +211,9 @@ export const DEFAULT_KNOWLEDGE_CONFIG: RuntimeKnowledgeConfig = {
|
|
|
192
211
|
// 只有显式写 `false` / `0` 才关;空值、其他任何写法都按默认「开」处理,
|
|
193
212
|
// 免得一个打错的 env 值把整条 Pass 0 悄悄关掉。
|
|
194
213
|
extractCandidates: !['false', '0'].includes((process.env.DSH_KNOWLEDGE_EXTRACT_CANDIDATES ?? '').trim().toLowerCase()),
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
extractWithModel: ['
|
|
214
|
+
// 0044:模型抽取默认**开**,因为「按 schema 提取实体」就是编译本身。只有在
|
|
215
|
+
// env 里显式写 `false` / `0` 才关(关掉后退回纯规则 Pass 0:按小节切,正文是原文片段)。
|
|
216
|
+
extractWithModel: !['false', '0'].includes((process.env.DSH_KNOWLEDGE_EXTRACT_WITH_MODEL ?? '').trim().toLowerCase()),
|
|
198
217
|
// 0029:三个「家当」路径的默认值是**空串 = 自动**。它们跟着 `<agentRoot>/resource/`
|
|
199
218
|
// 走,而 agentRoot 由设置决定、模块加载时还不知道 —— 所以在运行时解析
|
|
200
219
|
// (`runtime.ts` 的 `resolveResources`)。环境变量仍然优先。
|
|
@@ -229,13 +248,14 @@ export const DEFAULT_KNOWLEDGE_CONFIG: RuntimeKnowledgeConfig = {
|
|
|
229
248
|
*/
|
|
230
249
|
vectorThreshold: Number(process.env.DSH_KNOWLEDGE_VECTOR_THRESHOLD || 0.5),
|
|
231
250
|
contextTopN: Number(process.env.DSH_KNOWLEDGE_CONTEXT_TOP_N || 5),
|
|
251
|
+
contextMaxChars: Number(process.env.DSH_KNOWLEDGE_CONTEXT_MAX_CHARS || 20_000),
|
|
232
252
|
rerankThreshold: Number(process.env.DSH_KNOWLEDGE_RERANK_THRESHOLD || 0.2),
|
|
233
253
|
rerankProvider: (process.env.DSH_RERANK_PROVIDER as RerankProvider | undefined) || 'bert-python',
|
|
234
254
|
rerankModel: process.env.DSH_RERANK_MODEL || '',
|
|
235
255
|
rerankTimeoutMs: Number(process.env.DSH_RERANK_TIMEOUT_MS || 60000),
|
|
236
256
|
}
|
|
237
257
|
|
|
238
|
-
/** 一条原始可追溯文档(SourceRaw)。磁盘布局沿用 `
|
|
258
|
+
/** 一条原始可追溯文档(SourceRaw)。磁盘布局沿用 `01_source_raw`。 */
|
|
239
259
|
export interface SourceRaw {
|
|
240
260
|
sourceId: string
|
|
241
261
|
sourceType: SourceType
|
|
@@ -270,6 +290,42 @@ export interface SourceRawRead extends SourceRaw {
|
|
|
270
290
|
totalChars: number
|
|
271
291
|
}
|
|
272
292
|
|
|
293
|
+
/**
|
|
294
|
+
* 一条**人工备注**(0044)。
|
|
295
|
+
*
|
|
296
|
+
* 它永远不进 `content`。理由只有一个:wiki 正文由编译产出,人往正文里写字就等于让
|
|
297
|
+
* 「这一段是谁写的」失去答案 —— 下一次编译追加内容时,人写的那段该保留还是该被覆盖,
|
|
298
|
+
* 没有任何判据。备注单独存,两个问题一起消失:编译只管自己的正文,人只管自己的判断。
|
|
299
|
+
*
|
|
300
|
+
* `kind: 'anomaly'` 是「这里有异常点」的显式标记 —— 例如卡片里的数字与原始文档对不上、
|
|
301
|
+
* 两处口径互相矛盾。它比一条普通备注更需要被召回时看见,所以在上下文包里单独标出。
|
|
302
|
+
*/
|
|
303
|
+
export interface CardAnnotation {
|
|
304
|
+
id: string
|
|
305
|
+
kind: 'note' | 'anomaly'
|
|
306
|
+
note: string
|
|
307
|
+
author?: string
|
|
308
|
+
createdAt: string
|
|
309
|
+
/** 这条备注针对哪一份来源(可选);异常点常常能指到具体文档。 */
|
|
310
|
+
sourceRef?: string
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 一张卡片的 review 结论与评分(0044)。
|
|
315
|
+
*
|
|
316
|
+
* **入库在前、review 在后。** 卡片一编译出来就落盘、就进召回,`state` 是
|
|
317
|
+
* `unreviewed`;人看过了把它标成 `reviewed`,`score` 随之抬到
|
|
318
|
+
* `REVIEWED_SCORE_FLOOR`。评分的作用是**召回排序里的一个有界加权**,不是准入闸门 ——
|
|
319
|
+
* 没 review 过的卡片照样召回得到,只是排得稍后一点。
|
|
320
|
+
*/
|
|
321
|
+
export interface CardReview {
|
|
322
|
+
state: 'unreviewed' | 'reviewed'
|
|
323
|
+
/** 0..1。新卡是 `CARD_REVIEW_BASE_SCORE`,reviewed 之后不低于 `REVIEWED_SCORE_FLOOR`。 */
|
|
324
|
+
score: number
|
|
325
|
+
reviewedAt?: string
|
|
326
|
+
reviewedBy?: string
|
|
327
|
+
}
|
|
328
|
+
|
|
273
329
|
export interface KnowledgeCard {
|
|
274
330
|
entityId: string
|
|
275
331
|
entityType: SchemaType
|
|
@@ -279,12 +335,32 @@ export interface KnowledgeCard {
|
|
|
279
335
|
content: string
|
|
280
336
|
sourceRefs: string[]
|
|
281
337
|
provenance?: LineageRef[]
|
|
338
|
+
/**
|
|
339
|
+
* 负责人(0021 §6.4 的卡片信封)。
|
|
340
|
+
*
|
|
341
|
+
* 0049 之前 `loadCards` 不读它,于是**编译路径看不见这个字段**:任何一次
|
|
342
|
+
* 「重写这张卡片」(补图、补相关块)都会把它从文件里抹掉,而 `check.owner` 是
|
|
343
|
+
* 那个 schema 的必填字段 —— 一次与它无关的编译就能让卡片变成待补。现在读进内存,
|
|
344
|
+
* 重写时原样带回去。
|
|
345
|
+
*/
|
|
346
|
+
owner?: string
|
|
282
347
|
metadata: Record<string, unknown>
|
|
283
348
|
updatedAt: string
|
|
284
349
|
/** Resolved local markdown image references used by the multimodal embedder. */
|
|
285
350
|
imagePaths?: string[]
|
|
351
|
+
/** 人工备注(0044)。正文之外的东西,编译不碰它。 */
|
|
352
|
+
annotations?: CardAnnotation[]
|
|
353
|
+
/** review 结论与评分(0044)。缺省 = 还没人看过。 */
|
|
354
|
+
review?: CardReview
|
|
286
355
|
}
|
|
287
356
|
|
|
357
|
+
/** 新卡片的出厂评分:入库即生效,review 不是前提。 */
|
|
358
|
+
export const CARD_REVIEW_BASE_SCORE = 0.35
|
|
359
|
+
/** review 通过之后的评分下限。 */
|
|
360
|
+
export const REVIEWED_SCORE_FLOOR = 0.8
|
|
361
|
+
/** 标记为异常点时,评分降到这个值 —— 它仍然召回得到,只是排在后面。 */
|
|
362
|
+
export const ANOMALY_SCORE = 0.15
|
|
363
|
+
|
|
288
364
|
/**
|
|
289
365
|
* A compiled recall unit. Stage 2 turns every wiki card into one or more
|
|
290
366
|
* wikiNotes; stage 3 recalls wikiNotes (never the wiki directly) and resolves
|
|
@@ -312,6 +388,11 @@ export interface WikiNote {
|
|
|
312
388
|
status: KnowledgeStatus
|
|
313
389
|
/** Local markdown image references are embedded with the text when possible. */
|
|
314
390
|
imagePaths?: string[]
|
|
391
|
+
/**
|
|
392
|
+
* 父级 wiki 的 review 评分(0044)。**不进 note 的正文,也不参与向量指纹** ——
|
|
393
|
+
* 它只影响融合分的加权,所以 review 一下不会让整个语料重新编码。
|
|
394
|
+
*/
|
|
395
|
+
reviewScore?: number
|
|
315
396
|
}
|
|
316
397
|
|
|
317
398
|
/** Backwards-compatible alias: compiled retrieval units are now wikiNotes. */
|
|
@@ -451,11 +532,27 @@ export interface ContextGraph {
|
|
|
451
532
|
edges: Array<Record<string, unknown>>
|
|
452
533
|
}
|
|
453
534
|
|
|
535
|
+
/**
|
|
536
|
+
* 上下文里的一条:**一张 wiki 卡片**,不是一条 note(0050)。
|
|
537
|
+
*
|
|
538
|
+
* 0050 之前一条上下文对应一条 note,于是同一张卡片的多个小节能吃掉 `context_top_n`
|
|
539
|
+
* 的全部名额。现在按卡片去重:命中的那几张卡片各带**它命中的那些 note**(按重排分降序),
|
|
540
|
+
* 再按「该卡片最好那条 note 的重排分」排序 —— 卡片级顺序仍然由片段级重排决定。
|
|
541
|
+
*
|
|
542
|
+
* 顺序是硬约束:**先去重,再扩展**。反过来就分不清哪张是召回来的、哪张是邻居。
|
|
543
|
+
*/
|
|
454
544
|
export interface ContextItem {
|
|
455
|
-
note: RecallHit
|
|
456
545
|
wiki: KnowledgeCard | null
|
|
457
|
-
/**
|
|
546
|
+
/** 命中的 note(按重排分降序)。`reason === 'expansion'` 时为空。 */
|
|
547
|
+
notes: RecallHit[]
|
|
548
|
+
/** `notes[0]` 的别名:只要「最好那一片」的消费方用它。扩展条目没有它。 */
|
|
549
|
+
note?: RecallHit
|
|
550
|
+
/** 这张卡片自己的一跳子图。 */
|
|
458
551
|
graph: ContextGraph
|
|
552
|
+
/** 这张卡片为什么在上下文里:命中,还是 1 跳扩展。 */
|
|
553
|
+
reason: 'hit' | 'expansion'
|
|
554
|
+
/** 扩展条目:它是从哪张命中的卡片、沿哪条边连过来的。 */
|
|
555
|
+
via?: { entityType: string; entityId: string; title: string; relation: string }
|
|
459
556
|
/**
|
|
460
557
|
* 这条命中引用的 SourceRaw 证据片段(0031)。
|
|
461
558
|
*
|
|
@@ -465,12 +562,101 @@ export interface ContextItem {
|
|
|
465
562
|
evidence?: RecallEvidence[]
|
|
466
563
|
}
|
|
467
564
|
|
|
565
|
+
/**
|
|
566
|
+
* 召回各阶段的耗时(毫秒)——「为什么慢」的答案必须能在界面上看见(0050 第二版)。
|
|
567
|
+
*
|
|
568
|
+
* 拆得比「总耗时」细,是因为这几个数**量级差得很远**:查询编码在模型已加载时约 270ms,
|
|
569
|
+
* 首次(含加载 2B 模型)约 8s;重排同理(首次含加载 BERT)。只给一个总数时,人只会觉得
|
|
570
|
+
* 「这个测试很慢」,而看不出慢在加载模型还是慢在算法。
|
|
571
|
+
*
|
|
572
|
+
* 每个字段都是**这一段自己的**耗时,不是累计值。
|
|
573
|
+
*/
|
|
574
|
+
export interface RecallTimings {
|
|
575
|
+
/** 关键词路:FTS5 查询 + 多字词覆盖率算分。 */
|
|
576
|
+
ftsMs: number
|
|
577
|
+
/** 向量路整体(= `embedMs` + `scanMs` + 阈值与排序)。 */
|
|
578
|
+
vectorMs: number
|
|
579
|
+
/** 其中:查询编码。**首次含模型加载**,所以它可能是 8000ms 而不是 270ms。 */
|
|
580
|
+
embedMs?: number
|
|
581
|
+
/** 其中:索引扫描(汉明预筛 + int8 精算)。 */
|
|
582
|
+
scanMs?: number
|
|
583
|
+
/** 图谱 boost 的名次表。 */
|
|
584
|
+
graphMs: number
|
|
585
|
+
/** RRF 融合 + 图谱加权 + 排序。 */
|
|
586
|
+
fuseMs: number
|
|
587
|
+
/** cross-encoder 重排。**首次含模型加载**;跳过重排时没有这个字段。 */
|
|
588
|
+
rerankMs?: number
|
|
589
|
+
/** 组装:按卡片去重 + 1 跳扩展 + 上下文预算装箱。 */
|
|
590
|
+
assembleMs: number
|
|
591
|
+
/** 这一整次召回(从进入到返回)。 */
|
|
592
|
+
totalMs: number
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* 面板账本的一行(0050):**这一路**对这个候选算出的分数与它的名次。
|
|
597
|
+
*
|
|
598
|
+
* 它只在 HTTP 的 `detail=true` 上返回,工具调用不带 —— 账本是给人看的(哪条被丢了、
|
|
599
|
+
* 各自多少分),进模型上下文只会变成几十行噪声。
|
|
600
|
+
*/
|
|
601
|
+
export interface RecallLedgerRow {
|
|
602
|
+
unitId: string
|
|
603
|
+
title: string
|
|
604
|
+
entityType: string
|
|
605
|
+
entityId: string
|
|
606
|
+
/** 这一路自己的分数(关键词 BM25 / 向量余弦 / 融合分)。 */
|
|
607
|
+
score: number
|
|
608
|
+
/** 这一路内部的名次(1 起)。 */
|
|
609
|
+
rank: number
|
|
610
|
+
/** 过没过这一路的阈值。 */
|
|
611
|
+
kept: boolean
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/** 面板账本:每一路的全部候选 + 重排的全部候选(含被丢弃的)。 */
|
|
615
|
+
export interface RecallLedger {
|
|
616
|
+
/** 关键词路:`rows` 含没过阈值的那些。 */
|
|
617
|
+
fts: RecallLedgerRow[]
|
|
618
|
+
/** 向量路:同上。 */
|
|
619
|
+
vector: RecallLedgerRow[]
|
|
620
|
+
/** 融合后进入重排的候选。`logit` 是模型原始分(跨查询可比的那个刻度)。 */
|
|
621
|
+
rerank: Array<RecallLedgerRow & { fused: number; logit?: number; rerankScore?: number; dropped: boolean }>
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** 1 跳扩展的结果(0050):命中卡片之外,按图上的边拉进来的邻居。 */
|
|
625
|
+
export interface RecallExpansion {
|
|
626
|
+
/** 邻居卡片。`via` 是「从哪张卡、沿哪条边」—— 一条邻居可能有多条来路。 */
|
|
627
|
+
nodes: Array<{ entityType: string; entityId: string; title: string; via: string[] }>
|
|
628
|
+
/** 这些邻居之间的边(只含两端都在扩展集里的)。 */
|
|
629
|
+
edges: Array<Record<string, unknown>>
|
|
630
|
+
/** 其中真的进了上下文的(其余被预算挡在外面)。 */
|
|
631
|
+
included: string[]
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* 上下文预算(0050)的真实账:装进去多少、挤掉多少、溢出文件在哪。
|
|
636
|
+
*
|
|
637
|
+
* 每个数都来自一次真实的拼接,没有估算 —— 面板据此显示「还有 N 张卡片没进上下文」。
|
|
638
|
+
*/
|
|
639
|
+
export interface ContextBudgetReport {
|
|
640
|
+
/** 这次生效的上限(`0` = 不限)。 */
|
|
641
|
+
maxChars: number
|
|
642
|
+
/** 装进上下文的字符数。 */
|
|
643
|
+
usedChars: number
|
|
644
|
+
/** 因为预算没进上下文的卡片数与字符数。 */
|
|
645
|
+
omittedCards: number
|
|
646
|
+
omittedChars: number
|
|
647
|
+
/** 命中的卡片数(去重后)与扩展进来的卡片数。 */
|
|
648
|
+
hitCards: number
|
|
649
|
+
expandedCards: number
|
|
650
|
+
/** 溢出文件的绝对路径(有溢出时才有;它在系统临时目录里,不随库走)。 */
|
|
651
|
+
overflowPath?: string
|
|
652
|
+
}
|
|
653
|
+
|
|
468
654
|
export interface RecallResult {
|
|
469
655
|
/** Stage 3: 融合(+ 重排)之后的 wikiNotes,最多 `recallTopK`。 */
|
|
470
656
|
hits: RecallHit[]
|
|
471
657
|
/** Stage 4: the `contextTopN` notes expanded to their wiki + 1-hop graph. */
|
|
472
658
|
context: ContextItem[]
|
|
473
|
-
/** Distinct wiki cards hit by the recalled notes
|
|
659
|
+
/** Distinct wiki cards hit by the recalled notes(去重后,按各自最好那条 note 排序)。 */
|
|
474
660
|
wiki: KnowledgeCard[]
|
|
475
661
|
/** Union of the 1-hop subgraphs of the recalled wiki cards. */
|
|
476
662
|
graph: ContextGraph
|
|
@@ -493,6 +679,14 @@ export interface RecallResult {
|
|
|
493
679
|
insufficientEvidence: boolean
|
|
494
680
|
/** 每一路在这一轮里发生了什么(阈值、候选数、最高分、降级原因)。 */
|
|
495
681
|
diagnostics: RecallDiagnostics
|
|
682
|
+
/** 1 跳扩展的结果(0050)。没有邻居时是空集,不是缺字段。 */
|
|
683
|
+
expansion?: RecallExpansion
|
|
684
|
+
/** 上下文预算的真实账(0050)。 */
|
|
685
|
+
budget?: ContextBudgetReport
|
|
686
|
+
/** 面板账本(0050):只在 `detail: true` 时返回。 */
|
|
687
|
+
ledger?: RecallLedger
|
|
688
|
+
/** 各阶段耗时(毫秒)。它很小,所以不藏在 `detail` 后面 —— Agent 也会问「为什么慢」。 */
|
|
689
|
+
timings?: RecallTimings
|
|
496
690
|
recallId: string
|
|
497
691
|
indexVersion?: number
|
|
498
692
|
compileRunId?: string
|
|
@@ -519,10 +713,13 @@ export interface KnowledgeBase {
|
|
|
519
713
|
*/
|
|
520
714
|
wikiTypes: WikiTypeDef[]
|
|
521
715
|
/**
|
|
522
|
-
*
|
|
523
|
-
*
|
|
716
|
+
* 0044 撤掉了 `wikiSchemaMode`(`strict` 拒绝不合规写入 / `report` 只标记)。
|
|
717
|
+
*
|
|
718
|
+
* 两个理由:**缺信息也入库**是这一版的核心决定(卡片记下来,后面有涉及到的文档再补),
|
|
719
|
+
* 而一个「拒绝写入」的开关与它直接冲突;而且校验本身已经不再决定写不写 —— 它只把问题
|
|
720
|
+
* 写进 `metadata.schema_issues`,由面板显示成「待补」。留一个什么都不拦的开关,
|
|
721
|
+
* 比没有更糟:界面上摆着它,人就会以为它在起作用。
|
|
524
722
|
*/
|
|
525
|
-
wikiSchemaMode: WikiSchemaMode
|
|
526
723
|
/**
|
|
527
724
|
* 计算字段:当前 `wikiTypes` 是否已偏离出厂定义。界面据此决定「恢复默认」是否可点,
|
|
528
725
|
* 也让人一眼看出这套 schema 是本库自己调过的。
|
|
@@ -558,7 +755,7 @@ export interface KnowledgeDocument {
|
|
|
558
755
|
createdAt: string
|
|
559
756
|
updatedAt: string
|
|
560
757
|
versionCount: number
|
|
561
|
-
/** 相对知识库根目录,如 `
|
|
758
|
+
/** 相对知识库根目录,如 `01_source_raw/<slug>.md`。 */
|
|
562
759
|
relativePath: string
|
|
563
760
|
ext: string
|
|
564
761
|
}
|
|
@@ -570,8 +767,21 @@ export interface KnowledgeAssetRef {
|
|
|
570
767
|
url: string
|
|
571
768
|
}
|
|
572
769
|
|
|
770
|
+
/**
|
|
771
|
+
* 文档详情。
|
|
772
|
+
*
|
|
773
|
+
* `content` **可能只是正文的前一段**:明细接口按 `SOURCE_PREVIEW_CHARS` 封顶,
|
|
774
|
+
* 因为抽屉里的预览读不完一份几十万字文档,而把它整份发过来正是「打开弹层要等半天」
|
|
775
|
+
* 的原因。要全文走独立整页的分页接口(`GET /knowledge/documents/:docId/content`)。
|
|
776
|
+
* 两件事分开之后,`contentChars` / `contentTruncated` 就是界面必须读的那两个字段 ——
|
|
777
|
+
* 「这里显示了多少、全文有多少」不能靠猜。
|
|
778
|
+
*/
|
|
573
779
|
export interface KnowledgeDocumentDetail extends KnowledgeDocument {
|
|
574
780
|
content: string
|
|
781
|
+
/** 正文总字符数(不是 `content.length`)。 */
|
|
782
|
+
contentChars: number
|
|
783
|
+
/** `content` 是否被截断过。为真时界面要给「新页面打开」。 */
|
|
784
|
+
contentTruncated: boolean
|
|
575
785
|
frontmatter: Record<string, unknown>
|
|
576
786
|
assets: KnowledgeAssetRef[]
|
|
577
787
|
lineage?: Record<string, string>
|
|
@@ -613,10 +823,27 @@ export function deriveSourceExt(metadata: Record<string, unknown> | undefined):
|
|
|
613
823
|
if (dot > 0 && dot < base.length - 1) return base.slice(dot).toLowerCase()
|
|
614
824
|
}
|
|
615
825
|
const format = metadata && typeof metadata.source_format === 'string' ? metadata.source_format.trim().toLowerCase() : ''
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
826
|
+
return FORMAT_EXTENSION[format] ?? (format ? `.${format}` : '.md')
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* 格式类 → 扩展名。
|
|
831
|
+
*
|
|
832
|
+
* 为什么需要这张表:扩展名与格式类**不是一回事**(`web_page` 的底稿是 `.html`,
|
|
833
|
+
* `office` 可能是 `.docx` / `.xlsx` / `.pptx`),而 0048 之后 front matter 里只有格式类
|
|
834
|
+
* 和底稿文件名,没有那个已经失效的暂存路径可查。表里没有的格式退回 `.${format}`:
|
|
835
|
+
* 一个看得出是推导出来的扩展名,好过假装知道。
|
|
836
|
+
*/
|
|
837
|
+
const FORMAT_EXTENSION: Record<string, string> = {
|
|
838
|
+
markdown: '.md',
|
|
839
|
+
text: '.txt',
|
|
840
|
+
html: '.html',
|
|
841
|
+
pdf: '.pdf',
|
|
842
|
+
office: '.docx',
|
|
843
|
+
web_page: '.html',
|
|
844
|
+
video: '.mp4',
|
|
845
|
+
audio: '.mp3',
|
|
846
|
+
image: '.png',
|
|
620
847
|
}
|
|
621
848
|
|
|
622
849
|
export interface RebuildReport {
|
|
@@ -668,19 +895,40 @@ export interface RebuildReport {
|
|
|
668
895
|
* 「SourceRaw → Wiki」第一段的诚实计数。
|
|
669
896
|
*
|
|
670
897
|
* 这八个数**每一个都来自真实发生过的事件**,没有一个是估算出来的:抽取了几个
|
|
671
|
-
*
|
|
898
|
+
* 候选、其中几个被类型闸门拒了、新建了几张卡、更新了几张卡、几条关系真的连上了、
|
|
672
899
|
* 几条因为端点不存在被丢掉、几份来源这次被重抽、几份因为指纹没变被跳过。
|
|
673
900
|
* `extractCandidates` 关掉时它们全部回 0,而不是从响应里消失 —— 「跳过」和
|
|
674
901
|
* 「没有这个能力」是两件事,用缺字段表达会让人分不清。
|
|
675
902
|
*/
|
|
676
903
|
candidatesExtracted: number
|
|
677
904
|
candidatesRejected: number
|
|
905
|
+
/**
|
|
906
|
+
* 这一轮**新建**的 wiki 卡片数。
|
|
907
|
+
*
|
|
908
|
+
* 名字从 `proposalsCreated` 改回来(0044):Pass 0 重新直接落卡,不再只提议。
|
|
909
|
+
* 0040 v1.5 那道「人确认之后才入库」的门在 0044 撤掉了 —— 入库在前,review 在后,
|
|
910
|
+
* 所以计数必须如实说「新建了 N 张卡片」,而磁盘上真的有 N 张。
|
|
911
|
+
*/
|
|
678
912
|
cardsCreated: number
|
|
679
|
-
|
|
913
|
+
/** 这一轮**更新**(把新抽到的正文追加进去、或补上来源)的已有卡片数。 */
|
|
914
|
+
cardsUpdated: number
|
|
680
915
|
relationsExtracted: number
|
|
681
916
|
relationsDropped: number
|
|
682
917
|
sourcesExtracted: number
|
|
683
918
|
sourcesSkipped: number
|
|
919
|
+
// ------------------------------------------------ Finalize · 索引 / 断链 / 相关
|
|
920
|
+
/**
|
|
921
|
+
* 收尾段(0049)的真实计数。三段都不调模型,全是规则与图算法。
|
|
922
|
+
*
|
|
923
|
+
* `relatedLinks` 是**相关链接条数**(一张卡片指向另一张算一条,双向各算一条),
|
|
924
|
+
* `relatedCards` 是有至少一条相关的卡片数,`deadLinks` 是正文里指向不存在卡片的
|
|
925
|
+
* `[[…]]` 条数 —— 它是三层校验里 `reference` 那一层。
|
|
926
|
+
*/
|
|
927
|
+
relatedLinks?: number
|
|
928
|
+
relatedCards?: number
|
|
929
|
+
deadLinks?: number
|
|
930
|
+
/** 这一轮因为补相关块或改写断链问题而重写的卡片数。 */
|
|
931
|
+
finalizeRewritten?: number
|
|
684
932
|
}
|
|
685
933
|
|
|
686
934
|
// ------------------------------------------------- 来源业务分类注册表(0013)
|
|
@@ -712,10 +960,96 @@ export interface SourceTypeDef {
|
|
|
712
960
|
builtin: boolean
|
|
713
961
|
/** 兜底分类(`document`):必须存在且不可停用,判定链最后一步落在它上。 */
|
|
714
962
|
fallback: boolean
|
|
963
|
+
/**
|
|
964
|
+
* 这个分类的来源**该带哪些元数据字段**(0048)。
|
|
965
|
+
*
|
|
966
|
+
* 为什么放在类型定义里而不是写死一份全局清单:一张指标表的元数据和一篇公众号
|
|
967
|
+
* 文章需要的完全不是同一批东西 —— 指标要口径与负责人,文章要作者与发布时间。
|
|
968
|
+
* 声明在分类上,面板才能在「录入向导 / 来源详情」里说清**这一类**缺了什么,
|
|
969
|
+
* 而不是对所有来源提同一份要求。
|
|
970
|
+
*
|
|
971
|
+
* `required: true` 的字段缺失时**不拒收**(缺信息也编译进去),只如实报出来。
|
|
972
|
+
*/
|
|
973
|
+
metadataFields?: SourceMetadataField[]
|
|
715
974
|
/** 计算字段:该库中引用该分类的来源数,由 `refresh` 实时算出,不参与持久化。 */
|
|
716
975
|
sourceCount: number
|
|
717
976
|
}
|
|
718
977
|
|
|
978
|
+
/**
|
|
979
|
+
* 一个来源分类声明的元数据字段(0048)。
|
|
980
|
+
*
|
|
981
|
+
* `key` 用 front matter 里的**顶层键名**(OKF v0.2 的词汇:`resource` / `description` /
|
|
982
|
+
* `sources[].author` / `generated.at` …),这样面板能直接拿它去 front matter 里取值,
|
|
983
|
+
* 不需要第二张映射表。
|
|
984
|
+
*/
|
|
985
|
+
export interface SourceMetadataField {
|
|
986
|
+
/** front matter 顶层键,或 `sources[0].author` 这种点路径。 */
|
|
987
|
+
key: string
|
|
988
|
+
label: string
|
|
989
|
+
/** 一句话说明这个字段是什么,面板上作为提示。 */
|
|
990
|
+
description?: string
|
|
991
|
+
/** 缺失时提示「这一类来源通常要带它」,但**不拒收**。 */
|
|
992
|
+
required?: boolean
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* 一份来源的 OKF v0.2 溯源信息(0048)。
|
|
997
|
+
*
|
|
998
|
+
* 字段名直接取自 OKF v0.2(<https://github.com/GoogleCloudPlatform/open-knowledge-format>):
|
|
999
|
+
* `description` / `resource` / `generated{by,at}` / `sources[]`。规范里没有「采集时间」
|
|
1000
|
+
* 这个概念,所以采集时间写在 `generated.at`(本文件是什么时候、被谁写出来的)。
|
|
1001
|
+
*/
|
|
1002
|
+
export interface SourceProvenance {
|
|
1003
|
+
/** 一句话摘要(OKF `description`)。 */
|
|
1004
|
+
description?: string
|
|
1005
|
+
/** 底层资产的规范地址(OKF `resource`):原始网页地址,或库内相对路径。 */
|
|
1006
|
+
resource?: string
|
|
1007
|
+
/** 谁把这份文件写出来的(OKF `generated.by`),如 `convert/web@1/wx_article`。 */
|
|
1008
|
+
generatedBy?: string
|
|
1009
|
+
/** 什么时候写出来的(OKF `generated.at`),即采集/转换时间。 */
|
|
1010
|
+
generatedAt?: string
|
|
1011
|
+
/** 这份文件的来源清单(OKF `sources[]`)。 */
|
|
1012
|
+
sources?: SourceOriginRef[]
|
|
1013
|
+
/** 标签(OKF `tags`)。 */
|
|
1014
|
+
tags?: string[]
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* 元数据字段声明的规范化。
|
|
1019
|
+
*
|
|
1020
|
+
* 只做「去掉空值、去重、trim」这类整理,不做严格校验:字段清单是**提示性**的
|
|
1021
|
+
* (缺失只报不拒),为它设一道硬闸门只会让人没法保存一个还在想字段名的草稿。
|
|
1022
|
+
* `key` 必须非空且不含空白 —— 它要被当成 front matter 的路径去取值。
|
|
1023
|
+
*/
|
|
1024
|
+
export function normalizeMetadataFields(input: unknown): SourceMetadataField[] {
|
|
1025
|
+
if (!Array.isArray(input)) return []
|
|
1026
|
+
const seen = new Set<string>()
|
|
1027
|
+
const fields: SourceMetadataField[] = []
|
|
1028
|
+
for (const item of input) {
|
|
1029
|
+
if (item === null || typeof item !== 'object') continue
|
|
1030
|
+
const record = item as Record<string, unknown>
|
|
1031
|
+
const key = typeof record.key === 'string' ? record.key.trim() : ''
|
|
1032
|
+
if (!key || /\s/.test(key) || seen.has(key)) continue
|
|
1033
|
+
seen.add(key)
|
|
1034
|
+
const label = typeof record.label === 'string' && record.label.trim() ? record.label.trim() : key
|
|
1035
|
+
const description = typeof record.description === 'string' && record.description.trim() ? record.description.trim() : undefined
|
|
1036
|
+
fields.push({ key, label, ...(description ? { description } : {}), ...(record.required === true ? { required: true } : {}) })
|
|
1037
|
+
}
|
|
1038
|
+
return fields
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/** OKF `sources[]` 里的一条。`resource` 是条目内唯一必填。 */
|
|
1042
|
+
export interface SourceOriginRef {
|
|
1043
|
+
/** 稳定键,正文引用与逐条归属都用它。 */
|
|
1044
|
+
id: string
|
|
1045
|
+
resource: string
|
|
1046
|
+
title?: string
|
|
1047
|
+
/** 来源的产出者(作者 / 账号 / 团队)。 */
|
|
1048
|
+
author?: string
|
|
1049
|
+
/** 来源自身最后变更时间(ISO 8601),对网页就是发布时间。 */
|
|
1050
|
+
lastModified?: string
|
|
1051
|
+
}
|
|
1052
|
+
|
|
719
1053
|
/** 分类 id 的形状约束;与 0013 规范第 1 节一致。 */
|
|
720
1054
|
export const SOURCE_TYPE_ID_PATTERN = /^[a-z][a-z0-9_]{1,40}$/
|
|
721
1055
|
/** 兜底分类 id;判定链全不命中时落它,因此它不可停用、不可删除。 */
|
|
@@ -735,14 +1069,23 @@ export const BUILTIN_SOURCE_TYPES: ReadonlyArray<Omit<SourceTypeDef, 'sourceCoun
|
|
|
735
1069
|
label: '聊天日志',
|
|
736
1070
|
description: '人与人之间在日常沟通中产生的消息记录,以对话往返为主要形态。',
|
|
737
1071
|
signals: ['有发言人与时间戳的短消息流', '多话题跳转、口语化、缩写多', '常见 @提及、表情、附件'],
|
|
738
|
-
excludes: ['与 AI 的对话归
|
|
1072
|
+
excludes: ['与 AI 的对话归 agent_chat_message', '整理后的会议纪要归 document', '已成问题—根因—方案的记录归 question'],
|
|
739
1073
|
enabled: true,
|
|
740
1074
|
builtin: true,
|
|
741
1075
|
fallback: false,
|
|
742
1076
|
},
|
|
743
1077
|
{
|
|
744
|
-
|
|
745
|
-
|
|
1078
|
+
/**
|
|
1079
|
+
* `ai_chat_message` 在 0040 改名为 `agent_chat_message`(**只改名,不改语义**)。
|
|
1080
|
+
*
|
|
1081
|
+
* 理由:原有八个内置分类里只有它带 `ai_` 前缀,而链路里其他地方一律用 `agent`
|
|
1082
|
+
* (`agent_diagnosis_case`、Agent 工具、`agentRoot`)。统一词根,避免同一份设计里
|
|
1083
|
+
* 出现两套命名。存量来源靠一次性迁移换目录 + 改 frontmatter(见
|
|
1084
|
+
* `runtime.migrateLegacySourceTypeRename`),注册表侧的旧 id 在
|
|
1085
|
+
* `normalizeSourceTypes` 里按别名认。
|
|
1086
|
+
*/
|
|
1087
|
+
id: 'agent_chat_message',
|
|
1088
|
+
label: '用户与 AI 的对话日志',
|
|
746
1089
|
description: '用户与 AI 助手之间的完整对话记录,包含双方往返消息。',
|
|
747
1090
|
signals: ['user / assistant 角色交替', '提示词与回答成对出现', '代码块密集、常与会话 id 或模型名同现'],
|
|
748
1091
|
excludes: ['人在群里转贴 AI 回答归 chat_message', '由 AI 对话整理出的方法论文档归 document 或 article'],
|
|
@@ -750,6 +1093,28 @@ export const BUILTIN_SOURCE_TYPES: ReadonlyArray<Omit<SourceTypeDef, 'sourceCoun
|
|
|
750
1093
|
builtin: true,
|
|
751
1094
|
fallback: false,
|
|
752
1095
|
},
|
|
1096
|
+
{
|
|
1097
|
+
/**
|
|
1098
|
+
* `agent_diagnosis_case`(0040 §3.1)—— **AI 诊断日志**。
|
|
1099
|
+
*
|
|
1100
|
+
* 它装的是**一次排查的过程记录**:现象、走过的路径(有序)、每个检查点的产出
|
|
1101
|
+
* (含被否定与给不出结论的)、确认的根因、耗时、误导段。作者是 Agent,
|
|
1102
|
+
* 每次排查结束自动写一条(`externalId` = runId,重跑不产生重复记录)。
|
|
1103
|
+
*
|
|
1104
|
+
* **与 `question` 的边界是「谁写的、是不是已经归纳过」**:
|
|
1105
|
+
* 人写的结论(手册、人工复盘)→ `question`;Agent 自动记录的过程 → 这里。
|
|
1106
|
+
* 同一件事可以有两条来源,它们不是重复 —— 一个回答「结论是什么」,
|
|
1107
|
+
* 一个回答「当时怎么走的」。
|
|
1108
|
+
*/
|
|
1109
|
+
id: 'agent_diagnosis_case',
|
|
1110
|
+
label: 'AI 诊断日志',
|
|
1111
|
+
description: 'Agent 跑完一次问题排查后留下的过程记录:现象、路径、各检查点的产出、确认的根因、耗时与误导段。',
|
|
1112
|
+
signals: ['带 runId / 现象 id / 耗时与有序路径的结构化记录', '每个检查点有成对的「实际查询 + 结果 + 这个结果算不算数」', '含被否定的假设与「给不出结论」的检查'],
|
|
1113
|
+
excludes: ['人写的手册与人工复盘归 question', '由这次排查归纳出的卡片是 wiki 卡片而不是来源,那类内容归 document', '日常取数问答归 query_case'],
|
|
1114
|
+
enabled: true,
|
|
1115
|
+
builtin: true,
|
|
1116
|
+
fallback: false,
|
|
1117
|
+
},
|
|
753
1118
|
{
|
|
754
1119
|
id: 'document',
|
|
755
1120
|
label: '企业内部文档',
|
|
@@ -795,7 +1160,7 @@ export const BUILTIN_SOURCE_TYPES: ReadonlyArray<Omit<SourceTypeDef, 'sourceCoun
|
|
|
795
1160
|
label: '问题排查文档',
|
|
796
1161
|
description: '以某个具体问题或故障为中心的记录:现象、影响、排查过程、根因、解决方案与后续动作。',
|
|
797
1162
|
signals: ['错误码、堆栈或日志片段', '复现步骤、临时规避、根因、修复版本', '时间线、一问一答或一问题多方案'],
|
|
798
|
-
excludes: ['一般性经验总结归 document', '产品需求归 prd', '外部作者的公开分析归 article'],
|
|
1163
|
+
excludes: ['一般性经验总结归 document', '产品需求归 prd', '外部作者的公开分析归 article', 'Agent 跑出来的一次排查过程记录归 agent_diagnosis_case'],
|
|
799
1164
|
enabled: true,
|
|
800
1165
|
builtin: true,
|
|
801
1166
|
fallback: false,
|
|
@@ -862,6 +1227,20 @@ export const BUILTIN_SOURCE_TYPES: ReadonlyArray<Omit<SourceTypeDef, 'sourceCoun
|
|
|
862
1227
|
|
|
863
1228
|
const BUILTIN_SOURCE_TYPE_IDS: ReadonlySet<string> = new Set(BUILTIN_SOURCE_TYPES.map(def => def.id))
|
|
864
1229
|
|
|
1230
|
+
/**
|
|
1231
|
+
* 改名之前的来源分类 id → 新 id(0040 §3.2)。
|
|
1232
|
+
*
|
|
1233
|
+
* 它有两个消费者,缺一个就会静默丢东西:
|
|
1234
|
+
* 1. **注册表**(`normalizeSourceTypes`)—— 存量 `knowledge-bases.json` 里那一项写着
|
|
1235
|
+
* 旧 id,不认别名的话它会被当成一个**自定义分类**留下来,于是「注册表里到底有什么」
|
|
1236
|
+
* 永远说不清(内置项和它并存)。
|
|
1237
|
+
* 2. **磁盘**(`runtime.migrateLegacySourceTypeRename`)—— `01_source_raw/` 是按分类
|
|
1238
|
+
* 分目录的,存量来源的 frontmatter 里写着 `source_type: ai_chat_message`。
|
|
1239
|
+
*/
|
|
1240
|
+
export const LEGACY_SOURCE_TYPE_IDS: Record<string, string> = {
|
|
1241
|
+
ai_chat_message: 'agent_chat_message',
|
|
1242
|
+
}
|
|
1243
|
+
|
|
865
1244
|
export function isBuiltinSourceType(id: string): boolean {
|
|
866
1245
|
return BUILTIN_SOURCE_TYPE_IDS.has(id)
|
|
867
1246
|
}
|
|
@@ -919,12 +1298,16 @@ function deepFreeze<T>(value: T): T {
|
|
|
919
1298
|
|
|
920
1299
|
export const SOURCE_CLASSIFIER_PROMPT: SourceClassifierPrompt = deepFreeze({
|
|
921
1300
|
readonly: true,
|
|
922
|
-
version:
|
|
1301
|
+
version: 3,
|
|
923
1302
|
title: '来源业务分类判定规则',
|
|
924
1303
|
priority: [
|
|
925
1304
|
{ type: 'user', rule: '用户显式指定分类 → 直接用,记 source_type_source: user。' },
|
|
926
|
-
{ type: '
|
|
1305
|
+
{ type: 'agent_chat_message', rule: '内容是人与 AI 的对话记录(含 AI 回复)→ agent_chat_message。' },
|
|
927
1306
|
{ type: 'chat_message', rule: '内容是人与人之间的消息流(IM / 会议聊天 / 客服会话)→ chat_message。' },
|
|
1307
|
+
// `agent_diagnosis_case` 紧跟对话类、排在通用文档之前:它的形状最特征化
|
|
1308
|
+
// (runId + 有序路径 + 每个检查点的「实际查询 / 结果 / 算不算数」),而它和
|
|
1309
|
+
// `question` 最容易混 —— 落到 question 之后再想捞回来就晚了。
|
|
1310
|
+
{ type: 'agent_diagnosis_case', rule: '内容是 Agent 跑完一次排查留下的过程记录(runId + 有序路径 + 每个检查点的实际查询与结果 + 确认的根因 + 耗时)→ agent_diagnosis_case。' },
|
|
928
1311
|
// 数据域四类排在通用文档之前:它们的形状(DDL、公式、SQL、问答)比
|
|
929
1312
|
// 「像不像一篇内部文档」更特征化,落到 document 之后再想捞回来就晚了。
|
|
930
1313
|
// 四类之间的次序按「定义的确定性」排:口径与表结构有明确文本特征,脚本次之,
|
|
@@ -932,7 +1315,7 @@ export const SOURCE_CLASSIFIER_PROMPT: SourceClassifierPrompt = deepFreeze({
|
|
|
932
1315
|
{ type: 'metric', rule: '内容在定义一个可计算的量化口径(指标名 + 公式 + 维度 / 粒度 / 负责人)→ metric。' },
|
|
933
1316
|
{ type: 'table', rule: '内容在描述一张表或数据集本身(字段清单、类型、分区、主键、更新频率)→ table。' },
|
|
934
1317
|
{ type: 'data_dev_script', rule: '内容是生产数据的加工逻辑(SQL / PySpark / Shell / 调度定义,读什么写什么)→ data_dev_script。' },
|
|
935
|
-
{ type: 'question', rule: '
|
|
1318
|
+
{ type: 'question', rule: '内容是**人写**的、以某个具体问题或故障为中心的结论(现象 → 排查 → 根因 → 措施)→ question。' },
|
|
936
1319
|
{ type: 'query_case', rule: '内容是一次取数问答的完整记录(问题 → 口径 → SQL → 结果)→ query_case。' },
|
|
937
1320
|
{ type: 'prd', rule: '内容面向「要做什么功能、给谁用、怎么算做完」→ prd。' },
|
|
938
1321
|
{ type: 'book', rule: '内容是正式出版物或成体系的整书 → book。' },
|
|
@@ -993,6 +1376,8 @@ export interface SourceTypeDraft {
|
|
|
993
1376
|
signals?: unknown
|
|
994
1377
|
excludes?: unknown
|
|
995
1378
|
enabled?: boolean
|
|
1379
|
+
/** 这一类来源该带哪些元数据字段(0048)。缺省 = 不改动磁盘上已有的声明。 */
|
|
1380
|
+
metadataFields?: unknown
|
|
996
1381
|
}
|
|
997
1382
|
|
|
998
1383
|
export interface SourceTypeDraftProblem {
|
|
@@ -1076,7 +1461,8 @@ export interface WikiSectionSpec {
|
|
|
1076
1461
|
/**
|
|
1077
1462
|
* 一条 front matter / metadata 字段要求(机器读的部分)。
|
|
1078
1463
|
*
|
|
1079
|
-
* `name`
|
|
1464
|
+
* `name` 对应卡片的顶层字段(`owner` / `summary`)或 `metadata` 里的键 ——
|
|
1465
|
+
* 编译器的抽取提示词会把这些字段名直接下发(0044),所以它同时是「机器读的部分」的契约。
|
|
1080
1466
|
*/
|
|
1081
1467
|
export interface WikiFieldSpec {
|
|
1082
1468
|
name: string
|
|
@@ -1088,7 +1474,14 @@ export interface WikiFieldSpec {
|
|
|
1088
1474
|
* 一个 wiki 卡片类型(0024)。
|
|
1089
1475
|
*
|
|
1090
1476
|
* 它是"SourceRaw → Wiki"这一段的**约束**:哪些类型能生成、每类必须有哪些小节与字段、
|
|
1091
|
-
*
|
|
1477
|
+
* 模板长什么样。判定与生成仍交给模型,这里给的是**判据与骨架**。
|
|
1478
|
+
*
|
|
1479
|
+
* **「期望来源分类」(`sourceTypes`)在 0040 删掉了。** 它当时的实际作用只有一条:
|
|
1480
|
+
* reference 层校验时,卡片引用的来源分类不在列表里就提示一下、不阻断 —— 既不参与拦截,
|
|
1481
|
+
* 也不参与生成,只是让人手填一份「我猜这类卡片大概会引用哪些来源」的静态清单。
|
|
1482
|
+
* 三条撤销理由(详见 prd/0040 §4.2):它是一份会漂移的猜测;「这类卡片该引用什么来源」
|
|
1483
|
+
* 需要读卡片内容与历史引用分布,那是 Agent 在一次生成里顺手就能判断的事;而留着一个
|
|
1484
|
+
* 不拦截的必填感字段,会教坏写定义的人。
|
|
1092
1485
|
*/
|
|
1093
1486
|
export interface WikiTypeDef {
|
|
1094
1487
|
/** 稳定 id,`^[a-z][a-z0-9_/-]{1,60}$`(允许 `diagnosis/symptom` 这类命名空间)。 */
|
|
@@ -1099,11 +1492,9 @@ export interface WikiTypeDef {
|
|
|
1099
1492
|
layer: 'type' | 'instance'
|
|
1100
1493
|
sections: WikiSectionSpec[]
|
|
1101
1494
|
fields: WikiFieldSpec[]
|
|
1102
|
-
/** 期望的来源业务分类 id(0013 的 `source_type`);空数组表示不限制。 */
|
|
1103
|
-
sourceTypes: string[]
|
|
1104
1495
|
/** few-shot:用户会怎么提到这种卡片。随工具描述下发。 */
|
|
1105
1496
|
examples: string[]
|
|
1106
|
-
/** 模板卡(Markdown
|
|
1497
|
+
/** 模板卡(Markdown 骨架),新建卡片时照抄。由 `sections` / `fields` **派生**。 */
|
|
1107
1498
|
template: string
|
|
1108
1499
|
enabled: boolean
|
|
1109
1500
|
/** 内置类型只允许停用,不允许删除。 */
|
|
@@ -1112,9 +1503,6 @@ export interface WikiTypeDef {
|
|
|
1112
1503
|
cardCount: number
|
|
1113
1504
|
}
|
|
1114
1505
|
|
|
1115
|
-
/** 卡片 schema 的生效方式。 */
|
|
1116
|
-
export type WikiSchemaMode = 'strict' | 'report'
|
|
1117
|
-
|
|
1118
1506
|
/** 卡片校验问题的一层。混成同一个「校验失败」,人就学不会怎么写卡片(0021 §1)。 */
|
|
1119
1507
|
export type CardIssueLevel = 'format' | 'reference' | 'semantic'
|
|
1120
1508
|
|
|
@@ -1125,15 +1513,28 @@ export interface CardSchemaIssue {
|
|
|
1125
1513
|
message: string
|
|
1126
1514
|
}
|
|
1127
1515
|
|
|
1128
|
-
/** 卡片类型目录:注册表 +
|
|
1516
|
+
/** 卡片类型目录:注册表 + 内置只读判定规则 + 当前卡片的问题清单 + review 进度。 */
|
|
1129
1517
|
export interface WikiTypeCatalog {
|
|
1130
1518
|
types: WikiTypeDef[]
|
|
1131
|
-
mode: WikiSchemaMode
|
|
1132
1519
|
/** 内置只读的判定规则:模型该按什么顺序挑类型。 */
|
|
1133
1520
|
rules: string
|
|
1134
1521
|
/** 已落库卡片的三层校验结果(前 N 条)。 */
|
|
1135
1522
|
issues: Array<CardSchemaIssue & { entityId: string; entityType: string }>
|
|
1136
1523
|
issueCount: number
|
|
1524
|
+
/**
|
|
1525
|
+
* review 进度(0044)。
|
|
1526
|
+
*
|
|
1527
|
+
* 它取代了原来的 `proposals`(待审提议条数)。队列退役之后「还有几条等人确认」不再
|
|
1528
|
+
* 是一个有意义的问题 —— 卡片一编译就入库了,剩下的问题是「入库的这些里我看过几条、
|
|
1529
|
+
* 有几条被标了异常」。这三个数就回答它。
|
|
1530
|
+
*/
|
|
1531
|
+
reviews: {
|
|
1532
|
+
total: number
|
|
1533
|
+
unreviewed: number
|
|
1534
|
+
reviewed: number
|
|
1535
|
+
/** 带 `kind: 'anomaly'` 备注的卡片数 —— 它们被评分压制,需要有人处理。 */
|
|
1536
|
+
anomalies: number
|
|
1537
|
+
}
|
|
1137
1538
|
}
|
|
1138
1539
|
|
|
1139
1540
|
/** 卡片类型 id 的形状;允许 `diagnosis/symptom` 这类带命名空间的 id。 */
|