Whisper 与 VibeVoice 单引擎识别
选择单个 ASR 引擎,复用相同上传、任务轮询和下载接口。
三个入口已完成生产调用、结果下载与实际计费核验;使用现有 API Key 即可调用。
选择模型
| 模型 ID | 运行引擎 | 结果 | 每音频分钟 | 单任务预授权上限 |
|---|---|---|---|---|
wildflow/whisper-asr-v1 | faster-whisper large-v3 | 文本与段级时间戳;不提供说话人分离 | ¥0.02 | ¥2.40 |
wildflow/vibevoice-asr-v1 | VibeVoice-ASR | 段级正文与时间戳,保留实际输出的说话人标签;不执行 Whisper | ¥0.04 | ¥4.80 |
需要同时保留两份结果时,继续使用 双引擎识别,价格为 ¥0.05 / 音频分钟。 单引擎入口不会顺带运行另一个引擎。三个入口共享执行池,不是每个入口各有 8 路独立容量。
快速调用
使用已有普通 API Key,不需要重新注册或申请专用 Key。主动限制 Key 模型范围时,应包含所选模型。
按音频准备和上传输入完成前两步,
取得 INPUT_ARTIFACT_ID。每条输入仍为一个 FLAC 文件,最多 120 分钟、256 MiB。
下面以 Whisper 为例;切换 VibeVoice 只改 WILDFLOW_ASR_MODEL,接口路径和请求字段不变。
切换模型是新任务,必须使用新的幂等键;仅重试原任务时保留原键和请求正文。
export WILDFLOW_ASR_MODEL="wildflow/whisper-asr-v1"
export WILDFLOW_IDEMPOTENCY_KEY="$(python3 -c 'import uuid; print(uuid.uuid4())')"
python3 - "$WILDFLOW_ASR_MODEL" "$INPUT_ARTIFACT_ID" > asr-request.json <<'PY'
import json
import sys
print(json.dumps({
"model": sys.argv[1],
"input_artifact_ids": [sys.argv[2]],
"parameters": {"language": "zh"}
}, ensure_ascii=False))
PY
curl --fail-with-body --silent --show-error \
-X POST https://api.wildflow.cn/v1/jobs \
-H "Authorization: Bearer $WILDFLOW_API_KEY" \
-H "Idempotency-Key: $WILDFLOW_IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
--data-binary @asr-request.json --output operation.json
export RESULT_FILE="single-asr-result.json"
export RESULT_MEDIA_TYPE="application/json"下面的命令使用 Python 3,读取上一步保存的 operation.json,等待任务完成后下载结果。等待超过两小时会停止;再次执行本段会继续查询原任务,不会重新提交。
python3 - <<'PY'
import hashlib
import json
import os
import time
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from pathlib import Path
from urllib.error import HTTPError
from urllib.parse import quote
from urllib.request import Request, urlopen
base = "https://api.wildflow.cn"
headers = {"Authorization": "Bearer " + os.environ["WILDFLOW_API_KEY"]}
operation = json.loads(Path("operation.json").read_text())
operation_id = operation["id"]
status_url = base + "/v1/jobs/" + quote(operation_id, safe="")
output = Path(os.environ["RESULT_FILE"])
expected_media_type = os.environ["RESULT_MEDIA_TYPE"]
deadline = time.monotonic() + 7200
def pause(value):
try:
seconds = float(value)
except (TypeError, ValueError):
try:
seconds = (parsedate_to_datetime(value) - datetime.now(timezone.utc)).total_seconds()
except (TypeError, ValueError, OverflowError):
seconds = 3
seconds = max(1, seconds)
if time.monotonic() + seconds >= deadline:
raise SystemExit("等待超时。保留 operation.json,稍后重新执行本段继续查询。")
time.sleep(seconds)
while time.monotonic() < deadline:
try:
with urlopen(Request(status_url, headers=headers), timeout=60) as response:
status = json.load(response)
retry_after = response.headers.get("Retry-After", "3")
except HTTPError as error:
if error.code in (429, 503):
pause(error.headers.get("Retry-After", "3"))
continue
raise SystemExit(f"查询失败:HTTP {error.code}。保留原任务,检查错误后继续查询。")
state = status["state"]
print("任务状态:", state, flush=True)
if state == "succeeded":
artifacts = status.get("artifacts", [])
if not artifacts:
raise SystemExit("任务成功但缺少结果文件,请保留任务 ID 并联系支持。")
artifact = artifacts[0]
if artifact["media_type"] != expected_media_type:
raise SystemExit("结果格式与预期不同,请检查任务和模型。")
download_url = base + "/v1/artifacts/" + quote(artifact["id"], safe="") + "/content"
temporary = output.with_name(output.name + ".part")
digest = hashlib.sha256()
size = 0
with urlopen(Request(download_url, headers=headers), timeout=60) as response:
with temporary.open("wb") as file:
while chunk := response.read(1024 * 1024):
file.write(chunk)
digest.update(chunk)
size += len(chunk)
if size != artifact["size_bytes"] or digest.hexdigest() != artifact["sha256"]:
raise SystemExit("文件校验失败,保留原任务并重新执行本段下载。")
temporary.replace(output)
print("已保存:", output)
break
if state not in ("queued", "running", "submitting"):
raise SystemExit(f"任务已停止:{state}。请保留任务 ID;不要换幂等键重复提交。")
pause(retry_after)
else:
raise SystemExit("等待超时。保留 operation.json,稍后继续查询原任务。")
PY网络中断时可重新执行本段。failed、cancelled、recovery_required 或未知状态会停止下载;需要帮助时查看任务恢复说明。
参数与结果
language:默认zh;Whisper 使用该语言,VibeVoice 根据音频识别语言,不保证此参数可强制其语言。context、hotwords:兼容接收,但当前 HTTP 执行路径未传递给引擎,不承诺热词增强效果。source_offset_seconds:仅记录在timeline中,消费方应自行叠加到片段时间,不会改写结果中的时间值。- 不接收客户端
asr_engine参数;请使用model选择产品。
结果保留与双引擎相同的 schema_version、source_artifact_id、timeline 和 engines 外层结构,
但 engines 中只出现所选引擎:
| 所选模型 | 读取正文 | 额外信息 |
|---|---|---|
| Whisper | engines.word_timestamps.segments[].text | 当前提供 start / end 段级时间戳 |
| VibeVoice | engines.segment_text.segments[].text | 段级时间戳及模型输出的说话人标签 |
输出 model_id 和 model_revision 标识实际引擎。Whisper 固定为 Systran/faster-whisper-large-v3
(edaa852ec7e145841d8ffdb056a99866b5f0a478);VibeVoice 固定为 microsoft/VibeVoice-ASR
(d0c9efdb8d614685062c04425d91e01b6f37d944)。不承诺两种引擎文字一致。
word_timestamps 是保留的历史角色名;当前生产响应未提供 segments[].words,不能据此依赖逐词时间戳。
VibeVoice 的 segments[].speaker 仅在模型输出说话人标签时存在,标签不等于真实身份识别。
费用与批量任务
按实际音频毫秒计量,不把不足一分钟的音频按整分钟收费。成功结算后退回预授权差额; 失败退款和未知结果处理沿用任务恢复规则。 例如 180 小时分别为 Whisper ¥216、VibeVoice ¥432、双引擎 ¥540(不含重建新任务产生的新费用)。 分段推理不会对同一任务重复收取音频时长费用。
三个入口共用现有队列与执行容量,多余请求排队。新任务排队与执行期限为 14 天,不是完成时延保证。 本次以两段音频分别调用三个入口,六个任务全部成功、下载和幂等重放通过;部分任务排队或自动重试。 这不是独占机器吞吐基准,不把双引擎测试成绩当成单引擎性能承诺。批量提交前确保余额足够覆盖所有待处理任务的预授权。
ASR 可能漏字、错字或产生时间戳偏差,专有名词、数字和说话人标注须人工复核;不提供实时流式识别。 更多计费规则见价格与计费。