OpenResearch:跨AI编程工具的会话与知识统一管理实践
1. 为什么我要做 OpenResearch 这个项目最早动这个念头是因为我自己在同时用 Claude Code、Codex、OpenCode、Cursor 这四套工具做开发。每个工具都有自己的会话历史、上下文管理、技能Skills体系但彼此之间完全不互通。我在 Claude Code 里调教好的一套提示词和技能配置换到 Codex 就得重新来一遍在 Cursor 里积累的项目上下文到了 OpenCode 又得从头喂。这种重复劳动每天要吃掉我将近一个小时而且更麻烦的是不同工具对同一个项目的理解会逐渐分叉最后我自己都搞不清楚哪个版本才是最新的。OpenResearch 就是在这个背景下长出来的。它的核心定位是一个跨工具的 AI 编程会话与知识管理层把 Claude Code、Codex、OpenCode、Cursor 这几个主流 AI 编程客户端的会话记录、技能定义、项目上下文统一收拢到一个本地优先的存储层里再通过一套标准化的接口对外暴露。说白了就是让你在任何一个工具里积累的东西其他工具也能用上。这个项目适合几类人一是像我这样同时用多个 AI 编程工具的重度用户二是团队里需要统一管理提示词和技能资产的 Tech Lead三是对 AI 编程工具底层机制感兴趣、想自己动手做二次开发二开的工程师。如果你只是偶尔用用 Cursor 写点小脚本那这个项目对你来说可能偏重了但如果你想搞清楚这些工具到底是怎么组织上下文的读下去会有收获。我先把结论放在前面OpenResearch 不是一个替代品它不替代 Claude Code也不替代 Codex它是一层“胶水”把散落在各个工具里的资产粘起来。理解这一点后面所有的设计决策就都顺了。2. 整体架构设计与选型思路2.1 为什么选择本地优先而不是云端同步做架构设计时第一个要拍板的就是数据存哪儿。我考虑过三种方案纯云端、纯本地、本地优先加可选同步。最后选了本地优先理由有三条。第一是延迟。AI 编程工具的会话数据量比想象中大得多一次完整的 Claude Code 会话包含工具调用、文件读写、多轮对话轻松超过几万 token。如果每次读写都走网络光是序列化和反序列化就能让工具卡顿。本地 SQLite 读写是微秒级的这个差距在交互式编程场景里非常明显。第二是隐私边界。会话记录里往往包含项目路径、代码片段、甚至一些内部接口定义。这些东西放在本地用户心里踏实。我在设计时定了一条硬规矩任何会话内容默认不出本机除非用户显式开启同步。第三是离线可用。我经常在没网的环境下写代码本地优先意味着核心功能完全不受网络影响。具体实现上我用 SQLite 做存储引擎配合 WAL 模式Write-Ahead Logging来支持并发读写。这里有个细节值得说SQLite 默认的 journal 模式在并发写入时会锁库而 AI 编程工具经常是多个进程同时写会话比如 Claude Code 主进程和它的子 agentWAL 模式能让读操作不被写操作阻塞实测下来并发场景下吞吐提升了大概 3 到 5 倍。-- 开启 WAL 模式的核心配置 PRAGMA journal_mode WAL; PRAGMA synchronous NORMAL; PRAGMA busy_timeout 5000;注意synchronous NORMAL在 WAL 模式下是安全的它保证数据不会因为进程崩溃而丢失只在操作系统崩溃的极端情况下可能丢最后几条事务。对于会话记录这种场景这个取舍是合理的。如果你对数据完整性要求极高改成FULL但写入性能会下降约 40%。2.2 统一数据模型把四种工具的会话抽象成同一套结构这是整个项目里最费脑子的部分。Claude Code、Codex、OpenCode、Cursor 的会话格式各不相同有的用 JSONL有的用 JSON字段命名也五花八门。我的做法是先抽象出一个通用会话模型再为每个工具写适配器。通用模型的核心字段包括会话 ID、工具来源、创建时间、更新时间、消息列表、工具调用记录、关联的项目路径、使用的技能列表。消息列表里每条消息又包含角色user/assistant/system/tool、内容、时间戳、token 计数。适配器的职责就是把各工具的原生格式翻译成这个通用模型。以 Claude Code 为例它的会话文件通常是 JSONL 格式每行一条消息字段里有type、message、timestamp等。适配器要做的是逐行解析把type映射到通用模型的角色字段把嵌套的message.content拍平成统一的内容结构。这里踩过一个坑Claude Code 的会话文件在写入过程中可能被截断比如进程被 kill导致最后一行是半截 JSON。我的处理方式是解析时对每一行做 try-catch遇到解析失败的行就跳过并记录警告而不是让整个会话加载失败。这个容错设计后来救了我好几次。2.3 技能Skills体系的统一管理Skills 是这几个工具里差异最大的部分。Claude Code 的 Skills 是一组 Markdown 文件加配置Codex 的技能更偏向于提示词模板OpenCode 有自己的 skill 目录结构Cursor 则主要通过 rules 文件来定义行为。我的统一方案是定义一个技能描述文件skill manifest用 YAML 格式描述技能的元信息名称、描述、触发条件、依赖、适用工具。技能的实际内容提示词、脚本放在独立的目录里manifest 只做索引。# skill manifest 示例 name: code-review description: 对指定文件进行代码审查 triggers: - review this file - 检查代码 tools: - claude-code - codex - opencode entry: prompts/code-review.md dependencies: - git这样做的好处是同一个技能可以同时被多个工具使用。安装到 Claude Code 时适配器把 manifest 转换成 Claude Code 认识的 Skills 格式安装到 Codex 时转换成 Codex 的提示词模板格式。用户只需要维护一份技能定义。2.4 接口层设计CLI 优先API 兜底对外接口我做了两层一层是 CLI一层是本地 HTTP API。CLI 是给日常用的比如openresearch sync、openresearch skills list、openresearch session export。HTTP API 是给二次开发用的跑在本地某个端口上其他工具可以通过它来读写 OpenResearch 的数据。为什么 CLI 优先因为 AI 编程工具本身就是命令行重度用户CLI 的集成成本最低。你可以在 Claude Code 里直接调用openresearch命令来查询历史会话不需要额外的网络配置。HTTP API 则是为了那些需要程序化访问的场景比如你自己写一个 VS Code 插件来展示跨工具的会话列表。3. 核心模块的实操细节3.1 会话采集如何做到不侵入、不丢数据会话采集是 OpenResearch 的地基。我的设计原则是只读不写——OpenResearch 绝不修改各工具的原生会话文件只读取和索引。这样即使 OpenResearch 出问题也不会影响你原本的工具使用。采集的触发方式有两种定时轮询和文件系统监听。定时轮询简单可靠每隔一段时间扫描各工具的会话目录发现新文件或修改过的文件就增量索引。文件系统监听用chokidar或fs.watch响应更快但在某些系统上会有事件丢失的问题。我最后用的是混合方案文件系统监听负责实时性定时轮询默认 5 分钟一次负责兜底。这样既保证了新会话能快速出现在索引里又不会因为监听器漏事件而丢数据。各工具的会话目录位置不太一样我整理了一张对照表工具默认会话目录文件格式Claude Code~/.claude/projects/JSONLCodex~/.codex/sessions/JSONOpenCode~/.opencode/sessions/JSONCursor工作区.cursor/下JSON提示这些路径可能随工具版本更新而变化。OpenResearch 的适配器里做了路径探测逻辑会先检查默认路径找不到再尝试几个备选路径。如果你发现某个工具的会话没被采集到先用openresearch doctor命令诊断一下路径配置。增量索引的关键是文件指纹。我用文件大小加修改时间mtime作为指纹指纹没变就跳过解析。这个优化让重复扫描的成本降到几乎为零。实测下来索引 1000 个会话文件的首次全量扫描大约需要 8 秒之后的增量扫描通常在 200 毫秒以内。3.2 上下文重建让跨工具的项目理解保持一致采集只是第一步真正有价值的是上下文重建。所谓上下文重建就是把散落在多个工具、多个会话里的信息按照项目维度重新组织起来形成一个统一的“项目知识视图”。举个例子你在 Claude Code 里让 AI 重构了auth.ts在 Cursor 里又让 AI 给auth.ts加了单元测试。这两个会话在各自的工具里是孤立的但 OpenResearch 会把它们关联到同一个项目路径下按时间排序形成一个完整的变更历史。当你下次在 OpenCode 里打开这个项目时OpenResearch 可以把这段历史作为上下文注入让 OpenCode 的 AI 知道这个文件之前经历过什么。实现上我用项目路径的规范化哈希作为关联键。这里有个细节不同工具记录的项目路径格式可能不同有的是绝对路径有的是相对路径有的带符号链接。我写了一个路径规范化函数统一转成绝对路径、解析符号链接、去掉末尾斜杠再做哈希。上下文重建的另一个难点是去重。同一个文件可能在多个会话里被反复讨论如果全部注入上下文token 会爆炸。我的策略是按时间倒序取最近的 N 个相关会话默认 N5并且对内容做摘要压缩。摘要用的是本地的小模型不依赖外部 API。3.3 技能安装与同步一次定义多处使用技能同步是 OpenResearch 最实用的功能之一。流程是这样的你在 OpenResearch 的技能目录里定义好一个技能然后运行openresearch skills install skill-name --target claude-code适配器就会把技能转换成 Claude Code 认识的格式安装到对应的目录。这里的关键是格式转换的准确性。以 Claude Code 的 Skills 为例它要求技能放在特定目录下有一个SKILL.md文件描述技能可能还有辅助脚本。适配器要做的就是读取 OpenResearch 的 skill manifest生成SKILL.md把提示词内容写进去把依赖的脚本复制到对应位置。# 安装技能到 Claude Code openresearch skills install code-review --target claude-code # 安装到多个工具 openresearch skills install code-review --target claude-code,codex,opencode # 查看已安装的技能 openresearch skills list --installed注意技能安装是幂等的。重复安装同一个技能不会产生重复文件适配器会先检查目标目录存在同名技能就先卸载再安装。这个设计避免了手动清理的麻烦但也意味着如果你手动修改过目标目录里的技能文件重新安装会覆盖你的修改。建议所有技能修改都在 OpenResearch 的源目录里做。3.4 会话导出与迁移换工具不丢历史会话导出解决的是“换工具焦虑”。很多人不敢换工具就是因为历史会话带不走。OpenResearch 支持把任意工具的会话导出成通用格式再导入到另一个工具。导出的格式我选了 Markdown 加 JSON 元数据的组合。Markdown 部分是人类可读的对话记录JSON 部分包含结构化的元信息时间戳、token 数、工具调用等。这样导出的文件既能直接阅读也能被程序解析。导入时适配器会把通用格式转换成目标工具的原生格式。这里有个现实问题不同工具对会话的字段要求不同有些字段在源工具里有、在目标工具里没有对应概念。我的处理是能映射的映射不能映射的放进扩展字段保证信息不丢失同时不破坏目标工具的格式要求。实测下来Claude Code 到 Codex 的会话迁移成功率在 95% 以上主要丢失的是工具特有的元数据比如 Claude Code 的某些内部标记。对于日常使用来说这个损失可以接受。4. 常见问题与排查实录4.1 会话采集不到怎么办这是被问得最多的问题。排查顺序我总结成一张表现象可能原因排查方法某个工具的会话完全没出现路径配置错误运行openresearch doctor检查路径新会话延迟出现监听器未生效检查轮询间隔手动运行openresearch sync部分会话缺失文件解析失败查看日志里的解析警告会话内容不完整文件被截断检查源文件最后一行是否完整最常见的原因是路径配置。不同版本的工具可能改了会话目录位置OpenResearch 的默认路径是基于某个版本写的如果你的工具版本不同就需要手动配置。配置文件在~/.openresearch/config.yaml里面可以覆盖每个工具的会话目录。另一个隐蔽的坑是权限问题。如果 OpenResearch 没有读取某个目录的权限采集会静默失败。我在日志里加了权限检查的警告但早期版本确实因为这个坑排查了很久。4.2 技能安装后工具不识别技能安装成功但工具不识别通常是两个原因格式不对或者位置不对。格式问题最常见的是换行符和编码。Claude Code 的SKILL.md对格式比较敏感如果用了 Windows 的 CRLF 换行或者文件编码不是 UTF-8可能导致解析失败。适配器里我统一做了换行符转换和编码检查但如果你手动改过文件就可能引入问题。位置问题则是目标目录的层级。不同工具对技能目录的层级要求不同有的要求技能直接放在一级目录下有的允许多级嵌套。适配器会按照各工具的要求生成正确的层级但如果你手动移动过文件就可能破坏这个结构。提示安装技能后先用工具自带的技能列表命令确认技能被识别了再去实际使用。比如 Claude Code 里可以用/skills查看已加载的技能。这一步能帮你快速定位是安装问题还是使用问题。4.3 上下文注入导致 token 超限上下文重建注入的会话历史如果太多会挤占正常的对话 token。我遇到过好几次因为注入历史太多导致 AI 回复质量下降的情况。解决办法是分层注入。把上下文分成三层核心层当前项目的最近变更必注入、参考层相关但非直接的文件历史按需注入、归档层更早的历史只在明确查询时注入。默认只注入核心层token 占用控制在总预算的 20% 以内。具体阈值可以在配置里调。我的经验值是如果用的是 200K 上下文的模型核心层控制在 30K token 以内如果是 128K 的模型控制在 20K 以内。超过这个比例AI 对当前对话的注意力就会被稀释。4.4 多工具同时写入的冲突虽然 OpenResearch 本身只读但多个工具可能同时修改同一个项目文件导致会话记录里的文件状态不一致。这个问题在 OpenResearch 层面无法完全解决但可以做冲突标记。我的做法是在上下文重建时如果发现同一个文件在相近时间点被多个工具修改过就在注入的上下文里加一个冲突警告提示 AI 注意文件可能有多个版本。这个警告不解决冲突但能让 AI 在生成代码时更谨慎避免基于过时信息做决策。5. 一些实操心得和后续扩展方向用 OpenResearch 大半年下来有几个心得值得分享。第一不要追求全量同步。我一开始想把所有历史会话都索引进来结果数据库膨胀到几个 G查询变慢上下文注入也变得臃肿。后来改成只索引最近 90 天的会话更早的归档到冷存储只在明确查询时才加载。这个调整让日常使用流畅了很多。第二技能要小而精。我早期定义了一些大而全的技能一个技能里塞了十几个功能结果 AI 经常搞不清楚该用哪个。后来拆成小技能每个技能只做一件事触发条件写得很具体识别准确率明显提升。第三定期清理索引。会话文件会随着工具使用不断增长索引也需要定期重建。我设置了一个每周自动重建索引的任务避免索引和实际文件长期不一致。后续我打算扩展的方向有两个一是支持更多的 AI 编程工具目前适配器架构是插件式的加新工具只需要写一个适配器二是做一个跨工具的会话搜索界面现在只能用 CLI 搜索体验还不够好。最后分享一个小技巧如果你在多个工具间切换频繁可以给每个工具配置一个 OpenResearch 的快捷命令比如在 Claude Code 里配置一个/or-recent命令来快速查看最近的其他工具会话。这个配置在 OpenResearch 的文档里有模板复制粘贴就能用。实测下来这个快捷命令让我跨工具查历史的频率提高了不少因为成本太低了。