客服三方OpenAPI对接指南

客服三方OpenAPI对接指南

麦芽 Agent 智能体第三方 Open API 对接文档(v1.6.0),含会话初始化、需求录入、任务查询、任务终止、文件下载与结果回调等接口的完整对接说明。

最近更新:2026.08.25 20:33

Agent智能体 Open API 对接文档(第三方)

文档版本v1.6.0更新日期2026-08-12适用产品麦芽Agent智能体 Open API文档性质正式对接文档(与当前线上实现一致)

1. 概述

1.1 能力说明

第三方业务系统可通过本 Open API,向已开通的Agent智能体助手提交用户咨询(支持附件 URL 传入)、查询任务进度与结果、下载 AI 产出文件,并接收平台终态回调。

#接口方向方法路径 / 地址说明0会话初始化三方 → 平台POST/api/open/session/init可选。提前创建 session_code 并异步预热 CLI,缩短首问冷启动;不创建任务1需求录入三方 → 平台POST/api/open/demand/create创建客服任务;默认异步 JSON,可选 sync=true SSE;可用 stream_output 控制文本是否增量落库2任务查询三方 → 平台POST/api/open/demand/query查询任务状态、动作流、成果3任务终止三方 → 平台POST/api/open/demand/stop终止未完成任务;终态后触发回调(与 query 一致)4文件下载三方 → 平台GET/api/open/demand/file/download凭短期 token 下载产出文件5结果回调平台 → 三方POST贵方配置的 callback_url任务终态后主动推送 TaskSnapshot
接口 0~4 由贵方调用平台;接口 5 由平台调用贵方服务。六者共同构成完整对接面。
不调用会话初始化仍可直接 demand/create(首问可能冷启动);调用 init 后首问须携带返回的 session_code

1.2 接入凭证

开通接入后,平台向贵方提供:

凭证说明third_party_id接入标识(写入请求体,须与 ACCESSKEY 归属一致)ACCESSKEY访问密钥,放在请求头 X-Access-KeySECRETKEY签名密钥,仅用于本地计算签名,禁止通过网络传输Base URL形如 https://{host}/api/open(以实际开通环境为准)

可选配置(由平台侧维护):

  • IP 白名单
  • 回调地址 callback_url
  • 内容长度、是否允许附件、附件数量上限等(message_input_config
  • 测试环境是否允许 HTTP 附件 URL(other_config.attachment_allow_http

附件能力说明:

方向方式说明三方 → 平台(输入)create.attachments[].file_url录入时由平台拉取并挂载;须开通 allow_attachments=true平台 → 三方(输出)query/callbackoutputs[].view_urlAI 生成文件后,凭 token GET 下载;与输入附件无关

1.3 会话与任务

概念字段含义会话session_code多轮对话容器;不传则新建,传入则续聊任务demand_id单次提交对应的执行单元;查询/下载/回调的主键之一

规则:

  • 每次调用录入接口都会新建一条任务(demand_id)。
  • 不传 session_code → 新建会话(首问可能冷启动)。
  • 传入有效 session_code → 在同一会话下续提新任务(须与 biz_user_id 匹配)。
  • 推荐:打开客服页时先调 POST /session/init 拿到 session_code 并后台预热;用户首问 demand/create 携带该码,可复用已启动的 CLI。biz_session_id 可做三方侧幂等键。
(可选)POST /session/init → session_code + 后台预热
session_code
  ├── demand_id = 10001   (首问,传入 init 返回的 session_code)
  └── demand_id = 10002   (追问,传入同一 session_code)

2. 通用约定

2.1 协议与编码

约定协议HTTPS(测试环境可能为 HTTP,以开通说明为准)字符编码UTF-8请求体JSON(Content-Type: application/json; charset=utf-8)成功业务码响应体 code = 200

2.2 统一响应结构

JSON 接口(会话初始化 / 录入 / 查询)成功与失败均返回:

{
  "code": 200,
  "message": "success",
  "data": {}
}
字段类型说明codeint业务码;成功为 200,失败见 §10messagestring说明信息dataobject / null业务数据;失败时可能为 null 或附加信息

HTTP 状态码与业务码对应关系见 §10。

文件下载接口成功时直接返回文件二进制流(非上述 JSON 包装);失败时返回纯文本错误信息。

2.3 时钟同步

签名依赖请求头中的 Unix 毫秒时间戳。请保证调用方服务器与标准时间偏差不超过 ±300 秒(5 分钟),否则返回 40004

3. 鉴权与签名(HMAC-SHA256)

会话初始化、录入、查询、终止接口必须按本节签名。
文件下载接口使用查询结果中的短期 token无需再带 HMAC 头(见 §8)。

3.1 请求头

Header必填说明Content-Typeapplication/json; charset=utf-8X-Access-Key是ACCESSKEYX-Timestamp是Unix 毫秒时间戳字符串,如 1722757920123X-Nonce是随机串,长度 16~64;同一 ACCESSKEY 下短期内不可重复X-Signature是按 §3.2 计算的十六进制签名(小写)

3.2 签名算法

算法:HMAC-SHA256

步骤

  1. 原始请求体字节raw body)。空 body 按空字节序列处理。
  • 必须对即将发送的原始字节做哈希;不要先 parse 再重新序列化(字段顺序、空格变化会导致验签失败)。
  1. 计算 Body 摘要:
body_hash = HEX( SHA256(raw_body) )
  1. 构造规范串(Canonical String),共 6 行,行之间以 单个换行符 `\n`(LF,0x0A) 连接,末尾不加多余换行
HTTP_METHOD
PATH
ACCESS_KEY
TIMESTAMP
NONCE
BODY_HASH
取值规则HTTP_METHOD大写,如 POSTPATH对外公开路径,必须/api/open/session/init/api/open/demand/create/api/open/demand/query/api/open/demand/stop(含 /api 前缀,不含 query string)ACCESS_KEYX-Access-Key 相同TIMESTAMPX-Timestamp 相同NONCEX-Nonce 相同BODY_HASH步骤 2 的十六进制小写字符串
  1. 使用 SECRETKEY(UTF-8)对规范串做 HMAC-SHA256,输出十六进制小写字符串,写入 X-Signature
signature = HEX( HMAC_SHA256(key=SECRETKEY, message=canonical_string) )

3.3 签名示例

假设:

ACCESSKEY  = AK_demo_001
SECRETKEY  = SK_demo_secret
METHOD     = POST
PATH       = /api/open/demand/create
TIMESTAMP  = 1722757920123
NONCE      = a1b2c3d4e5f6789012345678abcdef01
BODY       = {"third_party_id":"demo-tp","biz_user_id":"u1","content":"你好"}
  1. body_hash = SHA256(UTF8(BODY)) 的 hex
  2. Canonical:
POST
/api/open/demand/create
AK_demo_001
1722757920123
a1b2c3d4e5f6789012345678abcdef01
<body_hash>
  1. X-Signature = HMAC_SHA256(SK_demo_secret, canonical) 的 hex

3.4 代码示例(Python)

import hashlib
import hmac
import json
import time
import uuid
import requests
 
ACCESS_KEY = "AK_xxx"
SECRET_KEY = "SK_xxx"
BASE = "https://{host}/api/open"
 
def sign(method: str, path: str, access_key: str, timestamp: str, nonce: str, raw_body: bytes) -> str:
    body_hash = hashlib.sha256(raw_body or b"").hexdigest()
    canonical = "\n".join([
        method.upper(),
        path,
        access_key,
        timestamp,
        nonce,
        body_hash,
    ])
    return hmac.new(
        SECRET_KEY.encode("utf-8"),
        canonical.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
 
def post_open(path_suffix: str, body: dict):
    # path_suffix 例: "/demand/create"
    path = f"/api/open{path_suffix}"
    raw_body = json.dumps(body, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
    ts = str(int(time.time() * 1000))
    nonce = uuid.uuid4().hex  # 32 位,满足 16~64
    signature = sign("POST", path, ACCESS_KEY, ts, nonce, raw_body)
    headers = {
        "Content-Type": "application/json; charset=utf-8",
        "X-Access-Key": ACCESS_KEY,
        "X-Timestamp": ts,
        "X-Nonce": nonce,
        "X-Signature": signature,
    }
    url = f"{BASE.rstrip('/')}{path_suffix}"
    return requests.post(url, data=raw_body, headers=headers, timeout=30)

3.5 常见验签失败原因

现象常见原因40003 签名错误PATH 写成了 /open/... 或带了域名/query;body 被重新格式化;SECRETKEY 错误40004 请求已过期客户端时钟偏差超过 5 分钟;Timestamp 误用秒而非毫秒40005 重复请求同一 ACCESSKEY 下 Nonce 被重放(Nonce TTL 约 5 分钟)40002 ACCESSKEY 无效密钥停用、未生效或已过期40006 IP 不在白名单出口 IP 未登记40001 接入标识不一致Body 中 third_party_id 与 ACCESSKEY 所属接入不匹配

4. 接口:会话初始化(可选)

提前创建会话并异步预热客服 CLI。不创建 demand。适合在打开客服页、用户尚未输入时调用,缩短首问等待。

  • URLPOST /api/open/session/init
  • 鉴权:§3 HMAC 签名(PATH=/api/open/session/init

4.1 请求体

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "biz_session_id": "optional-idempotency-key",
  "display_name": "张三",
  "title": "退款咨询",
  "stream_output": true,
  "ext": {}
}
字段类型必填说明third_party_idstring是接入标识biz_user_idstring是三方用户 IDbiz_session_idstring否三方会话幂等键(≤64)。同一接入下重复调用复用同一 session_code 并再次触发预热display_namestring否展示名titlestring否会话标题stream_outputboolean否默认 true。须与后续首问 demand/create 保持一致(进预热指纹)。幂等重试未传时继承已有会话值;首问 create 未传时亦沿用 init 落库值extobject否扩展字段

4.2 成功响应

{
  "code": 200,
  "message": "success",
  "data": {
    "session_code": "CS-S-20260810120000-x9y8z7w6",
    "prewarm_status": "preparing",
    "stream_output": true,
    "created": true
  }
}
字段说明session_code后续 demand/create / 续聊须携带prewarm_statuspreparing / ready / failed(同进程瞬时状态;preparing 为常态,勿强依赖轮询)stream_output本次会话预热使用的流式开关createdtrue=新建会话;false=biz_session_id 命中已有会话

4.3 推荐时序

打开客服页 → POST /session/init → 拿到 session_code
用户输入 → POST /demand/create(带同一 session_code、建议相同 stream_output)
追问 → 继续带同一 session_code
预热为同进程 best-effort:失败不影响后续录入(自动冷启动)。多实例部署时需会话粘性/owner,否则首问仍可能冷启。

5. 接口一:需求录入

创建一条Agent智能体任务。默认异步执行;可选 sync=trueSSE 同步流式

  • URLPOST /api/open/demand/create
  • 鉴权:§3 HMAC 签名

5.0 执行模式(sync / stream_output

sync响应取结果方式终态 callback不传 / falseapplication/json(立即返回 demand_id)三方轮询 POST /demand/query会推送(若配置了 callback_urltruetext/event-stream(同连接推流)同一 POST 内收 created/action/finished不推送(结果已由 SSE 交付)stream_output含义不传 / true(默认)Agent 文本按增量(delta)写入 action,SSE/query 可更快看到首字falseAgent 关闭增量流式,文本整段落库后再可见(首字更晚,action 条数更少)
sync / stream_output 必须为 JSON boolean。字符串 "true" 会返回 40007
二者独立:sync 控制 HTTP 是否 SSE 同连接推送;stream_output 控制模型文本是否增量落库。
增加字段会改变 Body,须对新原始字节重新签名。
SSE 断开不会取消后台任务;请用首个 created 事件中的 demand_id + session_code 调用 query 兜底。
建议同步模式带上 biz_demand_id,便于断线重试对账(当前版本不自动幂等)。

5.1 请求体

新会话:

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "content": "我想咨询退款流程",
  "title": "退款咨询",
  "display_name": "张三",
  "biz_demand_id": "biz-order-001",
  "ext": {
    "order_id": "ORD-20260801"
  }
}

续聊(传入上次返回的 `session_code`):

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "session_code": "CS-S-20260805101200-x9y8z7w6",
  "content": "那退款大概多久能到账?",
  "title": "退款咨询-追问"
}

带附件(三方提供 HTTPS 临时下载链接):

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "content": "请阅读附件并总结退款条款",
  "attachments": [
    {
      "file_url": "https://partner-cdn.example.com/tmp/contract.pdf",
      "file_name": "合同.pdf",
      "mime_type": "application/pdf"
    }
  ]
}
平台会在录入时拉取 file_url 并注册为任务附件,AI 在工作区 resources/ 目录读取。在接入配置中开启 message_input_config.allow_attachments=true。单文件默认最大 20MB;数量以 max_attachment_count 为准(默认 5)。PDF 等二进制文件由 AI 通过 Skill / 命令行工具解析,请勿假设平台会原样转发文件内容。

附件相关接入配置示例(平台侧):

{
  "message_input_config": {
    "allow_attachments": true,
    "max_attachment_count": 5,
    "max_content_length": 32000
  },
  "other_config": {
    "attachment_allow_http": false
  }
}
字段类型必填说明third_party_idstring是接入标识,须与 ACCESSKEY 一致biz_user_idstring是贵方用户唯一 IDcontentstring是本轮用户输入;默认最大长度 32000(以接入配置为准)session_codestring否不传=新会话;传入=/session/init 返回或续聊码(须属于该 biz_user_idtitlestring否任务标题;缺省自动生成display_namestring否用户展示名biz_demand_idstring否贵方侧业务单号,最长 64attachmentsarray否附件列表;受接入配置 message_input_config 约束attachments[].file_urlstring条件附件下载 URL(HTTPS;测试环境可在 other_config.attachment_allow_http=true 时允许 HTTP)attachments[].file_namestring否文件名;缺省从 URL 推断attachments[].mime_typestring否MIME 类型,可选extobject否扩展字段;平台会关联存储。另:stream_output 会合并写入任务侧 ext_json 供 Job 读取(顶层字段优先于 ext.stream_outputsyncboolean否默认 falsetrue 时响应改为 SSE 同步流式(§5.3)stream_outputboolean否默认 true。控制客服 Job 是否增量流式写入文本 action;与 sync 独立

5.2 成功响应(异步,sync=false

{
  "code": 200,
  "message": "success",
  "data": {
    "session_code": "CS-S-20260805101200-x9y8z7w6",
    "demand_id": 12345,
    "status": 0,
    "status_name": "pending",
    "created_at": "2026-08-05T09:12:00",
    "sync": false,
    "stream_output": true
  }
}
字段说明session_code会话编码;续聊时带回demand_id本次任务 ID;查询/对账使用status / status_name初始一般为 0 / pendingcreated_at创建时间(ISO 风格本地时间字符串)sync回显请求中的 sync(异步一般为 falsestream_output回显本次任务实际使用的流式开关
异步任务请通过 查询接口回调 获取最终结果,勿在录入 JSON 响应中期待完整答复。

5.3 成功响应(同步 SSE,sync=true

  • HTTP200
  • Content-Typetext/event-stream
  • 响应头Cache-Control: no-cacheX-Accel-Buffering: no(请代理关闭缓冲并拉长读超时,建议 ≥ 15 分钟)
  • 最大连接时长:15 分钟;超时发送 stream_error 后关闭,任务继续执行

请求示例:

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "content": "请总结退款流程",
  "sync": true,
  "stream_output": true,
  "biz_demand_id": "biz-order-001"
}

事件顺序: createdaction* → finished(或 stream_error

id: created:12345
event: created
data: {"request_id":"...","session_code":"CS-S-...","demand_id":12345,"status":0,"status_name":"pending","created_at":"..."}
 
id: action:788001
event: action
data: {"id":788001,"action_name":"text","action_type":"unified_demand_action","action_detail":{"execution_result":"..."},"create_time":"..."}
 
: ping
 
id: finished:12345
event: finished
data: {"session_code":"...","demand_id":12345,"status":20,"status_name":"completed","finished":true,"result_summary":"...","outputs":[],"..."}
事件说明created建单成功后立即发送;含 demand_id/session_code,断线后用于 queryaction一条公开 action 一帧;白名单与 query 一致:thinking/text/interaction_question/interaction_replyfinished终态(20/90/99);字段对齐 TaskSnapshot,不含完整 actions 数组stream_error流传输/超时等;表示任务已失败,请改用 query注释 : ping约每 15 秒心跳,保持代理连接

流前错误(验签/参数等):仍返回普通 JSON + HTTP 4xx/5xx,不会进入 SSE。

curl 联调:

curl -N -X POST 'http://HOST/api/open/demand/create' \
  -H 'Content-Type: application/json; charset=utf-8' \
  -H 'X-Access-Key: ...' \
  -H 'X-Timestamp: ...' \
  -H 'X-Nonce: ...' \
  -H 'X-Signature: ...' \
  -d '{"third_party_id":"...","biz_user_id":"...","content":"...","sync":true}'

5.4 录入场景示例

场景 A:纯文本新会话(无附件)

请求:

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "content": "我想咨询退款流程",
  "title": "退款咨询"
}

响应:

{
  "code": 200,
  "message": "success",
  "data": {
    "session_code": "CS-S-20260805101200-x9y8z7w6",
    "demand_id": 12345,
    "status": 0,
    "status_name": "pending",
    "created_at": "2026-08-05T09:12:00"
  }
}

场景 B:带附件新会话(输入附件)

贵方先将文件上传至 OSS/CDN,生成 HTTPS 临时链接(建议 5~15 分钟有效),再调用录入:

请求:

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "content": "请阅读附件,用三句话总结文档内容",
  "title": "合同摘要",
  "attachments": [
    {
      "file_url": "https://partner-cdn.example.com/tmp/contract.pdf",
      "file_name": "合同.pdf",
      "mime_type": "application/pdf"
    }
  ]
}

响应: 与场景 A 结构相同(仅 demand_id / session_code 不同)。附件是否拉取成功在录入阶段即确定;拉取失败返回 40009,不会创建任务。

附件拉取失败示例:

{
  "code": 40009,
  "message": "当前接入配置不允许上传附件",
  "data": null
}
{
  "code": 40009,
  "message": "下载附件失败",
  "data": null
}

场景 C:续聊(同 session,无新附件)

请求:

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "session_code": "CS-S-20260805101200-x9y8z7w6",
  "content": "那退款大概多久能到账?",
  "title": "退款咨询-追问"
}

响应: 返回同一 session_code新的 demand_id(每次录入均新建任务)。

场景 D:续聊并补充新附件

请求:

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "biz_user_id": "user_10086",
  "session_code": "CS-S-20260805101200-x9y8z7w6",
  "content": "请再看一下这份补充材料",
  "attachments": [
    {
      "file_url": "https://partner-cdn.example.com/tmp/supplement.txt",
      "file_name": "补充说明.txt"
    }
  ]
}
每一轮 demand_id 独立挂载附件;历史轮次的附件不会自动带入新一轮,除非用户在续聊中再次传入。

6. 接口二:任务查询

按录入返回的 session_code + demand_id 查询该次任务的执行快照。

  • URLPOST /api/open/demand/query
  • 鉴权:§3 HMAC 签名

6.1 请求体

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "session_code": "CS-S-20260805101200-x9y8z7w6",
  "demand_id": 12345
}
字段类型必填说明third_party_idstring是接入标识session_codestring是录入返回的会话编码demand_idint是录入返回的任务 ID

6.2 成功响应(data = TaskSnapshot)

以下按典型场景分别示例。查询请求体在各场景中相同(见 §6.1)。

场景 A:执行中(无终态、无产出)

{
  "code": 200,
  "message": "success",
  "data": {
    "session_code": "CS-S-20260805101200-x9y8z7w6",
    "demand_id": 12345,
    "status": 10,
    "status_name": "running",
    "finished": false,
    "error_code": null,
    "error_message": null,
    "result_summary": null,
    "actions": [
      {
        "id": 90001,
        "action_name": "text",
        "action_type": "unified_demand_action",
        "action_detail": {
          "execution_result": "您好,正在为您查询退款政策..."
        },
        "create_time": "2026-08-05T09:12:18"
      }
    ],
    "outputs": []
  }
}

场景 B:已完成 — 纯文本答复(无输出文件)

常见于咨询类,或「阅读输入附件后文字总结」(create 有附件,但 AI 未生成可下载文件):

{
  "code": 200,
  "message": "success",
  "data": {
    "session_code": "CS-S-20260805164503-5573652c",
    "demand_id": 37412,
    "status": 20,
    "status_name": "completed",
    "finished": true,
    "error_code": null,
    "error_message": null,
    "result_summary": "该附件文档内容非常简短……(三句话总结正文)",
    "actions": [
      {
        "id": 782271,
        "action_name": "text",
        "action_type": "unified_demand_action",
        "action_detail": {
          "execution_result": "1. 文件性质:这是一份测试文档……"
        },
        "create_time": "2026-08-05T16:49:30"
      }
    ],
    "outputs": []
  }
}

场景 C:已完成 — 含 AI 产出文件(需下载)

{
  "code": 200,
  "message": "success",
  "data": {
    "session_code": "CS-S-20260805101200-x9y8z7w6",
    "demand_id": 12345,
    "status": 20,
    "status_name": "completed",
    "finished": true,
    "result_summary": "已为您生成退款流程说明文档。",
    "actions": [
      {
        "id": 90001,
        "action_name": "text",
        "action_type": "unified_demand_action",
        "action_detail": {
          "execution_result": "已为您整理退款流程,详见附件 PDF。"
        },
        "create_time": "2026-08-05T09:15:28"
      }
    ],
    "outputs": [
      {
        "output_type": "file",
        "output_id": 32019,
        "output_detail": {
          "file_name": "退款流程说明.pdf",
          "file_type": "pdf",
          "view_url": "https://{host}/api/open/demand/file/download?token=eyJ...",
          "expires_at": "2026-08-05T10:15:30+08:00"
        }
      }
    ]
  }
}

场景 D:已完成 — 失败终态

{
  "code": 200,
  "message": "success",
  "data": {
    "session_code": "CS-S-20260805163303-69e817db",
    "demand_id": 37408,
    "status": 99,
    "status_name": "failed",
    "finished": true,
    "error_code": "SESSION_CONTEXT_CORRUPTED",
    "error_message": "系统无法继续读取当前会话,是否清空上下文后继续执行。",
    "result_summary": "系统无法继续读取当前会话,是否清空上下文后继续执行。",
    "actions": [],
    "outputs": []
  }
}

场景对照速查

场景create 附件finishedoutputs主要阅读字段纯文本咨询无true[]result_summary / actions阅读附件后文字答复有true[]result_summary / actionsAI 生成文件可有可无true非空outputs[].view_url + result_summary执行中可有可无false[]actions(进度)失败可有可无true[]error_code / error_message

6.3 TaskSnapshot 字段

字段类型说明session_codestring会话编码demand_idint任务 IDthird_party_idstring接入标识statusint状态码,见 §6.4status_namestring状态名finishedbool是否终态biz_user_idstring贵方用户 IDassistant_idint执行助手 IDcreated_at / updated_atstring时间error_code / error_messagestring / null失败时错误信息result_summarystring / null结果摘要actionsarray公开动作流(未完成时也会返回)outputsarray文件成果;仅 `finished=true` 且本次 `demand_id` 有登记产出时有值,否则 []

6.4 任务状态

statusstatus_namefinished说明0pendingfalse排队中10runningfalse执行中11analyzingfalse分析中12processingfalse处理中16resumingfalse恢复中20completedtrue成功完成90interruptedtrue中断99failedtrue失败

6.5 actions 说明

仅返回公开白名单动作:

action_nameaction_detail 公开字段thinkingexecution_result(文本,最长约 8000 字符)textexecution_resultinteraction_questiontitle, contentinteraction_replyresponse.text / selected_label / selected_labels

内部工具调用、绝对路径、密钥等不会出现在公开 action_detail 中。

6.6 outputs 说明

字段说明output_type固定 fileoutput_id产出记录 IDoutput_detail.file_name文件名output_detail.file_type扩展名(如 pdfoutput_detail.view_url文件下载地址(含短期 token),见 §8output_detail.expires_attoken 过期时间(ISO8601,含时区)

注意:

  • 未完成任务:outputs 恒为 [](即便后台已生成文件)。
  • 输入附件(create 传入)不在 outputs 中返回;outputs 仅含 AI 产出 的可下载文件。
  • 按任务隔离outputs 只返回本次 `demand_id` 登记的文件,不包含session_code 下其他轮次(其他 demand_id)的产出。续聊时请分别 query 各 demand_id 获取对应文件。
  • 同一 demand_id 若产出多个文件,outputs 为数组,每项对应一个可下载文件。
  • view_url 每次查询重新签发,请勿长期缓存;过期后重新调用本接口刷新。
  • 不会返回服务器本地绝对路径。

7. 接口三:任务终止

按录入返回的 session_code + demand_id 终止该次任务。终止成功后任务进入终态 status=90interrupted),平台会异步触发 §9 结果回调(若已配置 callback_url);sync=true 的 SSE 连接也会收到终态事件。

  • URLPOST /api/open/demand/stop
  • 鉴权:§3 HMAC 签名

7.1 请求体

{
  "third_party_id": "agent-maiya-prod-xxxx",
  "session_code": "CS-S-20260805101200-x9y8z7w6",
  "demand_id": 12345
}
字段类型必填说明third_party_idstring是接入标识session_codestring是录入返回的会话编码demand_idint是录入返回的任务 ID

7.2 成功响应

{
  "code": 200,
  "message": "success",
  "data": {
    "session_code": "CS-S-20260805101200-x9y8z7w6",
    "demand_id": 12345,
    "status": 90,
    "status_name": "interrupted",
    "finished": true
  }
}

7.3 业务规则

规则说明可终止状态pending / running / processing / resuming 等非终态不可终止已完成(20)、已中断(90)、失败(99)→ 返回 40011权限须与 query 相同:session_code + demand_id 须属于当前 third_party_id执行中任务SSE(sync=true)检测到 90立刻推送 finished 并结束,不再继续推 action;Job 仍会在数秒内收尾排队任务尚未被 Job 领取的任务会立即变为 interrupted,并触发回调

8. 接口四:文件下载

任务完成后,通过查询(或回调)得到的 view_url 直接下载文件。

  • URLGET /api/open/demand/file/download?token={token}
  • 鉴权无需 HMAC 头;凭 query 参数 token 鉴权
  • 成功响应:文件二进制流
  • Content-Type:按文件类型推断(未知则为 application/octet-stream
  • Content-Disposition: attachment; filename="..."

7.1 请求参数

参数位置必填说明tokenQuery是由平台在 outputs[].output_detail.view_url 中签发

示例:

GET /api/open/demand/file/download?token=eyJ2IjoxLCJkZW1hbmRfaWQiOjEyMzQ1...<sig> HTTP/1.1
Host: {host}

浏览器、curl、HTTP 客户端均可直接 GET,无需额外签名头。

下载成功: HTTP 200,响应体为文件二进制流。

HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="退款流程说明.pdf"
 
<binary data>

下载失败示例:

HTTP/1.1 401 Unauthorized
Content-Type: text/plain; charset=utf-8
 
token 已过期

7.2 Token 规则

说明有效期默认 3600 秒(1 小时)绑定信息demand_idsession_code、接入配置、file_name签名使用接入配置当前有效 SECRETKEY 做 HMAC-SHA256(平台签发,贵方无需自行构造)刷新过期后重新调用 任务查询,使用新的 view_url

7.3 失败响应

下载失败时返回 纯文本(非 JSON),常见情况:

HTTP含义400token 无效 / 参数不完整 / 文件名非法401token 签名校验失败或已过期403无权访问该任务文件404任务或文件不存在500服务内部错误

7.4 安全说明

  • Token 具备时效与归属绑定,请勿公开传播长期有效的下载链接。
  • 服务端校验任务归属,并限制仅能下载该会话产出目录内文件,禁止路径穿越。
  • 响应中不会暴露服务器磁盘路径。

9. 接口五:结果回调(平台 → 第三方)

任务进入终态后,平台主动将结果推送到贵方服务。这是正式对接接口之一,方向与录入/查询相反。

说明方向平台 → 第三方方法POSTURL接入配置中的 callback_url(由平台侧开通时登记)Content-Typeapplication/json; charset=utf-8触发条件demand.status ∈ {20, 90, 99},且该任务尚未同步成功触发时机终态写入成功后立即异步 POST 一次数据体与 §6 任务查询的 data(TaskSnapshot)完全一致

8.1 前置配置

开通时向平台提供可公网访问的 HTTPS 回调地址,例如:

https://partner.example.com/hooks/mai/customer-service

要求:

  • 建议 HTTPS;若确需 HTTP,须由平台侧显式放行。
  • 地址须可被平台服务器访问(注意防火墙 / 专线)。
  • 未配置 callback_url 时,平台视为无需投递(不影响录入与查询)。

8.2 回调请求头

Header必填说明Content-Typeapplication/json; charset=utf-8X-MAI-Access-Key是接入配置当前有效 ACCESSKEYX-MAI-Timestamp是Unix 毫秒时间戳X-MAI-Nonce是随机串X-MAI-Signature是对原始回调 Body 的 HMAC-SHA256 签名(算法同 §3.2)
注意:回调头前缀为 `X-MAI-`,与贵方调用平台时使用的 X-Access-Key 等命名不同,请勿混用。

8.3 回调请求体

外层结构固定;data 与 §6 查询的 TaskSnapshot 完全一致(含各场景差异)。

通用结构:

字段类型说明eventstring固定 task.finishedtimestampint回调发出时间(Unix 毫秒)dataobjectTaskSnapshot,见 §6.3~§6.6

回调场景 A:成功 + 有产出文件(outputs 非空)

对应 §6.2 场景 C。贵方收到后应下载 data.outputs[].output_detail.view_url

{
  "event": "task.finished",
  "timestamp": 1722758772000,
  "data": {
    "session_code": "CS-S-20260805101200-x9y8z7w6",
    "demand_id": 12345,
    "status": 20,
    "status_name": "completed",
    "finished": true,
    "result_summary": "已为您生成退款流程说明文档。",
    "outputs": [
      {
        "output_type": "file",
        "output_id": 32019,
        "output_detail": {
          "file_name": "退款流程说明.pdf",
          "file_type": "pdf",
          "view_url": "https://{host}/api/open/demand/file/download?token=eyJ...",
          "expires_at": "2026-08-05T10:15:30+08:00"
        }
      }
    ]
  }
}

回调场景 B:成功 + 仅文字答复(含「阅读输入附件」)

对应 §6.2 场景 B。create 时可能传了输入附件,但 `outputs` 仍为空

{
  "event": "task.finished",
  "timestamp": 1722758976000,
  "data": {
    "session_code": "CS-S-20260805164503-5573652c",
    "demand_id": 37412,
    "status": 20,
    "status_name": "completed",
    "finished": true,
    "result_summary": "该附件文档内容非常简短……(三句话总结)",
    "outputs": []
  }
}

回调场景 C:失败终态

{
  "event": "task.finished",
  "timestamp": 1722758850000,
  "data": {
    "session_code": "CS-S-20260805163303-69e817db",
    "demand_id": 37408,
    "status": 99,
    "status_name": "failed",
    "finished": true,
    "error_code": "SESSION_CONTEXT_CORRUPTED",
    "error_message": "系统无法继续读取当前会话,是否清空上下文后继续执行。",
    "outputs": []
  }
}
回调仅在终态触发一次;data.actions 可能为空数组(与查询接口一致,以 result_summary / outputs / error_* 为准)。

8.4 贵方验签步骤(强烈建议)

算法与 §3.2 完全相同,区别仅在于 Header 名称与 PATH 取值:

  1. 读取原始请求 Body 字节(不要先 parse 再序列化)。
  2. body_hash = HEX(SHA256(raw_body))
  3. 构造规范串:
POST
{callback_url 的 path}
{X-MAI-Access-Key}
{X-MAI-Timestamp}
{X-MAI-Nonce}
{body_hash}

示例:若 callback_url = https://partner.example.com/hooks/mai/customer-service,则 PATH = /hooks/mai/customer-service(不含域名与 query)。

  1. expected = HEX(HMAC_SHA256(SECRETKEY, canonical))
  2. X-MAI-Signature 做常量时间比较;并校验 Timestamp 偏差不超过 5 分钟。

Python 验签示例:

import hashlib
import hmac
from urllib.parse import urlparse
 
def verify_mai_callback(secret_key: str, callback_url: str, headers: dict, raw_body: bytes) -> bool:
    access_key = headers.get("X-MAI-Access-Key", "")
    timestamp = headers.get("X-MAI-Timestamp", "")
    nonce = headers.get("X-MAI-Nonce", "")
    signature = (headers.get("X-MAI-Signature") or "").lower()
    path = urlparse(callback_url).path or "/"
    body_hash = hashlib.sha256(raw_body or b"").hexdigest()
    canonical = "\n".join(["POST", path, access_key, timestamp, nonce, body_hash])
    expected = hmac.new(
        secret_key.encode("utf-8"),
        canonical.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

8.5 贵方响应约定

建议返回:

{
  "code": 0,
  "message": "ok"
}

平台判定成功条件:

  1. HTTP 状态码为 2xx;且
  2. 若响应体为 JSON 且包含 code 字段,则 code 须为 `0` 或 `200`

不满足上述条件视为本次回调失败。

8.6 投递语义与幂等

行为成功标记该 demand_id 已同步,不再重复推送失败不自动重试;仅记录尝试次数;sync_status 保持未同步幂等贵方须按 data.demand_id 幂等处理(网络超时可能导致贵方已收、平台判失败)超时默认约 10 秒(以接入配置为准)与查询关系回调失败不影响贵方继续调用 §6 查询拉取结果

10. 错误码

codeHTTP说明200200成功40001403接入标识不存在/已停用,或与 ACCESSKEY 不一致40002401ACCESSKEY 无效或已过期40003401签名错误40004401请求已过期(时间戳偏差过大)40005401重复请求(Nonce 重放)40006403IP 不在白名单40007400参数校验失败40008404demand_id 不存在或无权访问40009400附件不符合要求(未开启、数量/大小/类型/URL 不合法或下载失败)40010400session 校验失败:session_code 不存在或无权访问;session_code / biz_session_idbiz_user_id 不匹配40011400需求状态不允许终止(已完成/已中断/失败)50001500服务内部错误

示例:

{
  "code": 40003,
  "message": "签名错误",
  "data": null
}

11. 推荐对接流程

10.1 纯文本咨询(无附件、无产出文件)

推荐(预热首问):

1. POST /session/init → 保存 session_code(stream_output 与后续保持一致)
2. POST /demand/create(带 session_code,无 attachments)
3. POST /demand/query 轮询至 finished=true
4. 读取 result_summary / actions(outputs 为 [])
5. (可选)收到 callback,data 与 query 一致

兼容(不预热,首问可能冷启动):

1. POST /demand/create(无 session_code、无 attachments)
2. POST /demand/query 轮询至 finished=true
3. 读取 result_summary / actions(outputs 为 [])
4. (可选)收到 callback,data 与 query 一致

10.2 带输入附件(三方传 file_url)

1. 贵方 OSS 生成 HTTPS 临时链接
2. POST /demand/create(带 attachments[])
   └─ 失败 40009 → 检查 allow_attachments / URL 可达性
3. POST /demand/query 轮询
4. finished=true 且 outputs=[] → 文字结论在 result_summary(常见)
5. finished=true 且 outputs 非空 → 另有 AI 生成文件,GET view_url 下载

10.3 要求 AI 生成文件(输出附件)

1. POST /demand/create,content 中明确说明「请生成 PDF/文档给我」
2. 轮询或等 callback
3. finished=true 且 outputs[].view_url 非空
4. GET view_url 下载(§8);token 过期则重新 query 刷新

10.4 续聊

1. 再次 POST /demand/create,带同一 session_code + biz_user_id
2. 得到新 demand_id;每轮 attachments 独立,不自动继承历史附件
3. 分别 query 各 demand_id

10.5 同步 SSE(sync=true

1. POST /demand/create,Body 含 "sync": true
2. 解析 text/event-stream:created → action* → finished
3. 若中途断开:用 created 中的 demand_id/session_code 调 query 兜底
4. 本模式不会发送终态 callback

取结果方式:

  • 异步:回调为主 + 查询兜底。
  • 同步 SSE:同连接收流;断线用 query 兜底。

12. 联调检查清单

  • [ ] 可选:打开页先调 /session/init,首问携带返回的 session_codestream_output 与 init 一致
  • [ ] 签名 PATH 使用 /api/open/session/init/api/open/demand/create/api/open/demand/query
  • [ ] 对原始发送字节计算 SHA256,避免 JSON 二次序列化改变内容
  • [ ] Timestamp 为 毫秒
  • [ ] Nonce 长度 16~64,且不重放
  • [ ] Body 含正确 third_party_id
  • [ ] 出口 IP 已加入白名单(若已启用)
  • [ ] 异步 create 后轮询或等 callback;勿在 JSON 响应中期待完整答复
  • [ ] sync=true 时按 SSE 解析,代理关闭缓冲并用 curl -N 验证
  • [ ] 按需传 stream_output(默认 true);与 sync 独立,改 Body 后须重签
  • [ ] SSE 断线后可用 query 兜底;同步模式不依赖 callback
  • [ ] 接入已开启 allow_attachments=true(若需传输入附件)
  • [ ] 输入附件使用 HTTPS 临时 URL;平台服务器须能访问
  • [ ] 区分输入附件(create)与输出文件(outputs);后者才需下载
  • [ ] 查询时同时传 session_codedemand_id
  • [ ] 仅在 finished=true 时期待 outputs 非空
  • [ ] 下载使用完整 view_url;过期后重新 query
  • [ ] 已配置可访问的 callback_url(仅异步模式需要)
  • [ ] 回调验签使用 X-MAI-* 头,PATH 取回调 URL 的 path
  • [ ] 回调按 demand_id 幂等;成功响应 HTTP 2xx 且 code 为 0 或 200

13. 修订记录

版本日期说明v1.6.02026-08-12新增 POST /demand/stop 任务终止接口;错误码 40011v1.5.22026-08-10明确 outputs 按 demand_id 隔离,续聊各轮 query 仅返回本轮产出文件v1.5.12026-08-10文档与实现对齐:session/init 鉴权说明、create 响应字段、40010/移除 40011、推荐流程与 Postman 签名 PATHv1.52026-08-10新增可选 POST /session/init 会话预热;首问可携带 init 返回的 session_codev1.42026-08-06create 支持 stream_output(默认 true)透传客服 Job include_partial_messagesv1.32026-08-06create 支持 sync=true SSE 同步流式;同步模式不发送终态 callbackv1.22026-08-05支持输入附件(file_url);补充多场景请求/响应示例;区分输入附件与输出文件v1.12026-08-05将结果回调提升为正式接口四;补充请求头、验签示例、投递语义v1.02026-08-05首版:录入、查询、文件下载与签名说明
业务
咨询