跳到主要内容
feedback
feedback

异步任务接口使用指南

概述

对于生成时长较长的任务,如 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>

工作流程

轮询模式

  1. 提交任务:获取 task_id
  2. 轮询查询:检查任务状态
  3. 获取结果:下载生成结果

回调模式

  1. 在提交任务时通过请求头 X-WebHook 传入业务方回调地址
  2. 平台创建任务后异步执行
  3. 当任务状态变化时,平台向该地址发送 POST 回调
  4. 业务方验证签名后处理业务
  5. 如业务方未收到回调或处理失败,可继续通过查询接口兜底

Webhook 回调接入

接入前提

异步任务回调默认不开启,需同时满足以下条件才会触发:

  1. 当前组织或个人账号的 WebHook密钥 默认为空,需先在管理后台完成重置
  2. 提交异步任务时,请求头中传入了 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密钥”区域点击“重置密钥”,并将该密钥保存在服务端。

Webhook 密钥页面

3. 回调请求格式

平台会向 X-WebHook 指定的地址发起 POST 请求:

  • Content-Type: application/json
  • X-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:异步任务 ID
  • status:任务状态,可能为 waitingin_progresssuccessfailurecancelled
  • output:任务输出结果,成功时通常可从中拿到文件地址、结果对象等
  • usage_info:用量信息,部分任务可能为空

签名校验

平台会对每次回调请求进行签名,业务方可据此校验该请求是否确实由平台发出,以及回调内容在传输过程中是否被篡改。建议业务方在处理回调业务前,先完成签名校验。

签名头

平台回调会携带以下请求头:

  • X-Sign-Timestamp
  • X-Signature

签名算法

签名逻辑如下:

  1. 读取请求头中的 X-Sign-Timestamp
  2. 取回调 URL 中的 query 参数,构造签名用 Query 参数字符串
  3. 读取原始请求体字符串 payloadText
  4. 按以下格式拼接签名原文:
timestamp + "\n" + signedQueryString + "\n" + payloadText
  1. 使用账号的 WebHook密钥 执行 HMAC-SHA256
  2. 将结果编码为十六进制小写字符串,与 X-Signature 比较

签名用 Query 参数字符串规则

如果回调 URL 带 query 参数,签名时需要按以下规则规范化:

  1. 保留重复参数,不去重
  2. 按参数名升序排序
  3. 参数名相同时,按参数值升序排序
  4. 空值按空字符串处理
  5. 最终按 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:任务类型,例如 text2music
  • duration:生成时长,单位秒
  • prompt:音乐风格描述
  • lyrics:歌词内容
  • infer_steps:推理步数,通常步数越高质量越高,耗时也越长
  • guidance_scale:引导强度
  • scheduler_type:调度器类型

错误处理

常见错误类型

  1. 认证错误(401)

    • 检查 API Token 是否正确
    • 确认 Token 是否有效且未过期
  2. 参数错误(400)

    • 验证必填参数是否完整
    • 检查参数格式是否正确
  3. 任务失败

    • 查看任务查询接口返回的错误信息
    • 查看业务方回调处理日志
    • 调整参数后重新提交
  4. 回调验签失败

    • 检查是否使用最新的 WebHook密钥
    • 检查参与签名的是否是“原始请求体字符串”
    • 检查签名用 Query 参数字符串是否按规则排序
    • 检查业务方读取的 URL 是否与平台实际回调 URL 完全一致
  5. 回调投递失败

    • 业务方接口需要返回 HTTP 2xx
    • 可通过 webhook 日志查看响应码与响应体摘要

回调排障与重试

平台提供了 webhook 回调记录、日志和手动重试能力。排障时,请到管理后台对应的异步任务 webhook 页面查看回调记录、失败原因和重试结果。

业务方排障时,需要先登录模力方舟平台,进入 工作台->设置->Webhooks,在“回调记录”区域查看投递结果,并在失败记录中使用“查看日志”或“手动重试”能力继续排查。

Webhook 回调记录页面

默认重试策略

当回调请求失败时,平台会按默认策略自动重试,当前默认重试间隔为 200ms/5s/15s/30s/120s

也就是说,首次投递失败后,平台还会继续进行 5 次自动重试。建议业务方回调接口保持幂等,避免重复消费。

建议排障顺序:

  1. 先查任务状态,确认任务是否已经进入目标状态
  2. 再查 webhook 回调记录,确认是否投递过
  3. 最后查 webhook 日志,定位是网络失败、业务方 4xx/5xx,还是验签问题

最佳实践

  1. 优先接入 Webhook,轮询只作为兜底
  2. 回调接口仅做验签、落库、投递消息等轻处理,避免长事务阻塞
  3. event_id 做幂等处理,避免重复消费
  4. 建议校验 X-Sign-Timestamp 的时间窗口,防止重放攻击
  5. 合理设置轮询间隔,建议 5 到 10 秒
  6. 实现超时和指数退避重试机制
  7. 提交任务前先检查可用配额
  8. 对不再需要的任务及时取消
  9. 下载结果文件后及时归档,避免链接过期带来二次失败

配额与任务管理示例

# 查询可用配额
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 可以取消 waitingin_progress 状态的任务。

Q: 什么是并发配额限制?
A: 每个账号的异步任务有并发配额限制,包括处于 waitingin_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: 可以等待现有任务完成、取消不需要的任务,或者自行实现任务队列来平滑提交请求。