WorkBuddy 跨设备迁移实战:47 条会话无缝续接的全流程缝合术
WorkBuddy 跨设备迁移实战:47 条会话无缝续接的全流程缝合术
换电脑是常事,但 AI 助手的"记忆"怎么搬?市面上的指南要么停留在"复制
.workbuddy文件夹"的粗粒度,要么用泛泛的"路径替换"带过关键坑。本文记录一次真实的跨设备迁移:从旧电脑 Windows 用户yanjing/ D 盘工作空间,到新电脑Administrator/ E 盘工作空间,47 条历史会话如何无缝续接,期间 9 条被覆盖冲掉的会话如何从 jsonl 恢复,以及最终把整套缝合流程交给 AI 自主完成的全过程。
一、背景:为什么 WorkBuddy 迁移是个真问题
WorkBuddy 作为本地优先的 AI 编码助手,几乎所有状态都存在本地:会话历史、技能、连接器、自定义模型、人设、长期记忆、Python/Node 运行时……账号只携带"身份",不带数据。这意味着换电脑时:
- 复制
.workbuddy文件夹 → 数据物理到位了,但路径全错 - 旧机用户名
yanjing在新机不存在,jsonl 里的所有文件引用失效 - 旧机 D 盘工作空间在新机变成 E 盘,DB 里 cwd 字段全部指向幽灵路径
- 直接整库覆盖
workbuddy.db→ 冲掉新机已产生的会话索引(实测踩坑)
更麻烦的是,这些残留分布在五个不同层次,每一层有不同转义变体,靠"全局替换"根本清不干净。
二、WorkBuddy 本地数据全景
先看 .workbuddy/ 目录的全貌,明确每一块的迁移策略:
| 目录 / 文件 | 内容 | 迁移策略 |
|---|---|---|
workbuddy.db |
会话索引、工作空间、自动化任务(SQLite) | 合并式导入,禁止整库覆盖 |
projects/ |
会话正文(jsonl + meta.json) | 整目录复制,需重命名 + 正文路径替换 |
skills/ |
用户级技能 | 整目录复制,meta 中 iconLocalPath 需修 |
memory/ |
云端画像本地缓存 + MEMORY.md | 整目录复制 |
connectors/ |
MCP 连接器配置(mcp.json) | 整目录复制,凭据可能需重新授权 |
binaries/ |
Python / Node / Git / ffmpeg | 整目录复制,pyvenv.cfg 必修 |
SOUL/IDENTITY/USER/MEMORY.md |
个性化四件套 | 整复制,默认模板则覆盖 |
settings.json / models.json |
客户端设置、自定义模型 | 整复制 |
local_storage/ / user-state.json |
客户端运行时状态 | 不迁(会被内存态回写) |
app/sessions.json |
UI 会话列表状态 | 不迁(启动时重建) |
注意最后一行——
sessions.json不迁是因为它只是 UI 状态,真正的会话索引在workbuddy.db的sessions表里。这是个容易踩错的认知点。
三、为什么"复制粘贴"不够:五层路径残留
把 .workbuddy 文件夹复制到新机后,旧路径残留分布在五个层次,每一层需要不同的处理方式:
第 1 层:数据库字段
sessions 表的 cwd 字段、workspaces 表的路径字段,全部指向旧机路径:
-- 修复前的 sessions 表
SELECT cwd, COUNT(*) FROM sessions GROUP BY cwd;
-- C:\Users\yanjing\Desktop\... → 20 条
-- C:\Users\yanjing\WorkBuddy\... → 5 条
-- D:\computerSoftware\workbuddy_workspace\... → 12 条
修复方式:SQL UPDATE,把 yanjing 替换为 Administrator,D:\computerSoftware 替换为 E:\computerSoftware。
第 2 层:projects/ 目录名
WorkBuddy 用 cwd 路径生成目录名(盘符和反斜杠转成连字符):
c-Users-yanjing-Desktop-售前支持相关-南昌金控相关 ← 旧机目录名
c-Users-Administrator-Desktop-售前支持相关-南昌金控相关 ← 新机应有的目录名
修复方式:os.rename,38 个目录全部重命名。
第 3 层:jsonl 正文(最坑的一层)
会话正文里嵌入了大量旧路径,且转义变体极多。我实测扫到的形态:
| 形态 | 示例 | 出现场景 |
|---|---|---|
| 标准反斜杠 | C:\Users\yanjing\... |
工具调用参数 |
| 双反斜杠 | C:\\Users\\yanjing\\... |
JSON 字符串内嵌 |
| 正斜杠 | C:/Users/yanjing/... |
跨平台代码 |
| 小写盘符 + 双反斜杠 | c:\\Users\\yanjing\\... |
每行末尾的 cwd 元字段 |
| D 盘同上四种变体 | D:\ / D:\\ / D:/ / d:\\ |
工作空间路径 |
只替换第一种会漏掉后四种,只查 yanjing 关键词会发现"清不干净"——其实残留的是小写盘符 + 双反斜杠形态。必须把全部八种变体列入替换清单。
第 4 层:pyvenv.cfg(Python venv 启动器)
home = C:\Users\yanjing\.workbuddy\binaries\python\versions\3.13.12
venv 启动器指向旧机 Python 路径,新机直接报错。不用重装 venv,改这一行即可:
from pathlib import Path
cfg = Path(r'C:\Users\Administrator\.workbuddy\binaries\python\envs\default\pyvenv.cfg')
text = cfg.read_text(encoding='utf-8')
new = text.replace(r'C:\Users\yanjing', r'C:\Users\Administrator')
cfg.write_text(new, encoding='utf-8')
# 验证
import sys, openpyxl, pandas
print('venv OK:', sys.version.split()[0])
第 5 层:skills meta 的 iconLocalPath
部分技能的 _skillhub_meta.json 里 iconLocalPath 字段写死了旧机绝对路径:
{"iconLocalPath": "C:\\Users\\yanjing\\Desktop\\xxx.png"}
修复方式:遍历 skills/*/_skillhub_meta.json,路径替换。
四、踩坑实录:理论派指南 vs 实测真相
迁移前我参考了一份 AI 生成的通用指南,对照实操发现了几处关键偏差:
| 项 | 通用指南说法 | 实测真相 |
|---|---|---|
workbuddy.db |
"直接覆盖" | ⚠️ 整库覆盖会冲掉新机已产生的会话索引,必须合并式导入 |
sessions.json |
"不要覆盖,让 AI 重建" | 会话索引实际在 DB 的 sessions 表,sessions.json 只是 UI 状态,不迁即可 |
| 路径替换 | "全局替换" | 五层残留 + 八种转义变体,必须分层处理 |
| 账号机制 | "同一账号登录" | 多账号共用 ~/.workbuddy,会话按 user_id 隔离,切换账号看不到属正常 |
| venv 报错 | "重装 Python" | 改 pyvenv.cfg 一行即可,不用重装 |
.skillhub 目录 |
"需创建" | 不必建,技能本体已随 skills/ 迁入 |
automations 表 |
"需迁移" | 实测为空表,无定时任务需处理 |
最有价值的一条教训:先迁移,后使用。新机装好 WorkBuddy 后先彻底退出,再做迁移,再启动。否则新机已经产生了新会话,整库覆盖会把它们冲掉——我就因此丢了 9 条,靠 jsonl 才恢复。
五、WAL 模式启示:运行时能不能动数据库
这是这次迁移里最技术性的一段。执行迁移的 AI 本身就住在 WorkBuddy 客户端里——这意味着"先关客户端再操作"对全自动迁移是个悖论。那运行时能改数据库吗?
SQLite 默认 journal 模式有 DELETE/TRUNCATE/WAL 等。WorkBuddy 用的是 WAL(Write-Ahead Logging),实测:
$ ls -la ~/.workbuddy/workbuddy.db*
workbuddy.db 2.5 MB
workbuddy.db-wal 3.1 MB ← 客户端运行时实时产生,未 checkpoint
workbuddy.db-shm
WAL 模式的核心特性:
| 操作 | 客户端运行时 | 原因 |
|---|---|---|
| SQL 变更(INSERT/UPDATE,短事务) | ✅ 安全 | WAL 设计初衷就是多连接并发,读写互不阻塞 |
| 文件级替换(覆盖 db 文件) | ❌ 绝对禁止 | 主库文件不含 WAL 中未 checkpoint 的数据,覆盖即丢失,新旧 WAL/shm 错位会损坏库 |
| 备份这个库 | ⚠️ 不能只拷文件 | 必须走 SQLite backup API,否则缺 WAL 数据 |
结论:迁移时数据库操作改用"合并式导入"——只读打开备份库 → 读出旧会话行 → INSERT 进活动库。这恰好支持运行时执行,且比整库覆盖更安全。
六、AI 自主缝合六阶段流程
理解了上述认知后,整个迁移可以交给 AI 自主完成。流程如下:
各阶段要点:
- 只读探查:扫描
.workbuddy、projects/、workbuddy.db、桌面工作目录、binaries/,找出旧路径残留层次;检测孤儿会话(有 jsonl 无 DB 行 = 整库覆盖过的典型症状) - 备份:用 SQLite backup API 做一致性备份(比直接拷 db+wal 可靠),个性化文件单独备份
- 数据搬运:合并式导入,禁止整库覆盖;projects/skills/memory 整目录复制;全量差异比对旧机副本,捞回易漏项(如
credentials/子目录、cloudstudio-deploy-history);跳过vendor空壳、.deprecated标记 - 路径缝合:五层残留逐层处理,jsonl 替换必须覆盖全部八种转义变体
- 会话合并注册:合并式 INSERT 缺失的会话行;若有孤儿会话,从 jsonl 重建索引行(schema 模仿现有行、cwd 重映射、user_id 按归属确认、标题从首条 user 消息剥离 system-reminder 和
<user_query>标签提取) - 校验与报告:七项验证清单,输出结构化报告
七、可执行代码片段
以下是这次迁移中验证过的核心代码,稍作参数化即可复用:
7.1 数据库合并式导入(运行时安全)
import sqlite3
from pathlib import Path
BACKUP_DB = Path(r'C:\Users\Administrator\数据迁移\.workbuddy\workbuddy.db')
LIVE_DB = Path(r'C:\Users\Administrator\.workbuddy\workbuddy.db')
# 只读打开备份库
src = sqlite3.connect(f'file:{BACKUP_DB}?mode=ro', uri=True)
# 读写打开活动库(设 busy_timeout 应对偶发锁)
dst = sqlite3.connect(str(LIVE_DB))
dst.execute('PRAGMA busy_timeout = 5000')
# 路径重映射函数
def remap(s):
if not s: return s
return (s.replace('C:\\Users\\yanjing', 'C:\\Users\\Administrator')
.replace('D:\\computerSoftware', 'E:\\computerSoftware'))
# 合并式导入:只 INSERT 缺失的会话行
existing = {r[0] for r in dst.execute('SELECT id FROM sessions')}
for row in src.execute('SELECT * FROM sessions'):
sid = row[0]
if sid in existing:
continue # 已存在,跳过(不覆盖)
# 重映射 cwd 等路径字段
row = list(row)
# 假设 cwd 是第 N 列,按实际 schema 调整
# row[N] = remap(row[N])
placeholders = ','.join('?' * len(row))
dst.execute(f'INSERT INTO sessions VALUES ({placeholders})', row)
dst.commit()
print(f'合并完成,当前会话数: {dst.execute("SELECT COUNT(*) FROM sessions").fetchone()[0]}')
7.2 jsonl 路径替换(覆盖全部转义变体)
import re
from pathlib import Path
PROJECTS = Path(r'C:\Users\Administrator\.workbuddy\projects')
# 八种转义变体清单(必须全部覆盖)
REPLACEMENTS = [
# 标准反斜杠
(r'C:\Users\yanjing', r'C:\Users\Administrator'),
(r'D:\computerSoftware', r'E:\computerSoftware'),
# 双反斜杠(JSON 字符串内嵌)
(r'C:\\Users\\yanjing', r'C:\\Users\\Administrator'),
(r'D:\\computerSoftware', r'E:\\computerSoftware'),
# 正斜杠
('C:/Users/yanjing', 'C:/Users/Administrator'),
('D:/computerSoftware', 'E:/computerSoftware'),
# 小写盘符 + 双反斜杠(每行末尾 cwd 元字段常见形态)
(r'c:\\Users\\yanjing', r'c:\\Users\\Administrator'),
(r'd:\\computerSoftware', r'e:\\computerSoftware'),
]
total_replaced = 0
for jsonl in PROJECTS.rglob('*.jsonl'):
text = jsonl.read_text(encoding='utf-8', errors='ignore')
original = text
for old, new in REPLACEMENTS:
text = text.replace(old, new)
if text != original:
jsonl.write_text(text, encoding='utf-8')
total_replaced += 1
print(f'处理文件数: {total_replaced}')
7.3 pyvenv.cfg 修复(不用重装 venv)
from pathlib import Path
cfg = Path(r'C:\Users\Administrator\.workbuddy\binaries\python\envs\default\pyvenv.cfg')
text = cfg.read_text(encoding='utf-8')
new = text.replace(r'C:\Users\yanjing', r'C:\Users\Administrator')
cfg.write_text(new, encoding='utf-8')
# 验证
import subprocess
r = subprocess.run([
str(Path(r'C:\Users\Administrator\.workbuddy\binaries\python\envs\default\Scripts\python.exe')),
'-c', 'import sys, openpyxl, pandas; print("venv OK:", sys.version.split()[0])'
], capture_output=True, text=True)
print(r.stdout, r.stderr)
7.4 孤儿会话恢复(从 jsonl 重建索引行)
import sqlite3, json, re, uuid
from pathlib import Path
from datetime import datetime
LIVE_DB = Path(r'C:\Users\Administrator\.workbuddy\workbuddy.db')
PROJECTS = Path(r'C:\Users\Administrator\.workbuddy\projects')
USER_ID = '11586a3d-ff86-4d41-aecc-bcbe0b991179' # 当前账号
con = sqlite3.connect(str(LIVE_DB))
existing = {r[0] for r in con.execute('SELECT id FROM sessions')}
for proj_dir in PROJECTS.iterdir():
if not proj_dir.is_dir(): continue
for meta in proj_dir.glob('*.meta.json'):
sid = meta.stem
if sid in existing: continue # 已注册,跳过
m = json.loads(meta.read_text(encoding='utf-8'))
# 从 jsonl 首条 user 消息提取标题
jsonl = next(proj_dir.glob(f'{sid}.jsonl'), None)
title = '未命名会话'
if jsonl:
for line in jsonl.read_text(encoding='utf-8').splitlines():
try:
obj = json.loads(line)
except: continue
if obj.get('role') == 'user':
content = obj.get('content', '')
if isinstance(content, list):
content = ''.join(b.get('text', '') for b in content if isinstance(b, dict))
# 剥离 system-reminder 和 user_query 标签
content = re.sub(r'<system-reminder>.*?</system-reminder>', '', content, flags=re.DOTALL)
content = re.sub(r'<[^>]+>', '', content).strip()
title = content[:80] or '未命名会话'
break
# 重映射 cwd
cwd = m.get('cwd', '').replace('C:\\Users\\yanjing', 'C:\\Users\\Administrator')\
.replace('D:\\computerSoftware', 'E:\\computerSoftware')
now = int(datetime.now().timestamp() * 1000)
con.execute('''INSERT INTO sessions
(id, user_id, title, cwd, source_mode, created_at, last_activity_at, is_background_automation)
VALUES (?, ?, ?, ?, ?, ?, ?, 0)''',
(sid, USER_ID, title, cwd, 'agent', now, now))
print(f'恢复: {sid} | {title[:50]}')
con.commit()
八、验证清单与最终结果
迁移完成后按以下清单逐项验证:
| # | 验证项 | 方法 | 期望结果 |
|---|---|---|---|
| 1 | 会话总数 | SELECT COUNT(*) FROM sessions |
等于旧机 + 新机会话数之和 |
| 2 | cwd 目录存在性 | 对每条会话的 cwd 检查 os.path.exists |
100% 存在 |
| 3 | projects 目录名编码 | 目录名与 DB cwd 编码一致 | 一一对应 |
| 4 | 功能性路径残留 | grep jsonl 中的功能性路径 | 0 处 |
| 5 | venv 可用 | import openpyxl, pandas |
无报错 |
| 6 | 连接器配置 | mcp.json server 名与旧机一致 | 完全覆盖 |
| 7 | 个性化四文件 | SOUL/IDENTITY/USER/MEMORY 非默认模板 | 已定制 |
本次实测结果:
| 指标 | 数值 |
|---|---|
| 迁移会话总数 | 47 条(37 旧 + 9 恢复 + 1 新建) |
| projects 目录重命名 | 37 个 |
| jsonl 路径替换 | 约 1.17 万行 |
| 功能性路径残留 | 0 处 |
| venv 修复 | 1 行配置改动 |
| skills 修复 | 4 个文件 |
| 总耗时 | 约 1.5 小时(AI 自主完成) |
唯一需要说明的"残留":26 个 jsonl 文件里仍有单词级
yanjing,这些是历史讨论文本(如"我在 yanjing 这台电脑上做过 XX"),不是功能性路径,官方脚本惯例保留,不影响功能。
九、经验沉淀:人机两份文档的分工
这次迁移最终产出两份文档,分工明确:
| 文档 | 读者 | 形态 | 内容侧重 |
|---|---|---|---|
| 《WorkBuddy 数据迁移指南(实践版)》 | 人 | docx | 认知 + 操作步骤 + 排查表,详尽到每步 |
| 《WorkBuddy 自动迁移指令书》 | AI | md | 六阶段执行流程 + 安全铁律 + 技术备忘,可被 AI 整份读入 |
为什么需要两份?人需要"为什么这么做"的认知铺垫和"具体怎么点"的操作步骤;AI 需要"六阶段流程 + 安全铁律 + 坑位备忘"的执行手册。把执行细节塞进 docx 会增加人的认知负担,把认知铺垫塞进指令书会稀释 AI 的执行密度。
下次迁移时,新机 WorkBuddy 里发一句话即可启动全自动流程:
请读取并严格执行 C:\Users\Administrator\Desktop\WorkBuddy自动迁移指令书.md,
按其中六阶段流程完成迁移。遇到指令书未覆盖的情况,先停下向我确认。
十、可复用经验提炼
最后提炼几条可复用的经验,适用于所有"AI 助手本地数据跨设备迁移"场景:
- 数据在本地还是云端,决定迁移复杂度——本地优先的助手(WorkBuddy、Claude Code、Codex)迁移复杂度高,云端优先的(ChatGPT、Gemini 网页版)几乎无需迁移
- 路径残留是主要矛盾,且分层分布——不要指望"全局替换"一招通吃,DB 字段、目录名、jsonl 正文、配置文件各有各的转义变体
- 运行中的 SQLite 用 SQL 操作安全,文件级覆盖危险——WAL 模式下合并式导入是运行时迁移的正确姿势
- 先迁移,后使用——新机装好后先别启动,否则产生的新会话会被整库覆盖冲掉
- AI 自主缝合是可行的——只要把流程、铁律、坑位备忘固化成指令书,AI 能自主完成 95% 的工作,剩下 5%(连接器重授权、系统定时任务、客户端重启)需用户手动
- 个性化配置要分清本地 vs 云端——SOUL/IDENTITY/MEMORY 是本地文件可迁,自定义指令纯云端只能 UI 粘贴,管理记忆云端每晚重生成属正常
- 备份保留 1-2 周再清理——日常使用无异常前别删备份
写在最后:这次迁移最让我感慨的是——执行迁移的 AI 就住在被迁移的客户端里,这本身是个有趣的递归。把"怎么做迁移"的方法论固化成 AI 能读的指令书,让 AI 自己执行,是这种递归场景下最自然的解法。希望这份记录能帮到同样要换电脑的 WorkBuddy 用户。
完整指南文档和自动迁移指令书已开源在桌面,欢迎参考。
本文来自博客园,作者:慵懒的小景,转载请注明原文链接:https://www.cnblogs.com/ailanlan/p/22827183
否则会进行维权,谢谢合作。
浙公网安备 33010602011771号