当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > Go 构建 AI 评测样本 CLI:校验 JSONL、抽样回放并生成通过率报告

Go 构建 AI 评测样本 CLI:校验 JSONL、抽样回放并生成通过率报告

来源:17golang原创 2026-07-27 11:01:47 0浏览 收藏
所属专题:AI 提示词工程与评测实践专题 - 从提示词设计、缓存命中到结构化输出与上线评测

模型迭代换版本、改系统提示词或是调整检索相关参数之后,大家很容易漏掉一个隐患:评测样本文件本身已经出问题了,比如某一行少了必填字段,另一行把数值型的评分误写成字符串类型,最后算出来的整体通过率看起来还挺规整,完全发现不了底层错误。这个小工具用Go读取JSONL格式样本,先逐条校验每条样本的输入和期望标签,再用固定随机种子抽取指定批量的样本回放,最后输出带明确失败原因的统计报告。

实践要点

  • 一行JSON对应一条评测样本,字段错误要在调用模型之前就暴露出来。
  • 固定种子抽样能让同一批样本支持重复回放,方便不同模型版本做横向对比。
  • 除了通过率之外保留case_id、规则名和原始输出,出问题才有可复查的入口。
  • 报告门槛只负责阻断明显的效果回落,不能代替人工校验和业务指标评估。

先把评测输入收敛成JSONL

评测文件放在 cases.jsonl,每一行都是独立的JSON对象。示例里的任务是判断客服消息是否需要转人工介入,工具本身不绑定任何模型供应商,只关心输入内容、期望标签和可选的元数据字段。

{"case_id":"refund-001","input":"订单重复扣款,想申请退款","expected":"human","meta":{"scene":"payment"}}
{"case_id":"password-002","input":"忘记密码怎么改","expected":"self_service","meta":{"scene":"account"}}

这里用 case_id 做样本的稳定唯一标识,用 expected 存储业务侧的期望值。不要把评分结果直接覆盖回原始样本文件,不然下次回放的时候很难区分原始输入和上次运行生成的产物。

JSONL 样本校验从逐行读取到字段通过和错误定位的工程证据插画

用Go写一个不依赖第三方库的校验器

标准库的 encoding/json.Decoder 完全能处理逐行JSON解析。为了把错误定位到具体文件位置,校验函数同时接收行号参数,直接拒绝空标识、空输入和不在枚举范围内的未知期望标签。

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "io"
    "os"
    "strings"
)

type Case struct {
    CaseID   string            `json:"case_id"`
    Input    string            `json:"input"`
    Expected string            `json:"expected"`
    Meta     map[string]string `json:"meta"`
}

var allowed = map[string]bool{"human": true, "self_service": true}

func readCases(path string) ([]Case, []string, error) {
    file, err := os.Open(path)
    if err != nil { return nil, nil, err }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    scanner.Buffer(make([]byte, 1024), 1024*1024)
    var cases []Case
    var problems []string
    line := 0
    for {
        line++
        if !scanner.Scan() {
            if err := scanner.Err(); err != nil { return nil, nil, err }
            break
        }
        raw := strings.TrimSpace(scanner.Text())
        if raw == "" { continue }
        var item Case
        if err := json.Unmarshal([]byte(raw), &item); err != nil {
            problems = append(problems, fmt.Sprintf("line %d: invalid json", line))
            continue
        }
        switch {
        case item.CaseID == "":
            problems = append(problems, fmt.Sprintf("line %d: missing case_id", line))
        case strings.TrimSpace(item.Input) == "":
            problems = append(problems, fmt.Sprintf("%s: empty input", item.CaseID))
        case !allowed[item.Expected]:
            problems = append(problems, fmt.Sprintf("%s: unsupported expected", item.CaseID))
        default:
            cases = append(cases, item)
        }
    }
    return cases, problems, nil
}

Scanner 默认的单行长度上限很小,而评测样本里经常会带一段很长的上下文内容,这里直接显式调高原生上限。校验失败的行不会进入后续的抽样流程,这样报告里的统计分母只代表结构完全合法的样本,样本文件本身的格式错误会单独列出来提示。

固定种子抽样,保证回放结果可横向对比

全量跑一遍评测的成本太高的时候,可以先抽部分样本快速验证,但随机抽样的结果必须是可复现的。命令行接收 -seed-limit 两个参数,同一份JSONL文件、同一个种子、同一个抽样上限,最后得到的case_id顺序完全一致。

func pick(cases []Case, limit int, seed int64) []Case {
    if limit  len(cases) { limit = len(cases) }
    r := rand.New(rand.NewSource(seed))
    order := r.Perm(len(cases))
    picked := make([]Case, 0, limit)
    for _, index := range order[:limit] {
        picked = append(picked, cases[index])
    }
    sort.Slice(picked, func(i, j int) bool { return picked[i].CaseID 

抽样完成后按 case_id 排序,是为了让报告做差异对比的时候更稳定。随机种子只用来决定选哪些样本,不要把业务规则逻辑塞到种子生成逻辑里;如果样本的场景分布明显不均衡,应该在文件里新增scene分层字段,再按不同分类分别抽取样本。

把模型回放和规则判定拆开

真实项目里可以把回放器直接接到内部的模型网关。为了让示例代码可以完全离线运行,这里用一个占位接口返回模型结果,接口只输出标签和原始返回文本,判定器再独立检查标签是否和期望一致、输出内容是否为空。

type Result struct {
    Label string `json:"label"`
    Text  string `json:"text"`
}

type Checker struct{}

func (Checker) Check(c Case, got Result) (bool, string) {
    if strings.TrimSpace(got.Text) == "" { return false, "empty_output" }
    if got.Label != c.Expected { return false, "label_mismatch" }
    return true, "ok"
}

拆分成两个独立模块之后,换成HTTP客户端拉取、离线假数据或者之前录制的历史响应,都不会影响样本校验的逻辑。生产环境跑回放的时候还要设置连接超时和整体任务超时,记录请求批次、模型版本和响应耗时,不要把完整的用户隐私原文直接写到公开的报告里。

输出通过率和可复查的失败明细

报告至少要统计四个核心数值:合法样本总数、抽样样本数、通过数、失败数。失败项里必须附带 case_id 和对应规则名,不能只笼统显示“失败3条”。一个结构紧凑的报告示例如下:

{
  "seed": 20260727,
  "sampled": 20,
  "passed": 18,
  "failed": 2,
  "pass_rate": 0.9,
  "failures": [
    {"case_id":"refund-014","rule":"label_mismatch"},
    {"case_id":"account-008","rule":"empty_output"}
  ]
}

门禁逻辑可以设置成 pass_rate 的时候返回非零退出状态,让CI直接终止后续的发布流程;阈值本身不是绝对的质量标准,只是用来把效果明显回落的版本挡在发布环节外面。少量高风险场景的样本还可以单独配置为“必须全部通过”,避免平均值掩盖支付、权限这类关键场景的问题。

AI 评测报告展示通过率、失败 case_id 与回归门禁的前后对比插画

本地运行与上线前检查

命令行入口逻辑可以保持得非常简单:

go run . -input cases.jsonl -limit 20 -seed 20260727 -report report.json
cat report.json
go test ./...

工具做完验收的时候重点核对三件事:格式错误的坏JSON能不能指出准确的行号;完全相同的参数重复运行能不能得到完全一致的case_id序列;出现不通过的报告的时候进程能不能返回非零状态。上线前再把模型版本、提示词版本、样本集版本都写到报告元数据里,后续出问题才有依据定位这次效果变动到底是改了哪部分内容导致的。

常见问题:AI 评测 CLI 怎么避免误判

为什么不直接用平均分做发布门禁?

平均分会直接掩盖少数关键场景的失败。支付、权限、合规类的样本更适合单独设置硬门槛,剩下的普通样本再参考总体通过率。

抽样种子应该每次自动变化吗?

回归对比阶段建议固定种子,保证前后两次运行的样本池完全一致;需要扩大评测覆盖范围的时候再按计划轮换种子,并且把种子值写到报告里存档,不能让随机生成的不可控结果变成发布的判断依据。

模型输出要不要全部保存?

调试阶段可以保存脱敏之后的原始输出。正式环境至少保留输出摘要、规则判定结果和必要的trace_id,原始文本的保存期限和访问权限要符合对应的业务合规要求。

JSONL 文件很大时怎么处理?

保持逐行扫描的读取逻辑,不要一次性把整个文件加载到内存里;同时设置单行内容的长度上限,超长的上下文可以改成外部引用或是单独的样本附件存储。

小结

这个CLI的价值不在于替代人工的模型评审,而在于把整个评测流程固定下来:输入先做校验,抽样可重复回放,结果附带规则名,门禁返回标准退出状态。后续样本量和业务复杂度上升之后,再接真正的模型网关、分层抽样逻辑和人工复核流程,也完全可以沿用这四个核心边界。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 接入模型流式输出后客户端断开怎么办:取消上游请求并清理 goroutineGo 接入模型流式输出后客户端断开怎么办:取消上游请求并清理 goroutine
上一篇
Go 接入模型流式输出后客户端断开怎么办:取消上游请求并清理 goroutine
Go json.Decoder 连续 JSON 怎么读:Decode 循环、EOF 与尾部数据校验
下一篇
Go json.Decoder 连续 JSON 怎么读:Decode 循环、EOF 与尾部数据校验
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    98次使用
  • OpenCompass大模型评测体系详解:功能、使用指南与应用场景
    OpenCompass
    OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
    28次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    252次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    180次使用
  • AI Prompt Library:免费AI提示词库,助力ChatGPT高效创作与营销
    AI Prompt Library
    探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
    113次使用