LLM评估集自建,自动化评测RAG问答准确率脚本
很多人在部署RAG问答系统后,只凭几次人工回答判断效果,很难发现知识库覆盖盲区或检索排序问题。
自己建一套LLM评估集,再用脚本自动化评测RAG问答准确率,就能把“回答得好不好”变成可量化的分数,每次调整检索策略或提示词后都能快速回归测试。
本文会用最简单的方式,带你从零搭出这个评测流程。
先理清评估集要长什么样
自建评估集不是随便找几个问题,而是需要每个问题配一个“标准答案”或“判定依据”。
常见格式如下:
[
{
"question": "服务器的RAID卡电池报警怎么处理?",
"reference": "先检查日志确认电池状态,再尝试重新学习或更换电池。",
"type": "fact"
},
{
"question": "Nginx返回502时应该先看哪个日志?",
"reference": "先查看error.log和upstream日志",
"type": "fact"
}
]
其中reference不要求逐字匹配,而是用来判断模型答案是否包含这些关键信息。
建议先从日常运维中收集50到100个真实问题,覆盖文档的不同章节,避免只挑简单问题。
文件保存为eval_set.json,后续脚本直接读取。
用Python脚本批量调用RAG接口
评测脚本的核心是:把评估集里的每个问题发给你的RAG服务,拿到回答后,再让一个裁判LLM帮你判断回答是否命中标准答案。
下面是一个可直接运行的脚本框架:
import json
import requests
def load_questions(path):
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
def call_rag(question):
resp = requests.post(
"http://localhost:8080/rag/query",
json={"question": question},
timeout=30
)
return resp.json().get("answer", "")
def judge(question, answer, reference, eval_api):
prompt = f"""请判断以下回答是否覆盖了参考答案中的关键信息。
问题:{question}
模型回答:{answer}
参考答案:{reference}
只返回1或0,1表示覆盖,0表示未覆盖。"""
resp = requests.post(eval_api, json={"prompt": prompt})
return resp.json().get("answer", "0").strip()
if __name__ == "__main__":
eval_set = load_questions("eval_set.json")
correct = 0
results = []
for item in eval_set:
ans = call_rag(item["question"])
score = judge(item["question"], ans, item["reference"], "http://localhost:8081/v1/chat/completions")
correct += int(score)
results.append((item["question"], ans, score))
total = len(eval_set)
print(f"准确率: {correct/total*100:.2f}% ({correct}/{total})")
with open("eval_result.json", "w", encoding="utf-8") as f:
json.dump(results, f, ensure_ascii=False, indent=2)
这里eval_api是你用来当裁判的大模型接口,可以是同一个私有化模型,也可以是独立部署的评测模型。
如果不想用大模型当裁判,也可以改成字符匹配:把参考答案里的关键实体或数字提取出来,看看模型回答里是否包含。
把评测结果变成可读的回归报告
光输出一个准确率还不够,建议把每道题的详情保存成报告。
运行结束后,脚本会生成eval_result.json,打开就能看到哪些问题答偏了。
你还可以在脚本里加一段统计,按文档章节或问题类型分组计算准确率,这样能更快定位是哪块知识库内容质量差。
例如在读取评估集时增加category字段,然后统计每个category的准确率:
from collections import defaultdict
type_acc = defaultdict(list)
for item, score in zip(eval_set, results):
type_acc[item.get("category", "other")].append(int(score[2]))
for cat, scores in type_acc.items():
print(f"{cat}: {sum(scores)}/{len(scores)} = {sum(scores)/len(scores)*100:.1f}%")
几个容易踩坑的细节
裁判模型容易“放水”。
如果直接用同一个模型既做RAG回答又做裁判,它往往倾向于给自己的回答打高分。
建议裁判模型使用不同参数或不同温度,或者至少把temperature设为0。
参考答案不要写太长。
参考答案越长,裁判模型越容易判定“基本覆盖”,准确率虚高。
每个参考回答控制在30到50字,只保留关键事实。
RAG接口要稳定且超时。
批量跑几十个问题,任何一个请求超时都会中断流程。
建议在call_rag里增加异常捕获和重试:
try:
resp = requests.post(...)
except Exception:
time.sleep(2)
resp = requests.post(...)
评估集要定期更新。
知识库内容更新后,旧问题可能失效。
每次更新知识库后,建议同步增删评估集里的问题,避免用过期问题自欺欺人。
验证你的评测脚本真的可信
脚本跑通后,拿10个已知答案的问题人工核对一遍。
如果脚本判定正确的题目,人工也认为没问题,说明评测逻辑可用。
如果出现脚本说“覆盖”但人工明显觉得答非所问,就要调整裁判模型或改成更严格的判定条件,比如要求回答包含至少两个参考答案中的关键实体。
完成以上步骤后,你就拥有了一套可持续运行的RAG问答准确率评测流程。
以后每次调整检索参数、修改提示词或更换向量模型,都先跑一遍脚本,用准确率变化决定是否上线,比拍脑袋可靠得多。
遇到异常时,优先回头检查评估集格式和裁判模型返回结果,这两处是问题高发区。
如果你对自动化评测RAG问答准确率的脚本还有其他疑问,可以在本站继续浏览相关排错文章。