# 测试嵌入接入 (/docs/developers/agent/embed/testing)



上线前，用真实宿主网页验证正常对话和拒绝路径。下面列的是要执行的验收情境；控制台出现组件或触发 `praxis.ready` 只证明挂载，不证明认证和模型回复成功。

## 准备测试环境 [#prepare]

准备隔离的服务智能体、可用模型与运行环境、已保存的嵌入策略，以及已经完成接入的宿主页面。将该页面的实际来源加入白名单，设置可承受的预算。使用两个宿主测试账户 A、B，分别由后端登录态得到不同的 `external_user_id`。

先在「服务智能体 → 选择智能体 → 接入与配置 → 试用调试」选择版本、发送问题，排除配置本身的问题。随后执行下表中的宿主测试；控制台模拟不替代宿主登录、域名和令牌端点测试，也不进入客户运营统计。

## 按情境验收 [#scenarios]

| 输入或操作                        | 预期结果与验证方法                                                          |
| ---------------------------- | ------------------------------------------------------------------ |
| A 登录宿主，发送一个课程问题              | 页面出现真实回复；「运营与管理 → 会话」能找到相同消息、用户和来源                                 |
| 未登录时调用你的宿主签发端点               | 宿主拒绝签发，不返回嵌入令牌                                                     |
| 从白名单之外的网站加载                  | 访问被拒绝；接口可返回 `EMBED_ORIGIN_NOT_ALLOWED`（403）。iframe 也可能先被浏览器的嵌入策略拦截 |
| 签发时提交 `ttl_seconds: 59`      | 返回 `INVALID_PARAMS`（400），不产生可用令牌                                   |
| 关闭宿主覆盖指令后提交 `agent_override` | 返回 `EMBED_FIELD_NOT_ALLOWED`（400）；不能靠前端输入改变可信指令                    |
| 用户被封禁后由宿主再次签发                | 返回 `EMBED_USER_BLOCKED`（403）                                       |
| 达到同时运行的会话上限后再建会话             | 返回 `EMBED_RATE_LIMITED`（429）；等待运行结束后再试，不靠删除空闲记录释放名额                |
| 月度上限触顶，动作为「停止新会话」            | 新建会话返回 `EMBED_MONTHLY_CAP_REACHED`（402）；不要在真实用户智能体上制造这项条件          |
| 页面声明了前端工具                    | 工具被调用时有预期页面效果和结果；未知工具、参数无效或无业务权限时失败，不能只看确认卡                        |

可用下面的独立脚本验证有效期下限。在服务器设置 `PRAXIS_API_BASE`（API 源站）、`PRAXIS_API_KEY`（组织 API Key）和 `PRAXIS_AGENT`（真实 `agent_…` ID）。用支持原生 `fetch` 的 Node.js 保存并执行 `node test-token-limit.mjs`。智能体必须已配置好策略，否则会先因缺少配置失败。

```js
import assert from 'node:assert/strict';

const { PRAXIS_API_BASE, PRAXIS_API_KEY, PRAXIS_AGENT } = process.env;
assert(PRAXIS_API_BASE && PRAXIS_API_KEY && PRAXIS_AGENT, 'Set the three environment variables');
assert(PRAXIS_AGENT.startsWith('agent_'), 'Use the public agent ID');
const response = await fetch(`${PRAXIS_API_BASE.replace(/\/$/, '')}/agent/v1/embed_tokens`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${PRAXIS_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ agent: PRAXIS_AGENT, external_user_id: 'docs_test_a', ttl_seconds: 59 }),
});
assert.equal(response.status, 400, 'Invalid TTL must be rejected');
console.log({ status: response.status, requestId: response.headers.get('X-Request-Id') });
```

**预期结果：** 脚本正常退出并输出 `status: 400`；这里只验证拒绝状态，错误体应另核对 `INVALID_PARAMS` 和有效期字段提示。脚本不打印凭证，也不替代成功签发和真实回复测试。

## 验证身份与生命周期 [#lifecycle]

1. A 开始会话后退出，销毁 A 的组件。B 登录并新建组件，检查历史里没有 A 的会话；用 B 的令牌读取 A 的会话也必须被拒绝。只清空界面不等于切换身份。
2. A 保持登录跨过一次令牌刷新，确认 `getToken(current)` 重新检查宿主身份，后续消息仍属于 A。单独用已过期令牌请求，应得到 `EMBED_TOKEN_EXPIRED`（401）。
3. 在「运营与管理 → 签发记录」撤销 A 当前令牌，确认旧访问失败，组件不自动绕过撤销继续刷新。用户级批量撤销、封禁与解封按[管理终端用户](/docs/developers/agent/end-users)验证。
4. 匿名接入用独立浏览器存储测试两位访客，再刷新同一访客页面检查身份延续；不要把 `visitor_secret` 分享给另一位访客。
5. Hosted 接入分别测试兑换链接超过 60 秒、重复兑换，以及已打开页面的令牌到期；前两者被拒绝，后者不会自动刷新，应从宿主重新获取链接。

## 排错与清理 [#cleanup]

记录每项的时间、环境、组件版本、预期与实际结果，以及失败响应的 `X-Request-Id`、状态和错误码。错误事件可能只有 `message`；从浏览器网络面板补齐响应信息，记录中不含 Key、令牌或访客凭据。

完成后撤销本轮测试令牌，归档不再使用的测试会话，恢复临时修改的限额和封禁状态。归档不会删除记录。再用正常配置执行一次成功对话，确认清理没有留下访问障碍。

下一步：[上线检查清单](/docs/developers/agent/embed/go-live) · [限制与错误](/docs/developers/agent/embed/limits-and-errors) · [智能体 API](/docs/api/native/agent)
