@liuhange/dsh-ai-dataset-inspector 3.1.0

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 ADDED
@@ -0,0 +1,22 @@
1
+ # @liuhange/dsh-ai-dataset-inspector
2
+
3
+ ## 功能说明
4
+
5
+ AI training dataset inspection plugin: annotation consistency, label distribution, data leakage detection
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ dsh plugin --profile web add @liuhange/dsh-ai-dataset-inspector
11
+ ```
12
+
13
+ ## 用法
14
+
15
+ ```javascript
16
+ import { apply } from '@liuhange/dsh-ai-dataset-inspector';
17
+ apply(ctx); // 注册工具: inspect_ai_dataset
18
+ ```
19
+
20
+ ## License
21
+
22
+ MIT
package/lib/index.js ADDED
@@ -0,0 +1,117 @@
1
+ export const name = '@liuhange/dsh-ai-dataset-inspector';
2
+ export const inject = ['tools'];
3
+ import { loadJsonConfig } from '@liuhange/dsh-data-asset-shared';
4
+ import { resolvePolicyBasis } from './policyBasis.js';
5
+ export function apply(ctx) {
6
+ ctx.tools.register({
7
+ name: 'inspect_ai_dataset',
8
+ description: '检查AI训练数据集质量,涵盖标注一致性、标签均衡性、数据泄露等维度',
9
+ parameters: {
10
+ type: 'object',
11
+ properties: {
12
+ datasetPath: { type: 'string', description: '数据集文件路径' },
13
+ datasetType: { type: 'string', enum: ['training', 'validation', 'test', 'full'] },
14
+ maskingResultRef: { type: 'string', description: 'data-masking工具输出引用(可选)' },
15
+ },
16
+ required: ['datasetPath'],
17
+ },
18
+ async execute(args) {
19
+ try {
20
+ if (!args?.datasetPath || typeof args.datasetPath !== 'string') {
21
+ return JSON.stringify({ error: 'AI_DATASET_INPUT_INVALID', message: 'datasetPath is required' });
22
+ }
23
+ let rulesConfig;
24
+ try {
25
+ rulesConfig = loadJsonConfig('BUSINESS_RULES_PATH', 'config/business-rules.json', '@liuhange/dsh-data-asset-shared/config/business-rules.json');
26
+ }
27
+ catch {
28
+ return JSON.stringify({ error: 'AI_DATASET_RULES_MISSING', message: '无法加载business-rules.json' });
29
+ }
30
+ const aiConfig = rulesConfig.aiDatasetInspection;
31
+ if (!aiConfig) {
32
+ return JSON.stringify({ error: 'AI_DATASET_RULES_MISSING', message: 'aiDatasetInspection配置段缺失' });
33
+ }
34
+ let dataset;
35
+ try {
36
+ const { readFileSync } = await import('node:fs');
37
+ const raw = readFileSync(args.datasetPath, 'utf-8');
38
+ dataset = JSON.parse(raw);
39
+ if (!Array.isArray(dataset))
40
+ dataset = [dataset];
41
+ }
42
+ catch {
43
+ return JSON.stringify({ error: 'AI_DATASET_INPUT_INVALID', message: `无法加载数据集: ${args.datasetPath}` });
44
+ }
45
+ const annotationConsistency = computeKappa(dataset, aiConfig.kappaThreshold);
46
+ const labelDistribution = analyzeLabelDistribution(dataset);
47
+ const dataLeakage = detectLeakage(dataset);
48
+ const policyBasis = resolvePolicyBasis('AI_DATASET_INSPECTION');
49
+ const result = {
50
+ datasetId: args.datasetPath,
51
+ inspectionType: args.datasetType ?? 'full',
52
+ annotationConsistency,
53
+ labelDistribution,
54
+ dataLeakage,
55
+ piiResidue: args.maskingResultRef ? '已引用data-masking结果检测PII残留' : '未提供masking结果引用',
56
+ cleaningStatus: '建议配合data-cleaning做预处理质量检查',
57
+ policyBasis,
58
+ timestamp: new Date().toISOString(),
59
+ };
60
+ return JSON.stringify(result, null, 2);
61
+ }
62
+ catch (e) {
63
+ return JSON.stringify({ error: 'UNKNOWN_ERROR', message: e.message });
64
+ }
65
+ },
66
+ });
67
+ }
68
+ function computeKappa(dataset, threshold) {
69
+ let agreed = 0;
70
+ let disputed = 0;
71
+ for (const row of dataset) {
72
+ if (typeof row === 'object' && row !== null) {
73
+ const obj = row;
74
+ if (obj.annotation1 !== undefined && obj.annotation2 !== undefined) {
75
+ if (obj.annotation1 === obj.annotation2)
76
+ agreed++;
77
+ else
78
+ disputed++;
79
+ }
80
+ }
81
+ }
82
+ const total = agreed + disputed;
83
+ const kappa = total > 0 ? agreed / total : 1;
84
+ return { kappaCoefficient: Math.round(kappa * 100) / 100, disputedSamples: disputed, passed: kappa >= threshold };
85
+ }
86
+ function analyzeLabelDistribution(dataset) {
87
+ const distribution = {};
88
+ for (const row of dataset) {
89
+ if (typeof row === 'object' && row !== null) {
90
+ const label = row.label;
91
+ if (label)
92
+ distribution[label] = (distribution[label] ?? 0) + 1;
93
+ }
94
+ }
95
+ const counts = Object.values(distribution);
96
+ const max = counts.length > 0 ? Math.max(...counts) : 0;
97
+ const min = counts.length > 0 ? Math.min(...counts) : 0;
98
+ const ratio = min > 0 ? max / min : Infinity;
99
+ return { isBalanced: ratio <= 2, imbalanceRatio: Math.round(ratio * 100) / 100, classDistribution: distribution };
100
+ }
101
+ function detectLeakage(dataset) {
102
+ const ids = new Set();
103
+ let overlapping = 0;
104
+ for (const row of dataset) {
105
+ if (typeof row === 'object' && row !== null) {
106
+ const id = row.id;
107
+ if (id) {
108
+ if (ids.has(id))
109
+ overlapping++;
110
+ else
111
+ ids.add(id);
112
+ }
113
+ }
114
+ }
115
+ return { detected: overlapping > 0, leakageType: overlapping > 0 ? 'train_val' : 'none', overlappingSamples: overlapping };
116
+ }
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,17 @@
1
+ import { loadJsonConfig } from '@liuhange/dsh-data-asset-shared';
2
+ export function resolvePolicyBasis(stage) {
3
+ try {
4
+ const config = loadJsonConfig('POLICY_REFS_PATH', 'config/policy-references.json', '@liuhange/dsh-data-asset-shared/config/policy-references.json');
5
+ const refs = config.policyReferences;
6
+ const stageEntry = refs?.find((s) => s.stage === stage);
7
+ if (!stageEntry) {
8
+ console.warn(`Policy stage "${stage}" not configured`);
9
+ return [`依据:政策依据未配置(stage=${stage})`];
10
+ }
11
+ return stageEntry.documents.map((d) => `依据:《${d.name}》(${d.docNumber})—${d.coreRequirement}`);
12
+ }
13
+ catch {
14
+ return ['依据:政策依据配置加载失败'];
15
+ }
16
+ }
17
+ //# sourceMappingURL=policyBasis.js.map
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@liuhange/dsh-ai-dataset-inspector",
3
+ "description": "AI training dataset inspection plugin: annotation consistency, label distribution, data leakage detection",
4
+ "version": "3.1.0",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/liuhange789/data-asset-inspector.git",
11
+ "directory": "packages/data-asset/ai-dataset-inspector"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./src/*": "./src/*",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "lib/**/*.js",
26
+ "lib/types/**/*.d.ts"
27
+ ],
28
+ "license": "MIT",
29
+ "scripts": {
30
+ "typecheck": "tsc --noEmit",
31
+ "test": "vitest run",
32
+ "build": "tsc"
33
+ },
34
+ "peerDependencies": {
35
+ "@deepseek-ai/cordis": "^4.0.0",
36
+ "@deepseek-ai/dsh-tools": "0.0.1-rc.1",
37
+ "@liuhange/dsh-data-asset-shared": "^3.0.0"
38
+ },
39
+ "dsh": {
40
+ "bundle": {
41
+ "patch": true
42
+ }
43
+ },
44
+ "keywords": [
45
+ "deepseek-harness",
46
+ "dsh-plugin",
47
+ "data-asset",
48
+ "data-asset-inspector",
49
+ "shujintong",
50
+ "数据资产",
51
+ "ai-dataset",
52
+ "annotation-consistency",
53
+ "data-leakage",
54
+ "kappa-coefficient",
55
+ "AI数据集",
56
+ "标注一致性",
57
+ "数据泄露"
58
+ ],
59
+ "homepage": "https://github.com/liuhange789/data-asset-inspector#readme"
60
+ }