ERI 规范 — Embedded Result Interface

版本:1.0  |  状态:Stable  |  许可:MIT  |  稳定性:Level 1 核心工作流已稳定——我们承诺不做破坏性变更。更新日志

快速开始——不到一天即可上线
  1. 创建嵌入页面——HTTPS,从 URL 参数读取状态(10 行示例 · hello-world.html
  2. 编写 skill.md——告诉 Agent 如何调用你的 API 和构造嵌入 URL(模板 · 真实示例
  3. 上线——大多数平台今天就能用。无需 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 自证合规清单

在 README 中添加徽章:[![ERI Compatible](https://img.shields.io/badge/ERI-Compatible-6366f1)](https://github.com/2234839/eri-spec) ERI Compatible

2. 工作流

1. 理解意图提取参数
2. 调用 API获取结构数据
3. 构造 URL编码参数
4. 输出 UIiframe / 纯文本

步骤 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。每轮新对话:

  1. Agent 从上下文中读取上一次的结果
  2. 结合新输入,重新调用 API
  3. 输出全新的嵌入界面(旧界面不更新)

上下文来自 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(namedescription)遵循标准 YAML 约定。正文标题(## Workflow## 工作流 或任意标题)及其内容都是自由格式——用 Agent 能理解的自然语言编写即可。格式为纯 Markdown。

平台skill.md 内容放置位置
ChatGPTCustom Instructions,或自定义 GPT 的 GPT Actions
ClaudeClaude Code:CLAUDE.md.claude/ 技能文件。Claude.ai:项目指令。
GeminiSystem 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-originallow-scripts 组合允许 iframe 访问自身 origin 的 cookie 和 storage——仅在嵌入页面需要自身 cookie/storage 时使用。iframe 仍无法跨域访问父页面。不得将用户凭证传递给第三方 iframe。如果平台使用 CSP(Content Security Policy),需将嵌入页面的 origin 加入 frame-src
提供者不依赖 document.cookie(沙箱环境可能不可用)。不使用 window.openerwindow.top 做导航/DOM 访问(Level 2 的 window.parent.postMessage 是安全的)。所有输入做 XSS(跨站脚本)防护。绝不在 URL hash/query 参数中放置 PII(个人身份信息)或敏感数据——URL 会被记录在浏览器历史、代理日志和 Referrer 头中。对于敏感数据,应在 URL 中传递短期令牌,由嵌入页面通过 API 调用获取实际数据。

7. 渐进等级

等级能力谁需要做什么
0纯文本输出Agent 即可——不需要其他任何配合
1iframe 嵌入提供者 嵌入页面 + 平台 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/calliframe → 平台iframe 请求平台调用一个工具
ui/messageiframe → 平台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. 局限性

大数据集模式

当 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 输出存在三种方案:

ERIMCP AppsA2UI (Google)
方式通过 URL 嵌入(iframe)沙箱 iframe 小应用声明式 JSON UI Schema
需要构建HTTPS 嵌入页面 + skill.mdMCP 兼容的应用包组件目录 + A2UI Schema
平台要求渲染 iframe(已有)MCP 运行时A2UI 渲染器
今天可用?是——大多数平台仅 MCP 平台仅 A2UI 平台
能力完整 Web 平台平台 API、实时通信原生渲染

MCP AppsA2UI 提供更丰富的能力(原生 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 上通用

规范性引用

治理

ERI 是由原作者维护的社区规范。Level 1 核心工作流已冻结——不做破坏性变更。增量变更(新场景、Level 2 消息类型)通过 GitHub Issues 的开放提案流程进行。所有实质性变更需要经评审的 Pull Request。详见 CONTRIBUTING.md