# N02 候选评分规则解释与合理性:详细设计 状态:N02 独立代码复验与真实 MySQL/Docker 黑盒验收通过,待 Git 检查点。 文档类型:解释。目标读者为产品负责人、后端开发、独立验收人员和前端联调人员。本设计说明现有候选评分实际上计算了什么、一期应如何向学生解释,以及哪些结论不得从这些分数推出。 ## 1. 背景与目标 当前候选评分由 `CandidateScoringCalculator` 计算八个维度,并由 `CandidateScoringEngineService` 写入或读取 `candidate_dimension_scores`。学生单维接口已有 `subScores`、`reasons` 和 `dataSources`,但这些信息由各计算方法临时拼装,缺少稳定的规则编码、解释版本、适用范围和限制说明。 N02 的一期目标是让学生能够回答以下问题: 1. 这个分数使用了哪些数据; 2. 哪些确定性规则实际命中; 3. 每条规则对当前结果产生了什么影响; 4. 哪些数据缺失、过期或仍未接入; 5. 用户可以通过哪些真实、合理的方式完善资料; 6. 该分数不能代表什么。 N02 同时要求企业端继续只查看聚合分与状态,不获得学生的私人明细、行为次数、测评细分或改进建议。 ## 2. 非目标 一期不做: - SHAP、机器学习特征贡献或 AI 生成解释; - 人口属性公平性推断; - 宣称规则已具备心理测量效度或预测效度; - 自动修改评分权重、排名或推荐逻辑; - 新增评分事实表、解释历史表或规则配置后台; - 向企业暴露学生的细分评分证据; - 将行为频次解释为人格、诚信、成长潜力或职业能力; - 用解释功能引导用户刷投递、刷消息、刷简历版本。 ## 3. 当前真实实现 ### 3.1 代码入口 - 计算:`mydreamjob-server/src/main/java/com/mydreamjob/candidatescore/CandidateScoringCalculator.java` - 服务:`CandidateScoringEngineService.java` - 学生与企业接口:`CandidateScoringController.java` - 快照:`CandidateDimensionScore.java`、`CandidateDimensionScoreRepository.java` - 能力映射:`AbilityAssessmentScoreSource.java`、`AbilityScoreMapper.java` - 申诉与回滚:`candidatescore/governance/` - 排名:`competition/CompetitionRankingCalculator.java` ### 3.2 综合分 当前八维默认展示权重均为 12.50。只有状态为 `OK` 或 `ESTIMATED` 且分数非空的维度参与综合分: ```text overallScore = sum(可计算维度分数) / 可计算维度数量 ``` 最终保留两位小数,`HALF_UP`。`PENDING` 和 `INSUFFICIENT_DATA` 被排除后重新归一化,因此两个相同综合分可能来自不同的维度集合。解释必须展示参与维度数量与排除维度,禁止把综合分描述为完整、统一口径的能力总分。 ### 3.3 规则版本 - 能力测评开关关闭:`candidate-scoring-v0.1`; - 能力测评开关开启:`candidate-scoring-v0.2`; - ABILITY 映射规则:`ability-assessment-scoring-v0.1`; - 历史趋势优先使用快照自身 `rulesVersion`。 N02 只增加解释契约时不升级候选评分规则版本,建议新增独立的 `explanationVersion=candidate-scoring-explanation-v1`。只有公式、阈值、参与维度或排名输入发生变化时才升级评分规则版本。 ## 4. 设计原则 ### 4.1 解释必须与计算同源 每个计分分支在计算贡献值的同时生成内部 `RuleContribution`,不能在 Controller、DTO mapper 或另一套公式中根据最终分数反推解释。 建议内部结构: ```text RuleContribution - ruleCode 稳定规则编码 - outcome APPLIED / NOT_APPLIED / MISSING / EXPIRED / UNAVAILABLE - contribution 实际加减分;不适用时为 null - maxContribution 该规则可能贡献的绝对上限 - publicLabel 面向学生的中性名称 - publicObservation 安全的区间或状态,不输出内部数据库字段 - explanationKey 固定解释模板编码 ``` 规则元数据由 `CandidateScoringExplanationCatalog` 按 `ruleCode` 提供名称、限制、建议和证据等级。计算器产生“事实和贡献”,目录提供“稳定语义”,Assembler 只组合,不重复计算。 当逐项贡献按两位小数展示而整体分数采用 HALF_UP 产生舍入差时,使用 `SCORE_ROUNDING_ADJUSTMENT` 显式记录对账差;它只解释展示精度,不改变任何评分公式。 ### 4.2 不把解释做成刷分指南 学生可以看到区间和缺失项,例如“近 90 天存在投递记录”“求职偏好尚未完整”,但默认不暴露风控阈值组合、审计动作名称、原始消息数量或内部 SQL 字段。已经在界面契约中公开的固定能力权重可继续展示。 ### 4.3 区分事实、推断与限制 每个维度必须有: - `whatItMeasures`:规则直接使用的数据; - `interpretation`:可谨慎解释的含义; - `limitations`:不能据此得出的结论; - `evidenceLevel`:`DIRECT_RULE`、`SELF_REPORTED`、`BEHAVIOR_PROXY`、`UNVALIDATED_PROXY`; - `improvementActions`:只给可合理完成的操作,不鼓励刷行为。 ## 5. 一期响应方案 ### 5.1 路径范围 保持现有 4 个路径,不新增 operation: - `GET /profile/me/candidate-scoring` - `GET /profile/me/candidate-scoring/dimensions/{dimension}` - `GET /profile/me/candidate-scoring/trend` - `GET /company/candidates/{applicationId}/scoring` OpenAPI 路径/操作数保持当前基线 172/210。 ### 5.2 学生综合卡兼容扩展 建议新增: - `explanationVersion`; - `coverage.computableDimensions`; - `coverage.totalDimensions=8`; - `coverage.includedDimensions`; - `coverage.excludedDimensions[]{dimension,status,reason}`; - `comparabilityNotice`,固定说明不同参与维度集合的综合分不宜直接比较。 不得返回内部快照 ID、测评结果 ID、题目、答案、审计详情、消息正文或数据库字段名。 ### 5.3 学生单维详情兼容扩展 保留现有 `subScores/reasons/dataSources`,新增: - `displayName`; - `whatItMeasures`; - `interpretation`; - `limitations[]`; - `evidenceLevel`; - `ruleContributions[]`; - `improvementActions[]`; - `explanationVersion`; - `validity`:仅在存在明确有效期时返回,例如 ABILITY; - `calculatedAt`:本次真实计算基准时间。 `ruleContributions` 只面向学生本人。旧字段在一期保留,避免前端破坏性升级;文档标记旧 `reasons/dataSources` 后续逐步由结构化字段替代。 ### 5.4 企业端 企业端保持现有字段:`applicationId/overallScore/status/dimensions/generatedAt/rulesVersion`。不新增 `ruleContributions`、`limitations`、行为次数、学校分档、资料缺失项、四维能力细分或有效期。 企业页面可增加平台级固定说明,但不得把学生解释 DTO 直接复用到企业响应。 ## 6. 时间一致性 `computeAll` 应在入口生成一次 `capturedNow`,并传递给所有时间敏感查询与规则: - 90 天投递; - 30 天登录和活跃操作; - 账号月龄; - 简历 90 天更新; - ABILITY 有效期; - `calculatedAt` 和数据源时间说明。 不得在同一次计算中多次调用 `LocalDateTime.now()` 形成不同边界。建议注入 `Clock`,生产使用系统时钟,测试使用固定时钟。 ## 7. 八维解释口径 | 维度 | 一期允许的名称 | 允许解释 | 禁止解释 | |---|---|---|---| | EDUCATION | 教育背景信息 | 已填写学历与学校分类规则的确定性结果 | 智力、能力上限、人才质量 | | INTERNSHIP | 实习经历信息 | 当前数据源尚未接入 | 无实习能力、经验不足 | | ABILITY | 四维能力测评 | 最新有效自研测评的规则化聚合 | 确定性职业能力、招聘结论 | | COMPLIANCE | 求职偏好完整度 | 五类偏好字段填写情况 | 合规意识、服从性 | | STABILITY | 平台求职行为稳定信号 | 账号时长、撤回与登录形成的行为代理 | 人格稳定、忠诚度 | | GROWTH | 简历维护与技能展示信号 | 简历维护和技能标签状态 | 成长潜力、学习速度 | | INTEGRITY | 履约与资料一致性信号 | Offer 响应、反馈与资料完整度的行为代理 | 道德、诚信人格、信用评级 | | ACTIVITY | 平台求职活跃度 | 近期投递、平台操作和沟通信号 | 工作积极性、职业投入度 | 维度代码为兼容字段,本版不改数据库枚举。产品展示名称采用上述中性名称。 ## 8. 合理性整改分级 ### 8.1 仅解释整改即可上线 - ABILITY:保留现有公式和有效期,补自评/情境测验限制; - INTERNSHIP:保持 PENDING,明确“未接入”不是低分; - 综合分:补参与维度和不可横向比较提示。 ### 8.2 需要改名但暂不改公式 - COMPLIANCE → 求职偏好完整度; - STABILITY → 平台求职行为稳定信号; - GROWTH → 简历维护与技能展示信号; - INTEGRITY → 履约与资料一致性信号; - ACTIVITY → 平台求职活跃度。 这里只修改展示语义,不改变维度代码、快照或历史接口。 ### 8.3 需要后续规则版本评审 - EDUCATION 的学校名单及 45% 权重; - STABILITY 的账号时长奖励和撤回惩罚; - GROWTH 的简历版本数量奖励; - INTEGRITY 的 Offer 接受奖励、过期惩罚和反馈评分; - ACTIVITY 的投递、审计和消息计数可操纵性; - 缺失维度重新归一化后仍进入企业评分与排名。 任何一项改变分数都必须升级评分版本、完成新旧结果对账和 V32 排名回归,不能混入纯解释提交。 ## 9. 权限与隐私 学生本人可以查看自身安全聚合解释。企业只能查看现有聚合评分卡。ADMIN 沿用治理接口,不因 N02 获得新的个人解释查询入口。 所有 HTTP 响应禁止新增: - 原始审计动作、IP、User-Agent; - 消息正文或对话对象; - Offer 具体企业、岗位或时间线; - 评价正文与评价人身份; - assessment/ability 的题目、答案、答案键、评分规则和结果 ID; - 内部 SQL、表名、快照 ID、用户 ID; - 密码、Token、密钥。 学生响应中的数据来源改为产品语义名称,如“个人教育资料”“求职偏好”“平台求职活动”,不直接返回 `student_profiles.education/school` 等物理字段名。 ## 10. 实现建议 建议新增内部类: - `CandidateScoringExplanationCatalog`:规则元数据白名单; - `CandidateScoringExplanationAssembler`:将 `DimensionResult` 与规则元数据组装为学生 DTO; - `RuleContribution`、`ScoreCoverage` 等内部/DTO 类型。 修改: - `CandidateScoringCalculator`:统一 `capturedNow/Clock`,每个实际分支同步生成贡献; - `CandidateScoringEngineService`:组装覆盖率与学生解释; - 学生 DTO:兼容新增字段; - OpenAPI、校验脚本、API 和联调文档。 原则上无新 Flyway、无新 ErrorCode、无新公网路径。解释元数据先随代码版本发布,不引入可在线修改规则的后台。 ## 11. 测试与验收 ### 11.1 规则同源 - 每个阈值边界独立手算; - `contribution` 之和与真实维度分形成可证明关系; - 未命中、缺失、过期和不可用规则不能伪造成 0 分命中; - 固定 Clock 验证 30/90 天和有效期边界; - 不允许修改解释而改变分数,或修改分支后遗漏解释。 ### 11.2 综合分 - 0、1、2、8 个可计算维度; - `OK/ESTIMATED` 参与,其他状态排除; - coverage 与实际参与集合完全一致; - 学生综合卡、趋势、企业评分和 V32 排名与既有纯函数对账。 ### 11.3 权限与脱敏 - STUDENT 仅查看本人; - COMPANY 只见聚合字段; - ADMIN 不获得新增学生详情入口; - 匿名 401,角色越权 403/2002; - 对完整 JSON 递归扫描禁止字段; - ABILITY 不泄露题目、答案或来源结果 ID。 ### 11.4 防刷与边界评审 用固定样例证明重复投递、发消息、制造简历版本可能改变哪些分数,并在本版明确标为未验证代理信号。若决定保留其排名影响,必须形成书面产品决策和监测指标。 ### 11.5 发布验证 - N02 专项、候选评分、治理、V32、V38、V39、V41、V42、V43 回归; - `mvn clean verify`; - 静态与运行时 OpenAPI 对账; - 真实 MySQL 主链路和响应脱敏; - 查询前后快照写入行为保持既有语义; - 无新增 RabbitMQ 消息。 ## 12. 编码前决策表 | 编号 | 决策项 | 建议方案 | 状态 | |---|---|---|---| | D01 | N02 是否只做解释,不改任何现有分数、权重和排名 | 是;规则合理性问题记录为后续独立评分版本 | 已确认 | | D02 | 是否保持现有 4 个接口、0 个新增 path/operation | 是 | 已确认 | | D03 | 是否无新 Flyway、无规则配置后台 | 是 | 已确认 | | D04 | 是否增加独立 `explanationVersion`,不升级评分 `rulesVersion` | 是,值为 `candidate-scoring-explanation-v1` | 已确认 | | D05 | 是否兼容扩展学生综合卡,展示参与/排除维度和可比性提示 | 是 | 已确认 | | D06 | 是否兼容扩展学生单维详情,增加结构化规则贡献、限制和建议 | 是 | 已确认 | | D07 | 企业端是否保持现有 schema,不展示任何规则贡献与私人细分 | 是 | 已确认 | | D08 | 是否采用中性展示名,但保持八个维度代码不变 | 是 | 已确认 | | D09 | 是否明确 `INTEGRITY` 不能解释为人格诚信或信用评级 | 是 | 已确认 | | D10 | 是否明确 `GROWTH/STABILITY/COMPLIANCE/ACTIVITY` 仅为平台数据代理 | 是 | 已确认 | | D11 | 是否用单次 `capturedNow` 并注入 `Clock` 修复解释时间一致性 | 是,不改变阈值与公式 | 已确认 | | D12 | 学生是否只看安全区间/状态,不显示可用于刷分的内部计数与审计动作 | 是 | 已确认 | | D13 | EDUCATION 学校分档是否暂时保留计算,但增加未验证与公平性警示 | 暂留,另开评分版本评审决定移除/降权 | 已确认 | | D14 | 缺失维度重新归一化是否暂时保留 | 暂留,明确可比性限制并在规则评审中决定 | 已确认 | | D15 | 是否安排题库和规则证据验证,不将当前确定性规则称为已验证模型 | 是 | 已确认 | D01–D15 已全部确认并按本设计实现。若未来 D01、D13 或 D14 的产品决策改变计分,本模块必须另行升级评分规则版本,不能通过解释增强提交混入。