不要向你的代理喂入 HTML
Source: Dev.to
TL;DR — 给 AI 一个白盒,而不是黑盒
大多数 AI 代理通过 黑盒 方法与 Web 应用交互:消费 DOM 转储或截图,然后猜测该点哪里。HTML 从未为机器而设计。从 AI 的视角来看,DOM 只是噪声,业务逻辑只在其中微弱埋藏。
本文主张采用 白盒 方法:暴露一个 语义状态层,直接揭示应用的结构、规则、状态以及有效的转移。这并不是要取代 UI,而是要在传统用户界面旁提供一个 智能接口 (II)。

自己动手试一试 → Manifesto Playground
1. 黑盒:AI + Web 应用的现状
下面是大多数团队今天向他们的 Web 应用“添加 AI”的方式:
- 使用 LangChain、AutoGPT 或浏览器自动化
- 驱动 Playwright 或 Puppeteer
- 将 DOM 或截图转存给模型
- 希望它能找出该点哪里
这就是 黑盒 方法。代理只能看到渲染后的表面,必须自行推断其余一切。
DOM 转储有什么问题?
<div class="product-name">
<label>Product Name</label>
<input type="text" name="name" />
<span class="error">This field is required.</span>
</div>
从代理的视角来看:
| 问题 | 影响 |
|---|---|
| 令牌浪费 | 90 % 的令牌都是类名和包装元素 |
| 缺少约束 | 是否必填?最大长度是多少? |
| 没有依赖关系 | 这个字段是否依赖其他字段? |
| 缺乏因果关系 | 提交按钮被禁用——但 为什么? |
代理被迫 猜测。一次 CSS 重构会把所有东西弄坏。布局变化会让模型困惑。逻辑从未被暴露——只有它的视觉投影。
信号 90 %。
2. 白盒:暴露应用的大脑
另一种选择是 白盒 协议。不是展示 HTML,而是让引擎暴露一个 语义快照——一种结构化的内部状态表示,代理可以直接读取。
{
"topology": {
"viewId": "product-create",
"mode": "create",
"sections": [
{ "id": "basic", "title": "Basic Info", "fields": ["name", "productType"] },
{ "id": "shipping", "title": "Shipping", "fields": ["shippingWeight"] }
]
},
"state": {
"form": { "isValid": false, "isDirty": false },
"fields": {
"name": {
"value": "",
"meta": { "valid": false, "hidden": false, "disabled": false, "errors": ["Required"] }
},
"productType": {
"value": "PHYSICAL",
"meta": { "valid": true, "hidden": false, "disabled": false, "errors": [] }
},
"shippingWeight": {
"value": null,
"meta": { "valid": true, "hidden": false, "disabled": false, "errors": [] }
}
}
},
"constraints": {
"name": { "required": true, "minLength": 2, "maxLength": 100 },
"shippingWeight": { "min": 0, "max": 2000, "dependsOn": ["productType"] }
},
"interactions": [
{ "id": "updateField:name", "intent": "updateField", "target": "name", "available": true },
{ "id": "updateField:productType", "intent": "updateField", "target": "productType", "available": true },
{ "id": "submit", "intent": "submit", "available": false, "reason": "Name is required" }
]
}
现在代理拥有:
- 拓扑 – 屏幕结构、分区、字段层级
- 状态 – 每个字段的当前值、有效性、可见性、错误信息
- 约束 – 必填、最小/最大、依赖关系
- 交互 – 哪些操作可用以及 为何 某些被阻塞
无需猜测。无需推断。代理直接读取应用的大脑。
3. 实际案例:“我该在哪里选择周?”
🎮 现场演示: Manifesto Playground – 尝试修改字段值,实时观察语义状态的更新。
场景
用户: “我看到一个日期选择器,但我该在哪里选择周?”
AI 聊天机器人: “只有当你把频率设为‘每周’时,周选择器才会出现。现在它被设为‘每日’。需要我帮你改成每周吗?”
要实现上述对话,AI 必须知道:
- 存在一个名为
weekSelector的字段且当前是隐藏的 - 当
frequency === 'WEEKLY'时它会出现 - 当前
frequency的值是'DAILY'
仅靠 DOM 无法可靠提供这些信息,但语义快照可以:
{
"fields": {
"frequency": {
"value": "DAILY",
"meta": { "hidden": false }
},
"weekSelector": {
"value": null,
"meta": { "hidden": true },
"visibleWhen": "frequency === 'WEEKLY'"
}
}
}
AI 读取后 知道——无需推断——字段为何隐藏以及怎样才能出现。
4. 协议循环
Manifesto 实现了引擎与 AI 代理之间的持续反馈循环:
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ [Context Injection] → [Reasoning] → [Action Dispatch] → [Delta] │
│ ▲ │ │
│ └─────────────── Continuous Snapshots ─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
步骤详解
- 上下文注入 – 引擎导出包含拓扑、状态、约束和交互的语义快照。
- 推理 – 代理基于快照规划下一步动作。
- 动作分发 – 代理调用抽象意图(例如
updateField、submit、reset、validate),而不是原始 DOM 事件。 - 增量反馈 – 引擎返回实际发生的变化,而不仅是“成功”。差异展示因果关系(例如 “我修改了 X,导致 Y 隐藏”。)
- 循环使用更新后的快照重复进行,给代理提供可预测、结构化的反馈,而不是“点击并祈祷”。
5. API:探索与执行
Manifesto 通过 @manifesto-io/ai 暴露此协议。
探索模式 – “我在这里能做什么?”
import { createInteroperabilitySession } from '@manifesto-io/ai'
const session = createInteroperabilitySession({
runtime, // FormRuntime 实例
viewSchema, // 视图定义
entitySchema, // 实体定义
})
// 获取当前语义快照
const snapshot = session.snapshot()
// snapshot.interactions 告诉代理:
// - submit: available = false, reason = "Name is required"
从这里,代理可以查询可用交互、更新字段、提交表单,并接收增量差异,所有操作都由白盒语义表示驱动。