我们只在预设的技术路线上,完成了小小的可行性认证。我们会因为展示一条道路的正确性而喜悦,也清楚地意识到两点事实——一,我们还只是在前人的框架与能力下进行了一些小小探索,实际上我们只是在现代DOM以及浏览器框架下取巧,并承接了大模型溢出的能力;二,目前距离最终预期的实现效果进度不足百一,如何在更多场景中完成丝滑无比的创作,如何将能力迁移到移动端或者其他操作平台,我们能不能解锁更多更完备的操作模式……想到这些我都激动不已。所以以下内容与其说是技术报告,不如说是技术未完成报告,说明我们解决了什么,更多列举了我们想解决什么。希望阅读能让您有所收获。
https://ollivander.inksight.tech/r/notion
总的来说,目前我们实现的功能很简单:通过画布与框选的方法,让任何一个普通人能够直观地表达自己的意图,并且可以按照自己的心意改造一个网页。相信您已经看过了演示。并且意识这个项目的实现及到了以下的核心问题:
| 意图理解 | 操作之间如何组合 |
|---|---|
| 运行环境理解 | 代码执行 |
其实我们会认为智能时代应用的最终追求也是在这四个象限上的:
| AI 理解人 | AI进行合作与社会化分工 |
|---|---|
| AI 理解物理世界 | AI 任务执行 |
所以,我们要去做的事情还是一些落在智能主线上的事情哩!不过,现在学界与工业界好像只对右侧的两个任务 感兴趣。而这对于制作一个“予人方便”的产品,是远远不够的。将这些基础问题的阶段性回答组合在一起,就是今天我们将要展示的。
下文将以逐一介绍我们在这四个方向上的任务的定义,一些实现方法,以及更重要的是,如何后续实践的方向与预期。
人类的行为往往是令人困惑的事情。我们将意图理解这个无从下手的问题约束到网页之上一层的画布上,对于用户涂鸦之于其上进行理解。
浏览器上的鼠标手势不是什么新的事情。2002年Opera浏览器已经内置了手势操作。到如今,所有主流的浏览器内置了鼠标手势功能。不过这些功能已经消失在了普通用户视野之外,因为它十分鸡肋。手势识别是一种费力不讨好的事情——方便人类绘制的手势只有那么几种(比如,我的手还没有稳到画一个直角,所以这不适合当做手势),并且这一功能并没有比鼠标热键方便太多。所以对于Chrome这样的浏览器,手势操作也变成了一个默认不激活的功能。
手势操作很像涂鸦,我们在纸张与数位板上的框选与勾画也很自然,但在页面去做处理就很别扭。可能其原因在于,UI是结构化数据,而手势与笔画是非结构化的。想象一下桌面,网页,或者移动端的APP 的所有UI元素,他们好像都是矩形的,这是因为计算机的前端逻辑本质上和Minecraft没有什么区别,都是使用方块式的元素组织起来的 ,而作为人类,你的操作是这样吗?

所以我们用了便利贴这种简单的结构去承载手势意图——简单来说,我们将所有的用户鼠标操作划分为两种:
flowchart TB
subgraph ROW1[意图]
direction LR
A[针对一个部分的意图]
C[两个部分之间的关系]
end
subgraph ROW3[操作]
direction LR
E[框选]
F[连接]
end
subgraph ROW2[结果]
direction LR
B[内容推断]
D[比较关联]
end
A--->E--->B
C--->F--->D
为了让实现更加简单,我们只做了便利贴之间的连线,此外配合可以输入文本的便利贴,这些内容足够完备让用户对任何区域发出任何指令。
将鼠标操作识别到在框选什么内容,创建新的空白便利贴,或者只是把两个便利贴连起来,这类的操作识别很基础,不会涉及到智能。不过,因为我相信模型的智能水平,所以也放宽了框选的内容筛选标准。它的效果很不错,总是能找到杂乱数据中我所指的那一项。在后文中我也会反复提及类似的事实——生成式模型总是能解决一些领域外的问题,这种解决完美到让我们甚至忽略了问题本应存在的定义与拆分,这也是所有人对于智能本身价值这么快达成共识的原因。
而在之后就是意图理解的部分。一种典型的PROMPT 工程。得益于可解释元素(后文会解释),后端很容易理解用户在框选内容的位置(在UI与代码块,这与Loveable类似),以及其可能代表的语义。那么在网页这个赛博环境中,我们当然会知道一次框选会产生什么。再添加依据规则产生的自然语言描述,我们就会得到:
{
"butlerThought": "主人的这次圈选,看起来像是:你圈选的是一个界面元素(类型:route-card),更多是在标记这个具体元素,而不是整个卡片或版块。",
"contextSummary": "页面 pageId: /\\n当前便利贴 id: sel-1766307893561\\n便利贴类型: Type A\\n手势类型: selection\\n创建时间: 2025-12-21T09:04:53.561Z\\n画布焦点描述: 用户圈选了一个界面元素:「入口 · 关于我们」;这次圈选一共命中了 3 个组件(块级 1 个,小组件 2 个),上述描述中已经突出其中最重要的部分,其余可以作为上下文。\\n画布助手内部总结: 主人的这次圈选,看起来像是:你圈选的是一个界面元素(类型:route-card),更多是在标记这个具体元素,而不是整个卡片或版块。\\n相关元素数量(LLM Payload): 3",
"createdAt": "2025-12-21T09:04:53.561Z",
"elementCount": 3,
"gestureKind": "selection",
"llmPayload": {
"elementCount": 3,
"focusDescription": "用户圈选了一个界面元素:「入口 · 关于我们」;这次圈选一共命中了 3 个组件(块级 1 个,小组件 2 个),上述描述中已经突出其中最重要的部分,其余可以作为上下文。",
"mainBlock": null,
"otherBlocks": [
{
"author": "",
"id": "hub-footer",
"kind": "block",
"summary": "显示版权信息与分隔线。",
"tagName": "footer",
"title": "页脚"
}
],
"primaryElement": {
"kind": "unit",
"role": "route-card",
"summary": "点击打开 /blog/about-us。 简介:故事,我们有很多 状态:stable",
"tagName": "a",
"title": "入口 · 关于我们"
},
"selectionBounds": {
"maxX": 1242,
"maxY": 793,
"minX": 859,
"minY": 555
},
]
},
"pageId": "/"
}
就是这样的结构保证了任何一个前端事件,后端都会有实时的状态更新,并且依靠视觉无关的方法。这无论是在传输带宽,延迟,还是与LLM的向性,这都是更优的选择。