Skip to content

AgentHub SaaS 接入 Cookbook

读者:要把 AgentHub 的「会话 Agent + 多模态 + 计费」能力接进自己 SaaS 的开发者(人或 AI coding agent)。 用法:每个 Recipe 自包含、可逐条复制执行;按 §0→§12 顺序走即完成一次完整接入。 契约事实源:本文所有端点/字段均对齐 openapi.yaml(SSOT)——冲突时以 openapi 为准并回报本文勘误。导航版总览见 ai-agent-integration-guide.md状态基准:2026-08-23(MM 四项多模态真栈闭环;热修 #293~#299 已上线)。 通道仲裁(ADR-0058,2026-08-29):自营 / 孵化 SaaS 的唯一使能路线就是本 Cookbook 的 API 通道——身份用模式 B(共享 realm OIDC,ADR-0047;本文 §4 影子用户三件套属模式 A,面向自带存量用户体系的外部二开方)。代码级垂直通道(saas-factory-kit.md)已冻结,除非满足 ADR-0058 D2 解冻条件,不要走同仓/独立仓垂直路径。 浏览器前端薄壳(模式 B 前端形态):不自建后端、直接用平台 JWT 直连会话面的接入方式,见 saas-mode-b-integration-guide.md(SM2-1:模板包 + bootstrap + ≤15 步 checklist);本文面向服务端集成(模式 A 视角)为主。


§0 入驻前置(先读,1 分钟)

你需要怎么获得
一个租户 + M2M client(CLIENT_ID/CLIENT_SECRET,scopes 已配置)双通道(IE-5 起):① 自助——正式租户 tenant_admin 在 Builder「设置 → 租户控制台 → M2M 接入凭据」一键生成/轮转/吊销(scope 白名单内置,secret 仅显示一次);② 人工——联系 AgentHub 运维走 runbook Mode A §2
平台地址 BASE部署方提供,形如 https://<eip>
供应商模型 key(如 DashScope)你自己在阿里云等供应商处申请;只经注册接口入 vault,永不回显
终端用户体系完全在你侧——AgentHub 只需要「影子用户」映射(§4)

全部响应为 Envelope{"code": "...", "message": "...", "data": {...}}code != SUCCESS 即业务错误。

§1 环境变量 + 取管理面 token

bash
BASE="https://<eip>"          # 平台地址
CLIENT_ID="..."               # ops 发放的 M2M client
CLIENT_SECRET="..."

TOKEN=$(curl -sk -X POST "$BASE/auth/realms/agenthub/protocol/openid-connect/token" \
  -d "grant_type=client_credentials&client_id=$CLIENT_ID" \
  --data-urlencode "client_secret=$CLIENT_SECRET" \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')
  • token 是 RS256 JWT;CLIENT_ID 需带本文用到的 scopes(builder:* / agent:create / billing:points:tenant_grant / delivery:api_key:write / billing:ledger:read);
  • 常见 401:直连 Keycloak :8080 取 token(iss 不匹配)、secret 轮换未同步。

§2 建模:vendor → 模型组 → 模型 → 路由

一次性操作;也可在 web-builder「资源建模」界面完成,API 路径为自动化基线。以下以 DashScope 为例。

bash
# 2.1 租户 vendor(协议族:openai_compatible | dashscope;解析顺序 租户表 > env > 内置)
curl -sk -X POST "$BASE/api/v1/builder/model-vendors" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"code":"dashscope-ws","api_base":"https://dashscope.aliyuncs.com/compatible-mode/v1","protocol_family":"dashscope"}'
# → data.id = VENDOR_ID(code 可覆盖内置同名 vendor)

# 2.2 模型组
curl -sk -X POST "$BASE/api/v1/builder/model-groups" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"LLM-chat"}'          # → data.id = GROUP_ID

# 2.3 注册模型(key 只入 vault 引用;api_url 仅连通性探针不落库)
curl -sk -X POST "$BASE/api/v1/builder/models" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{
    "group_id":"'$GROUP_ID'","vendor":"dashscope-ws","name":"qwen3-vl-plus",
    "model_type":"llm","context_max_tokens":32000,"api_key":"sk-...",
    "pricing_input_per_1m_cents":1,"pricing_output_per_1m_cents":1,
    "capabilities":["vision"]}'
# → data.id = VL_MODEL_ID
#   · 视觉模型 = 普通 llm + capabilities:["vision"];语音(stt/tts)模型 vendor 必须 dashscope 协议族
#   · 非 LLM 模态必填 unit_price/price_unit(second/char/image)——表单缺字段是已知欠账 MR4-1,API 侧直接带上即可

# 2.4 路由(按组 upsert;vision 模型放 fallback 位:带图轮次独占走它,纯文本走主模型)
curl -sk -X POST "$BASE/api/v1/builder/routes" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"group_id":"'$GROUP_ID'","primary_model_id":"'$CHAT_MODEL_ID'","fallback_model_id":"'$VL_MODEL_ID'"}'

语音/画图模型同法注册到各自组(model_typestt/tts/image_gen,带 unit_price/price_unit),并为每组设路由。

§3 会话 Agent:创建 + 发布 + 免费冒烟

bash
# 3.1 创建(agent_type=conversation 是硬约束)
curl -sk -X POST "$BASE/api/v1/builder/agents" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"project_id":"'$PROJECT_ID'","code":"chat-hello","name":"会话助手","agent_type":"conversation"}'
# → data.id = AGENT_ID

# 3.2 发布(多模态全开的最小快照;单价↔护栏强制配对,缺一 409)
curl -sk -X POST "$BASE/api/v1/builder/agents/$AGENT_ID/publish" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{
    "agent_id":"'$AGENT_ID'","expected_agent_version":0,
    "role_card":{"persona_prompt":"你是耐心的客服助手。"},
    "bindings":{"primary_model_group_id":"'$GROUP_ID'","knowledge_base_ids":[],"skill_ids":[],"tool_ids":[]},
    "conversation":{
      "points_per_turn":1,
      "guardrails":{"max_input_tokens":8000,"max_attachments":3,"max_tool_calls":5,
                    "max_images_per_turn":4,"turn_timeout_seconds":240},
      "points_per_transcription":1,"points_per_speech":1,"voice":"longanyang",
      "space_permissions":{"user_space":"read_write","project_space":"read"},
      "sop_allowlist":[],"card_types_enabled":["choice_card"]
    }
  }'

要点:turn_timeout_seconds 对含画图/SOP 的 Agent 建议 ≥180(qwen-image 实测 30~90s,默认 60 会掐断在跑工具——欠账 MM6-1/MM6-2);max_images_per_turn 缺席则文生图工具不注册;speech 组价与护栏配对(XOR 拒绝)。

bash
# 3.3 免费冒烟(零计费零落库)
curl -sk -X POST "$BASE/api/v1/builder/agents/$AGENT_ID/chat-dry-run" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"message":"你能做什么?"}'
# → data.assistant_text + 配置读数(points_per_turn / guardrails / tool_count ...)

§4 终端用户三件套:影子用户 → 入账 → API-Key

bash
# 4.1 影子用户(username = 你侧不可变用户 ID;幂等)
curl -sk -X POST "$BASE/api/v1/identity/users:provision" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"username":"saas-user-10086"}'
# → data.id = SHADOW_USER_ID

# 4.2 积分入账(operation_id 幂等 = 两侧对账键;重放返回 idempotent_replay:true)
curl -sk -X POST "$BASE/api/v1/billing/points:tenant-grant" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"user_id":"'$SHADOW_USER_ID'","project_id":"'$PROJECT_ID'","amount":1000,
       "operation_id":"grant:order-20260711-0001","expires_at":"2027-07-11T00:00:00Z",
       "reason":"用户充值订单 20260711-0001"}'
# ⚠ expires_at 为必填(长效积分到期时间,ISO-8601)——漏带会 422(DX4-1 冷启动走读揪出,已回填)

# 4.3 领 API-Key(明文只返回一次;支持 :rotate 轮转 / :revoke 吊销)
curl -sk -X POST "$BASE/api/v1/delivery/api-keys" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"saas-prod"}'
# → data.api_key = "ahk_..."(妥善保存)

此后你的 SaaS 服务端调运行面,统一带两个头:

X-AgentHub-Api-Key: ahk_...
X-AgentHub-On-Behalf-Of: <SHADOW_USER_ID>

§5 开会话 + 第一轮流式对话

bash
# 开会话(幂等键可选;命中既有会话返回 200 原会话体,不新建)
curl -sk -X POST "$BASE/api/v1/app/conversations" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"agent_id":"'$AGENT_ID'","client_conversation_key":"saas-u10086-20260824"}'
# → 201 data.id = CONVERSATION_ID(注意:会话从此钉死当前 agent_version,见附录 C)

# 一轮对话(SSE 流式)
curl -sk -N -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/turns" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"content_blocks":[{"type":"text","text":"帮我查退款政策"}]}'

SSE 原始输出长这样(帧负载全形状见附录 A):

id: 1
event: turn_started
data: {"turn_id":"65598f67-...","seq":1,"status":"generating"}

id: 2
event: turn_delta
data: {"turn_id":"65598f67-...","seq":1,"text":"退款政策如下"}

id: 3
event: turn_completed
data: {"turn_id":"65598f67-...","status":"succeeded","points_charged":1,
       "message_ids":["<user_msg_uuid>","<assistant_msg_uuid>"]}

服务端消费(Python / httpx)——浏览器可用 EventSource,你的后端这样接:

python
import json, httpx

def consume_turn(conv_id: str, text: str):
    url = f"{BASE}/api/v1/app/conversations/{conv_id}/turns"
    headers = {"X-AgentHub-Api-Key": API_KEY, "X-AgentHub-On-Behalf-Of": SHADOW_USER_ID}
    event, data = None, None
    with httpx.stream("POST", url, headers=headers, timeout=300,
                      json={"content_blocks": [{"type": "text", "text": text}]}) as r:
        r.raise_for_status()
        for line in r.iter_lines():
            if line.startswith("event:"):
                event = line.split(":", 1)[1].strip()
            elif line.startswith("data:"):
                data = line.split(":", 1)[1].strip()
            elif line == "":                     # 帧边界
                if event == "turn_delta":
                    yield json.loads(data)["text"]
                elif event == "turn_completed":
                    return json.loads(data)      # 含 points_charged / balance_warning
                event, data = None, None

服务端消费(Node 18+ / fetch)

js
const resp = await fetch(`${BASE}/api/v1/app/conversations/${CONV_ID}/turns`, {
  method: "POST",
  headers: { "X-AgentHub-Api-Key": API_KEY, "X-AgentHub-On-Behalf-Of": SHADOW_USER_ID,
             "Content-Type": "application/json" },
  body: JSON.stringify({ content_blocks: [{ type: "text", text }] }),
});
let event = "", data = "";
for await (const chunk of resp.body) {
  for (const line of chunk.toString().split("\n")) {
    if (line.startsWith("event:")) event = line.slice(6).trim();
    else if (line.startsWith("data:")) data = line.slice(5).trim();
    else if (line === "" && event) { /* 同 Python:delta 累积 / completed 结束 */ event = ""; data = ""; }
  }
}

§6 断连重拉 / 历史 / 结束会话

bash
# 断连重拉:轮次在 SSE 开始前已完整落库,凭首帧 turn_id 重拉,不重跑不重复扣费
curl -sk "$BASE/api/v1/app/conversations/$CONVERSATION_ID/turns/$TURN_ID" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID"
# → TurnOut(status / points_charged / messages)

# 历史分页
curl -sk "$BASE/api/v1/app/conversations/$CONVERSATION_ID/messages?limit=50&offset=0" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID"
# → MessagePageOut{items:[MessageOut(role: user|assistant|tool, content_blocks)], limit, offset}

# 结束会话(→ closing → 编制链把转写/画像/笔记/记忆写用户空间 → closed 只读;空闲超时也会自动闭合)
curl -sk -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID:end" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID"

§7 语音输入(STT)

bash
# multipart 直传(不转码);转写结果回填你的输入框,用户可修正,再作为普通 text 轮次发送
curl -sk -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/transcriptions" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -F "audio=@speech.mp3;type=audio/mpeg" -F "language=zh"
# → TranscriptionOut: {"transcription_id":"...","text":"今天天气很好","duration_seconds":3.0,"points_charged":1}
  • MIME 白名单:wav/mpeg/ogg/opus/webm/mp4/m4a 族(带参数如 audio/webm;codecs=opus 已接受);超白名单 → 422 VALIDATION_ERROR
  • 上游高峰单次 85s+ 属正常——你的前端要按分钟级等待设计(欠账 MM6-3);
  • 快照缺 speech 组价 → 422 CONVERSATION_SPEECH_NOT_ENABLED(优雅降级为不可用)。

§8 语音播报(TTS)

bash
# 助手消息按需合成;(message_id, voice) 缓存命中零扣点返回同 URL
curl -sk -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/messages/$MESSAGE_ID/speech" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID"
# → MessageSpeechOut: {"speech_id":"...","file_id":"...","url":"https://.../fs/...",
#                      "mime_type":"audio/mpeg","size_bytes":12345,"cached":false,"points_charged":1}

url 是短 TTL 签名地址,浏览器直接可播;音色取快照 voicelonganyang 实证可用)。

§9 附件(文本类)三步上传 + 随轮提问

bash
# ① authorize(会话须 active)
curl -sk -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/attachments:authorize" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"filename":"notes.txt","mime":"text/plain","size_bytes":123}'
# → {"fid":"3,abc123","upload_url":"https://<eip>/fs/3,abc123","bucket":"...","expires_at":"..."}

# ② 直传原始字节(PUT,无额外 token)
curl -sk -X PUT "$UPLOAD_URL" --data-binary @notes.txt

# ③ commit(sha256 为幂等键;服务端抽取摘录 ≤4000 字符)
curl -sk -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/attachments:commit" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"fid":"3,abc123","mime":"text/plain","size_bytes":123,
       "sha256":"<sha256-of-bytes>","filename":"notes.txt"}'
# → AttachmentCommitOut: {"attachment_id":"...","file_id":"...","kind":"text",
#    "excerpt":"<服务端摘录>","extraction_status":"ok","reused":false}

随轮发送(file 块的 excerpt 以服务端 commit 返回为准):

bash
curl -sk -N -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/turns" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"content_blocks":[
        {"type":"file","file_id":"<commit 的 file_id>","filename":"notes.txt","excerpt":"<服务端 excerpt>"},
        {"type":"text","text":"根据附件回答:要点是什么?"}]}'

支持:txt/md 直读、csv 列头+行摘要、pdf 文本层(pypdf);扫描版 pdf → extraction_status:"no_text_layer"

§10 图片理解(vision)

上传同 §9(mimeimage/png 等,commit 后 kind:"image"extraction_status:"vision_deferred" 是正常占位),随后带图提问:

bash
curl -sk -N -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/turns" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"content_blocks":[
        {"type":"file","file_id":"$IMG_FILE_ID","filename":"photo.jpg","excerpt":"(图片附件)"},
        {"type":"text","text":"描述这张图片"}]}'
  • 前提:chat 路由含 capabilities:["vision"] 端点(§2.4 fallback 位)——否则图片降级为占位文本,助手会说「只收到注记」;
  • 图片以原生 image_url parts 随轮(7MB/图守门,超限降级);服务端以附件行判图,不信客户端 mime。

§11 文生图

无需专门端点——自然语言触发,模型经内置工具 generate_image 出图:

bash
curl -sk -N -X POST "$BASE/api/v1/app/conversations/$CONVERSATION_ID/turns" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID" \
  -H 'Content-Type: application/json' \
  -d '{"content_blocks":[{"type":"text","text":"画一张全新的 logo:金色沙漏 + 世界地图网格,深蓝背景"}]}'
# SSE 帧序:turn_started → [text_delta...] → turn_file → turn_completed
#   turn_file: {"turn_id":"...","file":{"type":"file","file_id":"<uuid>",
#               "filename":"image-1.png","mime_type":"image/png","size_bytes":12345}}

# 取图(短 TTL 签名 URL;closed 会话历史文件仍可读)
curl -sk "$BASE/api/v1/app/conversations/$CONVERSATION_ID/files/$FILE_ID" \
  -H "X-AgentHub-Api-Key: $API_KEY" -H "X-AgentHub-On-Behalf-Of: $SHADOW_USER_ID"
# → data.url 浏览器直开即图

前提:快照带 max_images_per_turn + 已注册可达的 image_gen 模型;计费并入轮次价零额外扣点;生成 30~90s,轮次墙钟要给够(§3 要点)。

§12 计费对账

bash
curl -sk "$BASE/api/v1/billing/points/ledger?user_id=$SHADOW_USER_ID" \
  -H "Authorization: Bearer $TOKEN"
# 完整链可见 entry:manual_adjust(§4.2 入账)/ freeze / deduct / unfreeze
  • 轮次:freeze(operation_id=turn:{turn_id})settle(失败 unfreeze 全额退);
  • 语音:stt:{transcription_id} 按次 / tts:{speech_id} 按次(缓存命中 0);
  • 余额不足开轮 → 402 POINTS_INSUFFICIENT(零副作用,你的产品接「前往充值」);
  • 结清后可用余额 < 3×单价 → turn_completedbalance_warning:true(仅提示)。

附录 A:SSE 帧负载速查

外部交付副本:conversation-runtime-protocol.md(仅调用方单页)——本附录与其双源维护,变更需双改

次数data 负载
turn_started1{turn_id, seq, status:"generating"}(断连重拉凭 turn_id)
turn_delta0..N{turn_id, seq, text}
turn_card0..1{turn_id, card:{type:"choice_card", card_schema_version:1, prompt, options:[{id,label}], multi, fallback_text}};下一轮回传 {"type":"choice_selection","card_message_id","selected":[...]}
turn_file0..N{turn_id, file:{type:"file", file_id, filename, mime_type, size_bytes}}
turn_completed1{turn_id, status:"succeeded", points_charged:int|null, message_ids:[...]};可选 balance_warning:true

帧格式标准 SSE(id/event/data 三行),id 支持 Last-Event-ID 续传。

附录 B:错误码目录(对话接入域)

外部交付副本:conversation-runtime-protocol.md §6——本附录与其双源维护,变更需双改

HTTPcode含义 / 处置
401API_KEY_INVALIDAPI-Key 错/吊销;检查头
403AUTH_TENANT_MISMATCHOn-Behalf-Of 用户非本租户或非 active;不降级
402POINTS_INSUFFICIENT余额不足;零副作用,引导充值后重试
404CONVERSATION_NOT_FOUND / _TURN_NOT_FOUND / _MESSAGE_NOT_FOUND / _ATTACHMENT_NOT_FOUND资源不存在或跨租户(防枚举同形)
409CONVERSATION_CLOSED会话已闭合;开新会话
409CONVERSATION_ACTIVE_TURN_CONFLICT同会话已有非终态轮次;等完成或重拉
409CONVERSATION_AGENT_NOT_PUBLISHED / _AGENT_ARCHIVED发布 Agent / 恢复后重试
409AGENT_PUBLISH_VALIDATION_FAILED发布缺单价↔护栏配对;补齐字段
409MODEL_ROUTE_INVALID路由的模型不属于该组
422VALIDATION_ERROR请求形状/ MIME / 字节数不过白名单
422CONVERSATION_SPEECH_NOT_ENABLED快照缺 speech 组价;按不可用降级
500MODEL_USAGE_METADATA_MISSING模型注册缺 unit_price/price_unit(欠账 MR4-1;补数据)
500CONVERSATION_TURN_FAILED轮次执行异常(带 trace_id 找平台)
504CONVERSATION_GUARDRAIL_VIOLATION护栏拦截(墙钟超时等;message 有具体原因)

附录 C:已知坑速查(实证,编号=修复 PR / 欠账)

症状根因处置
STT 返回转写指令复读旧线形纯文本调用(#296 已修)已修;勿引用 file_urls+prompt 形状
转写「转写中」很久上游高峰排队 85s+等;UX 提示属欠账 MM6-3
轮次 60s 后空白「成功」墙钟掐断在跑工具被静默吞掉(欠账 MM6-1)规避:发布时 turn_timeout_seconds ≥ 180
助手回复一堆 JSON工具协议散文包裹泄漏(#299 已修)已修
图片「只传了注记」chat 路由无 vision 端点(fid 404 已由 #297 修)vision 模型放路由 fallback 位
模型首次调用 500注册缺单价(欠账 MR4-1)注册时带 unit_price/price_unit
语音/画图 url errorToken Plan 网关仅服务 chat;两把 key 不可混用语音/画图模型走经典 DashScope 网关
新版本不生效会话钉死打开时的 agent_version(设计)开新会话(换 client_conversation_key 或不传)

完整欠账清单:task_package_list.md § MM/MR 真栈落地欠账。

附录 D:事实源指针

主题位置
端点契约 SSOTopenapi.yaml
curl 级教程(SOP 任务 / 会话 / 界面操作)hello-agent-tutorial.md
会话运行面协议一页(仅调用方,随凭证交付)conversation-runtime-protocol.md
最小客户端示例 + 本地 mock(SSE / 断连重拉)sdk/python/examples/
能力总览(导航版)ai-agent-integration-guide.md
架构决策(语音/附件 ADR-0055 · vision ADR-0056 · vendor ADR-0057 · 计费 ADR-0050 · 委托 ADR-0044/0052)adr/
部署与环境docs/ops/