不要向你的代理喂入 HTML

发布: (2025年12月4日 GMT+8 23:49)
7 min read
原文: Dev.to

Source: Dev.to

TL;DR — 给 AI 一个白盒,而不是黑盒

大多数 AI 代理通过 黑盒 方法与 Web 应用交互:消费 DOM 转储或截图,然后猜测该点哪里。HTML 从未为机器而设计。从 AI 的视角来看,DOM 只是噪声,业务逻辑只在其中微弱埋藏。

本文主张采用 白盒 方法:暴露一个 语义状态层,直接揭示应用的结构、规则、状态以及有效的转移。这并不是要取代 UI,而是要在传统用户界面旁提供一个 智能接口 (II)

Manifesto Playground Demo

自己动手试一试 → 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 ─────────────┘       │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

步骤详解

  1. 上下文注入 – 引擎导出包含拓扑、状态、约束和交互的语义快照。
  2. 推理 – 代理基于快照规划下一步动作。
  3. 动作分发 – 代理调用抽象意图(例如 updateFieldsubmitresetvalidate),而不是原始 DOM 事件。
  4. 增量反馈 – 引擎返回实际发生的变化,而不仅是“成功”。差异展示因果关系(例如 “我修改了 X,导致 Y 隐藏”。)
  5. 循环使用更新后的快照重复进行,给代理提供可预测、结构化的反馈,而不是“点击并祈祷”。

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"

从这里,代理可以查询可用交互、更新字段、提交表单,并接收增量差异,所有操作都由白盒语义表示驱动。

Back to Blog

相关文章

阅读更多 »