dsh-data-domain-knowledge 0.1.0-alpha.2 → 0.1.0-alpha.3
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 +31 -7
- package/cordis.patch.yml +11 -5
- package/lib/client.js +5557 -3056
- package/lib/doc-page.js +7740 -0
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +36 -9
- package/lib/types/convert/host.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 +51 -0
- package/lib/types/diagnosis/run.d.ts.map +1 -0
- package/lib/types/diagnosis/run.js +239 -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 +18 -1
- 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 +114 -26
- package/lib/types/diagnosis/store.d.ts.map +1 -1
- package/lib/types/diagnosis/store.js +336 -167
- 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/fs-atomic.d.ts +12 -0
- package/lib/types/fs-atomic.d.ts.map +1 -0
- package/lib/types/fs-atomic.js +42 -0
- package/lib/types/fs-atomic.js.map +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +214 -24
- package/lib/types/index.js.map +1 -1
- package/lib/types/knowledge-bases.d.ts +10 -2
- package/lib/types/knowledge-bases.d.ts.map +1 -1
- package/lib/types/knowledge-bases.js +20 -18
- package/lib/types/knowledge-bases.js.map +1 -1
- package/lib/types/proposals.d.ts +166 -0
- package/lib/types/proposals.d.ts.map +1 -0
- package/lib/types/proposals.js +238 -0
- package/lib/types/proposals.js.map +1 -0
- package/lib/types/runtime.d.ts +199 -7
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +728 -145
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/settings-schema.d.ts +2 -2
- package/lib/types/types.d.ts +61 -10
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js +56 -8
- package/lib/types/types.js.map +1 -1
- package/lib/types/wiki-types.d.ts +8 -1
- package/lib/types/wiki-types.d.ts.map +1 -1
- package/lib/types/wiki-types.js +16 -25
- package/lib/types/wiki-types.js.map +1 -1
- package/package.json +4 -3
- package/preset/tita-data-agent/agent.cordis.yml +11 -5
- package/src/client/DirectoryPicker.tsx +12 -12
- package/src/client/InfoHint.tsx +21 -8
- package/src/client/IntentTab.tsx +20 -26
- package/src/client/KnowledgeBaseTab.tsx +190 -80
- package/src/client/KnowledgePanel.tsx +42 -9
- package/src/client/KnowledgeStepRail.tsx +3 -0
- package/src/client/NodeSettings.tsx +20 -22
- package/src/client/PreviewSections.tsx +87 -33
- package/src/client/ProposalReview.tsx +313 -0
- package/src/client/TypeDefinitionDrawer.tsx +72 -38
- package/src/client/WikiTypeEditor.tsx +193 -77
- package/src/client/arch/AgentArchitectureGraph.tsx +143 -14
- package/src/client/arch/NodeLogicPanel.tsx +6 -3
- package/src/client/arch/model.ts +164 -26
- package/src/client/architecture.css +29 -9
- package/src/client/architecture.tsx +54 -29
- package/src/client/attribution/AttributionGraph.tsx +481 -34
- package/src/client/attribution/AttributionTab.tsx +507 -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 +296 -140
- package/src/client/attribution/graph-options.ts +611 -47
- package/src/client/attribution/model.ts +559 -30
- package/src/client/convert/ConvertPanel.tsx +6 -1
- package/src/client/doc-page.css +134 -0
- package/src/client/doc-page.tsx +263 -0
- package/src/client/panel-state.ts +105 -0
- package/src/client/request.ts +36 -0
- package/src/client/styles.css +317 -43
- package/src/client/useFitScale.ts +15 -2
- package/src/client/wiki-type-groups.ts +86 -0
- package/src/convert/host.ts +41 -7
- package/src/diagnosis/cards.ts +120 -50
- package/src/diagnosis/run.ts +245 -0
- package/src/diagnosis/schema.ts +67 -26
- package/src/diagnosis/seed-source.ts +28 -3
- package/src/diagnosis/seed.ts +187 -85
- package/src/diagnosis/store.ts +358 -174
- package/src/diagnosis/types.ts +181 -16
- package/src/document-page.ts +51 -0
- package/src/fs-atomic.ts +42 -0
- package/src/index.ts +209 -22
- package/src/knowledge-bases.ts +20 -18
- package/src/proposals.ts +337 -0
- package/src/runtime.ts +736 -135
- package/src/types.ts +113 -19
- package/src/wiki-types.ts +17 -27
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'
|
|
@@ -184,7 +189,7 @@ export type RuntimeKnowledgeConfig = Omit<KnowledgeConfig, 'rootDir' | 'agentRoo
|
|
|
184
189
|
export const DEFAULT_KNOWLEDGE_CONFIG: RuntimeKnowledgeConfig = {
|
|
185
190
|
// 智能归因的五类在这里一并默认开启:没有它们,排查思路写不进 wiki、召不回来(见 SchemaType)。
|
|
186
191
|
schemaTypes: ['concept', 'metric', 'table', 'event', 'playbook', 'case', ...DIAGNOSIS_ENTITY_TYPES],
|
|
187
|
-
sourceAppendTypes: ['chat_message', '
|
|
192
|
+
sourceAppendTypes: ['chat_message', 'agent_chat_message', 'document'],
|
|
188
193
|
allowSourceTruth: true,
|
|
189
194
|
// 0029 撤掉了 `maxSourceBytes`:单条来源**没有**字符数上限。见
|
|
190
195
|
// `runtime.ts` 的 `addSource` —— 真正的边界是转换内核的 `maxInputBytes`
|
|
@@ -570,8 +575,21 @@ export interface KnowledgeAssetRef {
|
|
|
570
575
|
url: string
|
|
571
576
|
}
|
|
572
577
|
|
|
578
|
+
/**
|
|
579
|
+
* 文档详情。
|
|
580
|
+
*
|
|
581
|
+
* `content` **可能只是正文的前一段**:明细接口按 `SOURCE_PREVIEW_CHARS` 封顶,
|
|
582
|
+
* 因为抽屉里的预览读不完一份几十万字文档,而把它整份发过来正是「打开弹层要等半天」
|
|
583
|
+
* 的原因。要全文走独立整页的分页接口(`GET /knowledge/documents/:docId/content`)。
|
|
584
|
+
* 两件事分开之后,`contentChars` / `contentTruncated` 就是界面必须读的那两个字段 ——
|
|
585
|
+
* 「这里显示了多少、全文有多少」不能靠猜。
|
|
586
|
+
*/
|
|
573
587
|
export interface KnowledgeDocumentDetail extends KnowledgeDocument {
|
|
574
588
|
content: string
|
|
589
|
+
/** 正文总字符数(不是 `content.length`)。 */
|
|
590
|
+
contentChars: number
|
|
591
|
+
/** `content` 是否被截断过。为真时界面要给「新页面打开」。 */
|
|
592
|
+
contentTruncated: boolean
|
|
575
593
|
frontmatter: Record<string, unknown>
|
|
576
594
|
assets: KnowledgeAssetRef[]
|
|
577
595
|
lineage?: Record<string, string>
|
|
@@ -668,15 +686,23 @@ export interface RebuildReport {
|
|
|
668
686
|
* 「SourceRaw → Wiki」第一段的诚实计数。
|
|
669
687
|
*
|
|
670
688
|
* 这八个数**每一个都来自真实发生过的事件**,没有一个是估算出来的:抽取了几个
|
|
671
|
-
*
|
|
689
|
+
* 候选、其中几个被类型闸门拒了、提了几条新卡提议、几条更新提议、几条关系真的连上了、
|
|
672
690
|
* 几条因为端点不存在被丢掉、几份来源这次被重抽、几份因为指纹没变被跳过。
|
|
673
691
|
* `extractCandidates` 关掉时它们全部回 0,而不是从响应里消失 —— 「跳过」和
|
|
674
692
|
* 「没有这个能力」是两件事,用缺字段表达会让人分不清。
|
|
675
693
|
*/
|
|
676
694
|
candidatesExtracted: number
|
|
677
695
|
candidatesRejected: number
|
|
678
|
-
|
|
679
|
-
|
|
696
|
+
/**
|
|
697
|
+
* 这一轮**新提**的卡片提议条数。
|
|
698
|
+
*
|
|
699
|
+
* 名字原来是 `cardsCreated`(v1.5 改名):Pass 0 不再直接落卡,它只提议,
|
|
700
|
+
* 由人在「知识库 → Wiki」的待办区确认之后才走 `writeCard`(0021 §6.1)。
|
|
701
|
+
* 计数不改名的话,报告会持续说「新建了 N 张卡片」,而磁盘上一张都没有。
|
|
702
|
+
*/
|
|
703
|
+
proposalsCreated: number
|
|
704
|
+
/** 这一轮**更新**已有卡片内容的提议条数(原来叫 `cardsMerged`)。 */
|
|
705
|
+
proposalsMerged: number
|
|
680
706
|
relationsExtracted: number
|
|
681
707
|
relationsDropped: number
|
|
682
708
|
sourcesExtracted: number
|
|
@@ -735,14 +761,23 @@ export const BUILTIN_SOURCE_TYPES: ReadonlyArray<Omit<SourceTypeDef, 'sourceCoun
|
|
|
735
761
|
label: '聊天日志',
|
|
736
762
|
description: '人与人之间在日常沟通中产生的消息记录,以对话往返为主要形态。',
|
|
737
763
|
signals: ['有发言人与时间戳的短消息流', '多话题跳转、口语化、缩写多', '常见 @提及、表情、附件'],
|
|
738
|
-
excludes: ['与 AI 的对话归
|
|
764
|
+
excludes: ['与 AI 的对话归 agent_chat_message', '整理后的会议纪要归 document', '已成问题—根因—方案的记录归 question'],
|
|
739
765
|
enabled: true,
|
|
740
766
|
builtin: true,
|
|
741
767
|
fallback: false,
|
|
742
768
|
},
|
|
743
769
|
{
|
|
744
|
-
|
|
745
|
-
|
|
770
|
+
/**
|
|
771
|
+
* `ai_chat_message` 在 0040 改名为 `agent_chat_message`(**只改名,不改语义**)。
|
|
772
|
+
*
|
|
773
|
+
* 理由:原有八个内置分类里只有它带 `ai_` 前缀,而链路里其他地方一律用 `agent`
|
|
774
|
+
* (`agent_diagnosis_case`、Agent 工具、`agentRoot`)。统一词根,避免同一份设计里
|
|
775
|
+
* 出现两套命名。存量来源靠一次性迁移换目录 + 改 frontmatter(见
|
|
776
|
+
* `runtime.migrateLegacySourceTypeRename`),注册表侧的旧 id 在
|
|
777
|
+
* `normalizeSourceTypes` 里按别名认。
|
|
778
|
+
*/
|
|
779
|
+
id: 'agent_chat_message',
|
|
780
|
+
label: '用户与 AI 的对话日志',
|
|
746
781
|
description: '用户与 AI 助手之间的完整对话记录,包含双方往返消息。',
|
|
747
782
|
signals: ['user / assistant 角色交替', '提示词与回答成对出现', '代码块密集、常与会话 id 或模型名同现'],
|
|
748
783
|
excludes: ['人在群里转贴 AI 回答归 chat_message', '由 AI 对话整理出的方法论文档归 document 或 article'],
|
|
@@ -750,6 +785,28 @@ export const BUILTIN_SOURCE_TYPES: ReadonlyArray<Omit<SourceTypeDef, 'sourceCoun
|
|
|
750
785
|
builtin: true,
|
|
751
786
|
fallback: false,
|
|
752
787
|
},
|
|
788
|
+
{
|
|
789
|
+
/**
|
|
790
|
+
* `agent_diagnosis_case`(0040 §3.1)—— **AI 诊断日志**。
|
|
791
|
+
*
|
|
792
|
+
* 它装的是**一次排查的过程记录**:现象、走过的路径(有序)、每个检查点的产出
|
|
793
|
+
* (含被否定与给不出结论的)、确认的根因、耗时、误导段。作者是 Agent,
|
|
794
|
+
* 每次排查结束自动写一条(`externalId` = runId,重跑不产生重复记录)。
|
|
795
|
+
*
|
|
796
|
+
* **与 `question` 的边界是「谁写的、是不是已经归纳过」**:
|
|
797
|
+
* 人写的结论(手册、人工复盘)→ `question`;Agent 自动记录的过程 → 这里。
|
|
798
|
+
* 同一件事可以有两条来源,它们不是重复 —— 一个回答「结论是什么」,
|
|
799
|
+
* 一个回答「当时怎么走的」。
|
|
800
|
+
*/
|
|
801
|
+
id: 'agent_diagnosis_case',
|
|
802
|
+
label: 'AI 诊断日志',
|
|
803
|
+
description: 'Agent 跑完一次问题排查后留下的过程记录:现象、路径、各检查点的产出、确认的根因、耗时与误导段。',
|
|
804
|
+
signals: ['带 runId / 现象 id / 耗时与有序路径的结构化记录', '每个检查点有成对的「实际查询 + 结果 + 这个结果算不算数」', '含被否定的假设与「给不出结论」的检查'],
|
|
805
|
+
excludes: ['人写的手册与人工复盘归 question', '由这次排查归纳出的卡片是 wiki 卡片而不是来源,那类内容归 document', '日常取数问答归 query_case'],
|
|
806
|
+
enabled: true,
|
|
807
|
+
builtin: true,
|
|
808
|
+
fallback: false,
|
|
809
|
+
},
|
|
753
810
|
{
|
|
754
811
|
id: 'document',
|
|
755
812
|
label: '企业内部文档',
|
|
@@ -795,7 +852,7 @@ export const BUILTIN_SOURCE_TYPES: ReadonlyArray<Omit<SourceTypeDef, 'sourceCoun
|
|
|
795
852
|
label: '问题排查文档',
|
|
796
853
|
description: '以某个具体问题或故障为中心的记录:现象、影响、排查过程、根因、解决方案与后续动作。',
|
|
797
854
|
signals: ['错误码、堆栈或日志片段', '复现步骤、临时规避、根因、修复版本', '时间线、一问一答或一问题多方案'],
|
|
798
|
-
excludes: ['一般性经验总结归 document', '产品需求归 prd', '外部作者的公开分析归 article'],
|
|
855
|
+
excludes: ['一般性经验总结归 document', '产品需求归 prd', '外部作者的公开分析归 article', 'Agent 跑出来的一次排查过程记录归 agent_diagnosis_case'],
|
|
799
856
|
enabled: true,
|
|
800
857
|
builtin: true,
|
|
801
858
|
fallback: false,
|
|
@@ -862,6 +919,20 @@ export const BUILTIN_SOURCE_TYPES: ReadonlyArray<Omit<SourceTypeDef, 'sourceCoun
|
|
|
862
919
|
|
|
863
920
|
const BUILTIN_SOURCE_TYPE_IDS: ReadonlySet<string> = new Set(BUILTIN_SOURCE_TYPES.map(def => def.id))
|
|
864
921
|
|
|
922
|
+
/**
|
|
923
|
+
* 改名之前的来源分类 id → 新 id(0040 §3.2)。
|
|
924
|
+
*
|
|
925
|
+
* 它有两个消费者,缺一个就会静默丢东西:
|
|
926
|
+
* 1. **注册表**(`normalizeSourceTypes`)—— 存量 `knowledge-bases.json` 里那一项写着
|
|
927
|
+
* 旧 id,不认别名的话它会被当成一个**自定义分类**留下来,于是「注册表里到底有什么」
|
|
928
|
+
* 永远说不清(内置项和它并存)。
|
|
929
|
+
* 2. **磁盘**(`runtime.migrateLegacySourceTypeRename`)—— `01_source_truth/` 是按分类
|
|
930
|
+
* 分目录的,存量来源的 frontmatter 里写着 `source_type: ai_chat_message`。
|
|
931
|
+
*/
|
|
932
|
+
export const LEGACY_SOURCE_TYPE_IDS: Record<string, string> = {
|
|
933
|
+
ai_chat_message: 'agent_chat_message',
|
|
934
|
+
}
|
|
935
|
+
|
|
865
936
|
export function isBuiltinSourceType(id: string): boolean {
|
|
866
937
|
return BUILTIN_SOURCE_TYPE_IDS.has(id)
|
|
867
938
|
}
|
|
@@ -919,12 +990,16 @@ function deepFreeze<T>(value: T): T {
|
|
|
919
990
|
|
|
920
991
|
export const SOURCE_CLASSIFIER_PROMPT: SourceClassifierPrompt = deepFreeze({
|
|
921
992
|
readonly: true,
|
|
922
|
-
version:
|
|
993
|
+
version: 3,
|
|
923
994
|
title: '来源业务分类判定规则',
|
|
924
995
|
priority: [
|
|
925
996
|
{ type: 'user', rule: '用户显式指定分类 → 直接用,记 source_type_source: user。' },
|
|
926
|
-
{ type: '
|
|
997
|
+
{ type: 'agent_chat_message', rule: '内容是人与 AI 的对话记录(含 AI 回复)→ agent_chat_message。' },
|
|
927
998
|
{ type: 'chat_message', rule: '内容是人与人之间的消息流(IM / 会议聊天 / 客服会话)→ chat_message。' },
|
|
999
|
+
// `agent_diagnosis_case` 紧跟对话类、排在通用文档之前:它的形状最特征化
|
|
1000
|
+
// (runId + 有序路径 + 每个检查点的「实际查询 / 结果 / 算不算数」),而它和
|
|
1001
|
+
// `question` 最容易混 —— 落到 question 之后再想捞回来就晚了。
|
|
1002
|
+
{ type: 'agent_diagnosis_case', rule: '内容是 Agent 跑完一次排查留下的过程记录(runId + 有序路径 + 每个检查点的实际查询与结果 + 确认的根因 + 耗时)→ agent_diagnosis_case。' },
|
|
928
1003
|
// 数据域四类排在通用文档之前:它们的形状(DDL、公式、SQL、问答)比
|
|
929
1004
|
// 「像不像一篇内部文档」更特征化,落到 document 之后再想捞回来就晚了。
|
|
930
1005
|
// 四类之间的次序按「定义的确定性」排:口径与表结构有明确文本特征,脚本次之,
|
|
@@ -932,7 +1007,7 @@ export const SOURCE_CLASSIFIER_PROMPT: SourceClassifierPrompt = deepFreeze({
|
|
|
932
1007
|
{ type: 'metric', rule: '内容在定义一个可计算的量化口径(指标名 + 公式 + 维度 / 粒度 / 负责人)→ metric。' },
|
|
933
1008
|
{ type: 'table', rule: '内容在描述一张表或数据集本身(字段清单、类型、分区、主键、更新频率)→ table。' },
|
|
934
1009
|
{ type: 'data_dev_script', rule: '内容是生产数据的加工逻辑(SQL / PySpark / Shell / 调度定义,读什么写什么)→ data_dev_script。' },
|
|
935
|
-
{ type: 'question', rule: '
|
|
1010
|
+
{ type: 'question', rule: '内容是**人写**的、以某个具体问题或故障为中心的结论(现象 → 排查 → 根因 → 措施)→ question。' },
|
|
936
1011
|
{ type: 'query_case', rule: '内容是一次取数问答的完整记录(问题 → 口径 → SQL → 结果)→ query_case。' },
|
|
937
1012
|
{ type: 'prd', rule: '内容面向「要做什么功能、给谁用、怎么算做完」→ prd。' },
|
|
938
1013
|
{ type: 'book', rule: '内容是正式出版物或成体系的整书 → book。' },
|
|
@@ -1088,7 +1163,14 @@ export interface WikiFieldSpec {
|
|
|
1088
1163
|
* 一个 wiki 卡片类型(0024)。
|
|
1089
1164
|
*
|
|
1090
1165
|
* 它是"SourceRaw → Wiki"这一段的**约束**:哪些类型能生成、每类必须有哪些小节与字段、
|
|
1091
|
-
*
|
|
1166
|
+
* 模板长什么样。判定与生成仍交给模型,这里给的是**判据与骨架**。
|
|
1167
|
+
*
|
|
1168
|
+
* **「期望来源分类」(`sourceTypes`)在 0040 删掉了。** 它当时的实际作用只有一条:
|
|
1169
|
+
* reference 层校验时,卡片引用的来源分类不在列表里就提示一下、不阻断 —— 既不参与拦截,
|
|
1170
|
+
* 也不参与生成,只是让人手填一份「我猜这类卡片大概会引用哪些来源」的静态清单。
|
|
1171
|
+
* 三条撤销理由(详见 prd/0040 §4.2):它是一份会漂移的猜测;「这类卡片该引用什么来源」
|
|
1172
|
+
* 需要读卡片内容与历史引用分布,那是 Agent 在一次生成里顺手就能判断的事;而留着一个
|
|
1173
|
+
* 不拦截的必填感字段,会教坏写定义的人。
|
|
1092
1174
|
*/
|
|
1093
1175
|
export interface WikiTypeDef {
|
|
1094
1176
|
/** 稳定 id,`^[a-z][a-z0-9_/-]{1,60}$`(允许 `diagnosis/symptom` 这类命名空间)。 */
|
|
@@ -1099,11 +1181,9 @@ export interface WikiTypeDef {
|
|
|
1099
1181
|
layer: 'type' | 'instance'
|
|
1100
1182
|
sections: WikiSectionSpec[]
|
|
1101
1183
|
fields: WikiFieldSpec[]
|
|
1102
|
-
/** 期望的来源业务分类 id(0013 的 `source_type`);空数组表示不限制。 */
|
|
1103
|
-
sourceTypes: string[]
|
|
1104
1184
|
/** few-shot:用户会怎么提到这种卡片。随工具描述下发。 */
|
|
1105
1185
|
examples: string[]
|
|
1106
|
-
/** 模板卡(Markdown
|
|
1186
|
+
/** 模板卡(Markdown 骨架),新建卡片时照抄。由 `sections` / `fields` **派生**。 */
|
|
1107
1187
|
template: string
|
|
1108
1188
|
enabled: boolean
|
|
1109
1189
|
/** 内置类型只允许停用,不允许删除。 */
|
|
@@ -1125,7 +1205,7 @@ export interface CardSchemaIssue {
|
|
|
1125
1205
|
message: string
|
|
1126
1206
|
}
|
|
1127
1207
|
|
|
1128
|
-
/** 卡片类型目录:注册表 + 生效方式 + 内置只读判定规则 +
|
|
1208
|
+
/** 卡片类型目录:注册表 + 生效方式 + 内置只读判定规则 + 当前卡片的问题清单 + 待审提议。 */
|
|
1129
1209
|
export interface WikiTypeCatalog {
|
|
1130
1210
|
types: WikiTypeDef[]
|
|
1131
1211
|
mode: WikiSchemaMode
|
|
@@ -1134,6 +1214,20 @@ export interface WikiTypeCatalog {
|
|
|
1134
1214
|
/** 已落库卡片的三层校验结果(前 N 条)。 */
|
|
1135
1215
|
issues: Array<CardSchemaIssue & { entityId: string; entityType: string }>
|
|
1136
1216
|
issueCount: number
|
|
1217
|
+
/**
|
|
1218
|
+
* 待人确认的提议(0021 §6.1)。
|
|
1219
|
+
*
|
|
1220
|
+
* 它挂在**类型目录**上而不是单独一路,是因为这一屏(「知识库 → Wiki」的待办区)
|
|
1221
|
+
* 就是 review 的地方 —— 与 Pass 0 的候选草案、lint 待补清单是同一处,
|
|
1222
|
+
* 不新造第二套 review 界面。未确认的提议**不落 `02_wiki/`**,所以它们不在
|
|
1223
|
+
* `types[].cardCount` 里,必须单独报出来。
|
|
1224
|
+
*/
|
|
1225
|
+
proposals: {
|
|
1226
|
+
pending: number
|
|
1227
|
+
rejected: number
|
|
1228
|
+
/** 已经确认、等人或等 Agent 落卡的条数。 */
|
|
1229
|
+
confirmed: number
|
|
1230
|
+
}
|
|
1137
1231
|
}
|
|
1138
1232
|
|
|
1139
1233
|
/** 卡片类型 id 的形状;允许 `diagnosis/symptom` 这类带命名空间的 id。 */
|
package/src/wiki-types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DIAGNOSIS_ENTITIES } from './diagnosis/schema.js'
|
|
1
|
+
import { DIAGNOSIS_ENTITIES, LEGACY_ENTITY_TYPES } from './diagnosis/schema.js'
|
|
2
2
|
import type { CardSchemaIssue, WikiFieldSpec, WikiSectionSpec, WikiTypeDef } from './types.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -29,7 +29,6 @@ const GENERIC_TYPES: Array<Omit<WikiTypeDef, 'template' | 'enabled' | 'builtin'
|
|
|
29
29
|
{ label: '关联', required: false },
|
|
30
30
|
],
|
|
31
31
|
fields: [{ name: 'owner', required: false, note: '谁维护这个概念的定义' }],
|
|
32
|
-
sourceTypes: [],
|
|
33
32
|
examples: ['什么是活跃用户', '「有效订单」的口径是什么', '这个指标和那个指标什么关系'],
|
|
34
33
|
},
|
|
35
34
|
{
|
|
@@ -46,7 +45,6 @@ const GENERIC_TYPES: Array<Omit<WikiTypeDef, 'template' | 'enabled' | 'builtin'
|
|
|
46
45
|
{ name: 'formula', required: true, note: '可执行的表达式或伪代码' },
|
|
47
46
|
{ name: 'owner', required: true, note: '谁对这个口径负责' },
|
|
48
47
|
],
|
|
49
|
-
sourceTypes: ['document', 'prd'],
|
|
50
48
|
examples: ['上个月的 GMV 怎么算', '转化率的口径是什么', '这个指标怎么拆'],
|
|
51
49
|
},
|
|
52
50
|
{
|
|
@@ -63,7 +61,6 @@ const GENERIC_TYPES: Array<Omit<WikiTypeDef, 'template' | 'enabled' | 'builtin'
|
|
|
63
61
|
{ name: 'owner', required: true, note: '表责任人' },
|
|
64
62
|
{ name: 'grain', required: true, note: '一行代表什么(粒度)' },
|
|
65
63
|
],
|
|
66
|
-
sourceTypes: ['document'],
|
|
67
64
|
examples: ['订单表有哪些字段', '这张表的粒度是什么', '这张表多久更新一次'],
|
|
68
65
|
},
|
|
69
66
|
{
|
|
@@ -77,7 +74,6 @@ const GENERIC_TYPES: Array<Omit<WikiTypeDef, 'template' | 'enabled' | 'builtin'
|
|
|
77
74
|
{ label: '处理方式', required: true },
|
|
78
75
|
],
|
|
79
76
|
fields: [{ name: 'severity', required: true, note: '影响级别' }],
|
|
80
|
-
sourceTypes: ['chat_message', 'question'],
|
|
81
77
|
examples: ['上游延迟告警怎么处理', '这次大促爆单是怎么回事'],
|
|
82
78
|
},
|
|
83
79
|
{
|
|
@@ -91,7 +87,6 @@ const GENERIC_TYPES: Array<Omit<WikiTypeDef, 'template' | 'enabled' | 'builtin'
|
|
|
91
87
|
{ label: '验收与失败处理', required: false },
|
|
92
88
|
],
|
|
93
89
|
fields: [{ name: 'owner', required: true, note: '手册维护人' }],
|
|
94
|
-
sourceTypes: ['document', 'prd'],
|
|
95
90
|
examples: ['新人怎么接入一张表', '发布前要检查什么'],
|
|
96
91
|
},
|
|
97
92
|
{
|
|
@@ -105,7 +100,6 @@ const GENERIC_TYPES: Array<Omit<WikiTypeDef, 'template' | 'enabled' | 'builtin'
|
|
|
105
100
|
{ label: '结果与教训', required: true },
|
|
106
101
|
],
|
|
107
102
|
fields: [{ name: 'occurred_at', required: true, note: '发生时间' }],
|
|
108
|
-
sourceTypes: ['chat_message', 'question'],
|
|
109
103
|
examples: ['上次数据延迟是怎么定位的', '那次口径改动的来龙去脉'],
|
|
110
104
|
},
|
|
111
105
|
]
|
|
@@ -114,16 +108,16 @@ const GENERIC_TYPES: Array<Omit<WikiTypeDef, 'template' | 'enabled' | 'builtin'
|
|
|
114
108
|
const DIAGNOSIS_DESCRIPTIONS: Record<string, string> = {
|
|
115
109
|
'diagnosis/symptom': '一类可复用的现象:可观测口径 + 进入条件,是排查的入口。',
|
|
116
110
|
'diagnosis/hypothesis': '一条可判真假的假设,带反例边界,是排查的中间层。',
|
|
117
|
-
'diagnosis/check': '
|
|
118
|
-
'diagnosis/failure-
|
|
111
|
+
'diagnosis/check-point': '一步可执行的检查点,回答「怎么验证一个假设」;必须能回答谁、用什么、多久、看到什么算真、看到什么算假。',
|
|
112
|
+
'diagnosis/failure-reason': '失败原因切面:把假设按**原因出在链路的哪一环**分组,用于先验冷启动、跨症状复用与覆盖度盘点。',
|
|
119
113
|
'diagnosis/case': '一次真实排查的完整路径,是实例层的记录而不是可复用知识。',
|
|
120
114
|
}
|
|
121
115
|
|
|
122
116
|
const DIAGNOSIS_EXAMPLES: Record<string, string[]> = {
|
|
123
117
|
'diagnosis/symptom': ['销售额突然掉了', '今天报表数对不上'],
|
|
124
118
|
'diagnosis/hypothesis': ['上游同步漏了', '口径被人改过'],
|
|
125
|
-
'diagnosis/check': ['查一下上游任务状态', '比对两天的分区行数'],
|
|
126
|
-
'diagnosis/failure-
|
|
119
|
+
'diagnosis/check-point': ['查一下上游任务状态', '比对两天的分区行数'],
|
|
120
|
+
'diagnosis/failure-reason': ['上游未产出', '口径变更', '数据处理'],
|
|
127
121
|
'diagnosis/case': ['上次大促数据延迟的排查过程'],
|
|
128
122
|
}
|
|
129
123
|
|
|
@@ -139,7 +133,7 @@ export function templateCardFor(sections: WikiSectionSpec[], fields: WikiFieldSp
|
|
|
139
133
|
function make(def: Omit<WikiTypeDef, 'template' | 'enabled' | 'builtin' | 'cardCount'>): WikiTypeDef {
|
|
140
134
|
const sections: WikiSectionSpec[] = def.sections.map(section => ({ ...section }))
|
|
141
135
|
const fields: WikiFieldSpec[] = def.fields.map(field => ({ ...field }))
|
|
142
|
-
return { ...def, sections, fields,
|
|
136
|
+
return { ...def, sections, fields, examples: [...def.examples], template: templateCardFor(sections, fields), enabled: true, builtin: true, cardCount: 0 }
|
|
143
137
|
}
|
|
144
138
|
|
|
145
139
|
/**
|
|
@@ -157,13 +151,21 @@ export const DEFAULT_WIKI_TYPES: WikiTypeDef[] = [
|
|
|
157
151
|
layer: spec.layer === 'instance' ? 'instance' : 'type',
|
|
158
152
|
sections: spec.sections.map(label => ({ label, required: true })),
|
|
159
153
|
fields: spec.fields.map(field => ({ name: field.name, required: field.system !== true, note: field.note })),
|
|
160
|
-
sourceTypes: [],
|
|
161
154
|
examples: DIAGNOSIS_EXAMPLES[spec.entityType] ?? [],
|
|
162
155
|
})),
|
|
163
156
|
]
|
|
164
157
|
|
|
165
158
|
const BUILTIN_IDS: ReadonlySet<string> = new Set(DEFAULT_WIKI_TYPES.map(def => def.id))
|
|
166
159
|
|
|
160
|
+
/**
|
|
161
|
+
* 改名之前的卡片类型 id → 新 id。
|
|
162
|
+
*
|
|
163
|
+
* 定义处仍在 `diagnosis/schema.ts`(`LEGACY_ENTITY_TYPES`),这里只是**换一个面向注册表的
|
|
164
|
+
* 名字**再导出:`normalizeWikiTypes` 读它来决定「存量配置里那一项算不算内置」。
|
|
165
|
+
* 抄第二份的下场是两处迟早不一致,而不一致的表现是「注册表里同时有两个近义类型」。
|
|
166
|
+
*/
|
|
167
|
+
export const LEGACY_WIKI_TYPE_IDS: Record<string, string> = { ...LEGACY_ENTITY_TYPES }
|
|
168
|
+
|
|
167
169
|
export function isBuiltinWikiType(id: string): boolean {
|
|
168
170
|
return BUILTIN_IDS.has(id)
|
|
169
171
|
}
|
|
@@ -174,7 +176,6 @@ export function defaultWikiTypes(): WikiTypeDef[] {
|
|
|
174
176
|
...def,
|
|
175
177
|
sections: def.sections.map(section => ({ ...section })),
|
|
176
178
|
fields: def.fields.map(field => ({ ...field })),
|
|
177
|
-
sourceTypes: [...def.sourceTypes],
|
|
178
179
|
examples: [...def.examples],
|
|
179
180
|
cardCount: 0,
|
|
180
181
|
}))
|
|
@@ -191,6 +192,8 @@ export const WIKI_TYPE_RULES = [
|
|
|
191
192
|
'一张卡片只讲一件事:概念讲定义、指标讲口径、手册讲做法、案例讲发生过的事。混在一起时要拆成多张卡片。',
|
|
192
193
|
'卡片必须引用真实存在的 SourceRaw:`source_refs` 写了不存在的来源会被直接拒绝。',
|
|
193
194
|
'必填小节与必填字段来自类型定义;缺了它们卡片仍然可写(report 模式),但会被标成待补。',
|
|
195
|
+
'**写卡片之前先提提议**:用 `propose_wiki_cards` 把草案挂到触发它的那条 SourceRaw 上,由人在「知识库 → Wiki」的待办区确认之后才落卡。未确认的草案不落 `02_wiki/`、不进编译、不参与召回、不在图上出现。',
|
|
196
|
+
'`write_knowledge_card` 只接受**已确认且尚未落卡**的提议(带 `proposal_id`)—— 它没有「未确认也能写」这条路径。',
|
|
194
197
|
].join('\n')
|
|
195
198
|
|
|
196
199
|
/** 新增 / 编辑类型时提交的草稿。 */
|
|
@@ -201,7 +204,6 @@ export interface WikiTypeDraft {
|
|
|
201
204
|
layer?: string
|
|
202
205
|
sections?: unknown
|
|
203
206
|
fields?: unknown
|
|
204
|
-
sourceTypes?: unknown
|
|
205
207
|
examples?: unknown
|
|
206
208
|
}
|
|
207
209
|
|
|
@@ -325,18 +327,6 @@ export function validateCardAgainstType(
|
|
|
325
327
|
}
|
|
326
328
|
}
|
|
327
329
|
|
|
328
|
-
// ---- reference:来源分类是否符合该类型的期望(只提示,不阻断)
|
|
329
|
-
if (type.sourceTypes.length > 0) {
|
|
330
|
-
const mismatched = card.metadata.source_types
|
|
331
|
-
if (Array.isArray(mismatched) && mismatched.length > 0) {
|
|
332
|
-
const allowed = new Set(type.sourceTypes)
|
|
333
|
-
const outside = mismatched.map(String).filter(id => !allowed.has(id))
|
|
334
|
-
if (outside.length > 0) {
|
|
335
|
-
issues.push({ level: 'reference', field: 'source_types', message: `来源分类 ${outside.join(' / ')} 不在该类型的期望范围(${type.sourceTypes.join(' / ')})内` })
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
330
|
// ---- semantic:正文不能只有标题,也不能留着占位符
|
|
341
331
|
const body = card.content
|
|
342
332
|
.split('\n')
|