---
url: /v6.2/copilot-sdk-iframe.md
---
# 通过 iframe 集成 ChatBI

`iframe` 有两种常见用法：

1. 直接嵌入完整 ChatBI 页面，最快得到一整页问答体验。
2. 在父页面加载 Copilot JS SDK，再把衡石仪表盘放进 iframe，由父页面 Copilot 接管 iframe 内的智能助理能力。

> \[!TIP] 建议先看总览
> 如果你们还没判断清楚运行方式和接入方式的关系，建议先读 [Data Agent 集成总览](copilot-sdk.md)。

## 方式一：嵌入完整 ChatBI 页面

这种方式适合：

* 希望最快上线一个可用入口
* 前端资源有限，不想自己实现聊天界面
* 接受直接复用衡石完整页面
* 不打算把聊天结果拆成自己页面里的多个局部模块

最小示例：

```html
<iframe
  src="https://your-hengshi-host/copilot"
  width="100%"
  height="100%"
  style="border: 0;">
</iframe>
```

只要当前浏览器已经具备衡石登录态，或者你们已经完成了单点登录，页面就可以直接显示 ChatBI。

### 常用 URL 参数

#### 指定数据来源

与图表所在数据源对话：

```text
?appId={应用 ID}&chartId={图表 ID}
```

与数据包数据模型对话：

```text
?dataAppId={数据包 ID}&datasetId={数据集 ID}
```

与多个数据源对话：

```text
?dataSources=[{"dataAppId":{数据包 ID1},"datasetId":{数据集 ID1}},{"dataAppId":{数据包 ID2},"datasetId":{数据集 ID2}}]
```

> \[!TIP] 提示
> `dataSources` 需要先做 URL encode。

#### 指定图表主题

```text
?chartTheme={仪表盘主题 ID}
```

#### 只显示指定对话

```text
?conversationId={对话ID1,对话ID2}&chatUid={uid1,uid2}
```

#### 登录认证

```text
?activeAuth=jwt-param&jwtParam={JWT 参数}
```

## 方式二：父页面 Copilot SDK 接管 iframe 仪表盘

如果你们自己的门户页已经通过 [JS SDK 集成](copilot-sdk-js.md) 放了 Copilot 助手，同时又希望在页面中嵌入衡石仪表盘，就使用这种方式。

这种方式的效果是：

* Copilot 侧边栏只显示在父页面，不再显示在 iframe 仪表盘里
* iframe 仪表盘里的“打开助手”“智能解读”“图表解读”等入口会打开父页面 Copilot
* Agent 需要读取或编辑 iframe 仪表盘时，会通过 iframe 代理出来的业务动作和业务对象完成
* 父页面路径可以是任意路径，也可以和衡石不同域，不依赖 `/sdk/` 这样的测试路径

### 最小可运行示例

下面示例假设：

* 父页面是你们自己的门户页面
* Copilot JS SDK 加载自衡石服务
* iframe 内嵌的是衡石仪表盘 `/app/{appId}/dashboard/{dashboardId}`
* 父页面 Copilot 接管 iframe 内的智能助理能力

```html
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <title>Copilot SDK + iframe Dashboard</title>
  <script>
    window.__hs_sdk_base_url__ = 'https://your-hengshi-host';
  </script>
  <script src="https://your-hengshi-host/assets/hengshi-copilot@<version>.js"></script>
  <style>
    html,
    body {
      width: 100%;
      height: 100%;
      margin: 0;
    }

    #dashboard-frame {
      width: 100%;
      height: 100%;
      border: 0;
    }
  </style>
</head>
<body>
  <div id="copilot-root"></div>
  <iframe id="dashboard-frame" title="HENGSHI Dashboard"></iframe>

  <script>
    const hengshiHost = 'https://your-hengshi-host';
    const appId = 43900;
    const dashboardId = 2;

    const dashboardUrl = new URL(`/app/${appId}/dashboard/${dashboardId}`, hengshiHost);
    dashboardUrl.searchParams.set('agentHost', 'parent');
    dashboardUrl.searchParams.set('parentOrigin', window.location.origin);

    document.getElementById('dashboard-frame').src = dashboardUrl.toString();

    const copilot = new window.Copilot(document.getElementById('copilot-root'), {
      locale: 'zh-CN',
      draggable: {
        enable: true,
        bounds: 'window',
        minWidth: 440,
        minHeight: 440,
        position: { x: window.innerWidth - 480, y: 24 },
        size: { width: 440, height: window.innerHeight - 48 },
      },
    });
  </script>
</body>
</html>
```

通常不需要手动注册 iframe。父页面加载 `hengshi-copilot@<version>.js` 后，SDK 会跟踪页面中的 iframe；iframe 页面通过 URL 参数声明由父页面接管后，两边会自动建立通信。

### iframe URL 参数

| 参数 | 是否推荐 | 说明 |
|---|---:|---|
| `agentHost=parent` | 推荐 | 让 iframe 内的 Agent / Copilot 能力由父页面 SDK 承接。 |
| `copilotHost=parent` | 可用 | `agentHost=parent` 的兼容写法。新接入优先使用 `agentHost=parent`。 |
| `parentOrigin={父页面 origin}` | 推荐 | 指定允许通信的父页面 origin，例如 `https://portal.example.com`。只写 origin，不带路径、query 或通配符。 |
| `copilotParentOrigin={父页面 origin}` | 可用 | `parentOrigin` 的兼容写法。 |
| `agentHost=local` | 可用 | 明确让 iframe 页面使用自己的 Copilot，不由父页面接管。 |
| `copilotHost=local` | 可用 | `agentHost=local` 的兼容写法。 |
| `agentBridge=true` | 高级 | 只显式打开 Agent 桥接，不等同于完整的父页面 Copilot 接管。普通接入不要优先使用。 |

推荐写法：

```text
https://your-hengshi-host/app/43900/dashboard/2?agentHost=parent&parentOrigin=https%3A%2F%2Fportal.example.com
```

`parentOrigin` 用于安全校验。配置后，iframe 只接受这个 origin 的父页面消息；配置错误时不会自动退回到宽松模式。

### 接管后的行为边界

父页面接管后，用户看到的是一个 Copilot：

* iframe 仪表盘不再显示自己的 Copilot 侧边栏
* 用户点击仪表盘里的智能助理入口时，会打开父页面 Copilot
* 父页面 Copilot 的 actions 区域可以显示 iframe 仪表盘提供的“智能解读”
* 图表上的“解读”会把图表上下文发送给父页面 Copilot

对 Agent 来说，iframe 仪表盘不是父页面 DOM 的一部分。Agent 不应该通过父页面的点击、拖拽或路由跳转去操作 iframe 内部内容，而是通过 iframe 代理出来的业务能力读取和修改仪表盘。

调试日志里如果看到 `execute_action`、`inspect_element`、`read_path`、`patch_object` 等工具访问 iframe 仪表盘，这是正常行为。它们用于读取仪表盘上下文、查看图表配置和提交 dashboard patch。

### 跨域和安全要求

跨域接入时请确认：

* iframe `src` 指向真实的衡石页面 URL，例如 `/app/{appId}/dashboard/{dashboardId}`
* 父页面加载了 `hengshi-copilot@<version>.js`
* iframe URL 带上 `agentHost=parent`
* iframe URL 带上准确的 `parentOrigin`
* 衡石站点允许被你们的门户页面 iframe 嵌入
* 如果 iframe 使用 `sandbox`，至少需要允许脚本运行和同源能力，例如 `allow-scripts allow-same-origin`
* 登录态、JWT 或 SSO 能让 iframe 页面和父页面 SDK 都正常访问衡石接口

通信安全边界是 origin，而不是页面路径。父页面可以是任意路径，但 origin 必须和 `parentOrigin` 完全一致。

## 本地联调 Q\&A

### Q：本地怎么跑一个 SDK + iframe dashboard 的验证页？

研发本地环境通常可以用下面方式验证：

```bash
make production
bash ./nginx.sh
brew services reload nginx
```

然后访问本地代理地址，例如：

```text
http://localhost:8881/
```

如果父页面和衡石站点分开跑，常见组合是：

```text
父页面：http://localhost:8882/
iframe：http://localhost:8881/app/{appId}/dashboard/{dashboardId}?agentHost=parent&parentOrigin=http%3A%2F%2Flocalhost%3A8882
```

`/sdk/` 只是本地测试页路径，不是产品协议要求。真实接入时父页面路径可以由你们自己的系统决定。

### Q：为什么 iframe 里没有 Copilot 侧边栏？

当 iframe URL 使用 `agentHost=parent`，这是预期行为。Copilot 侧边栏由父页面 SDK 统一承接，iframe 内不再重复显示自己的侧边栏。

### Q：为什么父页面 Copilot 没有显示“智能解读”？

优先检查这些点：

* 父页面是否已经加载 `hengshi-copilot@<version>.js`
* iframe URL 是否带了 `agentHost=parent`
* `parentOrigin` 是否和父页面 origin 完全一致
* iframe 页面是否已经加载到仪表盘页，而不是登录页、空白页或错误页
* 当前用户是否有读取该仪表盘和图表的权限
* 浏览器控制台是否有 iframe 被 CSP、`X-Frame-Options` 或登录态拦截的错误

### Q：Agent 为什么不能直接用浏览器点击 iframe 内部按钮？

父页面 SDK 场景下，Agent 的浏览器窗口是父页面，不是 iframe 内部页面。直接点击、导航或拖拽只能作用在父页面上。

正确方式是让 iframe 仪表盘通过桥接暴露业务动作和业务对象，Agent 再通过这些能力读取、解读或修改仪表盘。

### Q：`/api/chat/completions` 报 400 或 500，和 iframe bridge 有关吗？

通常不是同一个问题。iframe bridge 负责父页面和 iframe 之间的上下文、动作和消息通信，不会把聊天请求体改成另一种 JSON 格式。

排查顺序建议是：

* 400 且提示 JSON parse error：检查请求体是否是合法 JSON，`content-type` 是否是 `application/json`
* 401 / 403：检查登录态、JWT、CSRF 和用户权限
* 本地 nginx 返回 500：先看 nginx error log，再确认 `/api` 代理到了预期后端，例如 develop 或 rc 环境
* 后端返回 500：继续看后端服务日志和模型配置

## iframe 不适合解决什么问题

* 不适合深度定制聊天窗口内部 UI
* 不适合把问答结果拆成多个业务模块嵌入不同位置
* 不适合只拿后端能力而不复用页面
* 不适合当成标准图表协议输出能力来使用

如果后续需要这些能力，通常意味着应该改看 [JS SDK 集成](copilot-sdk-js.md) 或 [API 集成](copilot-sdk-api.md)。

## 一句话总结

如果只想最快嵌入完整 ChatBI，直接 iframe `/copilot`。

如果父页面已经使用 Copilot JS SDK，同时 iframe 里嵌的是衡石仪表盘，就在 iframe URL 上使用 `agentHost=parent`，让父页面 Copilot 统一接管 iframe 内的智能助理能力。
