异步任务接口使用指南
概述
对于生成时长较长的任务,如 3D 建模、视频生成、长语音合成、音乐生成等,平台提供异步任务能力。接入方式支持两种:
- 轮询查询:提交任务后,主动调用任务查询接口获取进度和结果。
- Webhook 回调:提交任务时携带回调地址,任务状态变化后平台主动回调业务方服务。
建议优先接入 Webhook,再保留轮询作为兜底,兼顾实时性和稳定性。
接口架构
异步任务接口采用“提交 + 状态跟踪”模式:
- 提交接口:用于创建异步任务,例如文本生成音乐的提交接口为
/async/music/generations - 查询接口:统一的任务状态查询接口
https://api.moark.com/api/v1/task/<task_id> - 任务取消接口:
https://api.moark.com/api/v1/task/<task_id>/cancel - 配额查询接口:
https://api.moark.com/v1/tasks/available-quota - 任务状态接口:
https://api.moark.com/v1/task/<task_id>/status - 任务记录接口:
https://api.moark.com/v1/task/<task_id>
工作流程
轮询模式
- 提交任务:获取
task_id - 轮询查询:检查任务状态
- 获取结果:下载生成结果
回调模式
- 在提交任务时通过请求头
X-WebHook传入业务方回调地址 - 平台创建任务后异步执行
- 当任务状态变化时,平台向该地址发送
POST回调 - 业务方验证签名后处理业务
- 如业务方未收到回调或处理失败,可继续通过查询接口兜底
Webhook 回调接入
接入前提
异步任务回调默认不开启,需同时满足以下条件才会触发:
- 当前组织或个人账号的
WebHook密钥默认为空,需先在管理后台完成重置 - 提交异步任务时,请求头中传入了
X-WebHook
任一条件不满足,平台都不会触发回调,业务方只能通过轮询获取任务结果。
1. 配置回调地址
异步任务提交时,通过请求头 X-WebHook 指定回调地址,例如:
curl -X POST "https://api.moark.com/v1/async/music/generations" \
-H "Authorization: Bearer <your_token>" \
-H "X-WebHook: https://example.com/api/async-callback" \
-F "model=ACE-Step-v1-3.5B" \
-F "task=text2music" \
-F "duration=60" \
-F "prompt=pop, synth, drums, guitar, 120 bpm" \
-F "lyrics=[Verse]..." \
-F "infer_steps=60"
如果未传 X-WebHook,平台不会触发业务方回调,此时只能通过轮询获取结果。
如果 X-WebHook 传入的回调地址带有 query 参数,平台发起回调时会原样保留这些 query 参数。业务方进行验签时,也应使用实际回调 URL 中的完整 query 参数。
2. 配置回调密钥
平台会使用当前账号的 WebHook密钥 对回调请求签名。业务方接入前,需要先登录模力方舟平台,进入 工作台->设置->个人信息,在“WebHook密钥”区域点击“重置密钥”,并将该密钥保存在服务端。

3. 回调请求格式
平台会向 X-WebHook 指定的地址发起 POST 请求:
Content-Type: application/jsonX-Sign-Timestamp: <13位毫秒时间戳>X-Signature: <hex 小写签名串>
业务方回调接口处理完成后,请返回 HTTP 2xx 状态码。当前平台会按 200 <= statusCode < 300 判断回调成功。
回调 Body 示例:
{
"event_id": "7f2a278fad9c4300b4f114aaf30b84b6",
"task_id": "1b0c1f9e6b0e4f62ab89f9f8e92f2c2a",
"status": "success",
"output": {
"file_url": "https://example.com/output.mp3"
},
"usage_info": {
"unit": "seconds",
"quantity": 60,
"prompt_tokens": 0,
"completion_tokens": 0,
"resolution": null
}
}
字段说明:
event_id:回调事件 ID,同一任务状态事件唯一;如果平台对该事件重试投递,event_id不变task_id:异步任务 IDstatus:任务状态,可能为waiting、in_progress、success、failure、cancelledoutput:任务输出结果,成功时通常可从中拿到文件地址、结果对象等usage_info:用量信息,部分任务可能为空
签名校验
平台会对每次回调请求进行签名,业务方可据此校验该请求是否确实由平台发出,以及回调内容在传输过程中是否被篡改。建议业务方在处理回调业务前,先完成签名校验。
签名头
平台回调会携带以下请求头:
X-Sign-TimestampX-Signature
签名算法
签名逻辑如下:
- 读取请求头中的
X-Sign-Timestamp - 取回调 URL 中的 query 参数,构造签名用 Query 参数字符串
- 读取原始请求体字符串
payloadText - 按以下格式拼接签名原文:
timestamp + "\n" + signedQueryString + "\n" + payloadText
- 使用账号的
WebHook密钥执行HMAC-SHA256 - 将结果编码为十六进制小写字符串,与
X-Signature比较
签名用 Query 参数字符串规则
如果回调 URL 带 query 参数,签名时需要按以下规则规范化:
- 保留重复参数,不去重
- 按参数名升序排序
- 参数名相同时,按参数值升序排序
- 空值按空字符串处理
- 最终按
key=value用&连接
例如,回调地址为:
https://example.com/webhook?b=2&a=3&a=1&empty
则参与签名的 Query 参数字符串为:
a=1&a=3&b=2&empty=
如果回调地址不带 query 参数,则签名用 Query 参数字符串为空字符串,签名原文第二行留空。
Python 验签示例
import hmac
import hashlib
from urllib.parse import urlparse, parse_qsl
def build_signed_query_string(url: str) -> str:
pairs = parse_qsl(urlparse(url).query, keep_blank_values=True)
pairs.sort(key=lambda item: (item[0], item[1] or ""))
return "&".join(f"{k}={v}" for k, v in pairs)
def verify_signature(callback_url: str, timestamp: str, body: str, signature: str, secret: str) -> bool:
signed_query_string = build_signed_query_string(callback_url)
sign_content = f"{timestamp}\n{signed_query_string}\n{body}"
expected = hmac.new(
secret.encode("utf-8"),
sign_content.encode("utf-8"),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
完整示例:音乐生成
以下示例演示如何使用 ACE-Step 音乐大模型生成音乐,并同时保留轮询兜底:
Python 实现
import os
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
API_URL = "https://api.moark.com/v1/async/music/generations"
headers = {
"Authorization": "Bearer <your token>",
"X-WebHook": "https://example.com/api/async-callback",
}
def submit(payload):
data = {}
for key, value in payload.items():
if value is None:
data[key] = ""
elif isinstance(value, list):
data[key] = ",".join(map(str, value))
elif isinstance(value, bool):
data[key] = str(value).lower()
else:
data[key] = str(value)
multipart_data = MultipartEncoder(fields=data)
request_headers = dict(headers)
request_headers["Content-Type"] = multipart_data.content_type
response = requests.post(API_URL, headers=request_headers, data=multipart_data)
response.raise_for_status()
return response.json()
result = submit({
"model": "ACE-Step-v1-3.5B",
"task": "text2music",
"duration": 60,
"prompt": "pop, synth, drums, guitar, 120 bpm, upbeat, catchy, vibrant, female vocals, polished vocals",
"lyrics": """[Verse]
Neon lights across the sky
Every moment passing by
Your smile catches in my mind
Like a snapshot frozen time
[Chorus]
We're dancing through the night
Under these electric lights
Don't need to think about tomorrow
Just feel this moment right""",
"infer_steps": 60,
"lora_name": "None",
"guidance_scale": 15,
"guidance_scale_text": 0,
"guidance_scale_lyric": 0,
"scheduler_type": "euler",
"cfg_type": "apg",
"omega_scale": 10,
"guidance_interval": 0.5,
"guidance_interval_decay": 0,
"min_guidance_scale": 3,
"use_erg_tag": True,
"use_erg_lyric": True,
"use_erg_diffusion": True,
})
task_id = result["task_id"]
query_url = result["urls"]["get"]
while True:
resp = requests.get(query_url, headers={"Authorization": "Bearer <your token>"}).json()
print("task status:", resp["status"])
if resp["status"] == "failure":
print(resp)
os._exit(1)
if resp["status"] == "success":
break
file_url = resp["output"]["file_url"]
audio = requests.get(file_url)
audio.raise_for_status()
with open("output.mp3", "wb") as f:
f.write(audio.content)
主要参数说明
通用参数
model:使用的模型名称,例如ACE-Step-v1-3.5B- 请求头参数
X-WebHook:业务方回调地址,可选;传入后平台会异步推送状态变化
音乐生成专用参数
task:任务类型,例如text2musicduration:生成时长,单位秒prompt:音乐风格描述lyrics:歌词内容infer_steps:推理步数,通常步数越高质量越高,耗时也越长guidance_scale:引导强度scheduler_type:调度器类型
错误处理
常见错误类型
-
认证错误(401)
- 检查 API Token 是否正确
- 确认 Token 是否有效且未过期
-
参数错误(400)
- 验证必填参数是否完整
- 检查参数格式是否正确
-
任务失败
- 查看任务查询接口返回的错误信息
- 查看业务方回调处理日志
- 调整参数后重新提交
-
回调验签失败
- 检查是否使用最新的
WebHook密钥 - 检查参与签名的是否是“原始请求体字符串”
- 检查签名用 Query 参数字符串是否按规则排序
- 检查业务方读取的 URL 是否与平台实际回调 URL 完全一致
- 检查是否使用最新的
-
回调投递失败
- 业务方接口需要返回 HTTP
2xx - 可通过 webhook 日志查看响应码与响应体摘要
- 业务方接口需要返回 HTTP
回调排障与重试
平台提供了 webhook 回调记录、日志和手动重试能力。排障时,请到管理后台对应的异步任务 webhook 页面查看回调记录、失败原因和重试结果。
业务方排障时,需要先登录模力方舟平台,进入 工作台->设置->Webhooks,在“回调记录”区域查看投递结果,并在失败记录中使用“查看日志”或“手动重试”能力继续排查。

默认重试策略
当回调请求失败时,平台会按默认策略自动重试,当前默认重试间隔为 200ms/5s/15s/30s/120s。
也就是说,首次投递失败后,平台还会继续进行 5 次自动重试。建议业务方回调接口保持幂等,避免重复消费。
建议排障顺序:
- 先查任务状态,确认任务是否已经进入目标状态
- 再查 webhook 回调记录,确认是否投递过
- 最后查 webhook 日志,定位是网络失败、业务方 4xx/5xx,还是验签问题
最佳实践
- 优先接入 Webhook,轮询只作为兜底
- 回调接口仅做验签、落库、投递消息等轻处理,避免长事务阻塞
- 对
event_id做幂等处理,避免重复消费 - 建议校验
X-Sign-Timestamp的时间窗口,防止重放攻击 - 合理设置轮询间隔,建议 5 到 10 秒
- 实现超时和指数退避重试机制
- 提交任务前先检查可用配额
- 对不再需要的任务及时取消
- 下载结果文件后及时归档,避免链接过期带来二次失败
配额与任务管理示例
# 查询可用配额
curl -H "Authorization: Bearer <your_token>" \
"https://api.moark.com/v1/tasks/available-quota"
# 取消不需要的任务
curl -X POST \
-H "Authorization: Bearer <your_token>" \
"https://api.moark.com/api/v1/task/<task_id>/cancel"
# 查询任务状态
curl -H "Authorization: Bearer <your_token>" \
"https://api.moark.com/v1/task/<task_id>/status"
常见问题
Q: 任务提交后多久能完成?
A: 根据任务复杂度不同,通常在 1 到 10 分钟内完成。
Q: Webhook 和轮询可以同时使用吗?
A: 可以。建议生产环境同时保留轮询兜底,防止业务方回调偶发失败。
Q: 回调地址放在哪里传?
A: 在提 交异步任务请求时,通过请求头 X-WebHook 传入完整回调 URL。
Q: 什么情况下需要重置 WebHook密钥?
A: 当密钥疑似泄漏、接手人变更,或联调环境与生产环境需要隔离时,建议立即重置。重置后,业务方服务端也要同步更新。
Q: 是否可以取消正在进行的任务?
A: 支持。使用取消接口 https://api.moark.com/api/v1/task/<task_id>/cancel 可以取消 waiting 或 in_progress 状态的任务。
Q: 什么是并发配额限制?
A: 每个账号的异步任务有并发配额限制,包括处于 waiting 和 in_progress 状态的任务总数。超过配额将无法提交新任务。
Q: 如何查询我的可用配额?
A: 使用配额查询接口 https://api.moark.com/v1/tasks/available-quota 可以查询当前可用的异步任务并发配额。
Q: 下载链接有效期多长?
A: 下载链接通常有效期为 24 小时,建议在任务完成后尽快下载保存。
Q: 如何获取任务的详细信息?
A: 使用任务记录接口 https://api.moark.com/v1/task/<task_id> 可以获取任务的完整信息,包括参数、状态、结果等。
Q: 如何处理配额不足的情况?
A: 可以等待现有任务完成、取消不需要的任务,或者自行实现任务队列来平滑提交请求。