Juggler 的会话存在磁盘上。是文档,不是聊天记录。所以关掉应用、重启机器、再打开,所有对话精确恢复——包括那些正等用户批准的挂起状态:代理跑到一半、举着一个写文件操作等你点“允许”的那个瞬间,重启之后它还在原地举着,等你。这篇整篇就是从这个细节长出来的。
细节小到会被当成产品文案里的一行字。但它漏了一件事出来:在这个工具的世界观里,“一次代理工作”不是一段对话流,是一份文档。文档意味着可持久、可同步、可多端打开、可精确恢复到任意中间态——这几个“可”,聊天记录一个都给不了。
而编码代理这一整片地图,吵的全是模型层:谁的模型更强、谁的价格更低、谁先卷过下一个 benchmark——卷得热闹。会话以什么形状存在、存在哪、谁能改,这块几乎没人当回事。这篇画的就是这块。
问题先钉死:会话层要回答的是——一次代理工作,以什么数据结构存在、存在哪里、生命周期归谁管、谁有权对它做什么。主线大概六条:会话的形状(线性还是树)、会话的存储(易失还是落盘成文档)、上下文的可编辑性、工具层的可替换性、部署与信任边界、许可与生态。Juggler 是这篇的钉子,不是这篇的全部——地图是“编码代理的会话层”这一整块,钉子用来把坐标钉实。
证据基础也得先交代,这是全景的规矩:这篇的锚点基本只有一个仓库(Juggler,GitHub,星标和 fork 数以写这篇当天为准),功能描述以仓库自述为准,源码我没有逐行核过。全景平时画论文海,这次画一个仓库,原因后面会说——会话层这块的论文海还没涨起来,涨起来之前,坐标钉在具体 artifact 上,比钉在论文上稳。
一、问题定义:会话层为什么是整张地图上最薄的一块
线性聊天记录不是中性的技术默认,它是继承来的。对话式产品从聊天 bot 一路演化过来,转写记录(transcript)先是 UI 层的决定——一问一答滚上去——后来没人再质疑,它就悄悄变成了数据结构层的决定:会话等于消息数组。这个等号画了这么多年,基本没人擦过。编码代理整个长在这个地基上,绝大多数连质疑都没发生过——不是论证过线性是对的,是压根没人想起来问。
模型层卷成红海、工具层借着 function calling 和 MCP 热起来之后,会话层还是薄的。薄的原因不复杂:它是管道,管道不性感;它不可见,用户感知到的只有“聊得顺不顺”;更关键的是,在会话只有四分钟长的年代,会话的数据结构确实不重要——什么结构都装得下。但代理的工作时长在涨,从一问一答涨到几小时的连续工程作业,会话的结构就从装修变成了承重墙。四十小时的会话用什么结构存、能不能恢复、能不能分叉、能不能两台机器同时开着,每一个都是真问题。这是我画这块地图找到的第一条规律:四分钟的会话什么结构都行,四十小时的会话,结构就是承重墙。
还有一层为什么薄:会话层没有自己的“论文海”。模型层有 benchmark 逼着大家比,工具层有协议和生态逼着大家接,会话层的优劣没有公认度量——你没法用一个榜单证明树形会话比线性记录好。没有度量的层,学术不涌进来,工业也只按惯性做。所以这块的坐标,眼下只能钉在具体做出来的东西上。
二、地图骨架:纵切六层,横切两种范式
按技术栈纵切六层,一层一层梳:
- 模型接入层:谁来生成下一拍;
- 会话形状层:工作以什么拓扑展开——线、树、还是别的;
- 会话存储层:状态活在进程里、云端账号里,还是活在磁盘上的文档里;
- 上下文层:模型看到什么,谁有权在它看到之前动手改;
- 工具层:代理的手是平台神圣不可动的内置件,还是可替换可分叉的普通公民;
- 部署与许可层:跑在哪、谁能连、代码归谁、生态怎么长。
横切一刀,整个领域站成两种范式。助手范式:模型即产品,会话是对话流,云端托管,账号优先。工作台范式:会话即产品,模型是可换的零件,本地优先,无账号。Juggler 的位置一句话钉死:它把工作台范式这一整列,从模型层到许可层一口气占满了。下面逐层看它怎么占的、占了什么代价。
三、逐层深梳
3.1 会话的形状:线性是继承来的,树才是工作的真实拓扑
代理在真实代码库上干活,本质是搜索:试一条路,死了,退回来,换一条。线性转写把这件事的形状彻底藏起来了——它逼着工作的形状迁就日志的形状,“退回来”只能表现为“重新生成”,旧尝试被丢掉,或者堆在滚动条上方当尸体。树形会话干的是反过来迁就:Juggler 的会话以树呈现、线程可分支,一条走岔的路保留在树上,新开一条分支,两条并存,互不覆盖。
我在画 LLM 推理那张图的时候写过,ToT 那一支把推理链从线扩成树、允许在中间节点回溯——那是把模型内部的搜索显式化。树形会话干的是同一件事的另一半:搜索发生在人和模型之间,人是搜索策略的一部分,人负责剪枝。两边是同一棵树的上下两层。树形会话不是 UI 花样,是把搜索还给交互层。
为什么线性赢了这么久?三个原因叠着:线性最便宜;聊天心智人人都有;模型变强之后一次过的概率在涨,回溯的可见需求在降。但注意这个论证成立的前提是任务短。任务时长一拉长,一次过的概率塌下去,廉价回溯的价值就涨上来。会话树对 git 脑子是零成本,对聊天脑子是新增成本,这个工具显然选了前者当第一批用户。
说实话,动笔之前我把树形会话归在“锦上添花”那一档。写到下一节的持久化,我收回这个归类——原因马上说。
3.2 会话即文档:Yjs、落盘,和那个挂起状态
Juggler 的会话用 Yjs 文档格式做存储与同步。这句话里最重的不是 Yjs 这个名字,是“文档”两个字——它一口气改写了会话层的三个默认。
先说持久化。会话作为文档落在磁盘上,关闭、重启之后是精确恢复,不是“历史还在”那种糊的恢复——连等待批准的挂起状态都在。这个细节得单独拆:挂起状态能被恢复,说明批准队列本身就是文档的一部分,不是进程内存里的一阵风。工具把“等待人类”当成一种可持久的数据状态,而不是一个临时的 UI 现象,这颗钉子钉得很深。多数工具重启之后丢的恰恰就是这个——代理刚才想干什么,得靠你自己回忆。
再说多端。同一会话可以连多个客户端,桌面应用和浏览器视图保持同步,本地或远程都行。一份工作状态,两个窗口——这在“会话等于一次浏览器标签页”的世界里是结构性的差异,不是便利性的。
还有一条是我的读法,仓库没这么宣传,别当成它的功能:CRDT 文档不区分写者是谁。多客户端同步的底座,和多写者共写一份文档的底座,是同一个。也就是说,“几个人、或者一个人和几个代理,在同一份会话文档上协作”这块的地基已经浇好了,只是上面的语义——冲突怎么算、审批归谁、责任怎么分——还没人定义。地基在,楼层没图。
会话即文档,是这整个设计里最重的一颗钉子。
现在收回 3.1 的归类:如果树只是长在一份线性日志上的视图切换,那确实是锦上添花;但树是文档的原生形状、分支是状态的物理事实,那它就是结构本身。Juggler 属于后者。我一开始看走眼,因为我是从 UI 截面开始看的,而它的树长在存储层。
代价也钉一下:Yjs 会话文档是私有格式,没有交换标准,会话锁在工具里(这条判断段还要回来用);CRDT 这套机器对一个同时以“下载 Go 二进制就能跑”为卖点的工具来说是不轻的负重;文档结构以后怎么演化——老会话在新版格式下怎么办——仓库自述里没看到答案,这块先记成空着。
3.3 可编辑的上下文:从飞行记录仪变成工作文件
可视化工作台的三件套——可检查的工具调用、可分支的线程、可编辑的上下文——前两件说完了,第三件是三件里改动最狠的。上下文历来是飞行记录仪:发生了什么就被动记什么,只读,模型看到的是历史的不可修改投影。可编辑的上下文把它变成了工作文件:在模型看到之前,人可以动手改它将看到的东西。
我在 Agent 那张图上画过一条分隔线:无外部信号的自我纠正基本无效,有信号的纠正才有效。可编辑上下文是把纠正信号的注入点挪到了最便宜的位置——不是等模型答错了再让它反思(那条路已经被证伪过一轮),而是在它看上下文之前,人先把上下文改对。纠错发生在输入侧,不发生在输出侧,这是两个完全不同的经济学。上下文从飞行记录仪变成工作文件,这句可以单独截出去当图例。
可检查的工具调用是同一立场的另一半:代理干了什么,不是听它转述,是打开那次调用自己看。信任从“品牌担保”换成“可检视性担保”,这是工作台范式对助手范式的又一次站队。
但可编辑性有一个没人爱提的代价:它打碎了“转写即事实”。上下文一旦可编辑,“模型当时到底看到了什么”就不再能从日志本身恢复出来——溯源和审计变成需要单独记账的东西。单人干活这是纯增益,团队和合规场景这是个洞。这块地图上还空着,判断段回来。
3.4 工具层:连 read、write、bash 都可以替换和分叉
Juggler 的扩展系统用 JavaScript 定义四样东西:上下文项、策略、命令、工具。听着是常规扩展点,直到你注意到后半句——基础的读、写、bash 工具本身都可以被替换或分叉。
这是对行业默认的一次倒置。绝大多数产品的扩展模型是只加不减:你可以带新工具进来,但平台自己的那双手是神圣的内置件。Juggler 把基座工具降级成普通公民——read 可以被改了行为的 read 替换,也可以分叉出 read-v2 两个并存。“分叉一个工具”这个动作,就是 git 脑子长到了代理的手上。策略也用 JS 定义,意味着审批逻辑、权限边界本身就是用户可写的代码——信任边界从厂商策略挪到了用户代码,权力和风险是同一个动作的两面。它把工具层做成了全栈里最软的一层。
这一层我要专门对一下旧账:我之前画 Agent 地图时,把工具调用标成“必备层但非核心”,核心是规划、记忆、错误恢复。Juggler 的架构等于用钢筋同意了这个判断——最硬的骨头做在会话层(3.2 那颗钉子),最软的层留给工具。这不是巧合,是同一个判断的工程版。
技术栈顺手记一笔:后端 Go 加 Wails,前端 HTML/JS,会话走 Yjs 同步,扩展是 JavaScript,前端类型用 JSDoc 配严格静态检查维护。跑题一句:JSDoc 严格检查而不是上 TS 编译链,在 2026 年的前端圈算逆行,但一个把扩展当第一等公民的工具,用户写扩展少过一道构建仪式就少一道门槛——这个逆行有它的道理。拉回来。
3.5 模型接入层:一个列表里混着三种东西
支持的提供方列出来是:Claude Code、OpenAI、GitHub Copilot、Gemini、Mistral、Z.ai、Ollama、OpenRouter、Deepseek。这个列表有意思在它不纯——里面至少混着三种东西:模型提供方(OpenAI、Gemini、Mistral、Deepseek 这一类)、本地运行时(Ollama)、路由器(OpenRouter),还有 Copilot 这种订阅制产品和 Claude Code 这种本身已是代理产品线的东西。抽象层级统一到了“任何能接下一拍的东西”。
“模型无关”是所有工具都喊的口号,喊起来不要钱;但在这里这个口号是结构性的真诚——产品的价值住在会话层,模型层换血对产品概念不构成任何损失。模型可换、会话不可换,这是工作台范式和助手范式的分界线。
Copilot 和 Claude Code 混进“提供方”列表这件事,还有一层意思我没想透——代理底下垫着别家的代理,工作台底下垫着别家的工作台——先撂在这,想透了再说。
Ollama 这一项要多看一眼,它和 3.6 的部署立场是一对:会话文档在本地盘上、模型在本地跑、服务只听本地——三个“本地”凑齐,“离线工作台”这个坐标就有人实打实占了。
3.6 部署边界:默认只听本地,是立场不是技术限制
Juggler 分桌面应用和命令行服务器两块,服务器默认只监听本地地址,想开放局域网,按一个键或者带 --public 参数。而本仓库源码构建出来的版本只支持本地和局域网;跨互联网的 WAN 访问模式,只存在于官方发布的二进制里。
这个切法要慢读。信任边界从回环地址开始,往外推的每一步都是显式动作——这已经是本地优先那一支的标准姿态。更有信息量的是后半条:把“跨互联网”当成官方发行物里的一个分发特性,而不是开源构建里的一个协议能力。开源版被刻意裁成“你和你的局域网”,穿越公网是另一件事,由官方工件另说。免费、开源、无需注册、下载 Go 二进制直接跑——这几个卖点连起来是同一条立场线:不设账号门,不把会话押在注册流程后面。
顺带把一个冷判断放在这:云端托管工作台那一支现在产品最多、声量最大,走通的概率我持保留。不是云不对,是把会话锁在账号后面这一步,和会话层要长的方向拧着——会话一旦变成四十小时的承重墙,人会把它当自己的文件对待,而人不会把承重墙租在别人机房里。这是我的判断,不是共识。
3.7 许可双轨:平台收紧,生态放开
许可结构是双轨:应用代码 AGPL-3.0 或更高,扩展 SDK 和随附扩展 Apache-2.0。平台型开源的标准落法——地基用 AGPL 保护,改了地基拿去提供服务的人躲不开开源义务;生长层用 Apache 放开,让公司里的、想写私有扩展的人没有心理门槛。双轨的切线本身就是一个表态:护城河认在会话工作台这一层,价值放大认在扩展生态那一层。这颗钉子,钉得规矩。
3.8 谱系:音频工作台出身的人怎么做代理
最后一块拼图是人。Juggler 的开发者同时是 JUCE、Tracktion、Cmajor 的创建者——音频软件那一系的老人。这层读法是我的,不是仓库说的:音频工作台世界几十年攒下来的规矩,恰好一条条对得上——工程文件是神圣的文档,崩溃恢复是底线不是特性,一切操作可检视,非破坏性编辑是常识,工作台隐喻是母语。在那个世界里,“工程文件丢了就丢了”从来不被接受。
所以 Juggler 的会话即文档,与其说是一个新鲜的 AI 洞察,不如说是一个老工作台本能空降到了新领域:他们做过几十年的软件,用户的第一诉求就是“我的会话不能死”。这不是跨界,是搬家——把工作台世界的规矩整套搬进代理领域,顺手发现这个新领域的原住民还没立过这些规矩。
四、回看全景:一张表、一棵树
把前面梳过的压成一张表。右边一列画的是范式默认,不是点名任何具体产品——点名需要锚点,我没有,留白理由第六节交代。
| 层 | 助手范式的默认 | Juggler 钉的坐标 | 代价 |
|---|---|---|---|
| 模型接入 | 与强者绑定,模型即产品 | 一列可换的提供方,含本地 Ollama | 多方适配的工程负担自扛 |
| 会话形状 | 线性聊天记录 | 树形、线程可分支 | 对聊天心智是新增成本 |
| 会话存储 | 进程内或云端账号下 | Yjs 文档落盘,精确恢复含挂起态 | 私有格式,无交换标准 |
| 上下文 | 只读的飞行记录仪 | 可编辑的上下文项 | 打碎“转写即事实”,溯源没着落 |
| 工具 | 内置神圣,只能加不能换 | 连 read/write/bash 都可替换可分叉 | 信任边界交给用户代码 |
| 部署 | 云端优先,账号门在前 | 默认 localhost,--public 开局域网,WAN 只在官方二进制 | 自托管的运维自己扛 |
| 许可 | 闭源或单轨 | 平台 AGPL、生态 Apache 双轨 | AGPL 挡住一部分商业嵌入 |
再给一棵示意树,感受一下“会话是文档、分支是物理事实”长什么样(内容是示意,不是仓库实例):
会话(一份 Yjs 文档,落在磁盘上)
├── 主线:先读懂现有实现
│ ├── 工具调用:读文件(可检视,参数返回都在)
│ ├── 工具调用:跑测试(可检视)
│ └── 分支一:方案 A 的改法
│ ├── 工具调用:写文件 →【挂起:等待批准】
│ │ ↑ 重启后原地恢复的就是这个节点
│ └── 后续步骤
└── 分支二:方案 B 的改法(与分支一并存,互不覆盖)
横向看这张表:左列和右列的分界贯穿七层,不是某一层的功能差异,是两种范式各自站满一列——Juggler 是第一个把右列从头占到尾的完整样本。纵向看:模型层是红海,工具层在被协议标准化,会话形状、存储、上下文三行几乎全空——地图的重心迟早要往空地挪,因为第一节那条规律在起作用:会话时长在涨,承重墙的时刻在逼近。
星标数也得诚实放进来看:566 星、42 fork,在编码代理这个赛道上连噪声都算不上。但地图不按星标画,按坐标画——这篇画它,不是因为它赢了,是因为它站的那块地没人站。早坐标大多数会死,综述早坐标不是预测它赢,是先把地记下来,免得以后这块长出东西来,没人记得地基是谁浇的。
五、摊开这张地图,我的落子
摊开这张地图,我的落子是:编码代理现在站在“模型层红海、会话层空地”的阶段,空地上已经立了一颗命名清晰的钉子——会话即文档。地图上还空着三块大的。
第一块,会话的交换格式。Yjs 会话文档是私有格式,整个领域没有会话层的互操作标准——各家的会话互不迁移,像 git 出现之前各家的源码各玩各的版本管理。会话变成承重墙之后,“墙搬不走”会从抱怨变成刚需,谁先定义会话的交换格式,谁占的就是这一层的协议坐标。
第二块,多写者语义。CRDT 底座已经能承载多个客户端共写一份会话文档,但多个人、多个人加多个代理共写时的冲突语义、审批归属、责任边界,一层都没定义。地基浇好了,楼层没图——这块我判断是下一站竞争最凶的空地,代理协作的需求正在往这涌。
第三块,可编辑上下文的溯源与审计。上下文可编辑是输入侧纠错的正确方向,但它打碎了“转写即事实”,模型当时看到了什么需要专门记账。单人场景无感,团队场景迟早爆。
方向上,下一站最可能从第一块和第三块的交叉处长出来:会话文档成为版本管理的基本单元——会话会变成新的源文件,diff、merge、评审会追上来。Juggler 钉下了“会话即文档”这颗钉子,但钉的是私有格式;私有格式的文档是地基,不是标准,标准那格还空着。这个判断可以被证伪:一年之内,如果主流工具的会话还是线性日志、没有任何一家把会话级的 diff/merge 做成默认能力,那这块坐标我要么钉错了、要么钉早了。错了好更新坐标,比端着“两边都有可能”的姿态有用。你不同意,拿工具、拿仓库来挪这块坐标,地图我随时重画。
六、按全景的规矩,交代地图哪儿没画全
第一,竞品对比整块留白。给不出锚点的判断不下——我对其他编码代理的记忆全是印象,没有一手核过的材料,凭印象点名对比就是画印象派的角。宁可地图缺一角,不画错的角;等一手材料补齐,单独重画一张对比图。
第二,源码没有逐行核。多端同步、挂起恢复、工具分叉这些能力,我引的全是仓库自述,不是我的压测;自述和实现之间有多远,这篇不背书。
第三,星标和 fork 数是写稿当天的快照,交稿那天就开始过时——这个领域的数字没有保鲜期。
第四,这篇的画法本身不对称,得说明白:平时全景是三十篇论文压一张表,这次是一个仓库当钻杆、把一层钻穿。原因是第一节说过的——会话层的论文海还没涨起来,涨起来之前,这块地图只能这么画。等它涨起来,这张图要按正常比例重画,那时候 Juggler 在图上是起点、是支流还是 footnote,现在钉不了。
先把这块空地记在图上,是综述现在能做的全部。坐标会挪,图会重画。
