中职学生职业素养数字画像评价
> 版本 1.1.0 | 已在真实项目上验证:41 个锚点 / 9 张采集表 / 2569 行数据,体检无阻断项。
这个技能解决什么问题
做这类系统的难点从来不是"算平均分",而是三件容易翻车的事:
- 指标落不了地:写了"职业素养"四个字,但没有可采集、可计分、可解释的锚点,最后只能拍脑袋打分。
- 数据往返有损耗:采集 → 入库 → 导出 → 计分这条链路上,字段别名错一位(如"小组互评评分"vs"小组互评"),
某个指标会被静默置为 null 并重归一,结果看着正常,实际整条维度失真。 - 评完没法用:只出分数,没有"比上学期进步了多少""哪里是优势、哪里要补",背离发展性评价的初衷。
本技能给出可复用的做法,并配一个体检脚本把上面这些隐患一次性扫出来。
何时使用
- 从零设计指标体系:需要产出
config/indicators.json(四维 / 二级指标 / 锚点 / 权重 / 标签规则)。 - 设计或修改采集表(01~09),尤其是新增字段、改表头。
- 数据已采集,准备跑计分/出画像,想先确认链路没问题。
- 画像结果异常:某维度普遍偏低、某锚点全 null、班级分布不合理。
- 需要产出教师可用的班级报告 / 学生个人发展报告。
核心工作流(五步)
第 1 步:先定"看什么"——确立评价体系骨架
不要从"我有什么数据"出发,要从"职业素养包含什么"出发,再倒推数据。
推荐骨架(可作模板调整):
| 一级维度 | 关注点 | 典型锚点举例 |
|---|---|---|
| A 职业认知与态度 | 认同专业、规则意识 | 职业规划作业、出勤、差错控制 |
| B 专业知识与技能 | 学业 + 实训 | 课程成绩、技能考核、实训表现 |
| C 职业行为与实践 | 实践参与、服务 | 集体活动、志愿服务、实习反馈 |
| D 协作与发展 | 团队、反思成长 | 实训小组互评、自我反思、增值进步 |
数据表:每一个锚点必须能回答四个问题——数据从哪张表来、取哪个字段、做什么变换、缺了怎么办。
详细模板见 references/indicator-system.md。
第 2 步:再定"怎么采"——数据采集表契约
一口吃不成胖子。表要按"稳定、可复核"的原则设计:
- 学生名册单独一张表(
01),是其余表的学号来源,必须先导入,否则其它表验不住学号。 - 每张业务表都要有
学号+学期两列,没有学期的数据应忽略流式处理。 - 表头命名一经上线即为契约,改动要与别名表同步。
- 一行也不准造:
占位行(只有学号学期、业务列全空)必须宁默跳过而不是补 0——补 0 会让"低分"和"没数据"混淆。 - 活动类表(如 t06)要考虑主键唯一:
(学号, 学期, 活动名称)重复会被覆盖写入,导致"参与次数"计数被折叠。
详细字段与别名规则见 references/data-contract.md。
第 3 步:计分——先归一化,再加权,最后算增值
关键三条:
- 缺数据重归一:某锚点无数据时,不记 0 分,而是把该锚点权重剔除后按剩余权重重新归一为百分制。
这样"少一项数据"不等于"这一项很差"。
但先分清"没采集"和"没发生":奖惩、证书这类天然稀疏的项,"无记录"是有语义的
(无奖惩 / 未取证),应显式声明missing: useBase或missing: absent直接赋基准分,
不要让它掉进重归一——否则这一项在多数学生身上"消失",权重被转嫁给同级的其它锚点。
同时注意基准值别选得太低(低于群体均值就成了隐性扣分)。 - 反向指标要显式声明:如"差错次数",必须标记为反向扣分,且空值不等于满分,应计入缺数据处理。
- 增值比绝对值更重要:发展性评价的核心是
deltaComposite = 本学期综合分 − 上学期综合分,
配合年级动态权重,避免"起点高的永远排前面"。
第 4 步:出画像与报告——让数据能被听见
每个学生输出一份画像 JSON,至少包含:综合分、四维度得分、各锚点明细、增值 delta、优势/待发展标签。
每班输出统计 JSON:人数、四维度均值、低分段预警人数等。
标签规则要能用一句话解释清楚(例:"志愿服务参与度高"的判定条件是什么),否则家长/教师没法沟通。
第 5 步:上线前体检——跑一遍,别靠祈祷
改动指标配置、采集表、导入导出逻辑之后,务必跑:
# 体检:只看结构与字段,不写任何文件
node "<skill>/scripts/audit-pipeline.js" --root "<项目根>" [--data "<数据目录>"] [--json]
# 端到端一致性:本技能不管这件事,用另一个技能
# tempdb-e2e-validate —— 验证「数据库往返」与「直接计分」逐字段一致
audit-pipeline.js 只依赖 fs,纯只读、不连数据库、不改动任何文件,可随时跑。
退出码:0=无阻断项;1=存在阻断项(ERROR);2=前置错误(找不到配置)。
立即试用
技能自带一份最小样例(examples/mini-sample,含 4 张 CSV + 一份 indicators.json,故意埋了几处问题):
node "<skill>/scripts/audit-pipeline.js" --root "<skill>/examples/mini-sample"
预期:退出码 1,报出 2 条阻断(标签引用了不存在的锚点 Z9-99、06 表存在主键重复记录)
和 5 条提醒(别名命中、sumClamp 缺 norm/cap、年级维度权重和为 0.900、两个锚点字段含无法解析的值)。
看到这些说明脚本工作正常。
体检覆盖什么
| 类别 | 检查项 |
|---|---|
| 配置结构 | 锚点编号重复/缺失、未知 transform、exists/maxLevel/reversePenalty 缺必要参数 |
| 源表与字段 | 源表不存在、字段不在表头、别名歧义、过滤条件在源表中一条都匹配不到 |
| 权重 | 二级指标权重和、锚点权重和、年级权重和是否等于 1 |
| 标签与干预 | 标签规则引用不存在的编号、标签无判定条件、维度缺少干预策略 |
| 数据质量 | 缺少学号的行、占位行、活动/项目表主键重复、整列为空、覆盖率 < 20% |
| 学生级覆盖 | 某锚点实际只有很少学生有数据(<30% 提醒、30~60% 说明),这是最容易藏问题的一层 |
已验证兼容的写法
直接可被体检识别,无需改造你的配置:
- 二级指标容器写作
children/indicators/二级指标均可。 - 变换支持
meansumlastmaxcountcountClampsumClampreversePenaltypenaltySum
existsmaxLevelgrowth等。 - 标签支持
when对象与rule字符串(tagRules.strength/tagRules.develop/labels均可)。 source为「系统计算」的锚点(如增值)自动跳过源表检查。- 内置常见别名(如
小组互评⇄小组互评评分、中英文字段名),可在配置里用aliases扩展。
安装后自检
node "<skill>/scripts/self-check.js"
会检查目录完整性、Node 版本、样例能否正常体检,以及是否残留了把个人路径写死的痕迹(影响分发)。
退出码 0 表示可以直接打包上传。
必须遵守的约束
- 隐私与用途边界:画像用于支持性反馈,不做公开排名、不作惩罚性依据。导出/分享前完成脱敏。
- 别名必须双向一致:任何一处要把 A 名字写成 B 名字,它的反向映射必须同时存在。这是最常见的数据静默丢失源头。
- 不要为了凑数据造数:宁可锚点缺失走重归一,也不要填充默认值。
- 指标数量克制:一套评价体系锚点控制在 20~40 个以内,超出部分的维护成本远大于信息增益。
- 改表先改体检:新增/删除字段后先跑 audit 脚本,再跑计分。
已知坑(详见 references/pitfalls.md)
- 表头别名一进一出没对齐 → 整列被置空,检测到的方法是对比出库 CSV 与源 CSV 的表头差异。
- 活动表主键重复 → 计数类锚点被折叠,
countClamp结果偏低。 - 导出时误传学期参数 → 只导单学期,与全量口径不一致,产生"看似数据丢失"的假差异。
- Windows 本机 Git Bash 不可用时(连
ls/cd都报错),用 node 绝对路径 + 子进程注入环境变量,不要用 shell 包装脚本。
参考
references/indicator-system.md—— 指标体系设计模板、权重与增值、标签规则写法。references/data-contract.md—— 01~09 表清单、字段命名、别名双向规则、占位行规则。references/pitfalls.md—— 完整坑清单与分步排查路径(含 Windows 本机环境与合规边界)。- 相关技能:
tempdb-e2e-validate(数据库往返一致性回归验证,与本技能互补:本技能管设计阶段,
它管改动后的回归)。
实战验证记录
在真实项目「中职药学专业学生职业素养数字画像系统」上的体检结果
(41 个锚点 / 9 张采集表 / 2569 行数据 / 90 名学生):
- 0 阻断:所有锚点源表、字段、过滤列、权重和、标签引用、年级权重均正确。
- 2 项稀疏但可解释:
A1-04 品行奖惩积分仅 8/90 人有记录——引擎penaltySum在无记录时返回base,
属"无奖惩"的合法语义,不是数据缺失(体检前我一度误判,核对引擎实现后更正);B2-02 技能证书获取仅 17/90 人有记录——exists在无记录时返回absent,同理可解释。- 由此发现的真实设计问题:
base/absent取 70 低于群体均值,会对"无记录"学生形成隐性压低,
需要明确这是激励导向还是应调为中性值。 - 说明级(需注意):企业导师评价类锚点(
A1-03/C1-01~03/D1-02/D2-01/D3-03)
仅 30/90 名学生、且集中在实习学期;
竞赛/双创类锚点覆盖 28~40/90。
这些都不是错误,而是覆盖范围受限:未覆盖学生全部走缺数据重归一,需要配合年级动态权重处理。
维护与反馈
- 作者:黎文乾
- 单位:广西中医药大学附设中医学校(广西中医学校)药学专业部
- 许可:MIT
- 更新记录:
1.0.0初版:五步工作流 + 三份参考 + 体检脚本 + 样例1.1.0兼容children/tagRules/多种 transform 写法;新增过滤失配、学生级覆盖率检查;
修正对文本/等级字段(带convert)的误判;新增安装自检脚本