<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>全栈 on 姚玉亮的网络日志</title><link>https://yusanwen-code.github.io/tags/%E5%85%A8%E6%A0%88/</link><description>Recent content in 全栈 on 姚玉亮的网络日志</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Thu, 18 Jun 2026 10:30:00 +0800</lastBuildDate><atom:link href="https://yusanwen-code.github.io/tags/%E5%85%A8%E6%A0%88/index.xml" rel="self" type="application/rss+xml"/><item><title>独立完成前端页面：Vibe Coding 下的全栈闭环</title><link>https://yusanwen-code.github.io/posts/post-66/</link><pubDate>Thu, 18 Jun 2026 10:30:00 +0800</pubDate><guid>https://yusanwen-code.github.io/posts/post-66/</guid><description>&lt;img src="https://yusanwen-code.github.io/images/post-66-cover.jpg" alt="Featured image of post 独立完成前端页面：Vibe Coding 下的全栈闭环" /&gt;&lt;h2 id="排期等两周不如自己上"&gt;排期等两周，不如自己上&#10;&lt;/h2&gt;&lt;p&gt;我是后端出身，Go 和 Python 写得顺手，前端一直停留在&amp;quot;能改 Vue 模板、写点 jQuery&amp;quot;的水平。&lt;/p&gt;&#10;&lt;p&gt;alchemy-furnace 立项时，我想做一个多人格融合 Agent 的演示平台，需要一个能配置技能包、展示融合血统、对比生成结果的界面。前端同学的排期要等两周，我决定自己上，用 Claude Code 和 Cursor 做 Vibe Coding。&lt;/p&gt;&#10;&lt;p&gt;三周后，我一个人交付了 Go 网关 + Python 合成引擎 + Next.js 前端的完整 DEMO，前端包括技能包编辑器、融合过程可视化、多模型结果对比三个主页面。&lt;/p&gt;&#10;&lt;h2 id="分三步走不让-ai-一把梭"&gt;分三步走，不让 AI 一把梭&#10;&lt;/h2&gt;&lt;p&gt;第一步，先把后端契约定死。用 Go（Gin + GORM）把 API 写好，Swagger 文档直接生成。前端只需要对着文档调接口，不让 AI 猜后端长什么样。这是后端工程师做全栈的最大优势：你能自己定义契约。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&#10;&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;&#10;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 1&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 2&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 3&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 4&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 5&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 6&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 7&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 8&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 9&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;10&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;11&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;12&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;13&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;14&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;&#10;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// alchemy-furnace 网关侧的技能包接口&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;r.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;POST&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;/api/skills&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;func&lt;/span&gt;(c&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;gin.Context)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;var&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;req&lt;span style="color:#6e7681"&gt; &lt;/span&gt;CreateSkillReq&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;err&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;:=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;ShouldBindJSON&lt;/span&gt;(&lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt;req);&lt;span style="color:#6e7681"&gt; &lt;/span&gt;err&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;!=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;nil&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;JSON&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;400&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;gin.H{&lt;span style="color:#a5d6ff"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;:&lt;span style="color:#6e7681"&gt; &lt;/span&gt;err.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Error&lt;/span&gt;()})&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;return&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;}&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;skill,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;err&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;:=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;skillSvc.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Create&lt;/span&gt;(c.Request.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Context&lt;/span&gt;(),&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt;req)&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;err&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;!=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;nil&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;JSON&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;500&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;gin.H{&lt;span style="color:#a5d6ff"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;:&lt;span style="color:#6e7681"&gt; &lt;/span&gt;err.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Error&lt;/span&gt;()})&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;return&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;}&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;c.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;JSON&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;200&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;skill)&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;第二步，用 Cursor 的 Composer 搭骨架。我描述需求：&amp;ldquo;用 Next.js App Router + shadcn/ui + Tailwind，做一个三栏布局，左侧技能包列表，中间编辑器，右侧预览&amp;rdquo;，它生成初始代码后我再逐块调整。这里的关键是给足上下文：把 Swagger 文档、设计参考截图、已有的组件目录一起喂给它。&lt;/p&gt;&#10;&lt;p&gt;第三步，复杂交互用 Claude Code 细抠。比如融合血统的树形可视化（Promptbreeder 变异算子 + 血统追溯），我在 Claude Code 里打开相关组件，直接说&amp;quot;这里要改成 DAG 布局，节点颜色按代数区分，点击节点展示该代的 Prompt 全文&amp;quot;，它会读现有代码再精准改。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="Vibe Coding 全栈闭环：定契约、搭骨架、细抠交互，外加一条截图回修的反馈回路" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://yusanwen-code.github.io/images/post-66-vibe-fullstack.svg"&gt;&lt;/p&gt;&#10;&lt;h2 id="五个教训"&gt;五个教训&#10;&lt;/h2&gt;&lt;p&gt;第一，别指望 AI 生成的样式一次到位。布局错乱、响应式断点不对、暗色模式漏色，它经常顾头不顾尾。我的办法是每生成一块就在浏览器里看，不对就截图丢回去让它修。小步快跑，别攒到最后。&lt;/p&gt;&#10;&lt;p&gt;第二，状态管理别过度设计。AI 一上来就喜欢套 Zustand/Redux，其实大部分页面用 React Query 管服务端状态、&lt;code&gt;useState&lt;/code&gt; 管本地状态就够了。alchemy-furnace 这种 DEMO 规模，最后我什么全局状态库都没引入。&lt;/p&gt;&#10;&lt;p&gt;第三，类型安全一定要守住。TypeScript strict 模式全开，API 响应类型从 OpenAPI 生成（&lt;code&gt;openapi-typescript&lt;/code&gt;），不让 AI 随手写 &lt;code&gt;any&lt;/code&gt;。后端改字段，前端编译就能发现，比联调时互相甩锅高效太多。&lt;/p&gt;&#10;&lt;p&gt;第四，UI 库选 shadcn/ui 这类可复制的。组件源码直接进你的仓库，想怎么改都行，比黑盒组件库适合 Vibe Coding。AI 对它的代码也最熟，生成质量明显更高。&lt;/p&gt;&#10;&lt;p&gt;第五，后端思维得切换。后端是&amp;quot;请求-响应&amp;quot;，前端是&amp;quot;渲染是状态的函数&amp;quot;。一开始我总在 &lt;code&gt;useEffect&lt;/code&gt; 里绕圈子，后来想明白这一点，写起来就顺了。该花半天过一遍 React 官方文档，比让 AI 反复擦屁股强。&lt;/p&gt;&#10;&lt;h2 id="后来"&gt;后来&#10;&lt;/h2&gt;&lt;p&gt;Vibe Coding 没有把前端变成&amp;quot;不需要学&amp;quot;，只是把门槛从&amp;quot;记住所有 API&amp;quot;降到了&amp;quot;理解核心概念 + 会描述需求 + 能判断对错&amp;quot;。&lt;/p&gt;&#10;&lt;p&gt;后端工程师独立交付前端的最大红利，是契约和实现都能自己掌控，联调成本归零。alchemy-furnace 上线后我反而觉得，至少在产品快速迭代期，这种全栈闭环比前后端分工还快。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;封面图：&lt;a class="link" href="https://www.flickr.com/photos/38869431@N00/9791626204" target="_blank" rel="noopener"&#10; &gt;juhansonin / Flickr&lt;/a&gt; · CC BY 2.0&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;</description></item><item><title>AI Vibe Coding 工作流：用 Claude Code 全栈交付</title><link>https://yusanwen-code.github.io/posts/post-55/</link><pubDate>Sun, 28 Dec 2025 10:30:00 +0800</pubDate><guid>https://yusanwen-code.github.io/posts/post-55/</guid><description>&lt;img src="https://yusanwen-code.github.io/images/post-55-cover.jpg" alt="Featured image of post AI Vibe Coding 工作流：用 Claude Code 全栈交付" /&gt;&lt;h2 id="一个人三套技术栈"&gt;一个人，三套技术栈&#10;&lt;/h2&gt;&lt;p&gt;2026 年初我开源了 alchemy-furnace（炼丹炉），一个多人格融合 Agent 系统：Go(Gin+GORM) 做网关，Python(FastAPI) 做合成引擎，Next.js 做前端，三套技术栈一个人全栈交付。按传统写法，光是在语言和框架之间切上下文，就要吃掉大量业余时间。我拿 Claude Code 当主力开发助手，慢慢攒出了一套自己的 Vibe Coding 工作流。&lt;/p&gt;&#10;&lt;h2 id="先给-agent-立规矩"&gt;先给 Agent 立规矩&#10;&lt;/h2&gt;&lt;p&gt;第一步，先写 &lt;code&gt;CLAUDE.md&lt;/code&gt;，把项目结构、技术栈、启动命令、代码规范和关键领域概念（金丹、丹炉、血统追溯）写清楚，作为 Agent 的长期记忆。&lt;/p&gt;&#10;&lt;p&gt;第二步，复杂功能（Promptbreeder 变异算子、合成提示词缓存、多供应商适配）先用 Plan 模式让它出实施计划，我审完再让它动手写代码。&lt;/p&gt;&#10;&lt;p&gt;第三步，小步提交，每个子任务一个 commit，方便 review，也方便回滚。&lt;/p&gt;&#10;&lt;p&gt;第四步，测试先行。让 Claude 按函数签名写 table-driven tests，我负责补边界 case。&lt;/p&gt;&#10;&lt;p&gt;第五步，用 Task 跟踪多步任务，让它自己跑 &lt;code&gt;go build&lt;/code&gt;、&lt;code&gt;go test&lt;/code&gt;，失败了读报错自己修。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="Claude Code 全栈交付的五步工作流与分工边界" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://yusanwen-code.github.io/images/post-55-claude-workflow.svg"&gt;&lt;/p&gt;&#10;&lt;h2 id="两份配置"&gt;两份配置&#10;&lt;/h2&gt;&lt;p&gt;项目根的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 片段：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&#10;&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;&#10;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 1&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 2&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 3&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 4&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 5&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 6&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 7&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 8&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 9&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;10&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;11&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;12&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;13&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;14&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;&#10;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff;font-weight:bold"&gt;# alchemy-furnace&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;## 架构&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; gateway/ : Go 1.22, Gin + GORM, 负责 API Key 鉴权、路由、计费&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; engine/ : Python 3.11, FastAPI, 多人格融合 + Promptbreeder 变异&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; web/ : Next.js 14 App Router, Tailwind, shadcn/ui&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; deploy/ : docker-compose, 单机部署&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;## 约定&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; Go 代码通过 &lt;span style="color:#a5d6ff"&gt;`make lint`&lt;/span&gt; 检查，禁止裸 ignore error&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; Python 用 ruff + mypy，接口模型必须用 Pydantic&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; 所有外部供应商调用走统一 provider 抽象&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; API Key 入库前必须 AES-GCM 加密&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;-&lt;/span&gt; DEMO_MODE 下不调真实 LLM，返回内存 mock&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;code&gt;settings.json&lt;/code&gt; 里放行高频命令，减少权限弹窗：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&#10;&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;&#10;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 1&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 2&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 3&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 4&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 5&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 6&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 7&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 8&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt; 9&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;10&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;11&#10;&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"&gt;12&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;&#10;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;permissions&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#7ee787"&gt;&amp;#34;allow&amp;#34;&lt;/span&gt;: [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;Bash(go build ./...)&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;Bash(go test ./...)&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;Bash(go vet ./...)&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;Bash(ruff check *)&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;Bash(pytest -q)&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;Bash(docker compose config)&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="坑是真坑"&gt;坑是真坑&#10;&lt;/h2&gt;&lt;p&gt;第一，上下文是稀缺资源。我不会把整个仓库丢给 Claude，而是用子任务切给它：&amp;ldquo;只看 &lt;code&gt;gateway/internal/provider&lt;/code&gt; 目录，给 OpenAI 兼容层加一个 DeepSeek 适配&amp;rdquo;。大范围重构先让它列影响文件清单，再逐个改。&lt;/p&gt;&#10;&lt;p&gt;第二，Vibe Coding 不等于不看代码。写完让它自己跑测试，但 API Key 加密、变异算子的血统追溯这类关键路径，我逐行 review。能跑通不代表逻辑对，尤其涉及金额、权限、加密的时候。&lt;/p&gt;&#10;&lt;p&gt;第三，幻觉 API 是真问题，它会编出不存在的 eino 函数签名。我的要求是先 &lt;code&gt;go doc&lt;/code&gt; 或直接读 vendor 源码确认，不许凭印象写。&lt;/p&gt;&#10;&lt;p&gt;第四，多语言项目在同一会话里容易串味。我一般按服务分会话，或者明确说&amp;quot;接下来只写 Python，别把 Go 的错误处理风格带进来&amp;quot;。&lt;/p&gt;&#10;&lt;p&gt;第五，尽量用增量 Edit 而不是整体 Write。diff 清晰，跑偏了当场就能发现。&lt;/p&gt;&#10;&lt;h2 id="后来"&gt;后来&#10;&lt;/h2&gt;&lt;p&gt;Claude Code 让我这种以后端为主的人，敢一个人扛前端和算法服务。它最擅长样板代码、测试用例、跨文件重构和根据报错自修复；我负责架构、边界和验收。alchemy-furnace 能在业余时间快速做到 46 star，靠的就是这套工作流。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;封面图：&lt;a class="link" href="https://www.flickr.com/photos/39027808@N00/1423312308" target="_blank" rel="noopener"&#10; &gt;recursion_see_recursion / Flickr&lt;/a&gt; · CC BY 2.0&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;</description></item></channel></rss>