ERI 规范 — Embedded Result Interface
快速开始——不到一天即可上线
- 创建嵌入页面——HTTPS,从 URL 参数读取状态(10 行示例 · hello-world.html)
- 编写
skill.md——告诉 Agent 如何调用你的 API 和构造嵌入 URL(模板 · 真实示例) - 上线——大多数平台今天就能用。无需 SDK,无需审批。
1. 定义 · 合规清单 · 2. 工作流 · 3. 对话流程 · 4. Skill 编写 · 5. 嵌入页面 · 6. 安全 · 7. 等级 · 8. 场景 · 9. 局限性 · 10. 错误处理 · 11. 格局 · 治理
1. 定义
AI Agent 输出文字。ERI(嵌入式结果界面)让它们直接嵌入可交互的 UI——只需一个 skill.md 和一个 URL。
设计原则:无新协议,无运行时依赖,任何能渲染 iframe 的平台都可用——构建一次,到处部署。
| 角色 | 职责 |
|---|---|
| Agent | 理解意图、调用 API、构造嵌入 URL、输出 UI |
| 提供者 | 编写 skill.md 并提供一个 HTTPS 嵌入页面(见最小示例) |
| 平台 | 渲染 iframe(大多数已支持) |
快照式输出——Agent 不监听已输出的界面,每次新指令生成全新嵌入(见第 3 节)。第三方应用独立运行——只需提供一个 HTTPS 页面。
符合性——满足以下条件即达到 ERI Level 1 合规:(1)提供者提供从 URL 参数读取初始状态的 HTTPS 嵌入页面;(2)Agent 遵循四步工作流;(3)平台以 sandbox="allow-scripts" 渲染 iframe。无需注册,无需认证。
Level 1 自证合规清单
- 嵌入页面使用 HTTPS
- 从 URL hash 或 query 参数读取初始状态
- 自包含(不依赖宿主 JS/CSS)
- 正确处理无效/缺失的 URL 参数
skill.md包含 frontmatter +## 工作流- Agent 从服务端调用 API(非用户浏览器)
- Agent 包含纯文本降级
- iframe 使用
sandbox="allow-scripts"
在 README 中添加徽章:[](https://github.com/2234839/eri-spec)
2. 工作流
步骤 2 和 4 涉及外部系统:
调用 API Agent — Agent 从服务端调用第三方应用的 API(不是从用户浏览器)。标准 HTTP,结构化 JSON。无需配置 CORS(跨源资源共享)。
POST /api/calculate
Body: {"expr": "99.5 * 3"}
Response: {"expr": "99.5 * 3", "result": 298.5}
输出 UI Agent 平台 — Agent 输出 iframe。大多数 Web Agent UI 会自动添加 sandbox 属性渲染 iframe。不支持的平台降级为纯文本。
<iframe sandbox="allow-scripts" src="https://app.example.com/embed#encoded_data" width="100%" height="300"></iframe>
3. 对话流程
iframe 内的编辑不会通知 Agent。每轮新对话:
- Agent 从上下文中读取上一次的结果
- 结合新输入,重新调用 API
- 输出全新的嵌入界面(旧界面不更新)
上下文来自 Agent 平台的对话历史——Agent 能看到自己之前的文字输出(包括 API 结果)。无需额外的状态机制。
用户: "帮我算 3 件商品,每件 99.5 元"
Agent: 调用 API → 输出 iframe 显示 99.5 * 3 = 298.5
(用户在 iframe 中修改数值,Agent 不感知)
用户: "再加上 8% 的税"
Agent: 读取上下文(上次结果:298.5),调用 API 传入 298.5 * 1.08
→ 输出新 iframe 显示 298.5 * 1.08 = 322.38
4. Skill 编写 提供者
skill.md 放在 Agent 能发现的位置——可以是自定义指令、GPT Action、Claude 技能文件,或平台提供的任何 Agent 行为定义机制。格式在所有平台上都一样。
一个最小化的 ERI Skill:
---
name: 工具名称
description: 工具描述
---
## 工作流
1. 从用户输入中提取参数
2. 调用 API:POST https://api.example.com/calculate
Body: {"expr": "表达式"}
3. 将 API 响应编码到 URL hash 中
4. 输出为 iframe:https://app.example.com/embed#<编码后的响应>
(平台无法渲染时降级为纯文本)
Frontmatter(name、description)遵循标准 YAML 约定。正文标题(## Workflow、## 工作流 或任意标题)及其内容都是自由格式——用 Agent 能理解的自然语言编写即可。格式为纯 Markdown。
| 平台 | skill.md 内容放置位置 |
|---|---|
| ChatGPT | Custom Instructions,或自定义 GPT 的 GPT Actions |
| Claude | Claude Code:CLAUDE.md 或 .claude/ 技能文件。Claude.ai:项目指令。 |
| Gemini | System Instructions(Gemini API 或 Google AI Studio) |
| 自建 Agent | 任何定义 Agent 行为的机制(系统提示词、配置文件等) |
完整示例:NoteCalc(真实实现) · 模板(复制后填入自己的内容)。
5. 嵌入式页面要求 提供者
| 要求 | 说明 |
|---|---|
| HTTPS | 必须使用 HTTPS |
| URL 参数读取 | 从 URL hash 中读取初始状态(推荐),或使用 query 参数。推荐编码:encodeURIComponent(JSON.stringify(data))——但嵌入页面能理解的任何格式都有效。 |
| 自包含 | 所有交互自行处理。不依赖宿主 JS/CSS。响应式布局。 |
| 可访问性 (推荐) | ARIA 标签、键盘导航、焦点管理。尽可能遵循 WCAG 指南。 |
| Level 2 桥接 (可选) | 实现 ui/* JSON-RPC 桥接以支持双向通信——见第 7 节 |
| 主题适配 (可选) | 读取 URL 参数或宿主颜色方案适配明暗主题 |
| Iframe 尺寸 (推荐) | 设计为响应式宽度(Agent 通常设置 width="100%")。默认高度 300–400px 适用于大多数嵌入。Level 2 可使用 ui/message JSON-RPC 方法动态调整高度。 |
最小嵌入页面示例
Agent 在服务端调用 API,通过 URL hash 将结果传递给嵌入页面。页面只负责渲染——iframe 内无需任何计算逻辑。
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1"></head>
<body>
<div id="out" style="font-size:24px"></div>
<script>
const raw = decodeURIComponent(location.hash.slice(1));
try {
const data = raw ? JSON.parse(raw) : null;
document.getElementById('out').textContent =
data ? data.expr + ' = ' + data.result : '无数据';
} catch (e) {
document.getElementById('out').textContent = '无法加载结果。';
}
</script>
</body>
</html>
此最小示例渲染结果、处理空状态,并捕获格式异常的数据。想看可以直接打开的可运行页面,参见 hello-world.html——它从 URL hash 读取名字并允许用户编辑。生产级嵌入页面还需添加响应式布局、输入验证和主题适配。
6. 安全
| 角色 | 规则 |
|---|---|
| 平台 | 为 iframe 添加 sandbox="allow-scripts"。警告:allow-same-origin 与 allow-scripts 组合允许 iframe 访问自身 origin 的 cookie 和 storage——仅在嵌入页面需要自身 cookie/storage 时使用。iframe 仍无法跨域访问父页面。不得将用户凭证传递给第三方 iframe。如果平台使用 CSP(Content Security Policy),需将嵌入页面的 origin 加入 frame-src。 |
| 提供者 | 不依赖 document.cookie(沙箱环境可能不可用)。不使用 window.opener 或 window.top 做导航/DOM 访问(Level 2 的 window.parent.postMessage 是安全的)。所有输入做 XSS(跨站脚本)防护。绝不在 URL hash/query 参数中放置 PII(个人身份信息)或敏感数据——URL 会被记录在浏览器历史、代理日志和 Referrer 头中。对于敏感数据,应在 URL 中传递短期令牌,由嵌入页面通过 API 调用获取实际数据。 |
7. 渐进等级
| 等级 | 能力 | 谁需要做什么 |
|---|---|---|
| 0 | 纯文本输出 | Agent 即可——不需要其他任何配合 |
| 1 | iframe 嵌入 | 提供者 嵌入页面 + 平台 iframe 支持(大多数平台已有) |
| 2 | 双向通信 | Level 1 + 提供者 平台 双方实现 MCP Apps 宿主桥(ui/* JSON-RPC 2.0 over postMessage) |
Level 2 适用于平台需要对 iframe 内的用户编辑做出响应的场景(如自动保存、实时预览)。ERI Level 2 使用与 MCP Apps 相同的 ui/* JSON-RPC 2.0 桥接——没有自定义消息格式。这意味着 ERI Level 2 嵌入页面直接兼容 MCP Apps 平台。
| 方法 | 方向 | 用途 |
|---|---|---|
ui/initialize | 平台 → iframe | 握手:平台发送会话信息和能力声明 |
ui/notifications/tool-input | 平台 → iframe | 将工具调用的输入参数传递给 iframe |
ui/notifications/tool-result | 平台 → iframe | 将工具执行结果返回给 iframe |
tools/call | iframe → 平台 | iframe 请求平台调用一个工具 |
ui/message | iframe → 平台 | iframe 发送后续消息(如用户编辑了某个值) |
/** 平台 → iframe:加载时初始化 */
window.addEventListener("message", (event) => {
if (event.origin !== "https://agent-platform.example.com") return;
const { method, params } = event.data;
if (method === "ui/initialize") {
// 存储会话信息(params.sessionId, params.capabilities)
}
});
/** iframe → 平台:用户编辑了某个值(通知,无需响应) */
window.parent.postMessage({
jsonrpc: "2.0",
method: "ui/message",
params: {
content: { type: "text", text: "User changed items.price to 199" }
}
}, "https://agent-platform.example.com");
/** iframe → 平台:请求调用工具(期望匹配 id 的响应) */
window.parent.postMessage({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: { name: "updateCart", arguments: { items: [...], total: 597 } }
}, "https://agent-platform.example.com");
// 平台响应:{ jsonrpc: "2.0", id: 1, result: { content: [...] } }
8. 适用场景
适合——任何用户想要微调 Agent 输出的场景:
| 场景 | 嵌入页面做什么 |
|---|---|
| 计算器 / 电子表格 | 渲染表达式和结果;用户直接编辑数值 |
| 图表 / 仪表盘 | 渲染可视化;用户调整筛选条件或日期范围 |
| 地图 / 位置 | 渲染交互式地图;用户平移、缩放或选择标记 |
| 表单 / 问卷构建器 | 渲染预览;用户重排字段或修改选项 |
| 文档编辑器 | 渲染格式化内容;用户调整排版或字体 |
| 设计工具 | 渲染预览;用户调整颜色、间距或尺寸 |
| 代码演练场 | 渲染带语法高亮的代码;用户编辑并查看输出 |
不适合:纯信息查询(不需要交互)、长文本生成、实时同步(ERI 是快照式的)。
9. 局限性
- 快照式输出——每轮对话产生全新的嵌入,Agent 无法观察 iframe 内的编辑(第 1 节)。Level 2
ui/*JSON-RPC 可实现双向同步(第 7 节)。 - 每轮重新加载——每次用户对话触发全新的 iframe 加载。嵌入页面较重时会产生可见的重载闪烁。保持嵌入页面轻量。
- 设备能力受限——沙箱 iframe 对设备 API(GPS、摄像头、麦克风、蓝牙)的访问受限。仅支持标准 Web 能力。
- URL 大小限制——编码到 URL hash 中的状态受浏览器 URL 长度限制(安全范围约 2KB,上限约 8KB,参见 RFC 3986 实际限制)。数据量较大时,使用大数据集模式。
- 无离线支持——嵌入页面需要网络访问。用户断网时 iframe 无法渲染。
大数据集模式
当 API 响应超过 URL 大小限制时,使用令牌方式替代直接编码数据:
/** Agent:调用 API,获取结果令牌而非完整数据 */
POST /api/report
Response: {"resultToken": "abc123", "summary": "Q1 收入:120 万"}
/** Agent:用令牌嵌入,并包含文本摘要作为降级 */
<iframe src="https://app.example.com/embed#abc123" ...></iframe>
/** 嵌入页面:用令牌获取完整数据 */
const token = decodeURIComponent(location.hash.slice(1));
const data = await fetch('/api/results/' + token).then(r => r.json());
10. 错误处理
| 故障 | Agent 行为 |
|---|---|
| API 返回错误或超时 | 以纯文本报告错误。不嵌入包含错误状态的 iframe。 |
| 嵌入 URL 不可达 | 输出纯文本降级。用户仍然获得 API 结果。 |
| 平台阻止 iframe | 降级为纯文本——Agent 的文字输出仍然可见。 |
| URL 编码数据超出限制 | 提供者应设计嵌入页面接受短期令牌,并通过服务端 API 获取数据。 |
ERI 的纯文本降级不是缺陷——它是基线。每个 ERI 输出都应包含文本摘要,确保用户无论 iframe 是否渲染都能获得价值。
11. 行业格局
ERI 工作在输出层——Agent 如何向用户展示结果。它与工具调用协议(MCP、Function Calling、REST)互补——后者工作在输入层,即 Agent 如何调用外部系统。规范对 API 如何被调用不做限定。
交互式 Agent 输出存在三种方案:
| ERI | MCP Apps | A2UI (Google) | |
|---|---|---|---|
| 方式 | 通过 URL 嵌入(iframe) | 沙箱 iframe 小应用 | 声明式 JSON UI Schema |
| 需要构建 | HTTPS 嵌入页面 + skill.md | MCP 兼容的应用包 | 组件目录 + A2UI Schema |
| 平台要求 | 渲染 iframe(已有) | MCP 运行时 | A2UI 渲染器 |
| 今天可用? | 是——大多数平台 | 仅 MCP 平台 | 仅 A2UI 平台 |
| 能力 | 完整 Web 平台 | 平台 API、实时通信 | 原生渲染 |
MCP Apps 和 A2UI 提供更丰富的能力(原生 API、实时同步、平台集成)——ERI 用这些换取今天的通用可用性。由于 MCP Apps 同样使用沙箱 iframe 和相同的 ui/* 桥接(第 7 节),ERI 嵌入页面无需修改即可沿用。
ERI 相比普通临时 iframe 增加了什么:
| 没有 ERI | 有 ERI | |
|---|---|---|
| 可发现性 | Agent 不知道你的应用存在 | skill.md 告诉 Agent 何时、如何使用你的应用 |
| API 契约 | 每个 Agent-提供者对都是定制开发 | 标准化:调用 API → 编码结果 → 嵌入 URL |
| 降级方案 | iframe 失败 = 输出异常 | Agent 自动降级为纯文本 |
| 跨平台 | 每个 Agent 平台分别调优 prompt | 一个 skill.md 在 ChatGPT、Claude、Gemini 上通用 |
规范性引用
- HTML Living Standard —
<iframe>元素和sandbox属性 - HTML Living Standard —
Window.postMessage()API - RFC 3986 — 统一资源标识符(URI):通用语法
- Content Security Policy Level 3 —
frame-src指令 - JSON-RPC 2.0 规范 — Level 2 消息传输格式
- MCP Apps 宿主桥 — Level 2 双向通信的
ui/*JSON-RPC 方法
治理
ERI 是由原作者维护的社区规范。Level 1 核心工作流已冻结——不做破坏性变更。增量变更(新场景、Level 2 消息类型)通过 GitHub Issues 的开放提案流程进行。所有实质性变更需要经评审的 Pull Request。详见 CONTRIBUTING.md。