摘要:很多 AI 应用刚开始效果不错,真正接入业务后却反复出现“昨天能答,今天答错”“换个模型全线漂移”“Prompt 改了一行没人知道影响多大”。本文从 Java 后端视角,讲一套最小可用的 AI 评测体系:样本怎么建、指标怎么定、Spring Boot 怎么跑、结果怎么落库、上线前怎么卡口。
目录
- 为什么 AI 应用一定要做评测
- 一套最小可用评测体系长什么样
- 测试集:不要一上来追求大而全
- 指标:准确率之外还要看什么
- Java 侧如何组织评测代码
- RAG 和 Agent 场景怎么评
- 上线前的评测卡口
- 总结
1. 为什么不是继续调 Prompt
我一开始接触大模型应用时,也容易把问题归因到 Prompt:回答不稳定,改 Prompt;格式不对,改 Prompt;知识库召回差,还是改 Prompt。后来发现这种方式最大的问题不是“没用”,而是不可复现。
比如下面这些问题,靠感觉很难回答:
| 问题 | 没有评测时的状态 | 有评测后的状态 |
|---|---|---|
| Prompt 改了有没有变好 | 看几条样例,凭印象 | 跑固定样本,看指标变化 |
| 模型升级能不能上线 | 手动试几个问题 | 同一批样本对比新旧模型 |
| RAG 召回是不是拖后腿 | 只看最终回答 | 拆开看召回、引用、答案 |
| 线上投诉能不能复盘 | 翻日志找 prompt | 用 trace 重放当时请求 |
大模型应用和传统 CRUD 最大的不同是:传统代码的输出大多是确定的,而大模型输出天然有随机性。既然输出不稳定,就更需要一个稳定的尺子。
2. 最小可用评测体系
先别把评测平台想复杂。真正落地时,第一版只需要四个东西:
对应到工程里,可以拆成:
| 模块 | 作用 | 第一版建议 |
|---|---|---|
| eval_cases | 存问题、标准答案、标签 | JSON/YAML 文件即可 |
| model_runner | 调用当前模型或 RAG 服务 | 复用线上 service |
| scorer | 评分 | 规则评分 + 少量人工复核 |
| report | 输出报告 | Markdown/CSV 都行 |
这里的关键不是平台多漂亮,而是每次变更都用同一批样本跑一遍。只要样本固定,模型、Prompt、召回策略、参数变更就都有了可对比基础。
3. 测试集怎么建
测试集不要从“我要收集一万条问题”开始。个人项目或团队内部工具,先做 50-100 条高质量样本更现实。
我一般按这几类建:
| 类型 | 示例 | 目的 |
|---|---|---|
| 高频问题 | 用户每天都问的配置、流程、报错 | 保证基础体验 |
| 边界问题 | 文档里没有、信息不足、时间冲突 | 看模型会不会瞎编 |
| 反问场景 | 缺少设备编号、缺少日志 | 看是否能要求补充信息 |
| 权限场景 | 查询别人项目、导出敏感数据 | 看是否越权 |
| 格式场景 | 必须输出 JSON、表格、工单字段 | 看结构化稳定性 |
一个样本不需要很复杂,可以这样写:
{"id":"rag_001","tags":["rag","high_frequency"],"question":"知识库里有多份版本文档时,系统应该优先参考哪一份?","expected_keywords":["最新版本","生效时间","版本号"],"must_not_include":["随便","无法判断但强行回答"],"expected_behavior":"应该说明按版本号或发布时间优先,并在冲突时提示人工确认"}如果你做的是故障诊断类 AI,还可以按业务标签补一层:
| 标签 | 说明 |
|---|---|
| telemetry | 遥测数据解释 |
| alarm | 告警含义判断 |
| diagnosis | 故障原因分析 |
| operation | 操作建议 |
| safety | 高风险动作和人工确认 |
样本的核心不是“像考试题”,而是覆盖真实业务里最容易出错的地方。
4. 指标怎么定
AI 评测最容易误入一个坑:只看最终答案对不对。但在 RAG、Agent、工具调用场景里,错可能发生在多个环节。
建议至少拆成 5 个指标:
| 指标 | 看什么 | 评分方式 |
|---|---|---|
| answer_score | 最终回答是否解决问题 | 0/1 或 1-5 分 |
| citation_score | 是否引用正确资料 | 命中文档 ID |
| format_score | 是否满足 JSON/表格/字段要求 | Schema 校验 |
| safety_score | 是否拒绝危险操作 | 规则判断 |
| latency_ms | 是否慢到不可用 | 统计耗时 |
第一版评分器不用上来就搞“模型评模型”。能规则判断的先规则判断。比如 JSON 格式、必须包含字段、禁止出现敏感词,这些都应该用代码判断。
5. Java 里怎么跑
可以建一个简单的评测 runner,核心逻辑只有三步:读样本、调用服务、打分。
publicrecordEvalCase(Stringid,List<String>tags,Stringquestion,List<String>expectedKeywords,List<String>mustNotInclude,StringexpectedBehavior){}publicrecordEvalResult(Stringid,booleanpassed,intkeywordHits,longlatencyMs,Stringanswer){}评分逻辑先写朴素版本:
publicEvalResultrunCase(EvalCaseevalCase){longstart=System.currentTimeMillis();Stringanswer=aiService.ask(evalCase.question());longlatency=System.currentTimeMillis()-start;inthits=0;for(Stringkeyword:evalCase.expectedKeywords()){if(answer.contains(keyword)){hits++;}}booleanhasForbidden=evalCase.mustNotInclude().stream().anyMatch(answer::contains);booleanpassed=hits>=Math.max(1,evalCase.expectedKeywords().size()/2)&&!hasForbidden&&latency<10_000;returnnewEvalResult(evalCase.id(),passed,hits,latency,answer);}这段代码很简单,但已经能解决一个大问题:当你改 Prompt、换模型、调整知识库切分方式时,能立刻知道有没有伤到已有能力。
6. 报告怎么输出
报告不要只输出一堆日志。最好能一眼看出结论:
## 本次评测结果 - 总样本数:80 - 通过:68 - 失败:12 - 通过率:85% - 平均耗时:1860ms - P95 耗时:4200ms ## 失败样本 TOP | ID | 标签 | 原因 | |---|---|---| | rag_017 | rag | 未引用最新版本文档 | | json_004 | format | JSON 字段缺失 | | safety_002 | safety | 未提示人工确认 |真正有价值的是失败样本。成功样本只说明“当前没坏”,失败样本才告诉你下一轮改哪里。
7. RAG 场景要单独评召回
如果最终答案错了,不一定是模型的问题。很多时候是召回阶段已经把错误文档塞给了模型。
RAG 评测建议额外记录:
| 字段 | 说明 |
|---|---|
| expected_doc_ids | 这道题应该命中的文档 |
| retrieved_doc_ids | 实际召回文档 |
| top_k | 召回数量 |
| rerank_score | 重排后的分数 |
| answer_citations | 答案实际引用的资料 |
只要把召回记录下来,问题会清楚很多:
- 召回没命中:先调切分、Embedding、过滤条件
- 召回命中了但排序靠后:看 rerank
- 召回正确但答案错:再看 Prompt 或模型
- 答案正确但没引用:补引用约束和输出格式
这比“模型不行”四个字有用得多。
8. Agent 场景要评工具调用
Agent 不是只看最后一句话。它中间会计划、调用工具、读返回、继续决策,所以评测对象也要变成轨迹。
| 阶段 | 应该检查什么 |
|---|---|
| plan | 是否拆出合理步骤 |
| tool_call | 是否调用正确工具 |
| args | 参数是否完整、是否越权 |
| observation | 是否正确理解工具返回 |
| final | 是否给出可执行结论 |
比如一个“生成故障工单处理建议”的 Agent,不能只评最终建议,还要看它有没有查设备、有没有查历史告警、有没有在高风险操作前要求人工确认。
9. 上线卡口
我建议把评测接到 CI 或发布脚本里,但第一版不用太重。可以先约定:
| 变更类型 | 是否必须跑评测 |
|---|---|
| 改 Prompt | 必须 |
| 换模型 | 必须 |
| 改知识库切分 | 必须 |
| 改召回 topK | 必须 |
| 改 UI 文案 | 可选 |
上线门槛也别一开始定得太复杂:
核心样本通过率 >= 95% 全量样本通过率 >= 85% 安全样本必须 100% 通过 P95 延迟不能超过线上阈值 失败样本必须有人确认安全类样本不能用平均分掩盖。只要涉及权限、删除、导出、执行命令、生产操作,就应该一票否决。
10. 写给 Java 程序员的落地建议
如果你现在正在做 AI 应用,我建议按这个顺序来:
- 先从线上真实问题里整理 30 条样本。
- 给每条样本打标签,不要混在一起。
- 写一个最简单的 Java runner,能批量调用当前服务。
- 先用关键词、禁用词、JSON Schema 做规则评分。
- 每次改 Prompt 或模型前后都跑一次。
- 把失败样本沉淀回测试集。
这套东西不花哨,但它会让 AI 应用从“玄学调参”变成“工程迭代”。
总结
Prompt 很重要,但只调 Prompt 不够。AI 应用真正进入业务后,评测体系才是底座。它不需要一开始就很大,也不需要第一天就做成平台。固定测试集、统一调用入口、规则评分、评测报告,这四件事先跑起来,就已经能挡住很多线上问题。
对 Java 后端来说,最现实的做法不是追最新框架,而是把大模型调用纳入熟悉的软件工程流程:有样本、有指标、有回归、有上线门禁。这样你才能知道每一次改动到底让系统变好了,还是只是看起来更会说话了。