Trae Solo 实战指南:从"会用"到"用好"的协作方法论

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:"收到!我理解你需要:

  1. 一个表单页面
  2. 文字输入区域
  3. 图片上传功能
  4. 用IndexedDB做本地存储 对吗?"

我:"对,但图片要压缩,超过1MB的自动处理一下"

Solo:"明白,我加上图片压缩逻辑"

这个过程看起来多此一举,但相信我,它能帮你省掉50%的返工时间。

这其实就是提示词工程里说的------你给的上下文越清晰,AI在Embedding向量空间里"猜"的范围就越小,输出就越准。

2.需求会变的,Solo不知道

2.1 改需求之前,先改文档

真实项目哪有需求不变的?产品一拍脑袋,你的代码就得重写。

但问题是,Solo不知道你脑子里的需求变了。它只记得上次你说的话。

所以我现在的流程是:

  1. 先更新需求文档(哪怕只是个markdown)
  2. 用Plan模式让Solo重新理解整个项目
  3. 再让它改代码

别偷懒直接说"把那个按钮改成红色"。它可能会改,但它不知道为什么改,后面可能会在别的地方给你埋雷。

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协作也是一样------你越清楚自己要什么,它就越能帮到你。

它是你的编程搭档,不是你的产品经理。

最终拍板的,还是你自己。


参考资料

相关推荐
听风说图1 小时前
Figma画布协议揭秘:组件实例的SymbolOverrides覆盖机制
前端·canvas
小杨前端1 小时前
前端如何自己实现一个webpack的热更新?
前端
@大迁世界1 小时前
02.CSS变量 (Variables)
前端·css
鹏多多1 小时前
轻量+响应式!React瀑布流插件react-masonry-css的详细教程和案例
前端·javascript·react.js
用户345848285051 小时前
java中的tomicInteger/AtomicLong介绍
前端·后端
一颗宁檬不酸1 小时前
Vue.js 初学者基础知识点总结 第一弹
前端·javascript·vue.js
xiaoxue..1 小时前
解析 LocalStorage与事件委托在前端数据持久化中的应用
前端·javascript·面试
Mintopia1 小时前
「无界」全局浮窗组件设计与父子组件最佳实践
前端·前端框架·前端工程化
j***89461 小时前
MySQL数据的增删改查(一)
android·javascript·mysql