使用Evals API评估音频输入:OpenAI音频评估指南
技术背景
随着多模态AI的发展,音频交互在客户支持、语音助手等场景中日益重要。传统评估方法需将音频转录为文本,丢失了语气、情感等关键信息,无法准确反映真实交互质量。OpenAI Evals API原生支持音频输入,允许直接使用原始音频进行采样和评分,更贴近实际应用。本教程旨在帮助开发者掌握这一新能力,构建高效的音频评估流程。
核心原理
Evals API评估音频任务的核心流程包括:1) 数据准备:将音频文件编码为base64字符串,构建符合item_schema的数据源;2) 采样:使用支持音频输入的模型(如gpt-audio)根据用户消息生成响应,响应包含音频和文本转录;3) 评分:通过评分器(如score_model或string_check)对采样响应与参考答案进行比对,输出0-1的分数;4) 运行与结果分析:创建评估运行,轮询状态,获取输出项并分析结果。
架构分析
系统架构分为数据层、评估层和结果层。数据层负责音频数据的加载、编码和上传,使用Hugging Face数据集和OpenAI Files API。评估层包含评估对象(Eval)和运行(Run),评估对象定义数据源配置和测试标准(评分器),运行指定采样模型、输入消息模板和采样参数。结果层通过输出项接口获取每个样本的模型响应和评分结果,支持DataFrame展示和详细查看。关键设计决策包括:使用custom数据源类型以支持自定义item_schema;采样使用completions API而非responses API,因为后者不支持音频输入;评分器使用音频数据而非文本转录,以保留音频特征。
完整实战教程
Evals API: 音频输入
本教程演示如何使用OpenAI的Evals框架进行基于音频的任务。利用Evals API,我们将通过采样生成模型对音频消息和提示的响应,并使用模型评分对模型响应与输出音频和参考答案进行评分。请注意,评分将针对采样响应的音频输出。
在添加音频支持之前,要评估音频对话,首先需要将其转录为文本。现在,您可以使用原始音频,并从模型获取音频样本。这更准确地代表了诸如客户支持场景的工作流程,其中用户和代理都使用音频。对于评分,我们将使用音频模型对音频响应进行评分。我们也可以(或结合)使用采样音频的文本转录,并利用现有的文本评分器套件。
在此示例中,我们将评估我们的模型在以下方面的表现:
- 生成适当的响应:针对用户关于音频消息的提示
- 与参考答案对齐:代表高质量响应的参考答案
安装依赖 + 环境设置
# Install required packages
%pip install openai datasets pandas soundfile torch torchcodec pydub jiwer --quiet
# Import libraries
from datasets import load_dataset, Audio
from openai import OpenAI
import base64
import os
import json
import time
import io
import soundfile as sf
import numpy as np
import pandas as pd
数据集准备
我们使用托管在 Hugging Face 上的 big_bench_audio 数据集。Big Bench Audio 是 Big Bench Hard 问题子集的音频版本。该数据集可用于评估支持音频输入的模型的推理能力。它包含描述逻辑问题的音频片段、类别和官方答案。
dataset = load_dataset("ArtificialAnalysis/big_bench_audio")
# Ensure audio column is decoded into a dict with 'array' and 'sampling_rate'
dataset = dataset.cast_column("audio", Audio(decode=True))
我们提取相关字段,并将其放入类似 JSON 的格式中,作为 Evals API 的数据源传入。输入音频数据必须是 base64 编码的字符串。我们处理音频文件中的数据并将其转换为 base64。
注意:音频模型目前支持 WAV、MP3、FLAC、Opus 或 PCM16 格式。有关详细信息,请参阅音频输入。
# Audio helpers: file/array to base64
def get_base64(audio_path_or_datauri: str) -> str:
if audio_path_or_datauri.startswith("data:"):
# Already base64, just strip prefix
return audio_path_or_datauri.split(",", 1)[1]
else:
# It's a real file path
with open(audio_path_or_datauri, "rb") as f:
return base64.b64encode(f.read()).decode("ascii")
def audio_to_base64(audio_val) -> str:
"""
Accepts various Hugging Face audio representations and returns base64-encoded WAV bytes (no data: prefix).
Handles:
- dict or mapping-like with 'path'
- decoded dict with 'array' and 'sampling_rate'
- torchcodec AudioDecoder (mapping-like access via ['path'] or ['array'])
- raw bytes
"""
# Try to get a file path first
try:
path = None
if isinstance(audio_val, dict) and "path" in audio_val:
path = audio_val["path"]
else:
# Mapping-like access
try:
path = audio_val["path"] # works for many decoder objects
except Exception:
path = getattr(audio_val, "path", None)
if isinstance(path, str) and os.path.exists(path):
with open(path, "rb") as f:
return base64.b64encode(f.read()).decode("ascii")
except Exception:
pass
# Fallback: use array + sampling_rate and render to WAV in-memory
try:
array = None
sampling_rate = None
try:
array = audio_val["array"]
sampling_rate = audio_val["sampling_rate"]
except Exception:
array = getattr(audio_val, "array", None)
sampling_rate = getattr(audio_val, "sampling_rate", None)
if array is not None and sampling_rate is not None:
audio_np = np.array(array)
buf = io.BytesIO()
sf.write(buf, audio_np, int(sampling_rate), format="WAV")
return base64.b64encode(buf.getvalue()).decode("ascii")
except Exception:
pass
if isinstance(audio_val, (bytes, bytearray)):
return base64.b64encode(audio_val).decode("ascii")
raise ValueError("Unsupported audio value; could not convert to base64")
evals_data_source = []
audio_base64 = None
# Will use the first 3 examples for testing
for example in dataset["train"].select(range(3)):
audio_val = example["audio"]
try:
audio_base64 = audio_to_base64(audio_val)
except Exception as e:
print(f"Warning: could not encode audio for id={example['id']}: {e}")
audio_base64 = None
evals_data_source.append({
"item": {
"id": example["id"],
"category": example["category"],
"official_answer": example["official_answer"],
"audio_base64": audio_base64
}
})
如果你打印数据源列表,每个条目应该类似于:
{
"item": {
"id": 0
"category": "formal_fallacies"
"official_answer": "invalid"
"audio_base64": "UklGRjrODwBXQVZFZm10IBAAAAABAAEAIlYAAESsA..."
}
}
评估配置
现在我们已经有了数据源和任务,我们将创建我们的评估。有关 OpenAI Evals API 文档,请访问 API 文档。
client = OpenAI(
api_key=os.getenv("OPENAI_API_KEY")
)
由于音频输入较大,我们需要将示例保存到文件中并上传到API。
# Save the examples to a file
file_name = "evals_data_source.json"
with open(file_name, "w", encoding="utf-8") as f:
for obj in evals_data_source:
f.write(json.dumps(obj, ensure_ascii=False) + "\n")
# Upload the file to the API
file = client.files.create(
file=open(file_name, "rb"),
purpose="evals"
)
评估包含两个部分:“评估”和“运行”。在“评估”中,我们定义数据的预期结构和测试标准(评分器)。
数据源配置
根据我们整理的数据,数据源配置如下:
data_source_config = {
"type": "custom",
"item_schema": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"category": { "type": "string" },
"official_answer": { "type": "string" },
"audio_base64": { "type": "string" }
},
"required": ["id", "category", "official_answer", "audio_base64"]
},
"include_sample_schema": True, # enables sampling
}
测试标准
对于我们的测试标准,我们设置了评分器配置。在这个例子中,我们使用一个 score_model 评分器,它接收官方答案和采样模型响应(在 sample 命名空间中),然后根据模型响应是否与官方答案匹配输出 0 或 1 的分数。响应包含音频和音频的文本转录。我们将在评分器中使用音频。有关评分器的更多信息,请访问 API Grader 文档。
正确获取数据和评分器对于有效评估至关重要。您可能希望迭代优化评分器的提示。
grader_config = {
"type": "score_model",
"name": "Reference answer audio model grader",
"model": "gpt-audio",
"input": [
{
"role": "system",
"content": 'You are a helpful assistant that evaluates audio clips to judge whether they match a provided reference answer. The audio clip is the model''s response to the question. Respond ONLY with a single JSON object matching: {"steps":[{"description":"string","conclusion":"string"}],"result":number}. Do not include any extra text. result must be a float in [0.0, 1.0].'
},
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "Evaluate this audio clip to see if it reaches the same conclusion as the reference answer. Reference answer: {{item.official_answer}}",
},
{
"type": "input_audio",
"input_audio": {
"data": "{{ sample.output_audio.data }}",
"format": "wav",
},
},
],
},
],
"range": [0, 1],
"pass_threshold": 0.6,
}
或者,我们可以使用一个字符串检查评分器,它接收官方答案和采样模型响应(在 sample 命名空间中),然后根据模型响应是否包含参考答案输出0到1之间的分数。响应包含音频和音频的文本转录。我们将在评分器中使用文本转录。
grader_config = {
"type": "string_check",
"name": "String check grader",
"input": "{{sample.output_text}}",
"reference": "{{item.official_answer}}",
"operation": "ilike"
}
现在,我们创建 eval 对象。
eval_object = client.evals.create(
name="Audio Grading Cookbook",
data_source_config=data_source_config,
testing_criteria=[grader_config],
)
Eval 运行
为了创建运行,我们传入评估对象ID、数据源(即我们之前编译的数据),以及用于采样生成模型响应的聊天消息输入。
以下是我们在此示例中使用的采样消息输入。
sampling_messages = [
{
"role": "system",
"content": "You are a helpful and obedient assistant that can answer questions with audio input. You will be given an audio input containing a question to answer."
},
{
"role": "user",
"type": "message",
"content": {
"type": "input_text",
"text": "Answer the following question by replying with brief reasoning statements and a conclusion with a single word answer: 'valid' or 'invalid'."
}
},
{
"role": "user",
"type": "message",
"content": {
"type": "input_audio",
"input_audio": {
"data": "{{ item.audio_base64 }}",
"format": "wav"
}
}
}]
现在我们启动一次评估运行。
eval_run = client.evals.runs.create(
name="Audio Input Eval Run",
eval_id=eval_object.id,
data_source={
"type": "completions", # sample using completions API; responses API is not supported for audio inputs
"source": {
"type": "file_id",
"id": file.id
},
"model": "gpt-audio", # model used to generate the response; check that the model you use supports audio inputs
"sampling_params": {
"temperature": 0.0,
},
"input_messages": {
"type": "template",
"template": sampling_messages},
"modalities": ["audio", "text"],
},
)
轮询并显示结果
当运行结束时,我们可以查看结果。你也可以查看你所在组织的 OpenAI Evals 仪表板,以查看进度和结果。
while True:
run = client.evals.runs.retrieve(run_id=eval_run.id, eval_id=eval_object.id)
if run.status == "completed":
output_items = list(client.evals.runs.output_items.list(
run_id=run.id, eval_id=eval_object.id
))
df = pd.DataFrame({
"id": [item.datasource_item["id"]for item in output_items],
"category": [item.datasource_item["category"] for item in output_items],
"official_answer": [item.datasource_item["official_answer"] for item in output_items],
"model_response": [item.sample.output[0].content for item in output_items],
"grading_results": ["passed" if item.results[0]["passed"] else "failed"
for item in output_items]
})
display(df)
break
if run.status == "failed":
print(run.error)
break
time.sleep(5)
查看单个输出项
要查看完整的输出项,我们可以执行以下操作。输出项的结构在 API 文档中此处有详细说明。
first_item = output_items[0]
print(json.dumps(dict(first_item), indent=2, default=str))
结论
在本教程中,我们介绍了使用 OpenAI Evals API 评估模型原生音频输入的工作流程。我们演示了使用评分模型评分器对音频响应进行评分。
后续步骤
- 将此示例转换为您自己的用例。
- 如果您有较大的音频片段,请尝试使用 uploads API 以支持高达 8 GB 的文件。
- 导航到 Evals 仪表板 以可视化输出并深入了解评估的性能。
常见问题
常见错误:1) 音频编码失败:确保音频格式为WAV、MP3等支持格式,且audio_to_base64函数正确处理不同输入类型;2) 文件上传失败:检查文件大小限制,大文件使用uploads API;3) 评分器输出格式错误:确保评分器严格输出JSON对象,包含steps和result字段;4) 运行状态卡住:检查网络连接和API密钥,轮询间隔适当;5) 模型不支持音频:确认所选模型(如gpt-audio)支持音频输入。排查方法:查看运行错误信息,检查数据源格式,使用小样本测试,逐步调试评分器提示。
企业应用扩展
生产环境优化:1) 使用异步批处理提高吞吐量;2) 对音频进行预处理(降噪、归一化)提升模型效果;3) 设计更精细的评分器,如多维度评分(内容、语气、流畅度);4) 结合文本转录评分器进行交叉验证;5) 利用Evals仪表板监控评估性能,定期回归测试;6) 对于大规模评估,使用分布式运行或分片上传。
总结
本教程面向有编程基础的开发者,深入讲解如何使用OpenAI Evals API评估音频输入任务。通过采样生成模型对音频的响应,并使用模型评分器对音频输出进行评分,实现端到端的音频评估流程。文章涵盖环境搭建、数据集准备、base64编码、评估配置、运行与结果分析,并提供了常见错误排查与生产优化建议,帮助开发者构建可靠的音频AI评估系统。
- --
内容来源与验证
- 内容等级:PREMIUM_A
- 质量等级:A
- 难度:中级
- 验证等级:V1
- 原文作者:hendrytl
- 原文日期:2025-08-13
- 来源许可:MIT
- 来源版本:
88f048232037afe5319ed577aa90e9f309d17948 - 查看原文
站内还有更多完整实战教程
加入会员,解锁全部教程、完整代码与持续更新内容。
加入会员