Residue开源项目:为AI编程对话建立可追溯的代码记忆库
1. 项目概述:为AI编程对话建立可追溯的“记忆”
如果你和我一样,日常开发中深度依赖Claude Code、Cursor这类AI编程助手,那你一定遇到过这个场景:几天后回看某段代码,完全想不起来当时为什么要这么改,AI助手给出了什么建议,以及我们之间经过了怎样的讨论才得出最终方案。代码提交记录(Git Commit)只留下了“最终结果”,而丢失了最宝贵的“决策过程”。Residue这个开源项目,就是为了解决这个痛点而生的。
简单来说,Residue是一个自托管的CLI工具+后端服务。它的核心功能是自动捕获你与AI编程助手(如Claude Code、Cursor、Windsurf等)的完整对话,并将这些对话与你后续的Git提交(Commit)精确关联起来。每次你执行git push时,Residue会将这些“塑造了代码变更的对话”上传到你自己部署的后端,形成一个可搜索、可回溯的“代码创作记忆库”。这不仅仅是简单的日志记录,它建立了一套从“想法讨论”到“代码落地”的完整溯源链路。
想象一下,当你需要审查代码、复盘决策、向团队解释某段复杂逻辑的由来,或者仅仅是想找回当时那个绝妙的优化思路时,你不再需要去翻找零散的聊天记录或靠模糊的记忆。你只需要打开Residue提供的Web界面,定位到那个提交,就能看到生成这段代码的完整对话上下文。这对于个人深度工作流梳理、团队知识沉淀乃至项目交接,都有着巨大的价值。接下来,我将带你从零开始,完整部署、配置并深度使用Residue,分享我在实践中积累的配置技巧和避坑经验。
2. 核心架构与工作原理深度解析
Residue的设计非常巧妙,它没有采用侵入式的方式去修改你的AI助手或Git本身,而是通过“事件监听”和“状态关联”的机制,优雅地将两个独立的事件流编织在一起。理解这个机制,对于后续的故障排查和高级定制至关重要。
2.1 双事件源与本地状态机
Residue的运转依赖于两个独立的事件源:AI助手适配器(Agent Adapters)和Git钩子(Git Hooks)。它自身则维护着一个核心的本地状态(Local State),用于在两者之间建立临时关联。
AI助手事件流:当你启动一个支持Residue的AI助手(例如在Claude Code中开始一个新对话),该助手的适配器会调用
residue session start命令,Residue CLI会在本地创建一个新的“会话(Session)”记录,并为其生成一个唯一ID。这个会话此时处于“进行中(pending)”状态。当你结束对话时,适配器调用residue session end,CLI会标记该会话结束,并将完整的对话内容(通常是一个JSON文件)保存到本地缓存目录(如~/.residue/cache或项目内的.residue目录)。Git事件流:这是另一个并行的时间线。当你通过
git commit提交代码时,由residue init安装的post-commitGit钩子会被触发。这个钩子执行residue capture命令。该命令的核心逻辑是:获取当前最新提交的SHA哈希值,然后用这个SHA去“标记”所有当前处于“进行中”或刚结束但尚未被标记的本地会话。这意味着,一次提交可能会关联到多个并行的AI对话会话。关联与上传:最后,当你执行
git push时,pre-push钩子被触发,执行residue sync。这个命令会检查本地状态,找到所有已被标记(即关联了提交SHA)的会话,然后将这些会话的元数据(如会话ID、关联的提交SHA、时间戳、使用的AI助手类型)和实际的对话内容文件,一并上传到你自部署的Residue后端服务(Worker)。上传成功后,本地对应的会话记录会被清理,确保状态不会无限膨胀。
关键理解:一个AI对话会话(Session)可以跨越多次代码提交(Commit)。Residue的存储模型是,完整的会话内容只存储一份(在R2中),而多个提交可以同时引用(Reference)同一个会话。这完美匹配了现实开发中“我们围绕一个功能点进行多轮讨论,并分多次提交代码”的场景。
2.2 技术栈与组件职责
项目采用Monorepo结构,使用pnpm工作区和bun运行时,主要包含三个核心包:
@residue/cli(CLI工具):这是与用户直接交互的部分。它负责会话的生命周期管理(start/end)、与Git钩子交互(capture/sync)、本地状态维护、以及向后端发起API请求。它用Bun编写,确保了启动和执行的快速。@residue/worker(Cloudflare Worker后端):这是自托管的核心。它基于Hono框架构建,提供两套接口:- JSON API:供CLI调用,用于认证、获取上传凭证、提交元数据、查询数据等。所有API调用都需要Bearer Token认证(即你在
residue login时提供的token)。 - 服务器渲染的Web UI:提供一个直观的界面,用于按组织、仓库、提交来浏览和搜索关联的AI对话。UI路由在
/app路径下,采用基于Cookie的会话认证。
- JSON API:供CLI调用,用于认证、获取上传凭证、提交元数据、查询数据等。所有API调用都需要Bearer Token认证(即你在
installer(部署安装器):一个独立的Web应用(install.residue.dev),用于自动化整个后端Worker的部署流程。它通过引导式界面,帮你完成创建Cloudflare R2存储桶、D1数据库、配置环境变量和密钥等繁琐步骤,极大降低了入门门槛。
2.3 关键设计决策背后的考量
Residue的几个核心设计选择,体现了对性能、成本和实用性的深思熟虑:
原始数据存储(No Data Normalization):Residue不尝试将不同AI助手(Claude、GPT、Claude等)的对话格式统一或“标准化”后再存储。它直接将接收到的原始JSON数据原封不动地存入Cloudflare R2对象存储。这样做的好处是避免了格式转换可能带来的信息丢失,并且后端Worker无需理解各种复杂的AI输出格式,职责单一。Web UI在渲染时,才通过特定的“映射器(Mapper)”将不同格式的数据转换成统一的视图模型。这种“存储与呈现分离”的设计非常灵活,新增AI助手支持时,只需在前端添加一个对应的映射器即可。
直传R2与轻量元数据(Direct R2 Upload):对话内容(尤其是包含长代码块的对话)可能很大,超过Cloudflare Worker的请求体大小限制。Residue采用了经典的“预签名URL”方案:CLI先向Worker请求一个有时效性的、用于直接上传到R2的URL,然后CLI用这个URL将对话数据直接PUT到R2。Worker只处理非常轻量的元数据(会话ID、关联的提交SHA等)的存储(到D1数据库)。这保证了Worker的快速响应和高可靠性,也将存储成本(R2)和计算成本(Worker)解耦。
为搜索生成轻量文本文件(Search via Lightweight Text Files):原始的JSON对话文件不适合全文搜索(太大、结构复杂)。Residue在CLI执行
sync上传时,会做一项额外工作:解析原始对话JSON,提取出所有“人类消息”、“AI助手回复的文本部分”以及“工具调用(Tool Call)的摘要”,合并生成一个纯文本的.txt文件。这个文件会被上传到R2中一个独立的search/路径下。未来如果实现搜索功能,后端只需要对这个小巧的文本文件建立索引即可,效率极高。永不阻塞Git(Never Block Git):这是最重要的用户体验设计之一。Residue安装的所有Git钩子(
post-commit,pre-push),其内部逻辑都被包裹在try...catch中。即使Residue自身的capture或sync过程因为网络问题、配置错误等原因抛出异常,钩子脚本也会捕获这个异常,记录错误日志,但最终返回退出码0。这意味着,Residue的失败绝不会导致你的git commit或git push命令失败。数据收集是“尽力而为(best-effort)”的,绝不会干扰你的核心开发流程。这个设计让我在初期配置出错时依然能顺利提交代码,之后再去排查Residue的问题,体验非常友好。
3. 从零开始:完整部署与配置实战
理论清晰后,我们进入实战环节。我将以最常用的“安装器部署+Claude Code适配”为例,带你走通全流程,并穿插我踩过坑后总结的细节要点。
3.1 前置准备与环境检查
在开始之前,你需要确保准备好以下两样东西:
- Bun运行时:Residue的CLI工具依赖Bun。如果你的系统尚未安装,请前往 bun.sh 根据指引安装。安装后,在终端运行
bun --version确认安装成功。 - Cloudflare账户:你需要一个Cloudflare账户来部署Worker、创建R2存储桶和D1数据库。如果你没有,去 Cloudflare官网 注册一个免费账户即可。Cloudflare的Workers免费套餐提供了充足的额度供个人或小团队使用Residue。
个人经验:虽然项目说需要Cloudflare账户,但实际操作中,你只需要能登录Cloudflare Dashboard即可。安装器或Wrangler CLI会帮你处理认证。确保你使用的邮箱有权限在目标Cloudflare账户中创建Worker、R2和D1资源。
3.2 一站式部署后端Worker(推荐安装器)
对于绝大多数用户,我强烈推荐使用官方安装器,它能避免90%的手动配置错误。
- 访问安装器:打开浏览器,访问 https://install.residue.dev 。
- 获取Cloudflare API令牌:安装器首先会引导你创建一个Cloudflare API令牌。点击提示中的链接,会跳转到Cloudflare Dashboard的创建令牌页面。
- 在“创建令牌”页面,建议直接使用“编辑Cloudflare Workers”这个预设模板,它已经包含了管理Worker、R2、D1所需的权限。
- 令牌的权限范围(Account Resources)选择“所有账户”或你指定的账户均可。
- 点击“继续以摘要”,然后为令牌起个名字,例如“Residue-Deployer”。
- 关键一步:创建成功后,务必立即复制并妥善保存这个令牌字符串。它只显示一次!这个令牌将赋予安装器在你的Cloudflare账户中创建资源的权限。
- 执行安装流程:回到安装器页面,粘贴刚刚复制的API令牌。接下来的步骤完全是图形化引导:
- 创建D1数据库:安装器会帮你创建一个名为
residue-db的D1数据库,用于存储会话和提交的元数据。 - 创建R2存储桶:安装器会创建一个R2存储桶(名字类似
residue-bucket-xxxx),用于存储对话的原始JSON和搜索文本文件。同时,它会自动生成一对具有读写权限的S3兼容API密钥(Access Key ID和Secret Access Key)。 - 设置密钥与环境变量:安装器会将上述生成的
R2_SECRET_ACCESS_KEY以及你需要自定义的AUTH_TOKEN(CLI认证令牌)、ADMIN_PASSWORD(Web UI管理员密码)自动设置为Worker的加密密钥(Secret)。其他如R2_ACCESS_KEY_ID、R2_BUCKET_NAME等则作为普通环境变量(Var)注入。 - 部署Worker:最后,安装器会打包并部署Worker代码到你的Cloudflare账户。部署成功后,你会看到你的Worker域名(例如
https://residue.your-username.workers.dev)和刚刚设置的AUTH_TOKEN。
- 创建D1数据库:安装器会帮你创建一个名为
注意事项:请务必记录下安装器最后展示的Worker URL和AUTH_TOKEN。前者是CLI和浏览器访问的地址,后者是CLI认证的凭证,相当于你的后端服务的“密码”。如果关闭页面后忘记,你需要通过Cloudflare Dashboard的Workers页面查看域名,并通过Wrangler CLI重新设置
AUTH_TOKEN密钥。
3.3 手动部署方案(备选与理解)
如果你希望更深入地控制部署过程,或者安装器遇到问题,可以按照以下步骤手动部署。这能帮你更好地理解各个组件的关系。
# 1. 克隆代码库并安装依赖 git clone https://github.com/butttons/residue.git cd residue pnpm install # 2. 进入Worker目录,登录Cloudflare cd packages/worker npx wrangler login # 按提示在浏览器中完成授权 # 3. 创建D1数据库 npx wrangler d1 create residue-db # 成功后会输出一个数据库ID,形如 `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` # 4. 配置 wrangler.jsonc # 用你喜欢的编辑器打开 `packages/worker/wrangler.jsonc` # 找到并更新以下部分: # - `d1_databases[0].database_id`: 替换为上一步得到的数据库ID。 # - `r2_buckets[0].bucket_name`: 替换为你计划创建的R2存储桶名称,例如 `my-residue-data`。 # - 在 `vars` 块中,设置: # R2_ACCESS_KEY_ID: "你的R2 Access Key ID", # R2_ACCOUNT_ID: "你的Cloudflare账户ID", # R2_BUCKET_NAME: "你的R2存储桶名称", # ADMIN_USERNAME: "admin" (或你自定义的管理员用户名) # 5. 创建R2存储桶和API令牌(在Cloudflare Dashboard完成) # - 进入R2页面,创建存储桶,名称与上一步的 `R2_BUCKET_NAME` 一致。 # - 进入「R2 API令牌」页面,创建令牌,权限选择「读写」指定刚创建的存储桶。 # - 保存好生成的 `Access Key ID` 和 `Secret Access Key`。 # 6. 应用数据库迁移 npx wrangler d1 migrations apply residue-db --remote # 7. 设置加密密钥(Secret) echo "你的强密码" | npx wrangler secret put AUTH_TOKEN # 按照提示输入你的CLI认证令牌 npx wrangler secret put ADMIN_PASSWORD # 按照提示输入Web UI管理员密码 echo "你的R2 Secret Access Key" | npx wrangler secret put R2_SECRET_ACCESS_KEY # 8. 部署Worker npx wrangler deploy部署成功后,命令行会输出你的Worker URL。请同样记录好这个URL和你在第7步设置的AUTH_TOKEN。
3.4 安装与配置CLI工具
后端就绪后,我们在开发机器上安装CLI工具。
# 全局安装Residue CLI bun add -g @residue/cli安装完成后,首先需要让CLI知道你的后端在哪里以及如何认证。
# 进行全局登录(配置保存在 ~/.residue/config) residue login --url https://residue.your-subdomain.workers.dev --token YOUR_AUTH_TOKEN执行成功后,可以运行residue status测试一下连接,如果返回类似“Connected to worker at ...”的信息,说明配置成功。
实操心得:项目级配置:
residue login默认使用--global全局配置。但在某些场景下,你可能需要为不同的项目连接不同的Residue后端实例(例如,公司项目和个人项目分开)。这时可以使用--local参数:cd /path/to/your/project residue login --url https://company-residue.example.com --token COMPANY_TOKEN --local这会在当前项目的
.residue/config文件中保存配置,优先级高于全局配置,非常适合多环境管理。
3.5 为Git仓库启用Residue追踪
这是最后一步,也是将Residue融入你工作流的关键。进入你想要追踪AI对话的Git仓库目录。
# 1. 初始化Residue(安装Git钩子) residue init这个命令做了三件事:
- 在项目的
.git/hooks目录下创建(或修改)post-commit和pre-push钩子脚本。 - 在项目的
.gitignore文件中添加.residue/,避免本地缓存文件被意外提交。 - 在项目根目录创建
.residue目录,用于存储项目本地配置和缓存。
# 2. 配置AI助手适配器 # 根据你使用的AI助手选择对应的命令 residue setup claude-code # 适用于Claude Code # 或者 residue setup pi # 适用于Windsurf (Pi) # 或者 residue setup opencode # 适用于OpenCode以claude-code为例,residue setup claude-code会定位到Claude Code的配置文件(通常是~/.config/claude/claude_desktop_config.json或项目内的.claude/settings.json),并在其中添加一个钩子配置。这个配置告诉Claude Code,在对话开始和结束时,自动调用本地的residueCLI工具。
至此,所有配置完成!现在,当你在这个仓库中使用Claude Code进行编程对话并随后提交、推送代码时,整个对话过程就会被自动关联并上传到你的Residue后端。
4. 日常使用、高级查询与问题排查
配置好后,Residue会在后台静默工作。但掌握一些高级命令和排查方法,能让你用得更得心应手。
4.1 核心CLI命令详解
除了安装配置用的命令,Residue CLI提供了一系列用于查询和管理的命令,这些命令直接与你部署的后端Worker API交互。
residue push:手动触发上传。有时你可能在没联网的情况下做了多次提交,或者想立即同步数据而不执行git push,就可以使用这个命令。它会执行与pre-push钩子相同的同步逻辑。residue status:查看当前仓库的Residue状态。包括连接的后端地址、本地等待关联/上传的会话数量等。这是最快速的健康检查命令。residue clear:清理本地等待队列中的会话。如果某些本地会话数据损坏或你不想上传了,可以用这个命令清除。使用--id <session-id>可以清除指定会话。residue read <session-id>:将本地缓存的某个会话的原始JSON内容输出到标准输出。这个命令非常有用,可以结合jq等工具进行离线分析,或者用于调试适配器生成的数据格式。residue context:这个命令会输出一段给AI助手看的“使用文档”。如果你正在开发一个新的Residue适配器,或者想让其他AI助手(如通过自定义指令)学习如何与Residue交互,这段文档是标准参考。
查询命令:这是从你的“记忆库”中检索信息的强大工具。所有查询都针对已上传到后端的数据。
residue query sessions:列出所有会话。支持丰富的过滤器:residue query sessions --agent claude-code --repo my-org/my-repo --branch main --since 2024-01-01 --until 2024-01-31residue query commits:列出所有关联了会话的提交。同样支持按仓库、分支、作者、时间范围过滤。residue query session <id>:获取单个会话的详细信息,包括完整的元数据和关联的所有提交。residue query commit <sha>:获取单个提交的详细信息,以及与之关联的所有会话。
4.2 Web UI界面使用指南
通过浏览器访问你的Worker URL(例如https://residue.your-subdomain.workers.dev/app),即可进入Web管理界面。
- 登录:首次访问,如果部署时设置了
ADMIN_PASSWORD并处于私有模式,会跳转到登录页。使用ADMIN_USERNAME(默认为admin)和密码登录。 - 浏览结构:登录后,首页会展示所有检测到的“组织”(从Git远程仓库URL的域名或路径解析而来)。点击组织,进入仓库列表。点击仓库,你会看到一个类似Git历史的时间线视图,但每个提交旁边多了一个“对话”图标或数量提示。
- 查看对话:点击有对话关联的提交,页面会展开显示与该提交关联的所有AI会话。你可以点击任意会话,查看完整的、渲染良好的对话记录,包括代码块、思考过程等,就像你当初在AI助手界面里看到的一样。
- 设置:在
/app/settings页面,管理员可以切换实例的“公开/私有”模式,以及在/app/settings/users页面管理其他用户账号(创建、删除)。
4.3 常见问题与故障排除实录
即使设计再完善,在实际部署和使用中也可能遇到问题。以下是我遇到过的典型问题及解决方法。
问题一:git push后,Web UI里看不到对话记录。
这是最常见的问题。请按以下步骤排查:
- 检查钩子是否生效:进入你的项目目录,查看
.git/hooks/post-commit和.git/hooks/pre-push文件是否存在,并且其内容是否包含对residue capture和residue sync的调用。你可以手动在仓库目录运行residue capture和residue sync,观察输出是否有错误。 - 检查CLI连接状态:运行
residue status,确认CLI正确连接到了你的Worker,并且没有报认证错误。 - 检查AI助手适配器:确认你已运行
residue setup <agent>,并且你的AI助手(如Claude Code)的配置文件中确实添加了Residue的钩子配置。对于Claude Code,可能需要重启Claude Code应用才能使配置生效。 - 查看本地会话队列:Residue的本地状态通常存储在
~/.residue/state.json(全局)或项目.residue/state.json(本地)。你可以检查这个文件,看看是否有“pending_sessions”或“tagged_sessions”。如果这里有数据但没上传,问题可能出在同步步骤。 - 检查网络与Worker状态:确保你的开发机可以访问你部署的Worker URL。尝试在浏览器中访问
https://your-worker.workers.dev/api/ping,应该返回一个包含版本信息的JSON。如果不通,检查Cloudflare Worker的部署状态和网络设置。
问题二:上传失败,CLI日志显示R2 upload failed或SignatureDoesNotMatch。
这个错误明确指向了R2存储桶的认证问题。根本原因是Worker用于生成预签名URL的R2_SECRET_ACCESS_KEY与你R2存储桶当前的密钥不匹配。
- 触发场景:你在Cloudflare Dashboard上重新生成了R2 API令牌,获得了新的
Access Key ID和Secret Access Key,但只更新了环境变量R2_ACCESS_KEY_ID,忘记了更新加密密钥R2_SECRET_ACCESS_KEY。 - 解决方案:
更新密钥后,通常无需重新部署Worker,因为Secret是实时注入的。但如果是Access Key ID变化,则需要更新环境变量并部署。# 进入Worker项目目录 cd /path/to/residue/packages/worker # 更新密钥,将 <new-secret> 替换为新的 Secret Access Key echo "<new-secret>" | npx wrangler secret put R2_SECRET_ACCESS_KEY # 如果 Access Key ID 也变了,需要更新 wrangler.jsonc 中的 vars.R2_ACCESS_KEY_ID,然后重新部署 npx wrangler deploy
问题三:Web UI页面空白或显示异常。
- 检查D1数据库:确保在部署Worker后成功运行了数据库迁移命令(
npx wrangler d1 migrations apply residue-db --remote)。如果遗漏这一步,数据库表不存在,UI无法正常工作。 - 检查浏览器控制台:打开浏览器的开发者工具(F12),查看Console和Network标签页是否有JavaScript错误或API请求失败(403/500等)。这能快速定位是前端资源加载问题还是后端API问题。
- 验证用户登录:如果你的实例是私有模式,确保已登录。尝试清除浏览器Cookie后重新登录。
问题四:如何更新已部署的Worker到新版本?
Residue项目在持续迭代。当有新版本发布时,你可以通过安装器提供的更新页面( install.residue.dev/update )进行一键更新,它会自动拉取最新代码并重新部署。如果你是通过Wrangler手动部署的,则需要进入packages/worker目录,拉取最新代码,然后重新运行pnpm install和npx wrangler deploy。
5. 扩展思考与最佳实践
经过一段时间的深度使用,Residue已经成为了我开发流程中不可或缺的一环。它不仅是一个工具,更改变了我与AI协作的方式。以下是一些延伸的思考和实践中总结的最佳实践。
将Residue集成到团队流程中:对于团队项目,Residue的价值会倍增。建议团队统一部署一个Residue后端实例,并设置为私有模式。让所有成员都在自己的机器上配置CLI并指向这个统一的后端。这样,团队的任何一次由AI辅助的代码提交,其决策上下文都对其他成员可见。在代码审查(Code Review)时,审查者可以直接点击提交记录旁的链接,查看生成这段代码的完整对话,理解作者的思考过程,这能极大提升审查效率和质量。它相当于为团队的集体智慧建立了一个可搜索的“决策日志”。
处理敏感信息:Residue会记录你和AI助手的全部对话。这意味着,如果你在对话中不小心输入了API密钥、密码或其他敏感信息,这些信息也会被记录并上传到你自托管的R2存储桶中。虽然数据在你自己的控制下,但仍需保持警惕。我有两个建议:第一,养成良好的习惯,永远不要在AI对话中输入真正的生产环境密钥,使用占位符或环境变量。第二,定期审查你的R2存储桶,或者未来可以考虑为Residue增加一个简单的“对话内容过滤”插件,在本地上传前自动擦除特定模式(如sk-开头的字符串)的文本。
适配其他AI助手:目前Residue官方支持Claude Code、Windsurf (Pi)和OpenCode。但它的适配器架构是开放的。如果你使用其他AI编程工具(如GitHub Copilot Chat、Codeium等),理论上都可以为其编写适配器。适配器的核心就是在该工具提供钩子(Hook)或插件接口的地方,在对话开始时调用residue session start,在对话结束时调用residue session end,并将对话内容以JSON格式通过标准输入或文件传递给CLI。你可以参考packages/cli/src/adapters/目录下的现有实现,它们都是很好的范本。
数据备份与迁移:你的所有对话数据都存储在Cloudflare R2中,元数据在D1数据库中。Cloudflare提供了可靠的服务,但自己做好备份总是更安心。你可以定期使用R2的批量操作API或rclone等工具,将整个存储桶的数据同步到另一个云存储或本地。D1数据库也支持导出为SQLite文件。制定一个简单的备份策略,确保这些宝贵的“创作记忆”不会丢失。
最后,我想分享一个最直接的体会:Residue带来的最大改变,是让我在与AI结对编程时更加“放心”和“专注”。我不再需要分心去思考“这段对话要不要保存下来”,因为一切都在自动发生。当几周后我需要重构某个模块时,我能清晰地看到当初为什么选择这个方案,有哪些权衡和边界情况被讨论过。这种可追溯性,对于构建复杂、长期维护的软件系统来说,是一种质效的提升。它模糊了“编程”与“文档”的边界,让代码的“为什么”和“是什么”终于能够并肩而行。
