> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/serve/web-chat-sdk.md).

# Web Chat 嵌入與 SDK

將 MaiAgent Web Chat 嵌入任何網站：嵌入碼、視窗模式、身分識別、完整配置參考與 JavaScript SDK API

在網頁加入一段嵌入碼，即可將 MaiAgent 的 AI Agent 放進您的網站。嵌入碼載入 `embed.min.js`（以下稱 SDK）後，SDK 會在頁面上建立聊天按鈕與 iframe 聊天視窗，並提供全域 `MaiAgent` JavaScript API 供程式操作。

本頁是 Web Chat 嵌入的完整技術參考。相關主題：

* [串接 MaiGPT：選對 Agent、補齊能力](/tech/work/maigpt-system-integration-guide/maigpt-integration.md) — MaiGPT 產品 vs MaiGPT 視窗模式，以及讓嵌入的 Agent 會生圖、會用工具
* [Web Chat MaiGPT 模式嵌入](/tech/serve/web-chat-sdk/web-chat-maigpt-mode.md) — 類 ChatGPT 的全功能對話介面
* [Web Chat 注入頁面 Context](/tech/serve/web-chat-sdk/web-chat-context-data.md) — 把頁面資訊注入 LLM System Prompt
* [聯絡人身分同步與 Token 更新](/tech/serve/contacts/contact-credentials-sync.md) — 後端建立聯絡人與憑證
* [軟體廠商整合指南](/tech/serve/vendor-integration-guide.md) — 嵌入＋權限對接的完整旅程（批次補建、登入同步、登出註銷）

## 一、快速開始 <a href="#quickstart" id="quickstart"></a>

在頁面 `</body>` 前加入：

```html
<script>
  window.maiagentChatbotConfig = {
    webChatId: 'your-web-chat-id',
    baseUrl: 'https://chat.maiagent.ai/web-chats',
    primaryColor: '#1890ff',
  }
</script>
<script src="https://chat.maiagent.ai/js/embed.min.js" defer></script>
```

* `webChatId` 從管理後台的 Web Chat 設定頁取得（參考[串接對話平台：網站](https://docs.maiagent.ai/build/choose-channel/web-chat/website)，由後台產生的嵌入碼會自動帶入）
* `baseUrl` 與 SDK 網址以 SaaS 環境（`chat.maiagent.ai`）為例；私有雲／地端部署請換成您環境的網域
* `window.maiagentChatbotConfig` **必須在 SDK 腳本載入前定義**

預設行為：頁面右下角出現聊天按鈕，點擊開啟漂浮聊天視窗，使用者可在漂浮（floating）與側欄（sidebar）兩種視窗模式間切換。

### 初始化時機 <a href="#init-timing" id="init-timing"></a>

SDK 載入後會註冊 `document.body.onload` 處理器執行初始化，一般情況不需手動介入。需要控制初始化時機時（例如 SPA 等容器 mount、或延遲渲染）：

1. **延遲載入腳本**：在適當時機才動態插入 `embed.min.js` 的 `<script>` 標籤
2. **手動觸發**：先設定好 `window.maiagentChatbotConfig`，再呼叫 `document.body.onload()`

同一頁面 SDK 只會初始化一次，重複載入會被忽略。

## 二、視窗模式 <a href="#window-modes" id="window-modes"></a>

`enabledWindowModes` 陣列決定使用者可用的視窗模式，**第一個元素是預設模式**；不設定時等同 `['floating', 'sidebar']`。

| 模式         | 形態                                               | 適用情境                                                                             |
| ---------- | ------------------------------------------------ | -------------------------------------------------------------------------------- |
| `floating` | 疊在頁面右下角的漂浮視窗，可拖曳按鈕、可調整視窗大小（320×400 至 900×900 px） | 一般客服、諮詢                                                                          |
| `sidebar`  | 貼齊視窗右側的側欄（寬 320–800 px，可拖曳調整），頁面內容自動收縮避讓         | 需要邊看頁面邊對話的工作情境                                                                   |
| `inline`   | 聊天視窗直接放進您指定的容器（`container`），撐滿容器寬高、一直顯示，沒有聊天按鈕   | 頁面中固定一塊對話區，例如客服中心頁、產品頁側欄                                                         |
| `maigpt`   | 類 ChatGPT 完整介面（對話歷史側欄、搜尋、設定），佔滿指定容器或全屏           | 頁面主功能區塊、全站 AI 入口，見 [MaiGPT 模式](/tech/serve/web-chat-sdk/web-chat-maigpt-mode.md) |

```javascript
// 只允許側欄模式
window.maiagentChatbotConfig = {
  webChatId: 'your-web-chat-id',
  baseUrl: 'https://chat.maiagent.ai/web-chats',
  enabledWindowModes: ['sidebar'],
}
```

* 陣列有兩種以上模式時，聊天視窗會出現模式切換鈕；`allowWindowModeSwitch: false` 可隱藏
* `maigpt` 作為第一個元素時進入 MaiGPT 模式，其餘元素無效（該模式不提供切換）
* `inline` 只能單獨使用（`enabledWindowModes: ['inline']`）；和 `floating`、`sidebar` 放在同一個陣列時不會嵌入，console 會顯示錯誤
* 舊欄位 `defaultWindowMode` 已棄用：仍可運作（視同 `enabledWindowModes: [該值]`），但會在 console 顯示棄用警告

### inline 模式的容器 <a href="#inline-container" id="inline-container"></a>

`inline` 模式要用 `container` 指定嵌入位置，容器請自行給定高度（聊天視窗最小高 400 px）：

```html
<div id="chat-container" style="height: 640px;"></div>

<script>
  window.maiagentChatbotConfig = {
    webChatId: 'your-web-chat-id',
    baseUrl: 'https://chat.maiagent.ai/web-chats',
    enabledWindowModes: ['inline'],
    container: '#chat-container',
  }
</script>
<script src="https://chat.maiagent.ai/js/embed.min.js" defer></script>
```

| `container` 寫法                              | 行為                                                         |
| ------------------------------------------- | ---------------------------------------------------------- |
| CSS selector 字串（如 `'#chat-container'`）      | 等元素出現，最多 5 秒                                               |
| HTMLElement，或 `{ element }`                 | 直接使用；元素必須已掛在頁面上                                            |
| `{ selector, waitForElement, waitTimeout }` | `waitForElement: false` 不等待；`waitTimeout` 調整等待毫秒數（預設 5000） |

* 沒有設定 `container`、selector 寫錯或等不到元素時，聊天視窗不會出現，console 會說明原因
* 容器若為 `position: static`，SDK 會改成 `position: relative`
* inline 模式一直顯示，沒有開關：`MaiAgent.control.open()`／`close()` 不起作用

### floating、sidebar、maigpt 的實際畫面 <a href="#window-modes-gallery" id="window-modes-gallery"></a>

{% tabs %}
{% tab title="floating 漂浮視窗" %}

<figure><img src="https://527168072-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F38pkhhqHl1oA6yyE9R2n%2Fuploads%2Fgit-blob-56a6936e787340e6429586677d8be3bacc08b308%2Fwebchat-mode-floating.png?alt=media" alt="floating 模式：聊天視窗疊在頁面右下角"><figcaption><p>floating：聊天視窗疊在頁面右下角，不影響頁面排版，可拖曳按鈕移動位置</p></figcaption></figure>
{% endtab %}

{% tab title="sidebar 側欄" %}

<figure><img src="https://527168072-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F38pkhhqHl1oA6yyE9R2n%2Fuploads%2Fgit-blob-4d989af74c4a47e70c3d61a6f6f4b496a31f6a09%2Fwebchat-mode-sidebar.png?alt=media" alt="sidebar 模式：聊天視窗貼齊右側，頁面內容自動收縮"><figcaption><p>sidebar：聊天視窗貼齊視窗右側，頁面內容自動收縮避讓，適合邊看頁面邊對話</p></figcaption></figure>
{% endtab %}

{% tab title="maigpt 完整介面" %}

<figure><img src="https://527168072-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F38pkhhqHl1oA6yyE9R2n%2Fuploads%2Fgit-blob-aab515bf01f9122d1b558aeaefcd1e59079466a0%2Fwebchat-maigpt-mode-a-container.png?alt=media" alt="maigpt 模式：類 ChatGPT 完整對話介面"><figcaption><p>maigpt：類 ChatGPT 完整介面（含對話歷史側欄），詳見 <a href="/tech/serve/web-chat-sdk/web-chat-maigpt-mode.md">MaiGPT 模式嵌入</a></p></figcaption></figure>
{% endtab %}
{% endtabs %}

## 三、識別使用者 <a href="#identify-users" id="identify-users"></a>

Web Chat 依是否綁定聯絡人（Contact），分為三個身分層級：

| 層級      | 設定方式                                                                                                 | 對話歷史       | AI 個人化／以使用者權限呼叫工具 |
| ------- | ---------------------------------------------------------------------------------------------------- | ---------- | ----------------- |
| 匿名      | 什麼都不設定                                                                                               | 以瀏覽器為單位保留  | ✖                 |
| 前端 auth | config 帶 `auth: { sourceId, ... }`，SDK 自動建立或對應聯絡人                                                    | 跟著聯絡人、可跨裝置 | ✔                 |
| 後端串接    | 後端呼叫[身分同步 API](/tech/serve/contacts/contact-credentials-sync.md) 取得 `contactId`，config 帶 `contactId` | 跟著聯絡人、可跨裝置 | ✔                 |

**前端 auth**：SDK 初始化完成後，會自動以 `auth` 的內容呼叫 MaiAgent 的身分同步 API（`setup-contact-credentials`），依 `sourceId` 建立或對應聯絡人：

```javascript
window.maiagentChatbotConfig = {
  webChatId: 'your-web-chat-id',
  baseUrl: 'https://chat.maiagent.ai/web-chats',
  auth: {
    sourceId: 'user-12345',   // 必填：您系統中該使用者的唯一識別碼
    name: '王小明',            // 選填：聯絡人顯示名稱（只在首次建立時生效）
    contextData: { ... },     // 選填：注入 LLM System Prompt，見 contextData 頁
    ts: 1785500000,           // 開啟簽章驗證時必填：Unix 秒（UTC），由您的後端產生
    sig: '9f2c...',           // 開啟簽章驗證時必填：HMAC-SHA256 簽章，見下方「簽章驗證」
    getToken: async () => ({ accessToken, organizationId, sourceId }),  // 僅「成員身分嵌入」使用：由您的後端換發成員短效 token，SDK 自動續期
  },
}
```

`getToken`（或靜態的 `accessToken`＋`organizationId`）只在把組織內建 MaiGPT 嵌給組織成員時使用，此時 `sourceId` 必須是成員的使用者 ID，完整流程見[成員身分嵌入](/tech/work/maigpt-system-integration-guide/maigpt-integration.md#member-embed)。一般客戶／訪客的身分識別不需要這些欄位。

若該 Web Chat 的登入設定開啟了「允許嵌入端以 Source ID 免登入對話」並選擇簽章模式，`ts` 與 `sig` 為必填，缺少或驗證不通過時使用者會被導向登入頁。手動呼叫 `MaiAgent.auth.setup()` 時同樣接受這兩個欄位。

**後端串接**：由您的後端在使用者登入時呼叫同一支 API（可同時綁定 MCP 工具憑證），將回傳的 `contactId` 存進會員資料，前端只帶 `contactId`。

兩條路怎麼選：

* 只需要「認得使用者、保留跨裝置對話」→ **前端 auth** 最省事
* 需要把使用者的 Access Token 綁進 MCP 工具憑證（讓 AI 以使用者權限呼叫您的 API），且不希望 token 出現在前端頁面原始碼 → **後端串接**
* 兩者底層是同一套聯絡人機制，`sourceId` 相同即視為同一聯絡人，可混用

{% hint style="info" %}
另可用 `queryMetadata` 控制該使用者的知識庫檢索範圍（RAG 權限過濾），詳見[知識管理權限（Query Metadata）總覽](/tech/identity-and-access/zhi-shi-guan-li-quan-xian-query-metadata-cha-xun-yuan-zi-liao-zong-lan.md)。`queryMetadata` 不會進入 LLM prompt；要讓 AI「知道」某個值請用 [contextData](/tech/serve/web-chat-sdk/web-chat-context-data.md)。
{% endhint %}

### 安全注意事項 <a href="#identity-security" id="identity-security"></a>

{% hint style="danger" %}
**`contactId` 是身分憑證**：拿到它就能以該使用者身分對話、看到他的對話歷史。只在**登入後**的頁面把 `contactId` 輸出給該使用者本人，不要放進公開頁面的原始碼。

`auth.sourceId` 的要求取決於是否開啟簽章驗證：

* **未開啟簽章驗證**：身分同步是公開端點，可猜測的 `sourceId` 等於可冒充的身分。請使用不可猜測的值（如 UUID），不要用流水號、Email 或手機號碼。
* **已開啟簽章驗證**：安全性來自簽章而非 `sourceId` 本身，因此學號、員工編號、註冊 Email 等可讀值都可以直接使用。密鑰只存在您的伺服器，沒有密鑰就算知道 `sourceId` 也算不出有效簽章。
  {% endhint %}

### 簽章驗證 <a href="#source-id-signature" id="source-id-signature"></a>

Web Chat 的登入設定可開啟「允許嵌入端以 Source ID 免登入對話」，讓您網站上已登入的使用者不必在聊天視窗再登入一次。開關、密鑰、簽章有效期限與驗章失敗紀錄在後台的登入設定分頁，操作方式見使用者手冊的[以聯絡人 Source ID 免登入對話](https://docs.maiagent.ai/build/choose-channel/web-chat/source-id-access)。

驗證方式有兩種，簽章模式為建議值：

| 驗證方式         | 行為                                 |
| ------------ | ---------------------------------- |
| 要求簽章驗證       | 須帶 `ts` 與 `sig`，驗章通過才放行            |
| 僅憑 Source ID | 不驗章，帶了 `sourceId` 就放行；僅適合測試或內網封閉環境 |

**簽章算法**

```
HMAC-SHA256(密鑰, "{webChatId}.{sourceId}.{ts}")
```

以 hex 小寫字串傳遞。`ts` 為 Unix 秒（UTC）。密鑰由平台在後台產生，只能存在您的伺服器環境變數，不得寫入前端程式碼。

{% tabs %}
{% tab title="PHP" %}

```php
$ts  = time();
$sig = hash_hmac('sha256', "{$webChatId}.{$sourceId}.{$ts}", $SIGNING_SECRET);
```

{% endtab %}

{% tab title="Node" %}

```javascript
const ts = Math.floor(Date.now() / 1000);
const sig = crypto.createHmac('sha256', SIGNING_SECRET)
  .update(`${webChatId}.${sourceId}.${ts}`).digest('hex');
```

{% endtab %}

{% tab title="Python" %}

```python
ts = int(time.time())
sig = hmac.new(SIGNING_SECRET.encode(),
  f"{web_chat_id}.{source_id}.{ts}".encode(), hashlib.sha256).hexdigest()
```

{% endtab %}
{% endtabs %}

**前端帶入簽章**

把後端算好的 `ts`、`sig` 連同 `sourceId` 交給 SDK。頁面載入時就知道使用者身分，放在 config 的 `auth`；SPA 登入後才取得簽章，呼叫 `MaiAgent.auth.setup()`：

```javascript
// 方式一：放在 config，SDK 就緒後自動執行 auth.setup()
window.maiagentChatbotConfig = {
  webChatId: 'your-web-chat-id',
  baseUrl: 'https://chat.maiagent.ai/web-chats',
  auth: { sourceId, name, ts, sig },
}

// 方式二：SDK 就緒後手動呼叫
MaiAgent.auth.setup({ sourceId, name, ts, sig })
```

{% hint style="warning" %}
後台「整合範例」產生的前端範例若顯示 `setupAuth`，請改用 `auth.setup`。SDK 對外沒有 `setupAuth` 方法，照貼會在瀏覽器丟出 TypeError。
{% endhint %}

**驗證規則**

| 規則   | 說明                                      |
| ---- | --------------------------------------- |
| 有效期限 | 後台可設 1／5／15 分鐘，預設 5 分鐘                  |
| 時鐘容忍 | 允許 60 秒偏差；時間戳超出未來 60 秒一律拒絕              |
| 一次性  | 同一組簽章只能成功使用一次，簽章本身即為防重放 nonce           |
| 綁定平台 | 簽章含 `webChatId`，同組織其他 Web Chat 的簽章不能重用  |
| 拒絕回應 | 所有失敗原因對外回應一致，不透露原因，也不透露 `sourceId` 是否存在 |

{% hint style="warning" %}
簽章一次有效，**每次進場都要重新計算，不得快取**。有效期限限制的是「多久內可以用這組簽章換取聯絡人身分」，不是對話的存續時間；換得身分後對話不會因簽章逾期而中斷。
{% endhint %}

**有效期限與登出的關係**

換得的 `contactId` 本身沒有到期時間，平台目前也沒有作廢它的端點。使用者在您的網站登出後，您能控制的是不再簽發新簽章；前端可呼叫 `MaiAgent.auth.signOut()` 讓聊天視窗回到匿名，工具憑證的註銷見[登出與憑證註銷](/tech/serve/contacts/contact-credentials-sync.md#logout-revoke)。

**排查驗章失敗**

後台登入設定的「查看驗章失敗紀錄」列出最近的失敗事件與原因，每種原因的常見成因見使用者手冊的[驗章失敗紀錄](https://docs.maiagent.ai/build/choose-channel/web-chat/source-id-access#verification-failures)。最常見的「簽章不符」多半是密鑰不一致，或簽章字串組法有誤（分隔符號為半形句點）。

### 允許嵌入網域 <a href="#embed-origin-allowlist" id="embed-origin-allowlist"></a>

對話平台管理者可在後台 <mark style="color:blue;">基本設定</mark> → <mark style="color:blue;">允許嵌入的網域</mark> 自行填寫清單，操作步驟見使用者手冊的[允許嵌入的網域](https://docs.maiagent.ai/build/conversation-settings/platform-settings/basic-settings#allowed-embed-origins)。清單非空時同時限制兩件事：

* **嵌入**：瀏覽器會擋下清單以外網站的嵌入。
* **身分同步**：`auth` 與 setup-contact-credentials 都要通過來源比對。伺服器對伺服器呼叫沒有瀏覽器的 `Origin` 標頭，請在 body 帶 `embedOrigin`，見[聯絡人身分同步與 Token 更新](/tech/serve/contacts/contact-credentials-sync.md)。

比對規則：

| 清單狀態                     | 行為                                       |
| ------------------------ | ---------------------------------------- |
| 空清單（預設）                  | 不限制來源                                    |
| 列出 `https://example.com` | 精確比對 scheme＋host（＋port）                  |
| 列出 `*.example.com`       | 涵蓋 `example.com` 與其所有子網域（任何 scheme／port） |
| 清單有值但全部格式無效              | **拒絕所有來源**（fail closed），不會靜默退回不限制        |

有對外嵌入且使用身分功能的 Web Chat，建議一律設定此清單，縮小可取得 `contactId` 的來源。

## 四、配置選項參考 <a href="#config-reference" id="config-reference"></a>

`window.maiagentChatbotConfig` 支援的全部欄位。設定未知欄位不會報錯，但 console 會出現 `[maiagent] Unknown config options: ...` 警告，方便檢查拼字。

### 基本 <a href="#config-basic" id="config-basic"></a>

| 欄位              | 型別                  | 必填 | 說明                                                                                                                                                                                               |
| --------------- | ------------------- | -- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `webChatId`     | `string`            | ✅  | Web Chat 的唯一識別碼                                                                                                                                                                                  |
| `baseUrl`       | `string`            | ✅  | Web Chat 服務位址，SaaS 為 `https://chat.maiagent.ai/web-chats`                                                                                                                                        |
| `contactId`     | `string`            |    | 聯絡人 ID（後端串接時使用，見[識別使用者](#identify-users)）                                                                                                                                                        |
| `auth`          | `object`            |    | 前端身分設定：`sourceId`（必填）、`name`、`contextData`、`mcpCredentials`、`ts` 與 `sig`（開啟簽章驗證時必填，見[簽章驗證](#source-id-signature)）。SDK 就緒後自動執行 `auth.setup()`                                                     |
| `queryMetadata` | `object` 或 `string` |    | 知識庫檢索範圍過濾（不進 LLM prompt）                                                                                                                                                                         |
| `locale`        | `string`            |    | 介面語系（如 `zh-TW`、`en`），也是 AI 回覆語言的參考；支援清單與決定順序見 [MaiGPT 模式頁第五節](/tech/serve/web-chat-sdk/web-chat-maigpt-mode.md#locale)。優先權：使用者在視窗手動選過的語系 > config／`locale.set()` > 此瀏覽器上次使用的語系 > 瀏覽器語系 > `zh-TW` |

### 視窗模式與行為 <a href="#config-behavior" id="config-behavior"></a>

| 欄位                      | 型別                                | 預設值                       | 說明                                                                               |
| ----------------------- | --------------------------------- | ------------------------- | -------------------------------------------------------------------------------- |
| `enabledWindowModes`    | `string[]`                        | `['floating', 'sidebar']` | 可用視窗模式，第一項為預設；見[視窗模式](#window-modes)                                             |
| `allowWindowModeSwitch` | `boolean`                         | `true`                    | 是否顯示視窗模式切換鈕（僅在啟用兩種以上模式時有意義）                                                      |
| `showButton`            | `boolean`                         | `true`                    | 是否顯示聊天按鈕。設 `false` 時改由程式呼叫 `MaiAgent.control.open()` 開啟                          |
| `showChatNotification`  | `boolean`                         | `false`                   | 視窗關閉時收到回覆，是否在按鈕旁顯示通知                                                             |
| `chatNotificationTitle` | `string`                          |                           | 通知的標題文字                                                                          |
| `preventIosInputZoom`   | `boolean`                         |                           | 防止 iOS Safari 聚焦輸入框時自動放大頁面                                                       |
| `container`             | `string`、`HTMLElement` 或 `object` |                           | inline 模式專用：嵌入容器，見 [inline 模式的容器](#inline-container)                             |
| `targetElement`         | `string` 或 `HTMLElement`          |                           | MaiGPT 模式專用：嵌入容器，見 [MaiGPT 模式](/tech/serve/web-chat-sdk/web-chat-maigpt-mode.md) |
| `maigptTitle`           | `string`                          |                           | MaiGPT 模式專用：側欄品牌標題，預設顯示 `MaiGPT`                                                 |

### 外觀 <a href="#config-appearance" id="config-appearance"></a>

尺寸類欄位接受數字（視為 px）、`'Npx'` 或 `'Nrem'` 字串。

| 欄位              | 型別                  | 預設值                                  | 說明                              |
| --------------- | ------------------- | ------------------------------------ | ------------------------------- |
| `primaryColor`  | `string`            | `#1890ff`                            | 主題色（按鈕與介面主色）                    |
| `buttonSize`    | `string` 或 `number` | `3rem`                               | 聊天按鈕尺寸                          |
| `buttonRadius`  | `string`            | `50%`                                | 聊天按鈕圓角                          |
| `buttonIcon`    | `string`            |                                      | 按鈕圖示的圖片 URL（優先於 `openIconHtml`） |
| `openIconHtml`  | `string`            | 內建 SVG                               | 按鈕「開啟」狀態的自訂 HTML                |
| `closeIconHtml` | `string`            | 內建 SVG                               | 按鈕「關閉」狀態的自訂 HTML                |
| `windowWidth`   | `string` 或 `number` | `24rem`                              | 漂浮視窗寬度                          |
| `windowHeight`  | `string` 或 `number` | `40rem`                              | 漂浮視窗高度                          |
| `windowRadius`  | `string`            | `0.75rem`                            | 漂浮視窗圓角                          |
| `boxShadow`     | `string`            | `0.125rem 0.125rem 0.5rem #00000044` | 按鈕與視窗陰影                         |

### 位置 <a href="#config-position" id="config-position"></a>

| 欄位                     | 型別                  | 預設值      | 說明                     |
| ---------------------- | ------------------- | -------- | ---------------------- |
| `buttonPositionBottom` | `string` 或 `number` | `16`（px） | 按鈕距視窗底部                |
| `buttonPositionRight`  | `string` 或 `number` | `16`（px） | 按鈕距視窗右側                |
| `windowPositionBottom` | `string`            | `5rem`   | 漂浮視窗距底部                |
| `windowPositionRight`  | `string`            | `1rem`   | 漂浮視窗距右側                |
| `windowPosition`       | `string`            |          | 設 `'center'` 時漂浮視窗置中顯示 |

聊天按鈕支援拖曳移動，位置會記憶在瀏覽器中；再次載入時若記憶位置超出目前視窗範圍，會自動回到安全位置。

### 按鈕位置的決定順序 <a href="#button-position-behavior" id="button-position-behavior"></a>

* `buttonPositionBottom`／`buttonPositionRight` 是按鈕位置的唯一設定來源；使用者拖曳後的位置只存在該瀏覽器的視窗狀態中，優先於設定值，直到使用者再次拖曳或清除網站資料。
* 開啟聊天視窗（點按鈕或 `MaiAgent.control.open()`）時，漂浮視窗以按鈕目前的位置為錨點定位，按鈕本身不會被移動；視窗關閉後按鈕仍在原處。
* 瀏覽器視窗縮放時，使用者拖曳出的位置會依比例跟著調整，並夾在可視範圍內；設定值定位的按鈕由 CSS 自行跟隨。
* 要固定按鈕位置、不讓使用者移動，目前沒有關閉拖曳的設定；可改用 `showButton: false` 隱藏內建按鈕，自行放一個按鈕呼叫 `MaiAgent.control.open()`（見[完整範例](#full-example)）。
* 位置看起來「跑掉」時，先檢查頁面的 CSS 是否覆寫了按鈕容器的 `position`、`bottom`、`right`，再清除該網站的瀏覽器資料以重設記憶的位置。

## 五、SDK API <a href="#sdk-api" id="sdk-api"></a>

SDK 初始化後可透過全域 `MaiAgent` 物件操作。所有方法都應在 SDK 就緒後呼叫（見[疑難排解](#troubleshooting)的可用性檢查）。

### 視窗控制 `MaiAgent.control` <a href="#api-control" id="api-control"></a>

| 方法         | 說明                    |
| ---------- | --------------------- |
| `open()`   | 開啟聊天視窗                |
| `close()`  | 關閉聊天視窗                |
| `isOpen()` | 回傳聊天視窗是否開啟（`boolean`） |

另有等價的全域捷徑 `window.maiagentOpenChat()` / `window.maiagentCloseChat()`。

### 訊息 `MaiAgent.chat` <a href="#api-chat" id="api-chat"></a>

| 方法                  | 說明             |
| ------------------- | -------------- |
| `send(content)`     | 以使用者身分送出一則文字訊息 |
| `clearHistory()`    | 清除目前對話的歷史訊息    |
| `newConversation()` | 開始新對話          |

```javascript
MaiAgent.chat.send('你好，我想了解你們的服務')
MaiAgent.control.open()
```

### 事件 `MaiAgent.events` <a href="#api-events" id="api-events"></a>

| 方法                         | 說明                          |
| -------------------------- | --------------------------- |
| `on(eventType, callback)`  | 註冊事件監聽器                     |
| `off(eventType, callback)` | 移除事件監聽器（需傳入同一個 callback 參照） |

可監聽的事件：

| 事件             | 觸發時機              | callback 參數                                                                      |
| -------------- | ----------------- | -------------------------------------------------------------------------------- |
| `messageReply` | 收到 AI Agent 回覆    | 已解析的物件：`{ content: string, sender?: { name, avatar }, timestamp?: number, ... }` |
| `sdkReady`     | SDK 初始化完成         | —                                                                                |
| `authReady`    | `auth.setup()` 完成 | —                                                                                |
| `iframeReady`  | 聊天視窗 iframe 就緒    | —                                                                                |

```javascript
// 建議使用 MaiAgent.EVENT_TYPES 常數，避免拼字錯誤
MaiAgent.events.on(MaiAgent.EVENT_TYPES.MESSAGE_REPLY, (data) => {
  console.log('收到回覆：', data.content)
})
```

### 語系與語音 <a href="#api-locale-speech" id="api-locale-speech"></a>

| 方法                                     | 說明                                                                                                                                   |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `MaiAgent.locale.set(lang)`            | 切換介面語系，如 `'zh-TW'`、`'zh-CN'`、`'en'`；支援清單見 [MaiGPT 模式頁第五節](/tech/serve/web-chat-sdk/web-chat-maigpt-mode.md#locale)。使用者在視窗手動選過語系時不會覆蓋 |
| `MaiAgent.speech.set(lang, provider?)` | 設定語音辨識與合成的語言，`provider` 可指定語音服務供應商（如 `'azure'`）                                                                                      |

`locale` 決定介面文字，也會隨每則訊息送給 AI，作為無法判斷使用者偏好語言時的回覆語言參考；使用者用另一種語言提問時，AI 通常會跟著提問的語言回答（見[角色指令：平台附加的情境](/tech/advanced-genai-tech/system-prompt.md#platform-context)、使用者手冊[多國語言支援](https://docs.maiagent.ai/build/agent/multi-language-support#reply-language)）。使用者從 Web Chat 選單切換語言時會另開新對話；`locale.set()` 讓語系改變、且使用者還沒送出訊息時，Web Chat 也會以新語系重開對話。

### 身分 `MaiAgent.auth` <a href="#api-auth" id="api-auth"></a>

| 方法                  | 說明                                                                                                                               |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `setup(authConfig)` | 建立或對應聯絡人並套用身分，回傳 `Promise<string \| null>`（成功時為 `contactId`）。`authConfig` 欄位同 config 的 `auth`                                    |
| `signOut()`         | 登出目前身分，回到匿名狀態並清除 `contextData`。只作用於前端；後端已綁定的工具憑證要另外註銷，見[登出與憑證註銷](/tech/serve/contacts/contact-credentials-sync.md#logout-revoke) |

config 有 `auth` 時 SDK 會自動呼叫 `setup()`，不需手動執行；SPA 中使用者登入／登出、或需要更新 `contextData` 時才需要手動呼叫。

## 六、完整範例 <a href="#full-example" id="full-example"></a>

```html
<!doctype html>
<html lang="zh-TW">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>MaiAgent Web Chat 整合範例</title>
  </head>
  <body>
    <h1>我的網站</h1>

    <button onclick="MaiAgent.control.open()">開啟客服</button>
    <button onclick="startNewChat()">新對話</button>

    <script>
      window.maiagentChatbotConfig = {
        webChatId: 'your-web-chat-id',
        baseUrl: 'https://chat.maiagent.ai/web-chats',
        primaryColor: '#007bff',
        enabledWindowModes: ['floating', 'sidebar'],
        locale: 'zh-TW',
        auth: {
          sourceId: 'user-12345',
          name: '王小明',
        },
      }
    </script>
    <script src="https://chat.maiagent.ai/js/embed.min.js" defer></script>

    <script>
      document.addEventListener('DOMContentLoaded', () => {
        MaiAgent.events.on(MaiAgent.EVENT_TYPES.MESSAGE_REPLY, (data) => {
          console.log('收到 AI 回覆：', data.content)
          // 可在此串接分析工具，例如 GA4：
          // gtag('event', 'chat_interaction', { event_label: 'bot_reply' })
        })
      })

      function startNewChat() {
        MaiAgent.chat.newConversation()
        MaiAgent.control.open()
      }
    </script>
  </body>
</html>
```

## 七、疑難排解 <a href="#troubleshooting" id="troubleshooting"></a>

| 症狀                                       | 檢查                                                                                                                                                                                                                      |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 按鈕沒出現                                    | `maiagentChatbotConfig` 是否在 SDK 腳本之前定義；`webChatId` / `baseUrl` 是否正確；console 是否有錯誤                                                                                                                                       |
| console 出現 `Unknown config options: ...` | 對照[配置選項參考](#config-reference)檢查欄位拼字                                                                                                                                                                                     |
| `MaiAgent is not defined`                | SDK 尚未載入完成；在 `DOMContentLoaded` 之後再呼叫，或先檢查 `typeof MaiAgent !== 'undefined'`                                                                                                                                            |
| 事件監聽器沒觸發                                 | 確認事件名稱正確（建議用 `MaiAgent.EVENT_TYPES` 常數）；`off()` 需傳入與 `on()` 相同的函式參照                                                                                                                                                     |
| 對話換裝置就不見                                 | 匿名狀態的對話以瀏覽器為單位；要跨裝置請設定 `auth` 或 `contactId`（見[識別使用者](#identify-users)）                                                                                                                                                  |
| `auth` 設定後身分沒生效（400 錯誤）                  | Web Chat 若設定了「允許嵌入網域」，只有清單內網域的頁面能完成身分同步；請確認嵌入頁的網域已加入清單                                                                                                                                                                  |
| 按鈕位置異常                                   | 檢查頁面 CSS 是否與按鈕樣式衝突；位置參數格式是否正確（數字、`px` 或 `rem`）；拖曳後的位置會記憶在瀏覽器，見[按鈕位置的決定順序](#button-position-behavior)                                                                                                                    |
| 想自己放 iframe、用 `postMessage` 跟聊天視窗溝通      | 不支援。SDK 與聊天視窗 iframe 之間的 `postMessage` 訊息是 SDK 的內部協定，格式會隨版本變動，也不接受來自其他來源的訊息。請用嵌入碼載入 SDK，以 [事件](#api-events)（如 `messageReceived`）接收訊息、以 [`MaiAgent.chat`](#api-chat) 送出訊息；需要固定在頁面中的聊天區塊請用 [inline 模式](#inline-container) |

安全地呼叫 SDK：

```javascript
function safeOpenChat() {
  if (typeof MaiAgent !== 'undefined' && MaiAgent.control) {
    MaiAgent.control.open()
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.maiagent.ai/tech/serve/web-chat-sdk.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
