Cadau 嵌入 SDK 契约(V1.5.13)
- 目标:让宿主系统以最低接入成本集成“我的智能体”对话能力,并支持有限、可审计的页面操作联动。
来源 sdk/host-embed/SDK契约.md
适用范围:外部系统通过一段 JS 嵌入「我的智能体」,在宿主页面实现 AI 对话与受控操作。
对齐来源:
docs/产品规格.md§4.2.3、§7.5。实现对照:嵌入前端见
sdk/host-embed/widget/;静态脚本由GET /embed/mindlink-widget.min.js提供(backend/internal/embedsdk);令牌 API 见POST /api/v1/user-agents/{id}/embed-token。集成说明见同目录网站集成说明.md。
1. 目标与边界
- 目标:让宿主系统以最低接入成本集成“我的智能体”对话能力,并支持有限、可审计的页面操作联动。
- 边界:嵌入端不持有长期密钥;不允许任意脚本执行;所有对话能力统一走 Cadau 后端 REST。
2. 接入方式
2.1 Script + JS 初始化
官方部署由 Cadau 服务 提供静态脚本(与 API 同源),路径固定为 /embed/mindlink-widget.min.js。宿主升级 Cadau 后无需替换自托管的 JS 文件,只需继续引用该 URL。
A 路径(公开客服,推荐最小配置):
<script src="https://mindlink.example.com/embed/mindlink-widget.min.js"></script>
<script>
window.MindLinkWidget.init({
base_url: "https://mindlink.example.com",
user_agent_id: "ua_123",
auth: { token: "eyJ..." }
});
</script>
也可在 script 标签上使用 data-base-url / data-agent-id / data-token 自动挂载(可选 data-app-id、data-theme、data-position、data-entry、data-title、data-greeting、data-welcome)。「网站嵌入」复制脚本时可勾选入口形态。静态对照:examples/cadau-embed-site/。
B 路径(业务系统) 须传 host_actor(当前登录用户身份),并可传 api_base_url、app_id、workspace_id、auth.expires_at 等(见宿主增强文档)。
2.2 Web Component 初始化
HTML 属性可覆盖 app-id、api-base-url、base-url、user-agent-id、workspace-id、theme、position、locale;auth / host_actor 须用 JS 赋值(无 HTML 属性)。未设置 auth 时不会挂载。
元素暴露与 WidgetHostApi 相同的方法:on / open / close / destroy / updateAuth / updateHostActor / sendMessage。container 固定为元素自身(行内挂载时把元素放进业务容器即可)。
<mindlink-widget
base-url="https://mindlink.example.com"
user-agent-id="ua_123"
theme="auto"
position="bottom-right"
></mindlink-widget>
<script>
const el = document.querySelector("mindlink-widget");
el.auth = { token: "eyJ..." };
// el.host_actor = { external_user_id: "...", actor_kind: "employee", ... };
el.on("action", (ev) => { /* 同 Script init */ });
</script>
Script 行内挂载可传 container(选择器或 HTMLElement),与 position: "inline" 配合使用(见 WidgetInitOptions)。
3. TypeScript 契约(建议)
export type WidgetTheme = "light" | "dark" | "auto";
export type WidgetPosition = "bottom-right" | "bottom-left" | "middle-right" | "center" | "inline";
export interface EmbedAuth {
token: string;
/** 可选;缺省时挂件不本地预判过期 */
expires_at?: string; // ISO 8601
}
/**
* 当前宿主登录用户(B 路径必传)。Cadau 不以此建账号。
* 校验:`external_user_id` 必填;`actor_kind` 为 `employee` 时 `employee_id` 必填。
* 未传 `actor_kind` 时:有 `employee_id` 则视为 `employee`,否则 `business`。
*/
export interface HostActor {
external_user_id: string;
actor_kind?: "business" | "employee";
display_name?: string;
tenant_external_id?: string;
employee_id?: string;
emp_no?: string;
roles?: string[];
managed_org_unit_ids?: string[];
managed_employee_ids?: string[];
org_unit_id?: string;
org_unit_name?: string;
}
export interface WidgetInitOptions {
/** Cadau 站点根;未传 api_base_url 时推导为 `{base_url}/api/v1` */
base_url?: string;
/** 可选;缺省 `mindlink-embed` */
app_id?: string;
/** 可选;可由 base_url 推导 */
api_base_url?: string;
user_agent_id: string;
auth: EmbedAuth;
/** 可选;缺省依赖 JWT wid */
workspace_id?: string;
theme?: WidgetTheme;
position?: WidgetPosition;
/**
* 预留语言码(默认 `"zh-CN"`)。当前 UI 文案固定中文,设置后无切换效果。
*/
locale?: string;
entry?: {
auto_open?: boolean;
/** 用户明确请求打开界面时,自动执行回答里首条宿主可执行的 mindlink://action/(默认 true) */
auto_execute_navigation?: boolean;
/** 空态提示文案;空字符串时使用内置默认句 */
welcome_text?: string;
/** 面板标题;空则用「操作助手」 */
title?: string;
/** 标题下说明 */
subtitle?: string;
/** 首次来访在角落按钮旁出一句招呼(打开或关掉后不再出) */
greeting?: boolean;
greeting_text?: string;
};
/** 挂载父节点;默认 `document.body`。inline 模式建议传入页面内容器。 */
container?: HTMLElement | string;
/**
* 当前登录用户身份(B 路径必传)。写入 chat 请求的 `client_context.host_actor` 与顶层 `host_actor`;
* 历史会话 / 人工客服 / 工单按此人隔离;传软查数亦用。换用户时调用 `updateHostActor`。
*/
host_actor?: HostActor;
}
export interface WidgetHostApi {
open(): void;
close(): void;
destroy(): void;
updateAuth(auth: EmbedAuth): void;
/** 登录用户切换时更新身份(与 init.host_actor 同结构)。 */
updateHostActor(hostActor: HostActor | null | undefined): void;
sendMessage(message: string): Promise<void>;
on(event: WidgetEventName, handler: (event: WidgetEvent) => void): () => void;
}
export type WidgetEventName = "ready" | "message" | "action" | "error" | "close";
export interface WidgetBaseEvent {
type: WidgetEventName;
request_id?: string;
ts: string; // ISO 8601
}
export interface WidgetReadyEvent extends WidgetBaseEvent {
type: "ready";
app_id: string;
user_agent_id: string;
}
export interface WidgetMessageEvent extends WidgetBaseEvent {
type: "message";
session_id: string;
message_id: string;
role: "user" | "assistant" | "system";
content: string;
}
export type WidgetActionType = "open_url" | "open_module" | "emit_event";
export interface WidgetActionEvent extends WidgetBaseEvent {
type: "action";
action: {
type: WidgetActionType;
payload: Record<string, unknown>;
};
}
export interface WidgetErrorEvent extends WidgetBaseEvent {
type: "error";
code: string;
message: string;
}
export interface WidgetCloseEvent extends WidgetBaseEvent {
type: "close";
reason?: string;
}
export type WidgetEvent =
| WidgetReadyEvent
| WidgetMessageEvent
| WidgetActionEvent
| WidgetErrorEvent
| WidgetCloseEvent;
4. 鉴权与安全要求
4.1 令牌签发(当前实现)
- 由 Cadau 主站登录用户 调用
POST /api/v1/user-agents/{id}/embed-token签发(该助手「管理 → 网站嵌入」);请求体可选app_id(默认mindlink-embed)、ttl_seconds、permanent、host_actor(B 路径代签时写入登记,对话与数据策略以登记为准,浏览器无法改成别人)。 - 响应含
access_token、token_type(Bearer)、expires_in(秒)、expires_at、user_agent_id、workspace_id、app_id、permanent、record_id;登记写入embed_access_tokens(含app_id、吊销用jwt_jti)。同一user_agent_id可被多个第三方app_id复用;宿主侧用户与权限由第三方系统自行维护。 - 宿主后端代签为推荐生产形态,但须自行对接上述 API 或等价签发服务;契约不假定宿主自建 JWT。
4.2 JWT 与校验(当前实现)
- 嵌入 JWT Claims(
authx.Claims):sub(用户)、wid(工作区)、emb=1、jti(= 登记行jwt_jti)、exp。JWT 内不含app_id/user_agent_id;二者由init参数与登记行约束,业务上须与签发时一致。 - 受保护 API:
Authorization: Bearer;emb=1时 middleware 额外查embed_access_tokens未吊销且未过期,失败码embed_token_revoked。 - 过期/签名错误:通用
unauthorized(文案「令牌无效或已过期」),尚未单独返回embed_token_expired/embed_token_invalid。 - 可选请求头
X-Workspace-Id:与init.workspace_id一致时推荐携带(embed-sdk 已发)。 - 请求头
X-Host-External-User-Id(embed-sdk 已发):B 路径为签发时绑定的host_actor.external_user_id;A 路径为浏览器访客标识。用于历史会话按人/按访客隔离。未带此头且令牌未绑定身份时嵌入会话列表返回空(避免串话)。 - 数据权限身份:仅采用签发时写入
embed_access_tokens.host_actor_json的登记;请求体host_actor不能冒充他人。A 路径未绑定时不采信客户端身份做查数。 POST /api/v1/host/agent-runs拒绝嵌入令牌(forbidden_embed_token),须用集成账号在服务端调用。
4.3 其他
- 支持短期与长期有效令牌;长期仍可通过
DELETE /api/v1/embed-access-tokens/{id}收回。 embed_origin_not_allowed/ 来源白名单:配置http.allowed_origins(或环境变量HTTP_ALLOWED_ORIGINS)后生效;未配置时 CORS 仍反射请求 Origin(仅本地联调)。- 默认 Shadow DOM 隔离;宿主配置 CSP,禁止不可信脚本注入。
5. 对话与动作协议
5.1 Chat 请求(嵌入模式,当前实现)
嵌入挂件使用 POST /api/v1/chat/stream(SSE),非同步 POST /api/v1/chat。
挂件已处理的 SSE 事件:start / token / done / error,以及工具轮次的 round_start / tool_call / tool_output(用于展示进度;宿主一般无需监听)。
{
"message": "请给我今天的跟进建议",
"session_id": "optional",
"user_agent_id": "ua_123",
"request_id": "req_embed_001",
"workspace_id": "ws_001",
"client_context": {
"channel": "embed_widget",
"app_id": "crm-prod",
"page_url": "https://crm.example.com/home",
"host_actor": { "external_user_id": "…", "actor_kind": "employee" }
},
"host_actor": { "external_user_id": "…", "actor_kind": "employee" },
"attachment_ids": []
}
user_agent_id必填;init时为空会在前端抛错,无法发消息。client_context:客户端已发送,服务端已解析。channel=embed_widget(或 JWTemb=1)时会话来源记为嵌入;app_id写入会话的embed_app_id;host_actor(或顶层同名字段)用于会话隔离与数据连接策略。B 路径下列表/历史/人工客服/工单按host_actor.external_user_id只返回该登录用户自己的内容。- 历史与会话:
GET /api/v1/chat/sessions?kind=agent&user_agent_id=...(嵌入可带host_external_user_id或请求头X-Host-External-User-Id)、GET /api/v1/chat/history?session_id=...(embed-sdk 已用)。 - 可选附件:
attachment_ids(先POST /api/v1/uploads);编辑重发:POST /api/v1/chat/truncate;停止生成:GET /api/v1/chat/sessions/{id}/generation、POST …/generation/stop(挂件已接)。 - 同时正在回复人数达上限时,本接口返回 HTTP 429、码
embed_generation_limit。上限优先读该智能体配置max_embed_streams(Cadau:我的智能体 → 嵌入助手同时回复上限)。未设则用服务器默认(通常 20)。智能体配置不得高于服务器上限;服务器上限 > 0 时,智能体填 0 会回落到服务器默认,并不能突破成不限制。仅当服务器默认本身为 0 时,0 才表示不限制。硬顶 256。
5.2 动作白名单
- 允许:
open_url、open_module、emit_event - 禁止:
eval、动态脚本注入、未经声明的跨域代理请求
5.3 动作放行建议
open_url需校验域名白名单。open_module仅可访问宿主预注册模块 ID。emit_event限定允许字段,避免透传敏感数据。
5.4 回答内功能导航链接(当前实现)
智能体在 Markdown 回答中可输出(知识文档需教会模型):
`打开待审批订单`
- 用户点击后,挂件触发
action事件,action.type === "emit_event",payload.kind === "mindlink_action",payload.action为路径(如page.orders),payload.params为查询参数对象(含筛选字段与可选label)。 - 宿主页在
widget.on("action", …)中白名单处理;不会自动跳转业务路由。 entry.auto_execute_navigation(默认true):用户消息匹配「帮我打开 / 跳转 / 前往…」等意图时(亦匹配句首「打开|去|进入…」、英文open|navigate|go to等,见mindlinkAction.ts),流式回答结束后挂件 自动执行正文中首条在嵌入表面可点击的mindlink://action/链接(跳过主站专用动作,与点击过滤一致;仍走宿主白名单)。实现见sdk/host-embed/widget/src/mindlinkAction.ts、EmbedApp.tsx。- 与主站帮助一致的
mindlink://action/module.workspace等在嵌入表面不可点击(提示「请在 Cadau 中打开」);宿主自定义page.*/host.*/ 非主站module.*可执行。 - 详见
网站集成说明.md§5、§5.6;宿主知识撰写见宿主知识文档撰写要求.md§6.2。
尚未实现:后端在 SSE 流中直接下发结构化 open_url / open_module / emit_event 动作卡片。宿主侧 handleHostNavigation 可预留这些 action.type,但当前嵌入路径仅通过 Markdown mindlink://action/ → emit_event + mindlink_action 触发。
6. 嵌入界面(当前交付)
6.1 展示形态
- 悬浮窗模式(默认):
position: bottom-right(亦支持bottom-left、middle-right右侧居中、center页面正中);角落或指定位置入口,点击展开面板。entry.greeting为真时,该浏览器第一次来访会在按钮旁出一句招呼(打开或关掉后不再出)。data-entry:corner(默认)/greeting/open(打开即展开)。 - 侧边栏模式:未实现(无独立
sidebarposition)。 - 行内模式(inline):
position: inline+container;适合帮助中心、配置页内嵌。
6.2 theme 与宿主页面
light/dark:固定浅色或深色面板。auto:优先读取宿主<html data-theme="dark|light">(若存在则以此为准);否则若html或body带有 classdark,视为深色;最后再使用浏览器prefers-color-scheme。宿主切换主题时嵌入端会监听并同步亮暗,避免「系统浅色、应用内深色」时面板仍停留在浅色。
6.3 当前已交付(embed-sdk,供联调)
| 能力 | 状态 |
|---|---|
Shadow DOM + window.MindLinkWidget.init / <mindlink-widget>(含 Host API:on 等) | 已交付 |
chat/stream 流式对话、会话列表与历史 | 已交付 |
ready / message / action / error / close / updateAuth / updateHostActor / sendMessage | 已交付 |
init.host_actor → chat client_context.host_actor(会话隔离 / 数据策略) | 已交付 |
B 路径:历史会话 / 人工客服 / 工单按 host_actor.external_user_id 只显示本人 | 已交付 |
| A 路径:历史会话 / 人工客服 / 工单按浏览器访客标识只显示本人(与工单同一套隔离) | 已交付 |
mindlink://action/ 链接点击 → action(mindlink_action) | 已交付 |
用户明确要求打开时自动执行首条宿主可执行导航(entry.auto_execute_navigation,默认 true) | 已交付 |
入口/面板拖拽与尺寸记忆(按 app_id 键;B 路径会话键另含宿主用户) | 已交付 |
A 路径精简 init:base_url + user_agent_id + auth.token(workspace_id / app_id / expires_at 可选) | 已交付 |
挂件「人工客服」:GET …/human-support/status(enabled / live_available / tickets_available)+ /embed/cs-tickets*(排队、结束、评价、改提工单;A:浏览器访客标识;B:宿主登录用户) | 已交付 |
挂件「提交工单 / 我的工单」:/embed/support-cases*(异步;可从即时改提;隔离规则同上) | 已交付 |
多人同时对话;超额 embed_generation_limit(智能体可配同时回复上限) | 已交付 |
宿主 LLM 服务:POST /api/v1/host/agent-runs(集成账号;指定智能体代答,必带 host_actor;任务型可 fresh 新会话) | 已交付 |
上下文用量环(context_budget) | 已交付 |
| 回答末尾「下一步」建议芯片(与主站同解析) | 已交付 |
locale 切换多语言文案 | 未交付(字段保留,UI 固定中文) |
| 操作卡片、移动端全屏抽屉 | 未交付 |
面板标题 / 首次招呼(entry.title / entry.greeting;脚本 data-title / data-entry / data-greeting) | 已交付 |
令牌过期专用 UI / embed_token_expired 错误码 | 未交付(依赖宿主 updateAuth) |
| SSE 直接下发结构化动作卡片 | 未交付 |
7. 时序(端到端)
- 在 Cadau 签发嵌入令牌(
embed-tokenAPI,登记app_id+user_agent_id);A 路径在「网站嵌入」生成;B 路径可由宿主后端代调。 - 宿主前端加载 widget 并
init(A:base_url+user_agent_id+token;B 须传host_actor,换用户时updateHostActor)。 - widget 触发
ready事件。 - 用户发消息,widget 调用
/api/v1/chat/stream。 - 流式返回助手正文(可含
mindlink://action/链接)。 - (可选)用户点击导航链接,或自动导航 → 宿主白名单执行。
- (可选)开启人工客服后,挂件可提交
/api/v1/embed/cs-tickets(即时)或/api/v1/embed/support-cases(工单);客服在 Cadau 工作台处理(本区席位或已授权客服小组),列表实时刷新。小组工作台接单不切换顶栏当前工作区。宿主 不必 调用客服小组管理 API。 - 令牌失效:
unauthorized/embed_token_revoked;B 路径可updateAuth/updateHostActor。
8. 联调检查清单
- [ ] 可正常加载 widget(含 Shadow DOM)。
- [ ] A 路径仅
base_url+user_agent_id+auth.token可对话。 - [ ]
user_agent_id/auth.token缺失时init失败或无法发消息(当前为初始化抛错)。 - [ ]
chat/stream可收发;request_id可追踪。 - [ ] 开启人工客服后挂件可「人工客服」(须有人上班)与「提交工单」;未开启则无入口。客服工作台无需整页刷新即可看到新单。本区席位或已授权客服小组均可接单。
- [ ] (B)两名登录用户互看不到对方的历史会话、进行中客服与工单;
host_actor.external_user_id已传入。 - [ ] (A)两名网站访客互看不到对方的历史会话、进行中客服与工单(按浏览器访客标识)。
- [ ] 两名访客可同时提问并各自收到回复;过载时挂件提示「当前对话人数已达上限,请稍后再试」。
- [ ]
mindlink://action/链接可点击并触发action(kind: mindlink_action);宿主已实现白名单。 - [ ] 用户说「帮我打开 xx」时,回答完成后自动导航符合预期(或已设
auto_execute_navigation: false);主站专用动作不会被自动执行。 - [ ] (传软)
host_actor已传入;换用户后updateHostActor生效。 - [ ] 收回令牌后请求返回
embed_token_revoked。 - [ ] 令牌刷新后
updateAuth可继续对话。 - [ ] (生产)已配置
http.allowed_origins;未登记来源返回embed_origin_not_allowed。 - [ ] 用户可见错误为自然语言,非裸错误码。
9. 错误码
| 码 | 状态 | 说明 |
|---|---|---|
embed_token_revoked | 已实现 | 嵌入登记已收回或过期 |
unauthorized | 已实现 | 缺令牌、无效或 JWT 过期 |
embed_generation_limit | 已实现 | 同时正在回复的人数已达上限(HTTP 429);文案「当前对话人数已达上限,请稍后再试」 |
embed_token_expired | 规划 | 当前并入 unauthorized |
embed_token_invalid | 规划 | 当前并入 unauthorized |
embed_origin_not_allowed | 已实现 | 已配置来源白名单且请求 Origin 不在名单内 |
forbidden_embed_token | 已实现 | 用嵌入令牌调用了仅集成账号可用的接口(如 Host Agent Run) |
embed_agent_forbidden | 规划 | |
embed_action_not_allowed | 规划 | |
embed_workspace_mismatch | 规划 |
10. 版本记录
- V1.5.14(2026-08-18):网站嵌入「放在哪一边」增加右侧(
middle-right)与中间(center)。未写data-position时仍为右下角。 - V1.5.13(2026-08-18):网站嵌入复制脚本可选入口形态——角落按钮 / 首次招呼 / 打开即展开;可写面板名称与招呼一句(
data-entry/data-title/data-greeting/data-position)。未写这些属性时行为与 V1.5.12 相同。 - V1.5.12(2026-08-17):挂件脚本从约 8MB 降到约 500KB(gzip 约 150KB)——不再打进主站文件预览库与整份 API 客户端;示意图按需从 Cadau 站点加载 Mermaid,HTML 围栏在新窗口预览。
- V1.5.11(2026-08-17):签发可绑定
host_actor(对话/查数以登记为准);http.allowed_origins启用后来源白名单与embed_origin_not_allowed;Host Agent Run 拒绝嵌入令牌。 - V1.5.10(2026-08-17):文档与实现对齐——
max_embed_streams解析(服务器上限封顶;智能体填 0 在服务器默认 > 0 时回落默认)、回答「下一步」芯片已交付、data-app-id/data-theme、代签响应补token_type/expires_in/permanent。 - V1.5.9(2026-08-17):人工客服运营对齐——本区席位或客服小组均可接嵌入来单;
live_available计入覆盖该工作区的上班客服;小组工作台接单不切换顶栏工作区。挂件接口不变。 - V1.5.8(2026-08-13):文档与实现对齐——
HostActor字段、human-support/status分项、会话附件/停止/编辑重发、嵌入同时回复上限(max_embed_streams)。 - V1.5.7(2026-08-13):Host Agent Run 支持
fresh每次新会话;HR 范例「按部门建议岗位」走.env/ 宿主设置指定的分析智能体(只预览不落库)。 - V1.5.6(2026-08-13):嵌入助手允许多位访客同时对话;超额返回
embed_generation_limit。 - V1.5.5(2026-08-13):A 路径历史会话按浏览器访客标识隔离(与工单一致);嵌入未标明访客时会话列表返回空,避免串话。
- V1.5.4(2026-08-13):B 路径历史会话 / 人工客服 / 工单按
host_actor.external_user_id只显示当前登录用户自己的内容。 - V1.5.3(2026-08-13):B 路径与静态嵌入对齐——挂件人工客服 + 异步工单(
/embed/support-cases);客服工作台实时刷新。 - V1.5.2(2026-08-12):A 路径公开客服——
base_url精简 init、挂件人工客服与/embed/cs-tickets;静态范例examples/cadau-embed-site/。 - V1.5.1(2026-08-11):文档精简——删除未交付 UI 规划长文与线框图清单;功能导航独立文档并入
网站集成说明.md§5。 - V1.5(2026-08-11):与实现对齐:补充
host_actor/updateHostActor、client_context服务端解析与会话隔离、Web Component Host API、自动导航与点击过滤一致、SSE 扩展事件;标明locale与结构化动作卡片未交付。 - V1.4(2026-05-26):补充
entry.auto_execute_navigation(默认 true)、自动导航行为与联调项;集成说明见网站集成说明.md§5.6。 - V1.3(2026-05-19):与
embed-sdk/ 后端实现对齐:补充chat/stream、embed-token签发、mindlink_action导航、Web Componentauth/container;区分已交付与规划 UI/错误码。 - V1.2(2026-04-27):新增线框图级组件清单、页面骨架示意与前端状态机建议(已于 V1.5.1 移除)。
- V1.1(2026-04-27):新增嵌入界面设计说明(规划节已于 V1.5.1 精简)。
- V1(2026-04-27):首版,覆盖初始化参数、TS 契约、鉴权、安全、动作白名单与联调清单。