多 Agent 协作最大的坑不是模型,是失忆

文章来源声明: 原文作者:code_Bo; 来源站点:掘金; 原文链接:https://juejin.cn/post/7689633702608420927; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

把“换 Agent 不失忆”做成可落地的 Session Handoff,只读聚合与交接预算裁剪的思路很实用。适合同时使用多个 AI 编程工具、需要跨模型续写同一任务的开发者。

> 包名:[`cross-agent-sync`](https://link.juejin.cn?target=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fcross-agent-sync "https://www.npmjs.com/package/cross-agent-sync")(CLI 命令 `ass`) > 仓库:[lhbDesign/cross-agent-sync](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FlhbDesign%2Fcross-agent-sync "https://github.com/lhbDesign/cross-agent-sync") > 当前版本:0.2.2

同一台机器上同时开着 Claude Code、Codex、Cursor、OpenCode……同一个项目、同一件事,需求 1~3 用 Claude 做,4~5 想换 Codex。结果历史全留在上一个 Agent 里,换过去等于失忆,图片还得重新贴。

这篇文章讲四件事:我踩过什么坑、怎么用 Agent 把工具做出来、发布后怎么一轮轮修、以及现在怎么用、后面还想改什么。


一、场景、困难、阻碍

1.1 真实工作流长什么样

日常大概是这样:

  • 一个需求拆成多段,不同段用不同模型(便宜的扫代码、强的做架构)
  • 同一个 Agent 也经常「新开一个会话」,因为旧会话上下文脏了、或 token 快爆了
  • 偶发场景:Codex 里刚贴完截图问完一题,想立刻丢给 Claude 再答一遍——问题本身要搬过去,不只是结论

表面看是「复制粘贴总结一下」,真做几次就会发现这不是粘贴问题,是上下文所有权问题:每个 Agent 的会话是私有的,Claude 的 .jsonl Codex 读不懂,Cursor 的 state.vscdb 更是另一套。

1.2 手工交接的痛点

痛点具体表现
复述成本高每次换 Agent 都要口述「目标 / 已做 / 待办 / 别动哪些文件」
决策丢了「为什么这么定」只活在对话里,下一个 Agent 会重新发明一遍
踩坑重复上一个 Agent 验证过「方案 B 不行」,下一个又会再试一遍
图片难搬base64 / 附件路径各家不一样,复制对话文本等于丢图
会话对不上仓库从子目录启动、软链、Cursor workspace 清理后,会话「还在」但按项目一筛就没了

更糟的是:你以为「让 Agent 自己读历史」就行——但没有统一入口时,它只会猜,或者让你再讲一遍。

1.3 技术侧真正难的点

动手之前我以为难在「写个 CLI」。做完才发现难在这些:

  1. 存储形态完全不统一
    JSONL(Claude / Codex)、SQLite(Cursor / OpenCode)、附件目录各玩各的。没有「通用 Session API」。
  2. 「只读」必须是强制的,不能是口头承诺
    一旦工具写坏了某个 Agent 的会话库,用户会直接卸载。SQLite 默认还可能以可写方式打开,顺手生成 -journal / -wal。
  3. 摘要不能无脑长
    交接摘要最终要塞进下一个 Agent 的上下文窗口。硬编码截断要么砍错、要么占满 token;静默省略会让下一个 Agent 以为「摘要里没有 = 会话里没有」。
  4. 从对话里「猜」决策不可靠
    启发式一放宽就抓进整段叙述,一收紧就啥也抓不到。猜错的「决策」比没有更危险。
  5. 发布与接入比功能本身更碎
    npm 包名被占、Cursor 没有用户级全局 MCP 文件、IDE 注入标签把用户原话当成系统注入丢掉……这些都是上线后才暴露的。

一句话:缺的不是 Memory,是「跨 Agent 的 Session Handoff」——能按仓库对齐、能选会话、能带图、能把「决策/踩坑」置顶交给下一个 Agent。


二、我怎么解决的:模型、提示词、流程

2.1 用了什么模型 / Agent

这个工具本身就是用多 Agent 协作做出来的,也正好成了第一批「狗粮」:

阶段主要用谁干什么
需求澄清 / 方案拆解Codex、Claude Code把痛点拆成「会话 vs 任务」两层,定包形态
主开发Claude Code(对话里用过 deepseek-v4-flash 等)adapters、CLI、MCP、只读守护
换模型续写同一件事Codex ↔ Claude用「任务」把多段会话绑在一起,避免失忆
文档 / 规则 / 体验打磨Cursor + ClaudeREADME、规则块、首屏信息架构

包名最终是 cross-agent-sync(npm 上 agent-session-sync 已被占用);运行时配置目录、MCP server 名仍叫 agent-session-sync,避免改名冲掉已有安装。

  • codex + deepseek image.png

2.2 提示词怎么设计

第一版需求提示词大致长这样(已压缩,完整版在仓库 提示词.md):

我同时用 Claude / Codex / Cursor / OpenCode 等。同一个项目里,需求 1~3 用 Claude,4~5 想换 Codex。历史、提示词、处理记录过不去,还得在新 Agent 里复述。
希望:换 Agent 或新开会话时,能读取别的 Agent 相关历史;最好有 MCP / 规则,新对话能「选一个会话同步过来」。
另一个场景:Codex 问完问题 A,切到 Claude 还想问同一题——不要复制粘贴,图片也不要重贴。
做成独立 npm 包,可自检索 Agent,也可让用户配置;别默认污染业务仓库。

后续反馈里又钉死了几条产品约束:

  • 包名、技术栈、说明文档要明确
  • IDE 不局限于 Cursor(Kiro / Trae 等同类也要能接)
  • 项目级注入可以,但必须显式、可回滚
  • 参考之前做过的独立工具包形态(类似 swagger-ts-gen)

提示词设计上刻意做了几件事:

  1. 先写「用户故事」再写「技术方案」
    避免 Agent 一上来就堆 Memory / RAG 概念,偏离「选会话 → 搬上下文」。
  2. 把约束写成否定句
    「不要直接在业务项目里开发」「不要默认写仓库」「不要假装支持读不到的 Agent」。否定句比愿望句更不容易被忽略。
  3. 用「任务」绑定多段会话
    开发过程中真实发生了「Claude 做一段 → Codex 接着做」——于是产品里把「会话」和「任务」拆开:会话是一次对话,任务是一件事。
  4. 规则块写成纪律,而不是功能介绍
    装 MCP 后,Agent 要遵守:「干活过程中随手 session_remember,别攒到最后」「默认给摘要,全文要显式要」「收尾落 session_note」。

2.3 落地流程(可复用)

痛点口述(提示词.md)
    → 方案对齐(会话 vs 任务 / 只读 / 零依赖)
    → MVP:list + show + last + brief + MCP
    → 自用狗粮(用任务跨 Agent 续写本仓库)
    → 发布 npm
    → 真实机器上修适配器边缘 case
    → 把「只读 / 预算 / 决策通道」升级成硬能力

核心设计取舍(到现在也没改):

  • 只读聚合:源会话数据受保护区,写路径直接拒绝;读走 O_RDONLY;sqlite3 带 -readonly
  • 零运行时依赖:只用 Node 内置;SQLite 走 node:sqlite 或系统 sqlite3
  • 默认不碰仓库:项目注入必须 ass init / ass mcp --write,带 dry-run / undo
  • 不假装支持:读不到就明说原因,不静默空列表

三、发布之后:怎么一次次调优

0.1.0 能用,但真机一跑就露出「文档写了、现场不对」的缝。下面这些是我觉得值得写进文章的迭代,而不是 changelog 流水账。

3.1 包名与版本漂移

  • npm 包名被占 → 发布名改成 cross-agent-sync,运行时名字不动
  • ass --version 曾硬编码成旧版本:CLI 和 MCP 各写一份字符串,改 package.json 忘了改它们
    → 改成运行时读 package.json,两处常量消失

3.2 「静默」是体验杀手

连续修了几类静默行为:

  • 列表默认 15 条,超了不说 → 用户以为「就这么多」
    → 触顶时明确提示,并告诉你 ass --limit / --all
  • --limit 后面跟了另一个开关,被当成 true,Number(true) === 1 → 只显示 1 条
    → 非法用法直接报错退出
  • ass --all 提示写了但 status 不认 → 提示在教不存在的功能
    → 行为与文案对齐

原则:宁可报错,也不要猜;宁可多一行提示,也不要静默截断。

3.3 适配器边缘 case(真实数据教出来的)

  • Cursor 部分会话没有 workspace.json → 按仓库过滤永远匹配不上 → 回退 workspaceMetadata.displayPath
  • OpenCode 列表 preview 硬编码空串 → 62 个会话预览全空 → 与 read() 共用提取逻辑
  • IDE 前缀 <ide_selection> / <ide_opened_file> 和用户原话拼在同一块 → 整段被当注入丢掉 → ass last 搬到过期提问、轮次少算

这些几乎都不是设计阶段能穷举的,只能靠「本机真实会话」当测试集。

3.4 从「口头只读」到「可自证只读」

0.2.0 把只读做成运行时强制:

  • 写路径命中受保护区即拒绝(逃生门环境变量显式打开)
  • 读过的父目录自动进保护区(覆盖 adapter 声明之外的附件路径)
  • ass doctor 报告「本次运行未改动任何源数据」
  • 活跃会话还在被追加写入时,标成「无法判定」,避免信号恒红等于没信号

3.5 交接预算:摘要要「可声明地缺席」

ass brief --budget tiny|small|standard|large|full:

  • 每节有优先级,超预算按节降档(完整 → 精简 → 仅要点),再不行整节省略
  • 主动记录的决策/踩坑置顶且最后才砍(宁可少两条,不整类丢掉)
  • 被裁掉的部分写出来,并给出看全文的命令
    刻意加一句:省略 ≠ 不重要,免得下一个 Agent 脑补成「没有」

3.6 决策通道:别再靠猜

extractDecisions 默认关掉,改成 ass brief --guess。
正式路径是 ass context / session_remember:决策、死胡同、约束、待办由 Agent(或人)主动写入,换 Agent 时置顶出现。

这也反向要求规则块写清楚:记决策是纪律,不是可选项。

3.7 首屏信息架构

ass 首屏重排,只回答四个问题:有几个会话 / 有哪些任务 / 怎么合并 / 怎么恢复。
以前把整段交接摘要顶在上面,真正要看的东西全被埋掉。稳定引用改成 claude:<完整id>,同时服务 claude --resume 和 ass brief。


四、怎么用这个工具,以及后续优化

4.1 安装

npm install -g cross-agent-sync   <span># 得到 ass 命令</span>
<span># 或</span>
npx cross-agent-sync list

ass install    <span># 尽量自动接入本机各 Agent 的 MCP + 规则</span>
ass doctor     <span># 自检:能不能读、有没有动过源数据</span>
ass agents     <span># 探测到了哪些 Agent、数据在哪</span>

要求 Node >= 18.17。读 Cursor / OpenCode 的 SQLite 时:Node >= 22.5(或系统有 sqlite3)。

4.2 三条最常用路径

① 看看这个仓库大家都聊过什么

<span>cd</span> /path/to/your-project
ass
ass show <span>#2</span>

② 把上一个 Agent 的最后一问(含图)搬过来

ass list --agent codex --<span>limit</span> 5
ass last <span>#1 --rounds 2</span>

图片会解码落盘,路径打在输出里,新 Agent 直接读文件即可。

③ 换 Agent 继续同一件事(推荐)

<span># 开发过程中随手记</span>
ass context --decision  <span>"接口错误统一走 msg 字段"</span>
ass context --dead-end  <span>"在 axios 拦截器里重试会导致 refresh 死循环"</span>
ass context --constraint <span>"不要改旧版 SSO 签名"</span>
ass context --todo <span>"需求 5:表格换虚拟滚动"</span>

<span># 绑成任务</span>
ass task add 登录重构 claude:4134ec80-....

<span># 换到 Codex / Cursor 后</span>
ass resume 登录重构
<span># 或对已接 MCP 的 Agent 说:继续 登录重构</span>

接了 MCP 的 Agent(Claude Code / Codex / OpenCode 等)可以说自然语言,让它自己调 session_handoff / session_task_resume。
没接 MCP 的,跑 ass brief <引用> 把输出贴过去即可。

4.3 MCP 工具速查

你想做的事工具
当前仓库状态 / 进行中任务`session_status`
列会话让你挑`session_list`
交接摘要`session_handoff`
搬最后一问(含图)`session_last`
记决策 / 踩坑`session_remember`
收尾存档`session_note`
绑任务 / 恢复任务`session_task` / `session_task_resume`

4.4 后续还想优化什么

按优先级大致是这些(不保证排期,但方向明确):

  1. 更多 Agent 适配
    Trae / Windsurf / VS Code Copilot Chat 等:detect 能找到库,但解析还不完整;Gemini CLI 缺仓库路径,只能 --all 看。
  2. Cursor 全局 MCP 一键写入
    现在部分场景要靠 ass mcp --write 写工程级配置;用户级路径各家不一致,还要继续磨。
  3. 交接质量
    预算裁剪可以更「按目标 Agent 剩余上下文」自适应;任务视图可以挂上关键 diff / commit 引用。
  4. 更少的手工选择
    在「明显只有一个进行中任务」时,新会话自动提示 resume;但仍坚持:别替用户静默选错会话。
  5. 可观测性
    doctor / detect 报告再可读一点,方便别人在 issue 里贴自检结果。

4.5 使用测试

  • 使用 ass 命令,输出会话列表,默认展示15条,同时有使用提示。 image.png
  • 直接在另一个 agent 使用会话id ,让其读取另一个会话的概要,继续工作,实测在 claude、codex、cursro 中 都能正常工作。

image.png

image.png


写在最后

做这个工具之前,我以为痛点是「模型不够聪明」。做完之后更确定:很多时候不是模型不行,是上下文被各家私有格式锁住了。

如果你也在多 Agent 之间来回切,欢迎试一下:

npm i -g cross-agent-sync
<span>cd</span> your-repo && ass

Issue / PR 都欢迎:github.com/lhbDesign/c…