Agent 外部嵌入
将 Poco Agent 的完整对话、历史记录与产物预览安全嵌入第三方系统。
Agent 外部嵌入适合在业务门户、内部系统或客户页面中直接提供可用的 Agent 对话界面。宿主系统不需要自己实现消息渲染、历史记录和产物预览,只需提供当前访问用户的 Poco 登录 Token。
它与 API 访问是两套独立能力:
- 外部嵌入提供现成的对话 UI。
- API 提供协议接口,由接入方自己设计 UI 和业务流程。
接入前准备
开始前需要:
- 一个已经在 场景专家配置完成的 Agent。
- 一个可以登录 Poco 的访问用户。
- 第三方页面的完整 origin,例如
https://portal.example.com。 - 宿主系统服务端能够为当前用户取得 Poco 登录 Token。
如果第三方系统没有现成 Poco 登录态,可通过获取登录 Token完成服务端授权。不要把 API Key、账号密码或固定 Token 写入浏览器代码。
权限和数据边界
- 第三方页面必须为当前访问用户提供有效 Poco 登录 Token。任何未禁用的已登录用户都可以使用 Agent,不要求是 Agent 创建者。
- 对话历史按“Agent + 访问用户”隔离。访问用户只能读取自己通过外部嵌入创建的会话。
- Agent 指令、模型、MCP、技能、浏览器和产物读取使用 Agent 创建者配置的能力与权限,执行用量计入创建者。
- 外嵌会话不参与长期记忆保存、检索或提示词注入,也不会进入普通对话列表、工作台、自动发布、产物推荐和 WebHook。
- SDK 通过
postMessage把登录 Token 交给 Poco iframe。Token 只保存在 iframe 内存中,不会写入 URL 或浏览器存储。
不要向所有访问者返回 Agent 创建者的 Token。getAccessToken 必须始终返回当前正在使用宿主页面的用户身份。
在 场景专家启用
- 打开 场景专家,在目标 Agent 的“更多”菜单选择“外部嵌入”。
- 设置首次打开时展示的欢迎语。
- 添加允许嵌入的完整来源,例如
https://portal.example.com。 - 开启“启用外部嵌入”并保存。
- 在“效果预览”中检查欢迎页、历史对话、消息和产物预览。
允许来源只接受 HTTP 或 HTTPS origin,不能包含路径、查询参数、用户凭据、片段或通配符。例如:
| 输入 | 是否有效 | 原因 |
|---|---|---|
https://portal.example.com | 是 | 完整 HTTPS origin |
https://portal.example.com:8443 | 是 | 非默认端口属于 origin 的一部分 |
https://portal.example.com/page | 否 | 包含路径 |
*.example.com | 否 | 不支持通配符 |
默认端口会被归一化,例如 https://example.com:443 等价于 https://example.com。开发、测试和生产域名需要分别添加。
创建者查看外嵌对话
外部嵌入适用于单个专家,专家团当前不支持。访问者历史按身份隔离;创建者可从专家操作进入“外嵌对话”,搜索使用者和会话,并查看费用、消息、执行过程与产物。身份隔离不意味着创建者无法查看使用记录,宿主业务应据此说明数据处理方式。
Script 标签接入
直接从 Poco 域名加载 SDK:
<script src="https://poco.example.com/embed/poco-agent-embed.js"></script>
<script>
const widget = PocoAgentEmbed.mount({
agentId: "<agent-id>",
getAccessToken: () => window.yourAuth.getAccessToken(),
});
// widget.open();
// widget.close();
// widget.destroy();
</script>Script 方式会根据脚本地址自动推断 baseUrl。页面卸载或前端路由不再展示组件时调用 widget.destroy(),避免重复挂载 iframe 和事件监听。
npm 接入
安装:
pnpm add @ripperts/poco-agent-embedimport { mountAgentEmbed } from "@ripperts/poco-agent-embed";
const widget = mountAgentEmbed({
baseUrl: "https://poco.example.com",
agentId: "<agent-id>",
getAccessToken: () => yourAuth.getAccessToken(),
theme: "system",
position: "bottom-right",
defaultOpen: false,
draggable: true,
});仓库中的 npm 包源码位于 frontend/packages/agent-embed。发布静态脚本前执行:
cd frontend
pnpm sync:agent-embed-sdk固定容器模式
设置 target 后,SDK 不创建悬浮按钮,iframe 会填满指定容器:
<div id="agent-chat" style="height: 640px"></div>
<script>
PocoAgentEmbed.mount({
agentId: "<agent-id>",
target: "#agent-chat",
getAccessToken: () => window.yourAuth.getAccessToken(),
});
</script>宿主容器必须有明确高度,否则 iframe 可能没有可见空间。响应式页面可让容器宽度自适应,但仍应给出最小高度。
SDK 参数
| 参数 | 必填 | 说明 |
|---|---|---|
agentId | 是 | 场景专家中的 Agent ID |
getAccessToken | 是 | 返回当前用户 Poco 登录 Token 的同步或异步函数 |
baseUrl | npm 必填 | Poco Web 地址;Script 方式可从脚本地址推断 |
target | 否 | DOM 元素或选择器;设置后使用固定容器模式 |
language | 否 | 页面语言,默认读取宿主页面 lang |
theme | 否 | light、dark 或 system |
position | 否 | 悬浮入口位置,默认 bottom-right |
defaultOpen | 否 | 是否默认展开悬浮窗口 |
draggable | 否 | 是否允许拖动悬浮入口,默认允许 |
openIcon / closeIcon | 否 | 图片 URL、DOM Node 或返回 Node 的函数 |
onError | 否 | 鉴权或嵌入错误回调 |
getAccessToken 会在首次鉴权以及接口返回 401 后再次调用。它应返回当前用户最新的有效 Token。通过 OpenAPI 获取的登录 Token 默认有效 24 小时,普通网页登录 Token 也可以直接使用。
CSP 和反向代理
宿主系统使用 CSP 时,至少允许 Poco 来源:
Content-Security-Policy: script-src 'self' https://poco.example.com; frame-src https://poco.example.com反向代理必须转发:
/embed/poco-agent-embed.js/{language}/embed/agents/{agent_id}/api/v1/agent-embeds/*
嵌入页面的 API 和 SSE 都从 iframe 内同源访问 Poco,不要求把每个客户域名加入后端 CORS。
如果反向代理统一添加 X-Frame-Options 或 Content-Security-Policy: frame-ancestors 'self',需要对 /{language}/embed/agents/* 移除该限制,否则浏览器会拒绝加载 iframe。Poco 仍会使用 Agent 配置中的允许来源检查宿主 origin。
常见问题
| 现象 | 排查方向 |
|---|---|
| iframe 被浏览器拒绝加载 | 检查 X-Frame-Options 和 frame-ancestors |
| 页面提示来源不允许 | 确认协议、域名和端口与允许来源完全一致 |
| 首次能用,过期后返回 401 | 确认 getAccessToken 能刷新并返回最新 Token |
| 固定容器不可见 | 检查 target 是否匹配元素,以及容器是否有高度 |
| SDK 脚本 404 | 检查反向代理是否转发 /embed/poco-agent-embed.js |
| 对话没有出现在普通会话列表 | 这是外嵌会话的数据边界,不属于异常 |
完成接入后,分别使用两个不同用户验证历史记录隔离,并验证 Token 过期后的重新获取流程。