ARTICLE · 1143189
做一款 Claudix 式对话插件:从聊天框到能干活的 Agent 宿主
大家好,我是 James。
上一篇我讲了 Agent SDK 的双面宿主——真正跑 Agent 的是 SDK + CLI,无头服务与 IDE 只是两套适配。
今天把镜头拧到 IDE 这一面:在 VS Code / 云 IDE 里做类似 Claudix 的侧边栏对话插件,护城河到底在哪。
接个 Chat API、画个好看的 Webview,做出来的是「会说话的实习生」。真正能改文件、能停干净、能活过容器重启的,是一整套 三进程工程。用户给出的是「帮我改代码」,不是「随便起一堆进程、随便写盘、随便把会话写进会蒸发的目录」。

先花一分钟对齐几个词
| 三进程 | |
| Webview | |
| 插件宿主 | |
| CLI worker | |
| 通道 ID | |
| 消息泵 | |
| 执行授权 | |
| 宿主授权 | |
| 随包 CLI | |
| 软链持久化 |
(SDK、PID、SIGTERM、ACL、OOM、cgroup 这类通用技术名词就不解释了,直接用。)
补一句视角,它划出了这篇的边界:把 AI 当补全工具,插件做到「侧边栏能聊天」就到头了;把它当可以委派任务的执行单元,要处理的就变成进程归谁管、写到哪个盘、能不能真停下来。宿主做成什么样,决定了同一套内核能发挥多少——这一层业界叫 Harness(把模型、工具、权限和运行环境捆在一起的那层外壳)。
01 | 全局视角:为什么「侧边栏 Chat」不够用

把三进程想成一支乐团:
| 舞台 | |
| 指挥 | |
| 乐手 | |
| 乐谱 | |
这个类比把本篇四个硬问题一次讲清:
观众只看得见舞台。 界面做得再漂亮,没有乐手(CLI worker)就只是放录音——改不了任何文件。 指挥必须管得住乐手的生死。 指挥收了指挥棒(点了停止),乐手如果没收到信号还在拉,音乐就不会真的停——对应 PID 回收,这是安全问题不是体验问题。 乐谱要放在不会被擦掉的地方。 云 IDE 的「家目录」常常不落在持久卷上,你把会话写在那里,等于把乐谱写在沙滩上,一个浪过来就没了。 指挥不能因为等一个决定就僵住。 有乐手举手问「这一段要不要重复」,指挥不能放下指挥棒一直等,否则整个乐团都停摆——对应权限弹窗不能堵住消息泵。
先把痛点立起来。三类工单,值班第一反应经常是「模型变笨了 / 云 IDE 不稳」。展开后,根因几乎都在宿主。
现场 A:点了停止,再问就永远转圈
用户话术:「停一下再继续问,就一直 loading。」
误判:模型 hang。真相:Host 已关掉旧通道,Webview 还握着旧通道 ID;再发送找不到通道,响应永不回,界面 busy 永久为 true。坏的是会话管子的状态机。
现场 B:昨天还能聊,今天像新号
用户话术:「隔夜重开,会话和 MCP 配置全没了。」
误判:插件丢数据、模型失忆。真相:默认配置写在「家目录」类路径,而云 IDE 容器的家目录常常不落在持久卷上。插件以为在写家,其实在写会蒸发的临时房。
现场 C:人以为停了,进程还在改文件
用户话术:「都点停止了,怎么文件还在变?」
误判:幽灵 bug、别人在改。真相:SDK 内部 spawn CLI,往往不把 worker PID 交给你;你只关了自己的通道抽象,OS 进程还活着。这是安全事故,也是巨大的浪费。
用户的真实期望通常是这四条:
能读工作区、改多文件、跑终端; 危险操作会停下来问人; 云 IDE 关掉再开,会话还在; 点了停止,进程真的停——不会偷偷继续写盘。
这四条 全都落在插件工程层,不落在「模型更聪明」上。
没有右列,你手里只有聊天机器人。有了右列,才是编辑器里的 Agent 宿主。
当插件获得 Agent 执行能力时,它也同时获得了消费机器与改写工作区的能力。宿主工程如何管住这种能力?
02 | 行业调研:IDE 里的 Agent 在卷什么

做选型前,先横评一圈「编辑器里的 Agent 」。表面都是聊天气泡,底层差得很远。
| Cursor | ||||
| Continue | ||||
| Cline 同类 | ||||
| Claude Code / CLI 系 | ||||
| Claudix 式自研 | 卷上持久化 + 协作隔离 |
摘要一句:
开源扩展告诉你「UI 怎么搭」;CLI 系告诉你「 Agent 怎么转」;云 IDE 插件告诉你「宿主怎么活」。
我们偷什么、拒什么?
偷:CLI 系的 tool loop 边界;Cline 类的确认产品感;开源扩展的搭法。 拒:只抄 Chat UI;假设家目录天然持久;把 PID 回收当成「SDK 会处理好」;用 prompt 劝善代替写权限裁决。
选 Claudix 式,不是因为Chat 更漂亮,而是因为云 IDE 场景里,会话蒸发、僵尸进程、共享盘误写 会在第一周就爆——这些不是开源脚手架默认送的。
03 | 设计结论:先定三进程,再写 UI

为什么不先堆设置页?设置页漂亮、管道烂,用户只会说「插件坏了」。
因为真实干活的是 CLI worker,所以插件必须管得住它的生死;因为权限确认发生在 Webview,所以消息泵不能被 Modal await 堵死;因为云 IDE 的家目录常不落持久卷,所以配置根要主动焊到数据卷。
三个最容易混的词:
| 插件 Extension | |
| Agent SDK | |
| CLI Runtime |
口诀:
SDK 帮你造连接,CLI 才是干活的双手,插件才是真正决定产品行为的逻辑。
回到乐团:SDK 是乐器(能发声),CLI worker 是乐手(真正演奏),插件是指挥(决定演什么、什么时候停)。 买再好的乐器,没有乐手和指挥也开不了音乐会。
内核循环:
用户输入(Webview) → Extension Host(launch / 权限 / 通道) → CLI worker(Reason → Tool → Observe…) → 需要确认则 Modal → 继续 → 停止则关通道 + kill worker上一篇的 Agentic Loop 还在;这里多了两跳——UI 跳和宿主跳。少任何一跳,产品形态就塌。
Agent 在 IDE 里的自主性,其实至少两层授权:
| 执行授权 | ||
| 宿主授权 |
本篇把后者当成一等公民。
04 | 必做的壳:因为X,所以选 Y

下面不是功能清单,是工程骨架。
4.1 随包 CLI,而不是 PATH
因为云 IDE / 同学电脑上全局 CLI 版本会漂移,所以SDK spawn 必须指向扩展目录里的随包二进制,不要「方便地」改用 PATH。
代价很实在:安装包变大、发版跟 CLI 节奏、打包路径写错就是 P0。但这是对的。现场一半排障时间,会浪费在「我这边能跑你那边不能」——根因根本不在业务代码。
4.2 权限请求不能堵泵
因为权限弹窗可能挂几分钟,所以消息泵对长挂起请求 不能 await。普通 RPC 可以等;权限类必须派发后立刻返回,让其它响应与流式消息继续进泵。
// 示意 · TypeScript// 保证:权限弹窗挂起时,其它消息仍能进泵voiddispatchPermission(req).catch(logError); // 不要 await 在读泵循环里# 示意 · Python(无头对照)# 保证:没有 UI 时,问卷类工具 deny 并回喂文本选项asyncdefon_pre_tool_use(name: str, payload: dict) -> dict:if name == "AskUserQuestion":return {"decision": "deny", "reason": "请用文本选择题回复"}return {"decision": "allow"}问卷类工具在 IDE 走专用 Modal,且 永不进自动放行白名单。某「自动编辑」模式下黑名单外可自动执行——那是产品策略,不是 SDK 默认。做插件却按无头策略全自动放行,是抄错作业。
4.3 停了还能再聊
因为用户点停止会关掉通道,所以再发送不能死抱旧通道 ID。
否则:通道不存在 → Webview 永远收不到响应 → busy 永久 true。用户话术是「点了停止再问就一直转圈」——看起来像模型挂了,其实是宿主状态机挂了。
解法:发送路径幂等 relaunch——有活跃复用,没有就新建;in-flight promise 合并并发;失败路径必须把 busy 拨回 false。
4.4 配置与激活顺序
两级配置是这类产品标配:用户级配置目录 + 工作区级配置目录。共享容器再加两刀:
写权限代码裁决(ACL),不是 prompt 劝善; 按用户隔离配置子目录,避免协作误写他人配置。
激活顺序写错就全盘乱:
先 持久化(软链 / 备份)——早于任何读取默认配置路径; 再引导与依赖注入; 再挂 Transport、ACL、Webview。
生产上还有一层反直觉:软链的首要建设者甚至可能是容器入口脚本——扩展内逻辑经常只是自愈,因为无头运行时可能比 IDE 扩展更早写入配置目录。
05 | 硬问题:线上踩过的才叫护城河

这一章全是「现象 → 根因 → 怎么做」。
5.1 PID 回收:SDK 不给你 PID,你也得管住
现象:停对话后进程表里还有 CLI;卸载扩展后 CPU 继续涨。
根因:SDK 内部 spawn;关闭通道抽象 ≠ kill OS 进程。回到乐团:指挥收起了指挥棒,可乐手压根没看见——手上的弓还在拉,音符还在往外冒。
因为调用方拿不到官方 PID API,所以要在模块加载时登记 worker(例如 hook spawn、只认本插件 CLI),关闭走 SIGTERM → 超时 SIGKILL。
// 示意 · TypeScript// 保证:停对话 / 卸载扩展后进程表干净const worker = awaitwatchNextCliSpawn();awaitrunQuery();awaitterminate(worker, { escalateMs: 2000 });# 示意 · Python# 保证:取消后子进程不会继续写盘proc = await asyncio.create_subprocess_exec(*cmd)try:await run_agent(proc)finally: proc.terminate()try:await asyncio.wait_for(proc.wait(), timeout=2)except asyncio.TimeoutError: proc.kill()若不这样: worker 越积越多,被当成「机器配太小」;更糟的是用户以为停了,进程还在写盘。
5.2 并发 launch:多会话恢复会 OOM
现象:一开 IDE,多个历史会话同时恢复,容器直接被杀。
根因:N 个 launch 都读到「内存还够」→ 一起 spawn → cgroup 说再见。
因为内存判断与 spawn 之间有竞态,所以要串行拿「内存许可」:FIFO、发许可前强制刷新真实余量、锁持有到 spawn 完成、超时强制释放、拒绝时说人话「内存不足」。
5.3 持久化:有软链 ≠ 有备份
现象:家目录蒸发;或软链在、内容空了。
对策:
配置目录软链到数据卷; 定时备份; 空目录自动从备份恢复。
软链管「写哪」,备份管「回滚」。只做一件,另一类事故照样来。
5.4 首屏:别把 CLI 冷启串进关键路径
因为用户先要「能点」,所以先标记已连接,配置后台拉;别把命令行工具的冷启动串进首屏。预热只是提速手段,不是开聊的前提——这一点和连接层预热那篇是同一套判断。
5.5 共享盘 ACL:fail-closed
prompt 劝善不是护栏。写权限代码裁决;未就绪则拒绝写入。共享容器上「先放开再说」等于请人误删。
5.6 别造上帝类
第一天按域拆:薄 Host + 厚 handler(会话、权限、持久化、worker 登记各管一摊)。上帝类三个月后无人敢改,而线上事故专挑无人敢改的地方爆发。
06 | 一次对话全过程

带决策点的端到端:
激活决策:配置目录是否已焊到数据卷?否 → 软链 / 必要时恢复备份。先于任何读配置。
侧边栏首屏决策:等配置拉完再可点,还是先可点?——先可点;配置后台加载。
发送拿 launch 许可(串行)→ spawn 随包 CLI → 登记 PID → SDK query。决策:命中已有活跃通道则复用。
流式渲染消息块回 Webview。敏感操作 → Modal(不堵泵);共享盘路径再过 ACL。决策:问卷工具?——专用 Modal,永不自动放行。
停止关通道 + SIGTERM/SIGKILL。决策:再发送?——幂等 relaunch,失败拨回 busy。
后台备份仍在跑;容器重建靠卷 + 备份,不靠「家目录应该还在」的愿望。
只做「Webview + HTTP Chat」,你会省掉 PID、权限泵、ACL、卷与备份——每一项对应一类线上事故。省的是开发时间,买的是值班时间。
07 | 行业对比:三进程宿主怎么选

| 权限 UI | ||||
| 进程回收 | ||||
| 云 IDE 持久化 | ||||
| 首屏 | ||||
| 协作 ACL |
什么时候选谁:
选 Claudix 式插件:人要在编辑器里点允许、工作区即真相、要扛重启与误写。 选无头宿主:入口是 IM/HTTP 且无可靠 UI——去做上一篇的双宿主无头面,别硬做插件。 选 Chat 壳:只演示、不改盘——接受它不是 Agent。 选终端 CLI:本机重度用户;云 IDE 多租户不是它的主场。
08 | 常见坑

坑 1:权限 await 堵泵
诱因:Transport 里「收到请求就 await 处理」写起来最顺手。 错解药:把 Modal 超时调短——泵仍堵,只是更快失败。 正解药:长挂起请求派发后立刻返回;泵继续读;结果用回调/响应消息回去。
坑 2:停后仍握旧通道 ID
诱因:停止只关 Host 侧,Webview 状态未复位。 错解药:教用户「重载窗口」——那是产品失败。 正解药:发送路径幂等 relaunch;失败拨回 busy;停止时同步清 Webview 通道引用。回到乐团:这一场已经谢幕了,你还攥着上一场的节目单等下一个音符——它不会再来了。
坑 3:多会话同时 launch
诱因:恢复历史时 Promise.all 很香。 错解药:加大容器内存——治标,波峰仍会打死邻居。 正解药:串行 launch 许可 + 刷新真实余量 + 超时释放 + 人话错误。
坑 4:只软链不备份
诱因:软链一次「看起来持久了」。 错解药:发现空目录再手动拷——不可复制。 正解药:软链管写哪,备份管回滚;空目录自动恢复。
额外误判:把 busy / 丢会话 / 僵尸写盘当成模型变笨。先怀疑管道:通道、PID、卷、泵。
总结
Claudix 式插件的护城河是三进程工程,不是 Chat API 包装。 Webview + Extension + CLI 少一环都像玩具。 SDK 造连接,CLI 干活,插件决定产品行为。 宿主授权与执行授权同等重要。 权限泵、PID、卷上持久化、并发 launch,是上线第一周刚需。 不是「以后优化」。 有 UI 走 Modal,无 UI 走 Hook 降级。 两面策略对齐,不抄错通道。 先画三进程图再写 UI;薄 Host + 厚 handler。 设置页漂亮救不了管道烂。
Claudix 式插件的护城河是三进程工程,不是 Chat API 包装——少一环都像玩具。
下一篇我会写 Agent 的消费授权:用户给的是目标,不是空白支票——额度、可见、可停,先于安全专篇。
关注我,James 的成长日记,持续分享干货,帮你在 AI 时代少走弯路。