0.背景
说实话,一开始我以为能躺平了。后来发现,想得美。
我是怎么踩坑的Solo刚全量开放那天,我兴冲冲地打开它,说了句"帮我做个博客系统"。

然后它真的开始做了。
text
**功能概览**
- 本地博客系统,内置于现有 Electron 应用,无需独立服务器
- 后台:新增/编辑/删除文章,支持标签与 Markdown 内容
- 前台:文章列表与详情页,点击切换展示
- 存储:使用本地文件系统,将文章保存为 data/posts/*.json
**如何运行**
- 在项目根目录执行:npm install(已自动安装 marked 用于 Markdown 渲染)
- 启动应用:npm run start
- 顶部导航点击"博客后台"进行文章管理;点击"个人博客"浏览文章
**使用方式**
- 后台(导航"博客后台"):
- 填写"标题""标签(逗号分隔)""内容(Markdown)",点击"保存文章"
- 列表中可"编辑""删除";点击"新建文章"清空编辑器
- 前台(导航"个人博客"):
- 左侧为文章列表,默认展示最新文章;点击列表项切换详情
- 内容支持 Markdown 渲染;若渲染库不可用,自动降级为纯文本
**代码结构**
- 存储模块:blog/storage.js
- 列表:listPosts() 在 blog/storage.js:28
- 读取:getPost(id) 在 blog/storage.js:38
- 新增:createPost({...}) 在 blog/storage.js:44
- 更新:updatePost(id, {...}) 在 blog/storage.js:62
- 删除:deletePost(id) 在 blog/storage.js:76
- UI逻辑:blog/blog.js
- 初始化:initBlog() 在 blog/blog.js:9
- 后台显示:showAdmin(show) 在 blog/blog.js:18
- 前台显示:showViewer(show) 在 blog/blog.js:27
- 保存操作:saveCurrentPost() 在 blog/blog.js:48
- 列表渲染(后台/前台):renderAdminList() 在 blog/blog.js:64、renderViewerList() 在 blog/blog.js:99
- 详情渲染:renderViewerDetail(id) 在 blog/blog.js:126
- 导航与跳转:index.js
- 跳转逻辑:jump(type) 在 index.js:5(2=后台,3=前台,4=交易记录)
- 清理视图:clearJump() 在 index.js:21
- 展示后台:showBlogAdmin(show) 在 index.js:50
- 展示前台:showBlogViewer(show) 在 index.js:60
- 页面结构:index.html
- 新增后台容器:<div id="blogAdmin">...
- 新增前台容器:<div id="blogViewer">...
- 初始化:<script>window.require('./blog/blog.js').initBlog()</script>
- 样式:index.css(补充了后台表单、按钮、前台列表与详情基础样式)
**关键实现说明**
- 文件存储
- 每篇文章保存在 data/posts/<id>.json,字段包含 id/title/slug/content/tags/createdAt/updatedAt
- 目录创建与健壮读写:ensureDir()、safeReadJSON() 在 blog/storage.js:6、blog/storage.js:10
- slugify(title) 生成标题友好短链(目前用于规范化,未来可用于路由)
- Markdown 渲染
- 安装 marked 后自动用 marked.parse(content) 渲染;未安装时降级为 <pre> 文本显示(blog/blog.js:134)
- 视图管理
- 保持与原项目的显示/隐藏风格一致,通过 jump(type) 切换不同视图,避免相互覆盖
**验证结果**
- 已编写并运行本地测试脚本 blog_test.js 验证存储模块的新增/读取/更新/删除/列表流程全部通过
- 运行命令:node blog_test.js
**后续可扩展**
- 添加搜索与标签筛选(按标题/标签过滤列表)
- 草稿与发布状态、置顶、分页
- 图片上传与插入(本地文件引用 + 安全路径校验)
- 文章导入/导出(Markdown 文件双向转换)
- 简单权限与数据备份(如自动备份到用户目录)
如果你想要改成外部服务端(例如 Express + SQLite 或 MongoDB),或者希望主题化样式与路由浏览,我可以继续把当前实现升级为 Web 服务或引入前端框架。
java
window.addEventListener('load', function () {
})
function jump(type) {
clearJump()
switch (type) {
case 2:
showBlogAdmin(true)
break
case 3:
showBlogViewer(true)
break
case 4:
showTradeRecord(true)
break
}
}
function clearJump() {
showTradeRecord()
jumpWebHtml()
showBlogAdmin()
showBlogViewer()
}
function showTradeRecord(show) {
let tradeRecordDiv = document.getElementById('tradeRecord')
if (show) {
tradeRecordDiv.style.display = 'block'
} else {
tradeRecordDiv.style.display = 'none'
}
}
function jumpWebHtml(url) {
let linkWebHtmlDiv = document.getElementById('linkWebHtml')
let linkWebHtmlIframe = document.querySelectorAll('#linkWebHtml iframe')[0]
if (url) {
linkWebHtmlIframe.setAttribute('src', url)
linkWebHtml.style.display = 'block'
} else {
linkWebHtml.style.display = 'none'
}
}
function showBlogAdmin(show) {
const blogAdminDiv = document.getElementById('blogAdmin')
if (!blogAdminDiv) return
blogAdminDiv.style.display = show ? 'block' : 'none'
if (show && window.require) {
const blog = window.require('./blog/blog.js')
blog.showAdmin(true)
}
}
function showBlogViewer(show) {
const blogViewerDiv = document.getElementById('blogViewer')
if (!blogViewerDiv) return
blogViewerDiv.style.display = show ? 'block' : 'none'
if (show && window.require) {
const blog = window.require('./blog/blog.js')
blog.showViewer(true)
}
}
生成了一堆我看不懂的代码结构,用了我从没听过的框架,还贴心地加了十几个我根本不需要的功能。最后我盯着屏幕,陷入了沉思:这玩意儿是在帮我,还是在考验我?

后来我想明白了------你不能怪一个刚入职的实习生不懂你的项目,你得怪自己没把需求说清楚。
Solo再聪明,它也不会读心术。
1.先把需求捋明白,别急着动手
1.1 "自然语言"不是"随便说说"
Solo支持自然语言输入,很多人就真的很"自然"了:
❌ "做个登录功能" ❌ "优化一下性能" ❌ "这里有bug你看看"
说真的,你跟同事这么说话,同事都想打你,何况AI。
我现在的习惯是,哪怕需求再小,也用需求行动前的5W1H思维模型过一遍:
- What:具体要做什么
- Why:为什么要做这个
- Where:影响哪些模块
- When:有没有时间或顺序约束
- Who:给谁用的,用户场景是什么
- How:有没有技术偏好或约束
写下来你会发现,很多时候你自己都没想清楚。
1.2 让Solo先"复述"一遍
这是我踩坑之后养成的习惯------在让它写代码之前,先让它说说它理解的是什么。
我:"我要做一个用户反馈页,支持文字和截图,数据存本地"
Solo:"收到!我理解你需要:
- 一个表单页面
- 文字输入区域
- 图片上传功能
- 用IndexedDB做本地存储 对吗?"
我:"对,但图片要压缩,超过1MB的自动处理一下"
Solo:"明白,我加上图片压缩逻辑"
这个过程看起来多此一举,但相信我,它能帮你省掉50%的返工时间。
这其实就是提示词工程里说的------你给的上下文越清晰,AI在Embedding向量空间里"猜"的范围就越小,输出就越准。
2.需求会变的,Solo不知道
2.1 改需求之前,先改文档
真实项目哪有需求不变的?产品一拍脑袋,你的代码就得重写。
但问题是,Solo不知道你脑子里的需求变了。它只记得上次你说的话。
所以我现在的流程是:
- 先更新需求文档(哪怕只是个markdown)
- 用Plan模式让Solo重新理解整个项目
- 再让它改代码
别偷懒直接说"把那个按钮改成红色"。它可能会改,但它不知道为什么改,后面可能会在别的地方给你埋雷。
2.2 Solo的方案,听听就好
有时候Solo会很自信地给你一个方案,看起来很专业。
别急着用。
用[[5why分析法]]的思路,往下问几层:
- 为什么选这个库?
- 为什么用这个设计模式?
- 有没有更简单的做法?
- 会不会引入新的坑?
我有一次让它实现一个简单的列表筛选,它给我整了个状态机。我问它为什么,它说"更优雅"。
兄弟,我只是要筛选一个列表啊。
AI没有"过度设计"的羞耻心。你得有。
3.调教它,让它越来越懂你
3.1 给它立规矩
每个项目都有自己的脾气------你喜欢用什么框架、命名习惯是啥、代码放哪个文件夹。
Solo不知道这些,除非你告诉它。
我会维护一份"项目规矩",大概长这样:
技术栈
- React 18 + TypeScript
- Tailwind CSS,不用其他CSS方案
- Zustand做状态管理,别用Redux
代码规范
- 组件用PascalCase,文件名和组件名一致
- 不用class组件,全部函数式
- 接口定义放在types/目录
禁止事项
- 不要用any
- 不要在组件里直接调fetch,走封装好的api层
- 不要自作主张加console.log
贴给Solo,它就知道规矩了。
3.2 它做错了,记下来
每次Solo的输出不对,我就会想:是我没说清楚,还是它理解偏了?
如果是我的问题,我就补充到"项目规矩"里。几轮下来,Solo真的会越来越"懂"你。
这就跟带新人一样,你得有耐心。区别是,Solo不会离职,也不会摸鱼。
4.一些血泪教训
4.1 从小任务开始
别上来就"帮我重构整个项目"。先从一个半天能做完的功能开始,建立信任,摸清边界。
我第一次让Solo帮我重构一个老项目,它给我改了40多个文件,我花了两天才理清楚它改了什么。

js
import React, { useState } from 'react'
type Props = {
avatarUrl?: string
nickname: string
size?: number
className?: string
defaultAvatarUrl?: string
}
const DEFAULT_AVATAR =
'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><defs><clipPath id="c"><circle cx="50" cy="50" r="50"/></clipPath></defs><g clip-path="url(%23c)"><rect width="100" height="100" fill="%23e0e0e0"/><circle cx="50" cy="40" r="22" fill="%23bdbdbd"/><rect x="15" y="66" width="70" height="36" rx="18" fill="%23bdbdbd"/></g></svg>'
export function UserAvatar({ avatarUrl, nickname, size = 40, className, defaultAvatarUrl }: Props) {
const [src, setSrc] = useState<string>(avatarUrl || defaultAvatarUrl || DEFAULT_AVATAR)
const displayName = nickname.length > 6 ? nickname.slice(0, 6) + '...' : nickname
const fontSize = Math.max(12, Math.round(size * 0.4))
return (
<div className={className} style={{ display: 'inline-flex', alignItems: 'center' }}>
<img
src={src}
alt={nickname}
onError={() => setSrc(defaultAvatarUrl || DEFAULT_AVATAR)}
style={{ width: size, height: size, borderRadius: '50%', objectFit: 'cover', backgroundColor: '#eee' }}
/>
<span style={{ marginLeft: 8, fontSize, lineHeight: `${size}px`, maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{displayName}
</span>
</div>
)
}
export default UserAvatar

4.2 善用多任务
Solo支持多任务并行,这个功能真的好用。我的习惯是:
- 主任务:正在开发的功能
- 调试任务:遇到bug了,开个新的,别污染主任务
- 实验任务:想试试新方案,不确定就扔掉
就像开浏览器标签页一样,互不干扰。
4.3 它写的代码,你得审
这点最重要。
AI生成的每一行代码,你都得过脑子。特别是:
- 安全相关的(有没有XSS、注入风险)
- 边界处理(null、undefined考虑了吗)
- 性能问题(循环里有没有乱调API)
有一次Solo帮我写了个表单提交,看起来没问题。上线后发现,用户连点两下会提交两次。
它不知道要加防抖,你得知道。
写在最后
用了一个月Solo,我的感受是:它确实能帮你省时间,但省的是手上的活,不是脑子里的活。
好的SOP需要场景描述清楚、每一步可执行。跟AI协作也是一样------你越清楚自己要什么,它就越能帮到你。
它是你的编程搭档,不是你的产品经理。
最终拍板的,还是你自己。