作品三
一块跟 Agent 一起想事情的画布:就着自己的文件,把一个问题展开成一棵树。
思考时,一个回答引出的几个子问题全留在图上,一条条追下去;探索时,把手上的笔记、代码、资料挂上来指着问,答案连着材料;学习时,一个题目问出几层、几条线,整棵树存在自己的文件夹里,改天接着长。
它帮使用者做三件事,都在同一张图上。
它是什么:加在本机 Agent 外面的一层交互,不是又一个 AI 产品。
为什么接本机 Agent:市面的 AI 画布接云端模型,不认识使用者的文件;这块画布接本机已装好的 Agent,文件、技能、项目说明全在场。
它和什么连着:绑本机一个文件夹,接本机已装的 Agent,画布能导出给 Obsidian。
它由三样东西构成,下面先讲四节用途,再讲一轮提问怎么走。
一个回答引出的几个子问题,每个都在画布上长成一张卡,全部留着,追完一条回头追另一条。每张卡左上角一个号,看见号就知道它从哪张卡问出来,不必追线。
| 号 | 意思 |
|---|---|
| 1 | 一张根卡 |
| 1.2 | 从 1 问出来的第二张 |
| [1&2].1 | 把 1 和 2 放在一起问出来的第一张。「合问」指选中几张卡一起问 |
| [1&2].1.1 | 从那张合问卡再问下去的 |
| [1&2.1].1 | 把 1 和 2.1 放在一起问的。中括号让这两种分得开 |
合问的号原来把两种分隔符挤在同一根轴上,读不回来路。
卡片上只有三样:问、答、接着问。
每张画布指向本机一个文件夹,想事情从这个文件夹里的笔记、代码、资料出发:左边是文件树,文件挂到画布上,指着它问。
Agent 以这个文件夹为工作目录,文件夹里的东西它全认识。一个文件夹可以放任意多张画布。
打开应用看到的是画布清单,不是文件夹选择框。画布是主角,文件夹是它的属性。
同一张画布上的所有提问接在同一段对话上:后面的问题能看见、也能推翻前面的答案;画布文件存在文件夹里,下次打开从任意一张卡接着问。
两家 Agent 在同一张画布上各记各的对话。
两个已接受的代价,一个留作观察的未知。
卡片按使用者的意思摆,线按使用者的判断连,手画的线 Agent 下一句就按它来。画布上因此有两种关系,必然分家:对话谱系是事实,手画的线是判断。
画布可编辑,交互交给一个开源的节点图库。
「整理」按来路排成树林,一步撤销。
沉淀与导出建在里面,不进首屏。
上面四节的事由三样东西完成,都在一台机器上。一轮提问从画布出发,经本机服务到 Agent,回答流回画布,画布文件落在使用者自己的文件夹里。
| 部件 | 做什么 | 实际是什么 |
|---|---|---|
| 画布 | 显示文件树、卡片、连线;接住提问、拖动、连线、框选 | 一个网页,用浏览器的应用模式开成没有地址栏的窗口 |
| 本机服务 | 管画布文件;组装每轮递给 Agent 的文字;起 Agent 进程;流式回传 | 一个 Node 进程,只听本机回环地址,端口自动挑空的 |
| Agent | 按地址读文件,回答 | 使用者已装好的 Claude Code 或 Codex,每张画布选一家 |
| 文件夹 | 放材料,也放画布文件 | 使用者自己的任何一个文件夹。画布文件是普通 JSON,和材料放在一起 |
| 启动器 | 双击:起服务、挑端口、开窗口;退出即停服务 | 一段脚本装成应用图标,不是 Electron |
一轮提问走五步。
Agent 读到的是材料原文,不是我替它剪过的一段;资料从十篇涨到一万篇,每轮发出去的东西不变。做法:每一轮递给 Agent 的只有两样,问题的原话,和使用者此刻认为相关的每张卡一行地址;卡片正文一个字不进提示词,Agent 顺着地址自己去读。
| 类别 | 例子 | Agent 自己拿得到吗 | 所以 |
|---|---|---|---|
| 它自己产生的 | 说过的话、读过的内容 | 在对话里,本来就在 | 不发 |
| 磁盘上的 | 文件、笔记、代码、画布文件本身 | 它能读、能搜 | 给地址 |
| 只在使用者手上的 | 这次问什么;点了哪张卡、划了哪一段、连了哪条线 | 拿不到 | 问题给原文,指点给地址 |
地址一行长什么样。
三条理由,都不是省钱。
替它挑一段塞进去是在猜
我不知道使用者要用那份材料的哪一部分。猜错了,Agent 拿到的是被剪裁过的材料,它和使用者都看不出被剪掉了什么。
它定位比我准
它能在文件里搜,我只能猜。
地址的成本是常数
使用者的资料从十篇涨到一万篇,每轮发出去的东西一个字不变。给别人用的软件,每轮成本必须是常数。
代价:Agent 有权不去读那个地址,答案会浅。
手画的线在这一层算数。
装了哪家就用哪家:Agent 是使用者已装好的 Claude Code 或 Codex,画布自己不带模型,不要 API key。
Agent 接口只有两个动作:起一段对话、问一句。
材料、画布文件、Agent 全在本机,不上传任何东西。
服务只信本机的浏览器。
应用是一段启动脚本装成的图标,界面仍是网页。
使用者能看见 Agent 每一轮收到了什么、此刻在做什么:发出去的东西短到一眼能看完,这才是可审计。
这个项目反复踩的一类错是出错时静悄悄。规矩:任何失败路径都要在界面上留下痕迹,宁可吵,不可静。
测试的规矩:先把代码改坏,看测试会不会红。
harness 不产出内容,只让产出稳定:每一轮 Agent 收到什么、谁能连上服务、服务能读什么、画布上什么能改什么不能改、出错时怎么露出来,都定在一处,散在上面各节里;这里收成一张表。
| 项 | 做了什么 | 约束哪一层 |
|---|---|---|
| 只发地址,不发内容 | 每轮递给 Agent 的只有问题的原话和选中卡片的一行地址,卡片正文一个字不进提示词,Agent 顺着地址自己去读 | 本机服务递给 Agent 的输入 |
| 地址的写法 | 标注贴在每一行,不设全局抬头;「范围」只在多选时说,写成可判定的一句加反例,不枚举;划中的一段给开头那句话,不给行号;标题只取首行,不做内容特判;画布文件的路径只在列出的卡里有便签、地图块或另一家答的卡时才带 | 本机服务组装的地址清单 |
| 谱系不可改,手画的线是附加层 | 每张卡只有一个来处,画成派生线,不可改;手画的线标成「我连的」,进地址清单,不改写对话历史 | 画布上的两种关系 |
| 号是唯一钥匙 | 号分配一次不再变,空号如实留着;只有提问时选中的卡算来源;计数器丢了从现有的号重建;合问用中括号表达嵌套,已发出的号一个不动 | 画布上的卡片与 Agent 指认卡片的方式 |
| 一张画布一段对话 | 同一张画布的所有提问接在同一段对话上;只用公开的续接参数,不依赖没写进帮助文档的参数;对话满了交给 Agent 压缩,不自造兜底 | 本机服务与 Agent 之间的对话 |
| Agent 接口只有两个动作 | 起一段对话、问一句;每家一个文件,把自家的输出映射成同一套事件,服务其余部分不知道背后是谁;两家各存各的对话,另一家答过的卡改标注,不走「接着问」的捷径 | Agent 的接入边界 |
| 不替使用者批准写操作 | Agent 发来的审批请求一律拒绝 | Agent 的写权限 |
| 使用者的配置默认全加载 | 项目说明、技能、开工脚本全加载,不替使用者判断他的配置重不重要,曾有的精简开关整个删掉;等待时长写在卡片上,让使用者自己看见自己的配置多贵 | Agent 的启动与画布的显示 |
| 服务只信本机的浏览器 | 只听本机回环地址;连接握手时校验来源,不是本机的一律拒绝;以后加任何入口,沿用同一前提 | 本机服务的入口 |
| 图片三道门 | 请求来自本页面、那个文件夹开过画布、文件找得到,三道都过才给;只读 | 本机服务读文件的出口 |
| 每轮发出的原文落盘 | 这一轮实际发出去的原文一字不差存在画布文件里,卡片能展开看;等待期间卡片说出它此刻在读哪个文件、搜哪个词 | 画布文件与画布的可审计 |
| 失败路径必须留痕 | 任何失败在界面上留痕,宁可吵,不可静:断线自动重连,发送失败如实返回并禁用按钮,自检失败在界面和系统通知上都看得见,找不到 Agent 就明说 | 画布、本机服务、启动器的出错路径 |
| 运行时格式与交换格式拆开 | 运行时用自己的格式,和别的工具互通靠导出,不为了直接打开而绑死运行时 | 画布文件 |
| 不自动动使用者的排布 | 不做回答后自动整理,使用者拖过的位置是他的,只有他点「整理」才动;沉淀不自动触发 | 画布的自动动作边界 |
| 测试先改坏 | 每条防回归测试先故意改坏代码,确认真会报红,再装回去;前端手势类改动一律真浏览器实测;「它没做成」不能证明防线有效,要去看防线本身 | 全部代码的变更 |
十五项里最多的一类是「不」:不发内容、不改谱系、不批准、不砍配置、不信外部来源、不自动整理。理由是同一条:判断权在使用者和 Agent 手上,画布不替任何一方拿主意。
一百七十次提交里,反转过的是画布与对话的关系、每轮给 Agent 什么、怎么分发;删掉的有一本账、一个开关、一条隐藏参数依赖、一条横幅。功能增长不列。
画布与对话的关系画了三次:分叉的树,一段对话,地图铺在一段对话上。变的是给 Agent 什么,没变的是 Agent 在本机、材料在本机、画布是对话现场。
写这一页的日期是 2026-09-17。我自己在用,代码已开源。
数字只报能证明系统真实存在的。
代码开源在 GitHub。画布连的是我的个人 AI 操作系统里那个文件夹,见作品一。「只给地址,不给内容」和作品一里「索引只维护语义,不维护清单」是同一条判断。它背后的想法怎么来的,见思考。