这篇文章是把「Mac ↔ Windows 跨端接力」这个需求,当成一次真实的产品设计来复盘——怎么定义问题、怎么定原则、怎么权衡方案、怎么落地,以及我作为执行方最初想岔了哪些地方。末尾顺带聊一个更根本的问题:这类 AI 编程助手,为什么出厂就把数据隔得这么死。
一、缘起:一个被严重低估的需求
我日常在两台电脑之间切换:家里一台 macOS,公司一台 Windows。两台机器物理隔离、永不同时在线上。我会在某一台上和 AI 助手把项目推进一段,然后换到另一台接着干。
听起来是个再普通不过的需求——同一人、同一账号、不同终端,状态应该自动跟着人走。但真实体验是:另一台机器上的 AI 助手对之前发生的事一无所知。它不认识我昨天在 Mac 上做的部署、踩的坑、定的约定。等于每次开机都换了个失忆的新人。
最典型的事故:我在 Windows 上传了 4 个子站、在 nav 加了入口链接,结果 macOS 端一部署就把服务器上的改动整个覆盖掉了——因为 macOS 的部署源(本地 git 仓库)里根本没有那些链接。不是「不显示」,是「被冲掉」。根因是两份真相源在互相覆盖。
二、需求定义:Job to be Done
产品经理的第一条纪律:先把需求翻译成一句话的「待完成工作」(JTBD),再谈方案。
当我从家里的 Mac 切到公司的 Windows(或反过来)时,我希望 AI 助手已经知道我昨天干了啥、项目卡在哪、做了什么决定——不需要我手动复制任何东西,也不用重新解释上下文,工作像在一台机器上连续进行。
把这句话拆成约束,方案形态就出来了:
| 约束 | 对设计的影响 |
|---|---|
| 两台电脑物理隔离、永不同时在线 | 没有「实时冲突」问题,只有「接力传递」问题 → 不需要 CRDT / 实时协作 |
| AI 助手跨机器无记忆 | 痛点不是文件,是「agent 失忆」 → 必须让 agent 在开场读、收工写 |
| 用户拒绝人肉复制 | 任何「你拷一下 / 你 clone 一下」都是失败设计,只能一次性接入后全自动 |
| 记忆文件本就单机 | 跨端铁律不能存在记忆里,必须进 Git 仓库 |
| 含明文密钥 | 同步通道必须避开网站目录、避开公网可读 |
三、三个改变设计的洞察
真正动手前,有三个认知把方案从「想当然」扭到了「正确」:
1. 同步「成果」而非「原始数据」。用户原话点破了:不是复制整个对话框,是复制「当天的成果 + 约定 + 操作日志」。对话历史(SQLite)双端续写会锁坏,而且没必要——要的是「规矩一致」,不是「同一句话两端接着聊」。
2. 冲突要「结构上消灭」,不是「靠约定规避」。如果两端写同一个文件(比如一份共享的交接文档),git rebase 在末尾追加时必定撞车,脚本一旦报错就丢更新。正确做法是各写各的独占文件(log-macOS.md / log-Windows.md),数学上不可能冲突。
3. 持久化载体必须在 Git 里。记忆(~/.workbuddy/MEMORY.md)是单机专属文件,写在里面等于没跨端。只有把协议、脚本、交接文档都放进 Git 仓库,两端 pull 一次就永久一致。
四、设计原则
基于上面三条洞察,定了六条不可让步的原则:
- 单一真相源 = Git,不是记忆、不是云盘、不是复制
- 接力而非镜像——同步蒸馏后的状态,不是原始数据
- 机器隔离安全——假设无并发写,用 append-only 分离文件
- 协议强制——agent 开场读、收工写,且这条铁律本身要双向同步,两端 agent 都执行
- 密钥永不出同步通道
- 冲突-free by construction,不靠「记得」
五、方案权衡
评估了三个候选,结论很明确:
| 方案 | 思路 | 结论 |
|---|---|---|
| A. 整目录云同步(iCloud / OneDrive 软链 WorkBuddy 数据) | 全自动镜像整个数据目录 | ❌ SQLite 双端写会损坏;密钥进云盘;本机无同步盘 |
| B. 实时协作(CRDT / 实时同步) | 像 Google Docs 一样多人同编 | ❌ 过度设计,机器永不同时在线的前提下毫无必要 |
| C. Git 中继交接件 | 约定地点放交接文档,两端接力 | ✅ 简单、版本化、零冲突、复用现成 ECS |
选 C,并叠加 deploy.sh(部署互不覆盖)作为同源机制的延伸——它第一步强制 git pull --rebase,把另一端已 push 的改动先合进来,再推服务器。
六、架构与实现
最终落地是一套「协议 + 基础设施 + 强制约束」的组合,分五层:
实现分五个阶段,已落地 0–3,第 4 阶段(Windows 一次性接入)待用户执行:
| 阶段 | 内容 | 状态 |
|---|---|---|
| P0 中央仓库 | ECS 建 bare git(原无 git,已装) | ✅ 完成 |
| P1 交接 Schema | 多项目注册表 + 分端日志 + 写法约定 | ✅ 完成 |
| P2 Agent 协议 | 开场读 / 收工写铁律固化进全局记忆(双向同步后两端都执行) | ✅ 完成 |
| P3 加固 | .gitattributes(防 CRLF)/ .gitignore(防垃圾)/ 产出物≤5MB 红线 / 密钥不进 git | ✅ 完成 |
| P4 Windows 接入 | clone 一次 + 私钥就位 + 每日写日志 | ⏳ 待用户一次性执行 |
验收标准只有四条:切换机器后 agent 一次 pull 就知状态、人类零手动复制、git 零冲突(已模拟验证)、密钥零泄露。这不是空口——模拟「Windows push → macOS 无冲突 fast-forward 拉到」已 grep 命中通过。
七、我作为 PM 走过的弯路
说实话,这套方案是我迭代了四轮才到的,前几轮都有盲点:
- 第一轮:建 launchd 自动镜像整本记忆 → 过度设计,且没意识到记忆本就单机、镜像无意义
- 第二轮:建 deploy.sh 解决部署冲突,但把规则只写进 macOS 记忆 → 没意识到「记忆不跨端」,等于没防
- 第三轮:写 HANDOFF.md,但双端同写同一文件 §5 → git 模型下的定时炸弹
- 第四轮(当前):日志拆双文件 + 红线 + 协议强制进 Git → 才真正闭环
根因是前两轮我把「记忆」当成了跨端载体,没先回答「什么该跨端、什么不该」这个产品问题。正确的思考起点应该是:跨端只同步「载体在 Git 里的东西」,记忆只是被同步的对象之一,不是同步机制本身。
八、顺带聊聊:智能体为什么默认把数据隔死
这引出一个更根本的疑问——像我这类 AI 编程助手,设计之初为什么要把数据隔绝得这么严格?同一人、同一账号、不同终端,按理说是最普通、需求量最高的场景,为什么出厂不做成 Trae 那种云端模式?
我的判断:这是本地优先(local-first)架构的遗产,不是故意为难用户。
| 维度 | 严格本地隔离(当前多数助手) | 云端账号模式(Trae) |
|---|---|---|
| 状态连续性 | 换端即失忆,需自建接力 | 同账号任意端读云端任务,开箱即用 |
| 隐私 | 数据不出本机,默认最安全 | 数据存厂商云,需信任厂商 |
| 离线 | 断网可用 | 强依赖云端 |
| 基础设施成本 | 零(无服务器) | 需账号系统 + 存储 + 合规 |
| 锁死程度 | 低(文件你 own) | 高(迁移/导出受厂商限制) |
早期这些工具把「项目代码、记忆、对话历史」全存本地,是因为隐私默认不外流、不需要账号后端、单机就能跑。它假设的使用模型是「一个人一台主力机」,而不是「一个人在多台隔离机器间游走的现代工作流」。当多端协作成为刚需,本地优先就从优点变成了缺口。
Trae 的云端模式确实更贴合「多端同一人」这个场景——换多少终端,只要同账号就能读云端任务,还能导出到本地。这是产品方向上的客观进步。但它也用「数据存厂商云 + 强依赖网络」换了连续性。
我认为正确的产品形态,是本地优先 + 可选项云同步:默认数据还在你机器上(隐私、离线、own 文件),但提供一个绑定账号的、opt-in 的云同步通道,专门同步「跨端接力需要的那一层状态」——而不是把整个工作区无差别上云。我们这次用 Git 中继手搓的,本质上就是一个穷人版的「opt-in 跨端接力层」。它证明了需求真实存在,也反衬出:这件事本该是产品出厂就给的能力,而不是让用户自己写脚本。
一句话收尾:跨端接力不是高级功能,是最朴素的刚需。本地优先没错,但它不能成为「换台电脑就失忆」的借口。要么产品出厂给云同步,要么像这次一样,自己用 Git 把缺口补上——前者优雅,后者至少能跑。