Pling 如何工作
Pling 不把模型接到 DOM 上,也不把业务逻辑搬到独立的 Agent 服务。它在现有页面里增加一层语义接口:页面说明自己能做什么、当前有哪些业务对象;Agent 通过这些说明选择操作,执行仍回到页面原来的事件处理函数和后端请求。
这也是 Pling 的主要特点:开发者不需要重写页面,只需要在需要开放的按钮、数据行和区域上增加少量声明。本章先说明 Pling 由哪些部分组成,再说明它如何理解页面、如何执行操作,以及为什么接入改动可以保持在局部。
Pling 由哪些部分组成
从运行结构看,Pling 有四个部分。前两个是所有接入页面都需要的;后两个按使用方式选择。
| 部分 | 是否必需 | 职责 |
|---|---|---|
| 页面接入层 | 必需 | 把现有页面中的动作、数据行、区域和只读状态声明为统一的业务能力。业务组件里需要增加的内容集中在这一层。 |
| 页面运行时 | 必需 | 维护当前页面的能力清单,生成有界上下文,解析动作目标和记录,执行动作并返回结果。 |
| 页面内助手 | 可选 | 在页面里运行模型循环和对话界面,使用页面运行时提供的上下文与动作。 |
| Agent Surface | 可选 | 让页面外的 Agent 通过 describe 和 invoke 使用同一组能力,不把 DOM、闭包、Cookie 或后端凭证交给对方。 |
现有页面
├─ 业务状态 / 事件处理 / 后端请求
└─ Pling 声明
│
▼
页面运行时
├─ 页面内助手(可选)
└─ Agent Surface(可选) ◄── 外部 Agent页面、业务后端和模型都不属于这四部分。页面负责业务行为,后端负责授权和提交,模型负责决定下一步。Pling 连接它们,但不取代其中任何一方。
对应到代码,页面接入层由 @plingjs/vue、@plingjs/react 或 @plingjs/vanilla 提供;页面运行时由 @plingjs/core 与 @plingjs/dom 组成;页面内助手使用 @plingjs/agent 和可选的界面组件;Agent Surface 位于 @plingjs/surface。模型网关是外围组件,@plingjs/server 只提供可选的薄后端路径。
Pling 怎样理解页面
这里的“理解”不是让模型读取整页 HTML,也不是让模型从截图中猜按钮。Pling 把页面整理成一份业务视图,来源有两部分:
- 开发者声明的业务语义:一个动作做什么、接受什么输入、作用于哪类记录、位于哪个区域,以及哪些只读状态有助于判断当前任务;
- 运行时在当前时刻观察到的事实:实际存在哪些记录和字段、动作此刻是否可用及原因、区域里有多少内容可见、页面反馈是什么。
例如,一个待办页面可能被整理成下面这样:
页面:待办
动作:add_todo(text);toggle(参数 record)
记录:t1「买牛奶」未完成;t2「写周报」已完成
区域:待办列表(当前可见 3 条,共 20 条,可继续滚动)
状态:filter = open四类信息各自解决一个问题:
- 动作给模型一个稳定的调用入口,说明文字用于判断用途,输入结构用于填写参数;
- 记录给业务对象一个稳定 id,模型按 id 选择对象,不需要知道行号或 CSS 选择器;
- 区域说明动作和数据位于页面的哪个模块。两个都叫“确认”的按钮可以因此带上不同的区域路径,避免只靠名称混淆;
- 状态提供筛选、分页、选中项等只读上下文。写回仍然必须通过动作。
内容被折叠或在视口外时,上下文会说明还有多少内容以及如何继续查找。模型引用一条当前不可见的记录时,运行时会先滚动到它,再检查动作是否可用。
声明说明页面愿意开放什么,不授予权限。没有出现在能力视图中的操作,Pling 不会临时改成选择器点击或截图猜测。
Pling 怎样操作页面
一次操作从模型选择一个动作开始,之后由页面运行时完成检查和执行:
- 模型返回动作 id 和输入参数,例如
toggle与record: "t1"; - 运行时确认动作存在、当前会话允许使用、参数合法,并检查目标此刻是否可用;
- 运行时把记录 id 解析为当前页面上的业务对象和目标元素;目标不在视口内时先滚动到它;
- 需要用户确认的动作在这一步停下来,得到允许后才继续;
- 执行器走页面原有的操作路径,等待异步处理结束,再读取执行后的页面;
- 结果返回成功或带原因的失败,并进入下一轮上下文。
“走原有路径”是具体的行为,不是比喻。绑定在按钮上的动作默认触发该按钮原本的点击处理;输入动作写入值并触发页面正常监听的事件;开发者也可以提供一个 run 函数,直接调用页面已有的处理逻辑。框架不会复制一份业务处理函数,也不会绕过页面直接修改业务数据。
页面自己的校验、状态更新和后端请求照常发生。后端仍然决定这次操作是否被授权、是否提交成功。Pling 只负责把 Agent 的调用送到这条路径上,并把结果说清楚。
为什么接入改动小
最小接入通常只是在已有元素上增加一条说明。以保存按钮为例:
<button @click="submit">保存</button>接入后可以只多一个属性:
<button v-tool="'保存当前表单'" @click="submit">保存</button>@click="submit" 没有变化,submit 函数、表单状态和后端接口也没有变化。需要让模型按行选择对象时,再给列表行增加 v-record;需要说明模块时,增加可选的 v-scope;需要让模型理解筛选或分页时,增加只读状态视图。没有对应 DOM 元素的动作,可以注册为函数级工具,仍然复用同一条执行与确认路径。
应用的入口通常只需要安装一次插件;需要页面内问答时,再挂载一个助手入口。业务代码不需要改用 Pling 的状态容器,也不需要把原有的增删改查 API 暴露给模型。
所以低侵入的含义是:改动是声明式、局部、可移除的。开发者仍然需要补充足够的信息,让 Agent 知道页面能做什么;但不需要搬动业务逻辑。移除这些声明后,原页面仍按原来的方式工作。
一次操作怎样走完
假设一个待办页面有三条任务,用户说:“把牛奶标记为完成,再加一条面包。”
- 页面运行时生成上下文,包含
t1、t2、t3的记录、当前筛选和可用动作。 - 模型选择
toggle,把t1作为记录参数。 - 运行时检查动作和参数,解析
t1,确认复选框可用,然后触发复选框原本的点击处理。 - 页面更新任务状态,运行时返回变更后的记录和新的可用动作。
- 下一轮,模型选择
add_todo,输入文本“面包”。 - 页面再次更新,Agent 根据结果回复用户。
这条链路中,各方的职责没有重叠:
| 角色 | 负责 |
|---|---|
| 模型 | 根据目标、业务语义和当前事实选择下一步 |
| Pling 运行时 | 暴露能力、组织上下文、校验调用、解析目标、执行并报告结果 |
| 页面 | 执行原有校验、状态变更和界面行为 |
| 业务后端 | 作出最终授权与事务决定 |
页面内助手与外部 Agent
页面可以只提供页面内助手,也可以只提供 Agent Surface,或两者都提供。
页面内助手直接使用页面运行时:读取能力清单和上下文,调用同一个执行器。外部 Agent 通过 Agent Surface 获得序列化后的有界视图,再调用 invoke。两条路径的入口不同,但业务动作、目标解析、可用性检查、确认策略和执行结果来自同一套实现。
外部 Agent 能看到的只有页面允许投影的能力和上下文,不会获得页面闭包、DOM 或浏览器会话凭证。Surface 不要求页面内助手存在;页面内助手也不依赖 Surface。
哪些部分不在运行时里
| 组件 | 职责 | 与 Pling 的关系 |
|---|---|---|
| 模型 | 根据上下文和动作说明决定下一步 | 调用能力,不定义能力 |
| 模型网关 | 保存供应商凭证、验证请求、代理模型调用 | 可选后端路径,不属于页面运行时 |
| Surface 传输 | 通过 MCP、postMessage、HTTP 或其他通道承载 describe 与 invoke | 同一契约的通道,不是第二个执行器 |
| 业务后端 | 授权用户并提交事务 | 最终权威,Pling 不能替代 |
| 浏览器自动化 | 在没有声明能力时操作页面 | 另一条路径,权限边界不同 |
把成功调用协议当成业务已经授权并提交,或把模型看到的动作列表当成页面的全部业务约定,都会越过这些边界。
小结
Pling 由页面接入层、页面运行时、可选的页面内助手和可选的 Agent Surface 组成。页面理解来自开发者声明的业务语义与运行时观察到的当前事实;页面操作由模型选择动作,运行时解析并执行,页面走原有的业务路径,后端保留最终授权。开发者需要补充声明,但不需要重写业务逻辑、复制状态或另建一套后端接口。

