双引擎语音识别
上传 FLAC 音频,同时获得 VibeVoice 与 faster-whisper 的转写文本和段级时间戳。
wildflow/dual-asr-v1 是野生流动的通用语音识别模型。一次任务运行两个固定引擎,并返回一个可下载、
可校验的 JSON Artifact:
microsoft/VibeVoice-ASR负责段级正文、说话人和非语音标签;Systran/faster-whisper-large-v3提供另一份转写与段级时间戳。
本地电脑不需要安装模型或 NVIDIA 环境,只需要 ffmpeg、curl、python3 和自己的野生流动 API Key。
该接口只提供识别结果,不包含字幕排版、剪辑方案、OCR 或视频渲染。
只需要一个引擎时,可使用Whisper 与 VibeVoice 独立入口。原双引擎模型名、请求与价格不变。
账号、权限与计费
- 已注册账号可以在控制台创建普通 API Key;
- 普通 API Key 不需要管理员单独授权;调用方主动配置的模型范围仍然生效;
- 模型会出现在模型广场和鉴权目录
GET /v1/models中,接口类型为wildflow-jobs; - 目录价格为 ¥0.05 / 音频分钟,提交时按 120 分钟上限预授权,成功后按实际时长结算并退回差额;
- 平台并发槽位为 8,超出执行容量的任务排队;这不代表所有提交都会同时推理。实时可用状态以模型广场为准,不承诺实时流式识别或对外 SLA。
批量提交时,每条任务仍预授权 6 元,账户余额需要覆盖待处理任务的预授权总额。新 ASR 任务的排队与 执行期限为 14 天,这不是完成时延承诺。保留每条任务的 ID 和幂等键,等待时继续轮询原任务,不要换键重复提交。
快速调用
以下命令适用于 macOS / Linux,需要 Python 3 和 cURL。每次新任务生成新的幂等键,重试同一任务时保留原键。
1. 准备音频
export WILDFLOW_API_KEY="替换为控制台生成的密钥"
export INPUT_MEDIA="/绝对路径/音视频文件.mp4"
ffmpeg -hide_banner -y \
-i "$INPUT_MEDIA" \
-vn -ac 1 -ar 16000 -sample_fmt s16 -c:a flac \
input.flac
export INPUT_SHA256="$(shasum -a 256 input.flac | awk '{print $1}')"接口只接受 audio/flac;其他媒体类型会返回 input must be audio/flac。单个文件硬上限为 256 MiB,
音频时长硬上限为 120 分钟(60 分钟音频在范围内)。文件大小和时长必须同时满足,
256 MiB 等于 268,435,456 字节。推荐转换为 16 kHz、单声道、16-bit FLAC;
60 分钟同规格未压缩 PCM 约 110 MiB,120 分钟约 220 MiB,FLAC 大小随内容变化。
高采样率、多声道文件可能先触及大小上限,上传前应检查实际字节数。
建议先用几分钟的代表性内容确认术语和时间戳质量。
超过文件上限返回 HTTP 413。需要分段时可按约 10 分钟一段、相邻重叠 5–10 秒处理;
合并时自行叠加每段实际起点并去除重叠文字,复核跨段说话人编号。
2. 上传输入 Artifact
curl --fail-with-body --silent --show-error \
-X POST https://api.wildflow.cn/v1/input-artifacts \
-H "Authorization: Bearer $WILDFLOW_API_KEY" \
-H "Content-Type: audio/flac" \
-H "X-WildFlow-Content-SHA256: $INPUT_SHA256" \
--data-binary @input.flac \
--output input-artifact.json
export INPUT_ARTIFACT_ID="$(python3 -c \
'import json; print(json.load(open("input-artifact.json"))["artifact"]["id"])')"上传成功返回 HTTP 201。响应里的 size_bytes 和 sha256 应与本地文件一致。
3. 提交 ASR Job
每段真正的新内容使用一个稳定且唯一的幂等键:
export WILDFLOW_IDEMPOTENCY_KEY="$(python3 -c 'import uuid; print(uuid.uuid4())')"
python3 - "$INPUT_ARTIFACT_ID" > asr-request.json <<'PY'
import json
import sys
print(json.dumps({
"model": "wildflow/dual-asr-v1",
"input_artifact_ids": [sys.argv[1]],
"parameters": {
"language": "zh",
"context": "行业访谈与产品讨论",
"hotwords": ["在这里填写品牌名", "在这里填写专业术语"]
}
}, ensure_ascii=False))
PY
curl --fail-with-body --silent --show-error \
-D asr-response-headers.txt \
-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首次接收成功返回 HTTP 202。网络中断时使用完全相同的请求正文和幂等键重放,不要换新键重复提交
同一段音频。
4. 轮询并下载结果
export RESULT_FILE="dual-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 或未知状态会停止下载;需要帮助时查看任务恢复说明。
下载后可运行 python3 -m json.tool dual-asr-result.json 查看 JSON。
参数
| 参数 | 必需 | 边界 | 说明 |
|---|---|---|---|
language | 否 | 1–16 个字符 | 默认 zh,当前仅传给 Whisper;VibeVoice 自动识别 |
context | 否 | 最多 20,000 个字符 | 兼容接收;当前 HTTP 路径未传给引擎,不承诺增强效果 |
hotwords | 否 | 最多 200 个;每个 1–100 字符 | 兼容接收;当前 HTTP 路径未传给引擎,不承诺热词增强 |
source_offset_seconds | 否 | 0–7200 | 记录在 timeline;消费方自行叠加偏移,不自动改写时间戳 |
每个 Job 必须恰好提供一个 input_artifact_ids。不要传服务器路径、下载 URL、启动命令或未经转换的
媒体文件。
结果结构
{
"schema_version": 1,
"source_artifact_id": "输入 Artifact ID",
"timeline": {"source_offset_seconds": 0},
"engines": {
"segment_text": {
"model_id": "microsoft/VibeVoice-ASR",
"model_revision": "固定 revision",
"segments": []
},
"word_timestamps": {
"model_id": "Systran/faster-whisper-large-v3",
"model_revision": "固定 revision",
"segments": []
}
}
}从结果的 engines 对象中,读取 segment_text.segments 和 word_timestamps.segments 获取两份转写及段级时间戳。
word_timestamps 是历史角色名;当前生产响应未提供 segments[].words,不承诺逐词时间戳。两种结果
职责不同,不要用其中一份直接覆盖另一份,也不要仅凭识别结果自动删除原始内容。
当前限制
- ASR 可能出现错字、漏词、音乐段幻觉或时间戳偏差,事实、数字、专有名词和实际处理点必须人工复核;
- 120 分钟是输入硬上限,不是处理时延承诺;
- 超过 10 分钟的音频会分段推理后合并时间轴;分段边界附近的文字、时间戳和说话人连续性需要人工复核;
- 模型暂不可用时目录会显示
callable=false,此时不要继续创建新任务; - 上传内容和结果属于耐久 Artifact。不要上传无权处理的录音或不必要的敏感信息。
统一的幂等、状态、恢复和 Artifact 规则见任务与结果文件。
相关文档
获取 API Key · 价格与计费 · 常见问题