# 嵌入到网页 (/docs/cloud/services/agent/embed/quickstart)



本页带你把一个服务型智能体放进自己的网页，让登录用户直接在页面里和它对话。你需要一个 Praxis API Key、一个网站后端和一个前端页面。

本页用 Kit 组件完成接入。一行 iframe 和 Hosted 跳转的用法见[嵌入方式](/docs/cloud/services/agent/embed/methods)。所有示例使用 develop 环境。

## 工作方式 [#工作方式]

浏览器里的页面不持有 API Key。你的后端用 API Key 为当前登录用户签发一张短期令牌，前端的 Kit 组件拿这张令牌与智能体对话。

```text
浏览器 ──(你的登录态)──▶ 你的后端 ──(API Key)──▶ POST /agent/v1/embed_tokens
   ▲                                                      │
   └──────────── { id, token, expires_at } ◀──────────────┘
浏览器（Kit）──(token)──▶ Praxis API
```

## 1. 在控制台准备智能体 [#1-在控制台准备智能体]

1. 在 Console 新建一个**服务型智能体**，写好指令并保存。
2. 为它设置嵌入策略：选择**运行环境**，在**允许的来源**里添加你的网站来源（如 `https://shop.example.com`）；本地调试可允许 localhost。
3. 记下智能体的 `id`（形如 `agent_…`）或短号。

期望：嵌入策略保存成功，来源列表里能看到你的网站。没有嵌入策略的智能体不能签发令牌。

## 2. 后端签发令牌 [#2-后端签发令牌]

用 API Key 调 `POST /agent/v1/embed_tokens`。API Key 只放在后端，不要写进前端代码。

```ts
// 你的后端，以 fetch 为例
const API = "https://api-develop.deepraxis.com"; // 正式地址上线后替换

export async function issueToken(userId: string) {
  const response = await fetch(`${API}/agent/v1/embed_tokens`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PRAXIS_API_KEY}`, // 占位：你的 API Key
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      agent: "agent_xxx",       // 换成你的智能体 id 或短号
      external_user_id: userId, // 你系统里这位用户的稳定 ID
    }),
  });
  if (!response.ok) throw new Error("token request failed");
  const { token, expires_at } = await response.json();
  return { token, expires_at };
}
```

把它挂在你自己的登录保护之后，例如 `POST /api/praxis-token`：先确认用户已登录，再用**登录态里的用户 ID** 签发，不要信任前端传来的用户 ID。

期望：Praxis 返回 HTTP 200 和 `{ id, token, expires_at }`；你的宿主端点返回 `{ token, expires_at }`，`expires_at` 为 ISO 8601 时间。字段规则见[嵌入令牌与终端用户](/docs/cloud/services/agent/embed/tokens)。

## 3. 前端加载 Kit [#3-前端加载-kit]

```html
<script type="module">
  import "/praxis-kit.js"; // Kit 的构建产物，由你的站点提供

  const agent = document.createElement("praxis-agent");
  agent.setAttribute("layout", "popup");
  agent.setAttribute("launcher", "bubble");
  agent.setOptions({
    api: {
      url: "https://api-develop.deepraxis.com", // 正式地址上线后替换
      getToken: async (current) => {
        const response = await fetch("/api/praxis-token", { method: "POST", credentials: "same-origin" });
        if (!response.ok) throw new Error("客服暂时不可用，请稍后重试");
        return (await response.json()).token;
      },
    },
    agent: { name: "好物集客服" },
    startScreen: { greeting: "你好，有什么可以帮你？" },
  });
  document.body.appendChild(agent);
</script>
```

* `api.url` 填 API 域名，也可以是完整的 `/agent/v1` 地址。
* `getToken(current)` 由 Kit 在需要令牌时调用，参数是当前令牌（首次为 `null`），返回令牌字符串。令牌只保存在内存里。
* `setOptions` 把字段合并进当前配置；移除元素会取消进行中的流。
* 在 React 里使用 `@deepractice-ai/praxis-kit-react` 的 `<PraxisAgent>`，见[嵌入方式](/docs/cloud/services/agent/embed/methods)。

期望：页面右下角出现气泡，点开后能发送消息并收到回复；浏览器里没有 `praxis.error` 事件。

## 完整样例 [#完整样例]

仓库里的「好物集」演示站（`apps/kit-demo`）是一个可运行的完整宿主：假登录、`/api/praxis-token` 签发端点、把会员等级作为[上下文](/docs/cloud/services/agent/embed/context)随令牌传入，以及挂载 Kit 的前端。

## 常见问题 [#常见问题]

* **签发返回「Agent not found」**：智能体必须是服务型、未归档、属于你的 API Key 所在组织。
* **签发返回 `INVALID_PARAMS`「embed policy not configured」**：智能体还没有嵌入策略。
* **签发返回 `EMBED_USER_BLOCKED`**：该 `external_user_id` 在智能体的封禁名单里。
* **组件无法对话**：确认页面来源在「允许的来源」里，且 `api.url` 与签发令牌用的是同一个环境。

下一步：[选择嵌入方式](/docs/cloud/services/agent/embed/methods) · [令牌细节](/docs/cloud/services/agent/embed/tokens) · [传入上下文](/docs/cloud/services/agent/embed/context)
