ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Claude打造能自我进化代码的本地App:Muse实战

用Claude打造能自我进化代码的本地App:Muse实战 1. 从一句聊天指令到会自我进化的应用这个项目到底在做什么周末花了两天时间我用 Claude 搭了一个能自己改自己代码的本地 App取名叫 Muse。核心体验就一句话打开聊天窗口输入你想要的功能它自己写代码、自己编译、自己热更新下次打开就多了一个新模块。整个过程不需要我打开 IDE不需要手动改一行代码。这个项目的本质是把Claude Code这类具备文件读写和命令执行能力的 agent和一个本地 App 的运行时环境缝合在一起。用户输入自然语言需求agent 解析意图后直接操作项目源码目录修改代码、运行构建脚本、触发应用重载。App 本身是一个壳真正的“进化引擎”是背后那套 agent 工作流。适合谁来参考如果你已经用过 Claude Code 或者类似的 agent 工具想把它从“写代码的助手”升级成“应用的一部分”这个思路可以直接抄。如果你只是听说过 agent 但没动手做过建议先把 Claude Code 在本地跑通理解它的工具调用机制再来看这篇。前端、全栈、独立开发者都能从中拿到可复用的模式尤其是那些想做“个人专属工具”但懒得每次手动开发的人。我踩过的最大坑是一开始把 agent 和应用运行时放在同一个进程里结果 agent 改代码时把正在运行的应用搞崩了热更新直接变成冷重启。后来拆成两个独立进程通过文件系统和 IPC 通信才稳定下来。这个教训后面会详细展开。2. 整体架构设计为什么选择本地双进程 文件监听2.1 核心思路把 App 当成 agent 的工作区传统 App 开发是“人写代码 → 构建 → 运行”这个项目把它倒过来“人提需求 → agent 写代码 → 构建 → 运行”。App 本身只是一个展示层和功能容器真正的逻辑都在源码目录里agent 有权限读写这个目录。我选择本地运行而不是云端原因有三个。第一延迟低agent 改完代码立刻就能看到效果不需要等部署。第二隐私可控所有代码和数据都在自己机器上。第三调试方便出问题直接看本地日志和文件变化不用去翻云函数日志。架构上分成三层交互层聊天窗口、agent 层Claude Code 进程、应用层实际运行的 App。交互层把用户输入传给 agent 层agent 层操作应用层的源码目录应用层通过文件监听感知变化并重载。2.2 为什么用 Claude Code 而不是自己调 API自己调 Claude API 当然可以但你要自己实现文件读写、命令执行、多轮对话管理、错误重试这些基础设施。Claude Code 已经把这些封装好了它内置了 Read、Write、Edit、Bash 等工具agent 可以直接操作文件系统。你只需要给它一个工作目录和一句指令它就能完成“读代码 → 改代码 → 运行测试”的完整闭环。另一个原因是 Claude Code 支持 MCPModel Context Protocol可以挂载自定义工具。比如我给它加了一个reload_app工具agent 改完代码后可以主动触发应用重载而不是被动等文件监听。这个能力在自建 API 方案里要自己实现工作量不小。注意Claude Code 默认会在当前工作目录下操作一定要把它限制在项目源码目录内避免误改系统文件。我是在启动时通过--cwd参数指定工作目录并且在 agent 的 prompt 里明确写了“只能修改 src 目录下的文件”。2.3 文件监听方案选型chokidar vs fs.watch应用层需要感知源码变化并重载。Node.js 原生有fs.watch但它跨平台行为不一致macOS 上偶尔会丢事件Linux 上递归监听支持不好。我最后选了chokidar它封装了底层差异支持递归监听、防抖、忽略规则稳定性好很多。配置上关键就几个参数ignoreInitial: true避免启动时触发一堆事件awaitWriteFinish设置 200ms 稳定期防止 agent 写文件写到一半就触发重载。实测下来agent 连续修改多个文件时chokidar 会把它们合并成一次重载体验很顺。const watcher chokidar.watch(./src, { ignored: /node_modules/, persistent: true, ignoreInitial: true, awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 } }); watcher.on(all, (event, path) { console.log([watcher] ${event} ${path}); scheduleReload(); });scheduleReload里再加一层 300ms 防抖确保连续变更只触发一次重载。这个双层防抖是我试出来的单靠 chokidar 的awaitWriteFinish在大量文件同时变更时还是会有多次触发。3. 核心细节拆解agent 如何理解需求并改代码3.1 需求解析从自然语言到文件操作用户输入“给我加一个深色模式切换按钮”agent 需要做几件事找到 UI 组件目录、确定用哪个框架的组件写法、修改对应的文件、可能还要加样式和状态管理。Claude Code 的做法是先读项目结构再读相关文件然后生成修改方案。这里的关键是prompt 设计。我在系统 prompt 里写了几条硬规则先读后写、每次只改一个功能、改完必须运行构建命令验证、失败要回滚。这些规则不是随便写的是踩坑踩出来的。有一次 agent 一口气改了五个文件结果构建失败它自己也不知道哪里错了最后我手动回滚了整个目录。你是一个应用代码修改助手。工作目录是 ./src。 规则 1. 修改前必须先读取目标文件理解现有代码结构。 2. 每次只实现一个功能不要批量修改。 3. 修改后运行 npm run build 验证失败则回滚本次修改。 4. 只能修改 ./src 下的文件禁止操作其他目录。 5. 如果需求不明确先问清楚再动手。这套规则让 agent 的成功率从大概六成提升到九成以上。尤其是“先读后写”这条能避免 agent 凭想象改代码改出来的东西跟现有风格完全对不上。3.2 代码生成模板 约束比自由发挥更稳完全让 agent 自由生成代码风格会飘。今天用函数组件明天用类组件后天又换个状态管理库。我的做法是给项目定一套模板和约束在 prompt 里明确写出来。比如 UI 组件必须用函数式写法状态用useState样式用 CSS Modules文件命名用 PascalCase。这些约束写进 prompt 后agent 生成的代码风格就统一了。我还在项目里放了一个COMPONENT_TEMPLATE.tsx让 agent 参考这个模板来写新组件。// COMPONENT_TEMPLATE.tsx import styles from ./ComponentName.module.css; interface ComponentNameProps { // props 定义 } export function ComponentName({}: ComponentNameProps) { return ( div className{styles.container} {/* 内容 */} /div ); }实测下来给了模板之后agent 生成的组件一次通过率明显提高因为结构、导入、导出方式都是它见过的模式不需要自己发明。3.3 构建与热更新让改动立刻可见agent 改完代码后需要触发构建。我用的是 Vite它的 HMR热模块替换本身就很快但 agent 修改文件后 HMR 不一定能正确捕获尤其是新增文件的情况。所以我加了一个显式的构建触发agent 改完代码后调用npm run build构建成功后通过 IPC 通知应用层重载。重载策略分两种热重载和冷重启。热重载只刷新改动的模块速度快但有时状态会丢冷重启整个应用慢一点但状态干净。我的策略是如果改动只涉及样式或单个组件走热重载如果涉及路由、全局状态或新增依赖走冷重启。判断逻辑放在 agent 的 prompt 里让它根据改动范围选择。实操心得冷重启时一定要先杀掉旧进程再启动新进程否则端口占用会导致启动失败。我一开始没做进程管理重启几次后端口就被占满了后来加了一个killPort函数重启前先清理端口。4. 实操过程从零搭一个会进化的本地 App4.1 环境准备与依赖安装先确保本地有 Node.js 18 和 npm。然后安装 Claude Code官方推荐用 npm 全局安装。安装完成后运行claude命令按提示完成登录授权。这一步网络环境要正常否则授权会失败。node -v # 确认 18 npm install -g anthropic-ai/claude-code claude --version # 确认安装成功接着创建项目目录初始化一个 Vite React TypeScript 的项目。这个组合构建快、HMR 稳定、类型检查能帮 agent 提前发现错误。npm create vitelatest muse-app -- --template react-ts cd muse-app npm install npm install chokidar项目结构大概是这样src/放源码agent/放 agent 相关脚本scripts/放构建和重载脚本。agent 的工作目录限定在src/其他目录它碰不到。4.2 agent 启动脚本与工作目录配置写一个agent/start.js负责启动 Claude Code 进程并传入工作目录和系统 prompt。这里用child_process.spawn启动把 stdout 和 stderr 转发到主进程方便调试。const { spawn } require(child_process); const path require(path); const SRC_DIR path.resolve(__dirname, ../src); const SYSTEM_PROMPT 你是一个应用代码修改助手...; // 前面写的规则 const agent spawn(claude, [--cwd, SRC_DIR, --prompt, SYSTEM_PROMPT], { stdio: [pipe, pipe, pipe] }); agent.stdout.on(data, (data) { process.stdout.write([agent] ${data}); }); agent.stderr.on(data, (data) { process.stderr.write([agent:err] ${data}); }); module.exports { agent };启动后agent 就待命了。用户输入通过agent.stdin.write()传进去agent 的输出通过 stdout 读出来展示在聊天窗口。4.3 聊天窗口与 agent 的通信实现聊天窗口用 React 写一个输入框加一个消息列表。用户输入后通过 IPC 发给主进程主进程转发给 agent 进程。agent 的回复实时推回渲染进程展示在消息列表里。function ChatWindow() { const [messages, setMessages] useState([]); const [input, setInput] useState(); const sendMessage async () { setMessages(prev [...prev, { role: user, content: input }]); const reply await window.electron.invoke(agent:send, input); setMessages(prev [...prev, { role: agent, content: reply }]); setInput(); }; return ( div classNamechat {messages.map((m, i) ( div key{i} className{msg ${m.role}}{m.content}/div ))} input value{input} onChange{e setInput(e.target.value)} / button onClick{sendMessage}发送/button /div ); }这里用的是 Electron 的 IPC 通道如果你用 Tauri 或其他框架换成对应的通信方式即可。核心是保持 agent 进程和 UI 进程分离UI 只负责展示和转发不直接操作文件。4.4 文件监听与自动重载的完整配置前面提过用 chokidar 监听src/目录。完整配置还要加上重载逻辑监听事件触发后先判断改动类型再决定热重载还是冷重启。let reloadTimer null; function scheduleReload() { clearTimeout(reloadTimer); reloadTimer setTimeout(async () { const changedFiles getChangedFiles(); // 从 watcher 事件里收集 const needsColdRestart changedFiles.some(f f.includes(router) || f.includes(store) || f.endsWith(package.json) ); if (needsColdRestart) { await killPort(5173); await startApp(); } else { await triggerHMR(); } }, 300); }killPort用lsof -ti:5173 | xargs kill -9实现startApp用spawn启动 Vite。这套逻辑跑通后agent 改完代码大概 2 到 3 秒就能看到界面变化体验很流畅。注意文件监听要忽略node_modules和构建产物目录否则构建时会产生大量事件导致无限重载循环。我一开始没忽略dist结果构建触发监听、监听触发重载、重载又触发构建死循环了。5. 常见问题与排查技巧实录5.1 agent 改代码后应用崩溃怎么办最常见的原因是 agent 生成的代码有语法错误或类型错误。排查步骤先看构建日志Vite 会报具体哪个文件哪一行出错再看 agent 的修改记录对比改动前后的差异最后手动回滚到上一个可用版本。预防措施是在 prompt 里强制要求 agent 改完后运行npm run build构建失败就自己回滚。我还在项目里加了 git 自动提交每次 agent 修改前先 commit 一个快照出问题直接git reset --hard回滚。# agent 修改前的快照 git add -A git commit -m snapshot before agent edit5.2 热更新不生效的几种情况热更新失效通常有三个原因文件监听没触发、HMR 客户端断连、改动类型不支持热更新。排查时先看 watcher 日志有没有输出再看浏览器控制台的 HMR 连接状态最后确认改动是否涉及需要冷重启的模块。我遇到过一次 watcher 没触发原因是 agent 用fs.writeFileSync写文件时chokidar 的awaitWriteFinish还没稳定就触发了事件导致重载时读到的是半截文件。后来把stabilityThreshold从 100ms 调到 200ms 就解决了。5.3 agent 理解错需求怎么纠正agent 理解错需求时不要直接说“你错了”而是给它更具体的上下文。比如它把按钮加到了错误的位置你可以说“按钮应该放在顶部导航栏右侧参考 Header.tsx 里现有按钮的写法”。给它一个具体的参考文件比抽象描述有效得多。我总结了一个纠正话术模板指出问题 给出参考 明确期望。比如“深色模式切换按钮现在在页面底部应该移到顶部导航栏参考 Header.tsx 里主题按钮的位置和样式”。这样 agent 能快速定位并修正。5.4 常见问题速查表问题现象可能原因排查方法解决方案应用启动后白屏agent 生成的代码有运行时错误看浏览器控制台报错回滚到上一个快照热更新不触发watcher 未监听或防抖配置不当看 watcher 日志调整 awaitWriteFinish 参数端口被占用旧进程未退出lsof -ti:5173重启前先 killPortagent 无响应进程卡死或网络问题看 agent stderr重启 agent 进程构建失败类型错误或依赖缺失看构建日志让 agent 修复或手动回滚重载循环监听了构建产物目录看 watcher 事件频率忽略 dist 和 node_modules5.5 几个提升稳定性的独家技巧第一个技巧是给 agent 加超时。agent 有时候会陷入死循环一直读文件不改代码。我在启动脚本里加了 60 秒超时超时后自动终止并返回错误信息。第二个技巧是限制单次修改的文件数量。在 prompt 里写“每次最多修改 3 个文件”超过就让它分步执行。这样出问题时影响范围可控回滚也容易。第三个技巧是保留 agent 的操作日志。每次 agent 的输入输出都写到agent/logs/下按日期分文件。出问题时翻日志比凭记忆靠谱得多也能用来分析 agent 的行为模式优化 prompt。第四个技巧是用 git 分支隔离实验性修改。agent 要加新功能时先切一个新分支改完验证通过再合并回主分支。这样主分支始终是可用的不会因为 agent 的实验性修改而崩溃。6. 这个模式还能怎么扩展把 agent 嵌入应用运行时这个思路能玩的花样比想象中多。我现在跑通的是“聊天改代码”这一条路径但同样的架构可以支持更多场景。比如定时自进化加一个 cron 任务每天凌晨让 agent 根据用户行为日志自动优化 UI。用户经常点某个按钮但找不到agent 就把它移到更显眼的位置。这个需要给 agent 加一个日志分析工具让它能读取用户行为数据。再比如多 agent 协作一个 agent 负责写代码另一个负责审查第三个负责测试。三个 agent 通过文件系统交换产物形成一条自动化流水线。这个模式在复杂功能开发上比单 agent 更稳因为审查 agent 能发现写代码 agent 忽略的问题。还有跨应用复用把这套 agent 工作流抽成一个独立的服务任何本地应用都可以接入。你做一个笔记应用、一个记账应用、一个健身打卡应用都共用同一个进化引擎。这样你只需要维护一套 agent 逻辑应用本身只负责 UI 和业务数据。我目前正在尝试的是agent 记忆持久化。让 agent 记住之前做过哪些修改、用户偏好是什么、哪些方案被否决过。这样它下次改代码时能参考历史决策不会重复犯同样的错误。实现方式是用一个 JSON 文件存记忆agent 每次启动时读取修改后写回。这个还在调试中稳定了再单独写一篇。最后分享一个我在实际使用中的体会agent 改代码的能力很强但它的“审美”和“产品感”需要你来引导。同样的需求你给的约束越具体、参考越明确它产出的质量越高。不要指望一句话就能让它做出完美功能把它当成一个执行力很强但需要明确指令的初级工程师你的 prompt 就是你的管理能力。
返回列表