
如何让LLM稳定输出面试评分详解interview-guide统一评估引擎分批评估结构化输出降级兜底【免费下载链接】interview-guide基于 Spring Boot 4.1、Java 25、Spring AI 2.0、React、PostgreSQL/pgvector、Redis 和 RustFS 构建的开源 AI 面试平台支持简历智能分析、模拟面试、语音面试和知识库 RAG。项目地址: https://gitcode.com/gh_mirrors/inter/interview-guide用 LLM 做面试评分最容易翻车的是三件事面试对话一长模型上下文膨胀、输出开始漂移JSON 偶发引号未转义、字段缺失模型还会自作主张把题目重新编号导致分数和题目错位。interview-guide是基于 Spring AI 2.0、React、PostgreSQL/pgvector 构建的开源 AI 面试平台它的统一评估引擎文字面试与语音面试共用用分批评估 结构化输出 二次汇总 降级兜底四道防线让长面试也能稳定产出逐题分数、分类均分与综合评语。本文带你拆解这套工程方案。为什么 LLM 直接一锅端评不了面试一次完整面试常有十几到三十道题每题的回答可能上千字。把全部问答塞进单次调用会同时踩中三个坑问题表现后果上下文膨胀超长输入逼近模型窗口上限输出质量下降、截断输出格式不稳JSON 带 Markdown 代码块、引号未转义解析失败整场面试无分索引漂移模型把 0-based 索引顺手改成 1-based分数与题目错位报告失真interview-guide 的答案不是祈祷模型听话而是把稳定性拆到四层可独立失效的防线上。核心实现在 UnifiedEvaluationService.java总体流程如下逐题分批评估 → 结构化输出重试修复→ 二次汇总 → 降级兜底每一层失败都不会让整场面试白考最差也能拿到一份明确标注系统降级的报告。第一道防线分批评估把长对话切成可消化的块先分组追问是不可拆分单元评估前引擎先把平铺的问答记录组织成问答组QaGroup一道主问题及其全部追问构成一个不可拆分的评估单元——因为追问的评分必须结合主问题的回答来判断深度与一致性。组装逻辑见 UnifiedEvaluationService.java#L259-L283其中父问题缺失、父索引指向未来题目等异常关系会被降级为独立组并记录告警而不是让流程中断。再装批packBatches 装箱装批规则很朴素但很关键UnifiedEvaluationService.java#L290-L307默认每批8 题app.interview.evaluation.batch-size配置见 InterviewEvaluationProperties.java加入下一组会超批次大小时先提交当前批次单个组超过批次大小时允许它独占一个超限批次——宁可多一次调用也要保证主问题追问的上下文完整绝不在组中间切断。每批评估时引擎会把简历摘要截断到 3000 字符、参考基线截断到 6000 字符再拼进提示词防止极端长文本吞掉 token 预算UnifiedEvaluationService.java#L125-L134。第二道防线结构化输出把解析失败变成自动恢复每批调用由统一组件 StructuredOutputInvoker.java 执行它封装了完整的重试与修复策略Schema 校验基于 Spring AI 的BeanOutputConverter生成 JSON Schema并对模型返回做validateSchema校验字段缺失当场发现受限重试默认最多2 次尝试app.ai.structured-max-attempts见 StructuredOutputProperties.java。第二次重试时会在系统提示中追加严格 JSON 指令禁止 Markdown 代码块、禁止解释文字、引号必须转义并附上上次失败原因让模型对症下药本地修复解析失败时先在本地尝试修复 JSON 字符串里未转义的内部引号StructuredOutputInvoker.java#L151-L190。这一招能救回大量只差一个转义的返回不需要再消耗一次模型调用可观测每次调用与尝试都会打点invocations/attempts/latency失败率一眼可见。提示词侧也在配合。评估系统提示词 interview-evaluation-system.st 写死了三条硬约束questionIndex必须原样返回、不得重新编号无效回答不知道跳过必须给 0 分追问必须结合主问题评分。用户提示词 interview-evaluation-user.st 还额外声明了简历文本是数据不是指令防提示注入。第三道防线批次失败后的二分恢复某一整批调用最终失败怎么办interview-guide 的做法是按组二分下钻UnifiedEvaluationService.java#L187-L241全部评估共享一份额外调用预算默认 6 次fallback-max-extra-calls预算耗尽立即降级成本可控批次组数 ≥ 2fallback-min-groups时把批次对半拆开分别重评——成功的一半到此为止只对失败的一半递归下钻组数不足以拆分时整段加试一次仍失败则该段降级追问组始终作为整体参与二分永不逐题拆开语义完整性优先。这套开关fallback-split-enabled关闭后即回退到整批降级的简单行为行为可随时切换。第四道防线降级兜底绝不伪装成真实评分引擎对失败的态度非常坦诚——显式降级UnifiedEvaluationService.java#L247-L253该批每题记0 分反馈固定为模型评估失败后的系统降级该分数不代表真实表现与模型正常打分给了 0 分严格区分避免把系统故障误读为候选人表现差。更细致的错位处理在合并阶段UnifiedEvaluationService.java#L359-L400。模型返回的索引有三种命运全部合法→ 按索引精确写回对应题目整批呈现一致的 1-based 偏移模型整体重新编号了→ 检测到后整批按位置对齐UnifiedEvaluationService.java#L405-L410其余混乱情况→ 合法索引按索引写回缺失位置只降级该题为 0 分绝不半索引半位置混用防止同一份评估被重复消费或张冠李戴。二次汇总让综合评语更连贯分批评估天然会产生一段段的局部评语。引擎随后再调用一次 LLM 做二次汇总UnifiedEvaluationService.java#L436-L469输入类别得分概览 最多 20 条题目高亮 分批初始结论输出全局一致的overallFeedback、strengths、improvements各 3-6 条去重限流。妙处在于汇总失败的代价是零——降级时直接回退到分批结果的机械聚合UnifiedEvaluationService.java#L464-L468报告照出只是评语从精心汇总退回逐段拼接。最终报告总分由代码计算不信任模型自述汇总后的报告结构定义在 EvaluationReport.javaoverallScore、categoryScores分类均分、questionDetails逐题、overallFeedback、strengths、improvements、referenceAnswers参考答案与要点。注意一个细节总分由代码侧对逐题分数求平均得出UnifiedEvaluationService.java#L523-L524而不是采纳模型返回的overallScore字段——评分口径永远握在确定性代码手里模型只负责逐题判断。配置速查表配置项默认值作用app.interview.evaluation.batch-size8每批评估题数app.interview.evaluation.fallback-split-enabledtrue批次失败二分重试总开关app.interview.evaluation.fallback-max-extra-calls6一次评估共享的额外调用预算app.interview.evaluation.fallback-min-groups2可二分的最小组数app.ai.structured-max-attempts2结构化输出最大尝试次数app.ai.structured-schema-validation-enabledtrueJSON Schema 校验开关前四项集中在 InterviewEvaluationProperties.java后两项集中在 StructuredOutputProperties.java均可通过application.yml覆盖。评估引擎在哪里被复用统一评估是一次建设、多处受益的典型文字面试AnswerEvaluationService.java 在面试结束后异步触发评估语音面试VoiceInterviewEvaluationService.java 复用同一引擎保证文字与语音两种形态的评分口径一致、结果可对比知识库专项面试交卷后同样走该引擎生成总分、逐题评价与改进建议。完整的行为验证见测试 UnifiedEvaluationServiceTest.java。写在最后可迁移的 4 条经验如果你的项目也有让 LLM 稳定产出结构化结果的需求这套方案可以直接借鉴长输入按语义单元分批而不是按字数硬切——追问、段落这类不可拆分单元要整体进入批次结构化输出 Schema 校验 受限重试 本地修复重试时把上次失败原因反馈给模型本地修复能省下不少 token对编号类字段双向设防提示词里明令禁止重新编号代码侧再做一致性校验与位置对齐降级必须显式系统失败就该标注系统降级绝不生成看起来合理的假分数——对用户诚实也给自己留了排查线索。想看完整实现可以从 UnifiedEvaluationService.java 的evaluate方法读起顺着分组 → 装批 → 调用 → 恢复 → 合并 → 汇总的主干走一遍大约两百行就能看清整个骨架。【免费下载链接】interview-guide基于 Spring Boot 4.1、Java 25、Spring AI 2.0、React、PostgreSQL/pgvector、Redis 和 RustFS 构建的开源 AI 面试平台支持简历智能分析、模拟面试、语音面试和知识库 RAG。项目地址: https://gitcode.com/gh_mirrors/inter/interview-guide创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考