Today the developer used Claude to build ACKS Studio, a desktop client for the Hermes local AI Agent framework. In one day the project advanced from zero to v0.3, completing work that would normally take two weeks.

The Electron + React application features a three-panel layout, real-time SSE streaming from the Hermes API, local file protocol for image rendering, localStorage persistence, auto-generated task titles, syntax highlighting, Markdown and CSV table rendering, preset skill prompts, and file attachments. Two Electron black-screen bugs and unwanted UI auto-expansion were debugged and fixed.

The GitHub repository is at https://github.com/shynloc/acks-agent-studio. Future steps include search, history recall, export, and SQLite storage.

系列:ACKS Studio 开发日记
日期:2026 年 5 月 12 日
标签:开发日记、Electron、React、AI Agent、Claude


今天做了一件有点递归感的事:用 AI(Claude)帮我开发一个连接 AI Agent 的桌面客户端。

从早上读文档到晚上推上 GitHub,一天内完成了我自己大概要花两周才能做完的工作量。这篇文章是这个系列的第一篇,我想把这个过程记录下来——不只是技术细节,也包括这种「人机协同开发」的体验和思考。


项目背景:为什么要做 ACKS Studio

我在用一个叫 Hermes 的本地 AI Agent 框架(基于 OpenClaw),它可以调用工具、写文件、执行代码。但它原来只有命令行界面,用起来很不直观:工具调用的过程看不到,产出的文件找不到,对话历史重启就丢失。

我想要一个真正「好用」的桌面客户端:能看到 Agent 实时思考的过程,能预览图片和代码产物,能管理多个任务,能重启后恢复状态。市面上没有现成的——毕竟 Hermes 是我自己搭的框架——所以只能自己做。

这就是 ACKS Studio 的由来。ACKS 是 Agent Client for Knowledge and Skill 的缩写,也是我对这类工具的定位:不只是聊天界面,而是一个让 AI Agent 真正「可操作」的工作台。


技术选型:为什么是 Electron + React

选型其实很快就定了:

  • Electron:需要访问本地文件系统(读取 Agent 产出的图片、文档),需要原生协议注册(localfile://),需要原生文件对话框。纯 Web 做不到这些。
  • React:流式状态更新(SSE 事件流 → 实时消息渲染)用 React 的 useReducer 处理起来很自然。
  • Vite:开发体验好,HMR 让我能在 Electron 运行中实时看到 React 组件的变化,不用每次重启。
  • 无 UI 库:从零用 CSS 变量写设计系统,暗色主题、橙色主调,完全可控。这是刻意选择——避免引入大型 UI 库后难以定制的问题。

技术栈很轻:Electron 32 + React 18 + Vite 5 + highlight.js,其他全靠手写。


今天做了什么

一天内从零完成了 v0.3,大概涵盖以下内容:

基础框架(v0.1)

三栏布局:左侧任务列表(TasksPanel)、中间对话区(ChatPanel)、右侧产物面板(ArtifactsPanel)。最左一列是图标导航栏(Rail)。

Hermes SSE 对话流:用户发消息 → 主进程 IPC → Hermes API POST 启动 run → 订阅 SSE 事件流 → 分发 message.delta / tool.started / tool.completed / run.completed 事件 → 渲染层实时更新。

一个细节让我花了不少时间:本地图片的渲染。Hermes 用 MEDIA:/path/to/file.png 这种格式告诉客户端「这里有个文件」。最初我用 IPC 把图片读成 base64 再传给渲染层,结果大图片会卡住整个界面。后来改用 Electron 的 protocol.registerFileProtocol 注册了一个 localfile:// 自定义协议,渲染层直接 <img src="localfile://..."> 就能加载本地图片,完全绕过 IPC,丝滑很多。

持久化(v0.2)

重启后数据丢失是最影响日常使用的问题。用 localStorage 做持久化,key 是 acks_state_v1,存储任务列表、对话记录、产物路径、当前选中任务。

一个坑:产物文件(尤其是图片)如果把 base64 内容也存进 localStorage,很快就会超过 5MB 限制而静默失败。解决方案是只存路径元数据,文件内容按需从磁盘重读——反正路径不变,再读一次没有任何问题。

另一个有意思的功能是任务标题自动生成:新建任务默认标题是「新任务」,等第一条对话完成后,客户端会静默调用 Hermes,用一个简单的 prompt "请用不超过10个字概括这个任务的核心主题" 生成一个短标题,然后更新任务列表。用户完全感知不到这个后台调用,但任务列表突然就有了有意义的名字。

能力扩展(v0.3)

这部分主要是让 Agent 的输出更「可读」,以及让输入更方便:

语法高亮:用 highlight.js 的按需加载模式(只引入 10 种语言的解析器),避免全量 bundle 过大。每次代码块渲染时用 useEffect 调 hljs.highlight(),样式和设计系统的暗色主题对齐。

Markdown 表格:手写了一个表格解析器,识别 | col | col | 格式,支持分隔行检测,渲染为带斑马纹、可点击表头排序的 HTML 表格。

产物面板 CSV 视图:写了一个支持引号转义的 CSV 解析器,渲染为可排序表格。点击表头切换升降序,数字列用数值排序,文字列用中文字符串排序。

技能面板:Composer 底部的「技能」按钮弹出 8 个预设 prompt(总结、分析数据、写邮件、调研等),点击自动填充输入框。一个小细节:说明文字可能超过弹窗宽度,用 overflow-x: auto + 隐藏滚动条(scrollbar-width: none)实现左右滑动查看,不截断文字。

文件附件:点击 📎 按钮调用 Electron 的 dialog.showOpenDialog,或者直接拖拽文件到输入框。图片附件转成 MEDIA: 路径追加到消息,文本文件读取内容内联插入,让 Agent 能看到文件内容。


遇到的问题和解决过程

黑屏问题(两次)

第一次:把 Hermes URL 从常量改成变量后,webRequest.onBeforeSendHeaders 回调还在引用旧变量名,Electron 主进程报错 HERMES_URL is not defined,渲染层直接黑屏。

第二次:为了剥离发往 Hermes 的 Origin header(绕过 CORS),我把拦截范围设成了 ['http://*/*', 'https://*/*']——结果把 Vite 的 WebSocket HMR 连接也拦截了,页面加载直接失败。修复方案是在回调里加一个 if (url.startsWith(hermesUrl)) 的判断,非 Hermes 请求直接放行。

两次黑屏都是 Electron 主进程的问题,诊断起来比渲染层的错误要麻烦——没有 DevTools,只能看终端日志。

Agent Trace 自动展开

TracePanel 每次新对话都会自动展开,用户得不停手动收起,很烦。根本原因是父组件传了 collapsed={!msg.streaming} 作为 prop,每次 streaming 开始时强制展开。修复是把这个 prop 完全移除,让 TracePanel 内部的 useState(true) 作为唯一折叠状态控制,用户折叠后就保持折叠。


关于「用 AI 写 AI 客户端」这件事

整个过程里,我基本上扮演的是产品经理和 QA 的角色:定需求、测功能、发现问题。Claude 扮演的是开发者:读代码、找问题根源、写修复代码。

有几个感受:

效率高得惊人,但方向控制在人这边。一天内完成 v0.3 的工作量,凭我自己写大概需要两周。但每个功能要做成什么样、交互细节怎么定,还是我来决策。AI 负责执行,我负责判断。

错误诊断是最有价值的能力。Claude 不只是写代码,更重要的是能快速定位问题。两次黑屏,我自己看终端日志可能要找半小时,Claude 看了一眼代码就定位到了根本原因。这个能力在 debug 阶段比写新功能更值钱。

上下文是瓶颈。一次对话的上下文有限,超过后需要用交接文档接力。我们今天就遇到了这个问题,专门写了一份 HANDOFF_PROMPT.md,让下一个 session 能快速继承状态继续工作。这是 AI 协同开发目前最大的摩擦点。


代码已开源

今天的代码已推送到 GitHub:

https://github.com/shynloc/acks-agent-studio

如果你也在用类似的本地 Agent 框架,或者对 Electron + React 桌面应用开发感兴趣,欢迎参考。


下一步

v0.4 的主要方向是体验打磨:

  • 任务列表搜索(按标题/标签过滤)
  • Composer 历史指令召回(↑/↓ 键)
  • 对话导出(Markdown)
  • 产物「在 Finder 中显示」

再往后(v0.5)需要解决 localStorage 5MB 的容量限制,迁移到 SQLite。

这个系列会持续更新,记录每一个开发阶段的决策、踩坑和思考。下篇见。


本文由 Taojin 和 Claude Sonnet 4.6 协同完成。代码仓库:github.com/shynloc/acks-agent-studio


了解 ~/jintaoblog/朝夕见闻志⚡️ 的更多信息

订阅后即可通过电子邮件收到最新文章。