soukmind 2.0 对接文档

读者 = 把 soukmind 接进自己 App / 后端的开发者。基线 2026-09-17。 端点清单见 REF-api-external.md(代码生成)。 本文的端点路径、事件名、卡型由 backend/tests/test_docs_alignment_gate.py 回代码求值守护。

一句话:一个对话端点 + 一套 SSE 事件契约。你发一句商家原话,拿回答文与卡片; 需要落写的操作在对话里二次确认,不另开提交接口。

sequenceDiagram
    participant C as 你的客户端
    participant S as soukmind
    C->>S: POST /api/v2/mind/turns {message, conversation_id}
    S-->>C: conversation {conversation_id}
    S-->>C: step × N(recognize / start / intent / acting / exec)
    S-->>C: action_card {kind, data, …}
    S-->>C: final {answer, tier, streaming, note}

1. 接入前提

线上根地址https://soukmind.ichain.top/ai-agent
对话端点POST /api/v2/mind/turns
本机开发http://127.0.0.1:8001(无前缀)

线上地址必须带 /ai-agent:nginx 只在这个前缀下转发后端,根路径是运营端静态站。 少了前缀返回 404 —— 这是最常见的一次误判(会被读成「端点没上线」)。

2. 鉴权

生产是 token 模式:除登录与健康检查外,一律带 Authorization: Bearer <token>身份只取令牌(商户号、门店、角色都从令牌里解,不看请求头)。

TOKEN=$(curl -s -X POST $BASE/api/v1/auth/login -H 'Content-Type: application/json' \
  -d '{"username":"<账号>","password":"<密码>"}' | jq -r .access_token)

本机/测试环境是 headers 模式,身份取 X-Merchant-Id / X-Store-Id / X-User-Id / X-Role。 两种模式的判定与权限点见 TDD-auth-login-and-permission

3. 一轮对话

请求

{ "message": "本周营业额", "conversation_id": "<你生成并全程带着>", "stream": false, "lang": "zh" }
字段必填说明
message是(除非带 action商家原话。空串不会报错,会被当闲聊处理 —— 非空校验放在你这边
action卡片按钮的回传{gid, args, confirmed?}。见 §6
conversation_id见 §7:不传不会自动生成,多轮会接不上
stream建议显式false = 一次性 JSON · true = SSE。不传时由服务端档位定,别依赖默认
langzh / en。客户端显式语言是唯一能压过原句的信号

响应形态

stream: falseapplication/json,三个顶层键:

{ "events": [ {"event": "...", "data": {...}}, ... ],
  "answer": "本周营业额 AED 4,494.00。",
  "cards":  [ {...} ] }

stream: truetext/event-stream,逐条 event: <名> / data: <json>两条路同源:JSON 里的 events 就是 SSE 那串事件,解析代码可以共用一份。

出卡那一轮是确定性产物(一次识别、一次取数、模板直出),一两秒内返回,用 JSON 更省事; 多步分析十几秒起,用 SSE 让用户先看到「在查什么」。

answer按序拼接所有 final,不是取最后一条 —— 服务端语义是分段 (streaming: true 为部分、末帧 false)。取最后一条今天同值,等接上分段流式会只剩最后一句。

4. 事件契约

闭集五种,三个客户端 SDK 按这些名字消费,取值不会改名

事件何时出data
conversation每轮首帧conversation_id
step过程可观测见下
action_card有结构化结果时见 §5
final终答answer tier streaming note
error出错错误信息

stepphaserecognize(判给谁、什么轮型)→ start(落到哪个专家)→ intent(意图与槽)→ acting(开始执行)→ exec(能力、参数、证据键、是否成功); 写确认那一轮多一个 hitl_execute

step可观测面不是契约面:phase 与字段会随能力演进增删,渲染逻辑别依赖某个 phase 一定在。 契约面是 final / action_card / conversation / error

final.note —— 客户端要分支的就这一个字段

note闭集码app/mind/types/note_kind.py)。常用的几类:

note含义客户端该做什么
""正常答完渲染 answer + 卡
_needs_confirm写操作待确认展示预览卡 + 让用户回一句确认(§6)
_needs:<槽名> · _needs_slot缺参数answer 的问句追问
_no_data该时间段没有数据原样展示,别改写成「查询失败」
_no_kb知识库没有这条_no_data 分开:这类问句根本没有时间段
_write_failed · _commit_uncertain写失败 / 可能已落地后者要提示用户去核对,别重试
_cancelled用户放弃收起待确认态

不带下划线的 note 是自由文本(成色提示),不要求分支。

5. 卡片

action_card.data 里是结构化证据,kind 说明是哪一类:

receipt(写回执/预览)· list(列表)· detail(详情)· chart(序列图)· breakdown(构成)· data(单指标)· insight(洞察)· capability(能力说明)· reference(引用)· offer(主动提议)。

每张卡都带 actions[]没有按钮时是空数组,不是缺键)与骨架字段 id / title / body / version / indicators。按钮怎么回传见 §6。

哪些卡会有按钮:

按钮回传
确认卡(receipt + note=_needs_confirm「确认」souk_cap · {gid, args, confirmed:true}
推荐卡(list·行里带 suggested_args每方案一个「选「…」」(最多 3 个)souk_cap · {gid, args} —— 带的是那一条方案的参数
提议卡(offer「就这么办」ask · {text} —— 把这句诉求当新问题发
其余无(空数组)

推荐卡的按钮各带各的参数,所以「按第二个方案做」在按钮这条路上不需要指代 —— 点哪个就是哪个。列表类卡(活动/会员/商品)不出 actions,行交互走 form 由端上渲染。

列表类卡另有 form: {kind, items, total} 供直接渲染;form.kindnb_campaign_list / nb_campaign_detail / nb_member_list / nb_product_list。 ⚠ total筛选命中总数,不是当页条数 —— 显示「共 N 条」用它。

6. 按钮回传:App 用 API 提交

卡片上的 actions[] 就是按钮。每个按钮带 action 名与 paramsApp 把 params 原样 POST 回 /api/v2/mind/turnsaction 字段即可 —— 与 1.0 同一个机制、同一个动作名 (souk_cap),SDK 不用改。

{ "action": "souk_cap", "label": "确认",
  "params": { "gid": "marketing-expert.create",
              "args": { "activity_name": "…", "activity_type": "WHOLE_ORDER",
                        "discount": { "type": "DIRECT_AMOUNT_OFF", "condition": 100, "value": 20 } },
              "confirmed": true } }

两种用法,判据是 confirmed

作用谁出这种按钮
confirmed 不给 / false结构化直调:按 gid 直接执行、用带回的 args跳过识别与抽参推荐卡的「选「…」」
confirmed: true提交:执行服务端握着的那笔待确认写确认卡的「确认」

另有一种按钮不带 gid:提议卡的 ask —— params.text 是一句诉求, 把它当新消息发出去即可({"message": params.text, "conversation_id": …}), 它会照常走一遍识别、按当时的现场补参数。

为什么跳识别:参数在出卡时就是确定的,再让模型从自然语言抽一遍只会丢 (长尾商品名的三种写法都抽不回来)。按钮轮因此不调模型,快且省。

写操作仍是两段式

写类能力在运行时被闸住,必须拿到确认才放行。两条路等价,选一条即可:

① 发起(自然语言或直调)
   → action_card kind=receipt(预览 + 确认按钮)
   → final {"note":"_needs_confirm"}
② 提交:POST {"action": <按钮的 params>}        # App 点按钮
   或   POST {"message": "确认"}                 # 用户打字
   → step.hitl_execute → final {"note":"_write_ok"}
  • 判「要不要确认」看 final.note == "_needs_confirm",不要自己猜答文。
  • 打字确认由模型判,不是词表:「确认」「好的,就这么办」「行,听你的」都算。
  • 客户端带的 args 不作为写入参数:提交时服务端用自己握着的那份(预览给用户看的就是它), args 只用来核对 gid 对不对得上。凭据始终没离开服务端。
  • 幂等键在出卡时就算好:用户照着「可能已经成功」再点一次,落的仍是同一笔。
  • 待确认态会超龄,超龄回 _write_stale(重发确认卡,不执行)。
  • confirmed: true 但服务端没有对应的待确认写 → 不执行,当作一次普通直调 (写类于是重新出一张确认卡)。宁可多问一次,也不凭客户端一句话写数据。

7. 多轮

conversation_id 由你生成并全程带着。不传时服务端原样回显空串(不会替你生成), 后果是多轮接不上、历史也对不上。同一 conversation_id 下服务端保留上下文, 所以「那上周呢」「第二个」这类残句能接住。

8. 排障

现象多半是
404线上少了 /ai-agent 前缀
401没带令牌,或令牌过期(登出会让该账号所有令牌立即失效)
拿不到 event:没写 "stream": true,服务端按档位返了 JSON
多轮像失忆conversation_id 每轮都变,或压根没传
写没落下去只发了第一轮;要看 note == "_needs_confirm" 并把按钮 params(或用户的确认)发第二轮
按钮点了没反应action 里的 gid 写错或不属于任何专家;载荷形状不对会被当作没有 action,退回按 message

9. 完整示例

BASE=https://soukmind.ichain.top/ai-agent
TOKEN=$(curl -s -X POST $BASE/api/v1/auth/login -H 'Content-Type: application/json' \
  -d '{"username":"<账号>","password":"<密码>"}' | jq -r .access_token)
CID=$(uuidgen)

# 查数据
curl -s -X POST $BASE/api/v2/mind/turns -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d "{\"message\":\"本周营业额\",\"conversation_id\":\"$CID\",\"stream\":false}"

# 发起写 → 拿到 note=_needs_confirm 与预览卡
curl -s -X POST $BASE/api/v2/mind/turns -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d "{\"message\":\"建个满100减20的活动\",\"conversation_id\":\"$CID\",\"stream\":false}"

# 点确认按钮 → 把上一步卡里 actions[0].params 原样放进 action(这一轮才落写)
curl -s -X POST $BASE/api/v2/mind/turns -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d "{\"conversation_id\":\"$CID\",\"stream\":false,\"action\":{\"gid\":\"marketing-expert.create\",\"args\":{...},\"confirmed\":true}}"

10. 其余端点

登录、会话历史、记忆召回、任务、反馈、探针共 20 条,逐条见 REF-api-external.md(方法 · 路径 · 职责 · 鉴权 · 消费方,代码生成)。