跳转到内容

Wuu:把本地 AI Agent 做成一套可观察、可扩展的工作台

很多编程 Agent 的体验都从一句“帮我改好这个项目”开始,但真正决定可靠性的,是这句话之后发生了什么:Agent 读了哪些文件,调用了哪些工具,命令在哪个目录执行,权限边界在哪里,插件更新时正在进行的任务会不会被打断。Wuu 的设计重点,正是把这些过程变成可观察、可恢复、可扩展的系统。

Wuu 的核心使用 Go 实现,CLI 入口位于 cmd/wuu/main.go。CLI 不只是执行一条提示词,它还统一处理模型服务、会话、执行记录、插件、技能和 app-server 等能力。桌面端使用 Electron,负责工作区、对话流、文件预览、Review 和终端等交互;它通过 app-server 与 Go 核心通信,因此界面状态和任务执行可以保持清晰的边界。

一次普通任务可以抽象为:用户在工作区创建线程,选择模型和权限模式,提交一轮输入;app-server 为线程建立执行上下文,Agent 循环向模型请求下一步,模型再通过文件工具、Shell、浏览器或插件工具完成动作。工具结果、流式文本和状态变化持续写回会话,桌面端因此能同时展示过程和最终 diff。CLI 则可以复用同一套核心能力,以 JSONL 或文本形式接入脚本。

Agent 循环的关键不是“多调用几次模型”

Section titled “Agent 循环的关键不是“多调用几次模型””

Agent 循环需要处理比聊天更复杂的状态:模型可能先输出思考,再请求工具;工具可能被取消、超时或返回大段结果;用户也可能在当前回答结束前插话。Wuu 把轮次、执行租约、取消信号和工具作用域放在宿主中管理,让迟到的工具结果不能重新激活已经结束的执行。

上下文长度也是长期运行任务的现实限制。internal/agent 中的请求组装、上下文预算和压缩逻辑会在窗口接近上限时整理历史,同时保留继续工作所需的摘要和运行信息。这样,长对话不会简单地因为 token 用尽而失去方向;会话历史、工作区状态和当前任务仍由宿主持久化。

插件扩展的是业务,不是核心边界

Section titled “插件扩展的是业务,不是核心边界”

Wuu 没有把每个功能都塞进核心。待办、记忆、自动化、目标和子 Agent 等能力通过 TypeScript 插件提供,插件使用公开的 @wuu/plugin-sdk,Go 运行时继续负责宿主服务、工具分派、会话持久化、权限和清理。

插件通过 JSONL 与宿主通信,并在激活前协商能力、服务和依赖。运行时以 generation 为单位管理:一组插件、工具、Hook、技能和 MCP 连接一起发布,也一起拥有生命周期。更新时先准备候选版本,确认依赖和持久化提交成功后再切换;正在执行的轮次继续使用旧 generation,空闲资源才会被清理。这个设计避免了“插件文件刚替换,旧任务却读到新状态”的竞态,也让失败更新能够保留上一代可用运行时。

Wuu 将权限模式作为线程的一部分保存。只读模式拒绝修改类工具,标准模式允许在登记的工作区范围内工作,无限制模式才会移除普通路径边界。即使处于无限制模式,凭据文件、环境文件和 SSH 私钥等敏感路径仍由专用工具拒绝写入。

工具层检查解决的是“Agent 想做什么”,进程沙箱解决的是“命令实际能写到哪里”。macOS 使用 Seatbelt,Linux 使用 Landlock;受限后端不可用或只能部分实施限制时,命令会失败,而不是悄悄降级为无限制执行。这个边界也解释了为什么插件、MCP 服务和外部引擎仍需要单独评估:它们是受信代码或独立执行路径,不能被简单包装成安全沙箱。

当文件变更、命令输出、模型流和工具调用都进入会话,Review 就不再只是结果查看器,而成为调试界面。开发者可以先用只读模式调查项目,再切换到标准模式修改;出错时,可以从执行记录定位是哪一轮、哪个工具或哪个插件产生了问题。对系统本身来说,持久化会话、可取消执行和 generation 清理也为崩溃恢复、热更新和远程调用提供了共同基础。

Wuu 的技术路线可以概括为三点:用 Go 核心统一执行和状态,用 Electron 把过程呈现出来,用公开插件契约承载变化中的业务能力。这样,AI Agent 不再只是一个会生成补丁的聊天窗口,而是一套可以被观察、被控制、被扩展的本地软件系统。