@liuhange/dsh-city-data-classifier 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 +22 -0
- package/lib/index.js +139 -0
- package/lib/policyBasis.js +17 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @liuhange/dsh-city-data-classifier
|
|
2
|
+
|
|
3
|
+
## 功能说明
|
|
4
|
+
|
|
5
|
+
City data AI classification plugin: auto-recommend GB/T 47949-2026 category and sensitivity grading
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
dsh plugin --profile web add @liuhange/dsh-city-data-classifier
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 用法
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import { apply } from '@liuhange/dsh-city-data-classifier';
|
|
17
|
+
apply(ctx); // 注册工具: classify_city_data
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## License
|
|
21
|
+
|
|
22
|
+
MIT
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
export const name = '@liuhange/dsh-city-data-classifier';
|
|
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: 'classify_city_data',
|
|
8
|
+
description: 'AI自动推荐城市/组织数据的国标分类和敏感度分级,生成登记台账',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
dataSource: { type: 'string', description: '数据文件路径' },
|
|
13
|
+
autoConfirm: { type: 'boolean', default: false },
|
|
14
|
+
},
|
|
15
|
+
required: ['dataSource'],
|
|
16
|
+
},
|
|
17
|
+
async execute(args) {
|
|
18
|
+
try {
|
|
19
|
+
if (!args?.dataSource || typeof args.dataSource !== 'string') {
|
|
20
|
+
return JSON.stringify({ error: 'CITY_CLASSIFY_INPUT_INVALID', message: 'dataSource is required' });
|
|
21
|
+
}
|
|
22
|
+
let rulesConfig;
|
|
23
|
+
try {
|
|
24
|
+
rulesConfig = loadJsonConfig('BUSINESS_RULES_PATH', 'config/business-rules.json', '@liuhange/dsh-data-asset-shared/config/business-rules.json');
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return JSON.stringify({ error: 'CITY_CLASSIFY_RULES_MISSING', message: '无法加载business-rules.json' });
|
|
28
|
+
}
|
|
29
|
+
const cityConfig = rulesConfig.cityDataClassification;
|
|
30
|
+
if (!cityConfig) {
|
|
31
|
+
return JSON.stringify({ error: 'CITY_CLASSIFY_RULES_MISSING', message: 'cityDataClassification配置段缺失' });
|
|
32
|
+
}
|
|
33
|
+
let data;
|
|
34
|
+
try {
|
|
35
|
+
const { readFileSync } = await import('node:fs');
|
|
36
|
+
const raw = readFileSync(args.dataSource, 'utf-8');
|
|
37
|
+
data = JSON.parse(raw);
|
|
38
|
+
if (!Array.isArray(data))
|
|
39
|
+
data = [data];
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return JSON.stringify({ error: 'CITY_CLASSIFY_INPUT_INVALID', message: `无法加载数据: ${args.dataSource}` });
|
|
43
|
+
}
|
|
44
|
+
const aiRecommendation = recommendClassification(data, cityConfig.confidenceThreshold);
|
|
45
|
+
const sensitivityLevel = gradeSensitivity(data, cityConfig.sensitivityGradingRules);
|
|
46
|
+
const policyBasis = resolvePolicyBasis('CITY_DATA_CLASSIFICATION');
|
|
47
|
+
const autoConfirm = args.autoConfirm ?? false;
|
|
48
|
+
const result = {
|
|
49
|
+
aiRecommendation,
|
|
50
|
+
humanReview: autoConfirm
|
|
51
|
+
? { reviewer: 'auto-confirm', reviewedAt: new Date().toISOString(), confirmed: true }
|
|
52
|
+
: { reviewer: null, reviewedAt: null, confirmed: false, note: '待人工复核' },
|
|
53
|
+
sensitivityLevel,
|
|
54
|
+
registrationEntry: {
|
|
55
|
+
assetCode: aiRecommendation.dataAssetCode,
|
|
56
|
+
registeredAt: new Date().toISOString(),
|
|
57
|
+
initialRegistration: true,
|
|
58
|
+
},
|
|
59
|
+
policyBasis,
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
};
|
|
62
|
+
return JSON.stringify(result, null, 2);
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
return JSON.stringify({ error: 'UNKNOWN_ERROR', message: e.message });
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
ctx.tools.register({
|
|
70
|
+
name: 'confirm_city_classification',
|
|
71
|
+
description: '人工复核确认AI推荐的城市数据分类结果',
|
|
72
|
+
parameters: {
|
|
73
|
+
type: 'object',
|
|
74
|
+
properties: {
|
|
75
|
+
classificationId: { type: 'string', description: '分类结果ID' },
|
|
76
|
+
reviewer: { type: 'string', description: '复核人' },
|
|
77
|
+
confirmed: { type: 'boolean' },
|
|
78
|
+
adjustments: { type: 'string', description: '调整说明(可选)' },
|
|
79
|
+
},
|
|
80
|
+
required: ['classificationId', 'reviewer', 'confirmed'],
|
|
81
|
+
},
|
|
82
|
+
async execute(args) {
|
|
83
|
+
return JSON.stringify({
|
|
84
|
+
classificationId: args.classificationId,
|
|
85
|
+
reviewer: args.reviewer,
|
|
86
|
+
confirmed: args.confirmed,
|
|
87
|
+
adjustments: args.adjustments ?? null,
|
|
88
|
+
reviewedAt: new Date().toISOString(),
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function recommendClassification(data, confidenceThreshold) {
|
|
94
|
+
let structured = 0, semiStructured = 0, unstructured = 0;
|
|
95
|
+
for (const row of data) {
|
|
96
|
+
if (typeof row === 'object' && row !== null && !Array.isArray(row)) {
|
|
97
|
+
const keys = Object.keys(row);
|
|
98
|
+
if (keys.length > 0) {
|
|
99
|
+
if (keys.every((k) => /^[a-zA-Z_]+$/.test(k)))
|
|
100
|
+
structured++;
|
|
101
|
+
else
|
|
102
|
+
semiStructured++;
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
unstructured++;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
unstructured++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const total = data.length || 1;
|
|
112
|
+
const primaryCategory = structured >= semiStructured && structured >= unstructured ? 'structured' : semiStructured >= unstructured ? 'semi-structured' : 'unstructured';
|
|
113
|
+
const confidence = primaryCategory === 'structured' ? structured / total : primaryCategory === 'semi-structured' ? semiStructured / total : unstructured / total;
|
|
114
|
+
const codeMap = { structured: 'A0101', 'semi-structured': 'A0201', unstructured: 'A0301' };
|
|
115
|
+
return {
|
|
116
|
+
primaryCategory,
|
|
117
|
+
specificType: '城市数据',
|
|
118
|
+
dataAssetCode: codeMap[primaryCategory] ?? 'A0000',
|
|
119
|
+
confidence: Math.round(confidence * 100) / 100,
|
|
120
|
+
reasoning: `基于${total}条样本分析,${primaryCategory}数据占比${Math.round(confidence * 100)}%`,
|
|
121
|
+
meetsThreshold: confidence >= confidenceThreshold,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function gradeSensitivity(data, _rules) {
|
|
125
|
+
const sensitiveKeywords = ['身份证', '手机号', '地址', '密码', '银行'];
|
|
126
|
+
let maxSensitivity = 0;
|
|
127
|
+
for (const row of data) {
|
|
128
|
+
if (typeof row === 'object' && row !== null) {
|
|
129
|
+
const json = JSON.stringify(row);
|
|
130
|
+
for (const kw of sensitiveKeywords) {
|
|
131
|
+
if (json.includes(kw))
|
|
132
|
+
maxSensitivity = Math.max(maxSensitivity, 3);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const levels = ['public', 'internal', 'confidential', 'restricted'];
|
|
137
|
+
return levels[maxSensitivity] ?? 'internal';
|
|
138
|
+
}
|
|
139
|
+
//# 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-city-data-classifier",
|
|
3
|
+
"description": "City data AI classification plugin: auto-recommend GB/T 47949-2026 category and sensitivity grading",
|
|
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/city-data-classifier"
|
|
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
|
+
"city-data",
|
|
52
|
+
"ai-classification",
|
|
53
|
+
"sensitivity-grading",
|
|
54
|
+
"gbt-47949",
|
|
55
|
+
"城市数据",
|
|
56
|
+
"AI分类",
|
|
57
|
+
"敏感度分级"
|
|
58
|
+
],
|
|
59
|
+
"homepage": "https://github.com/liuhange789/data-asset-inspector#readme"
|
|
60
|
+
}
|