桌面 AI 如何隔离项目记忆并支持撤销
作者介绍 Ankita 的项目管理实现:按根目录关联项目,分别保存笔记、决策、待办与工作日志,并提供会话级撤销。目录识别强调严格边界匹配,防止相似路径导致项目记忆串用。
AI 深度解读
AI 助手跨项目工作时,记忆很容易走向两个极端:每次重新交代上下文,或者把几个项目的记录混在一起。Ankita 作者给出的办法很克制:按工作目录识别项目,每轮自动留一条工作记录,再允许用户一键撤销本次会话的自动写入。
这套设计值得看的地方,是它把路径匹配、记录去重和撤销边界做成了确定性规则。对正在开发 AI 编程工具的程序员来说,它提供了一组可以直接讨论和实现的取舍,但「自动维护」离真正理解项目还有距离。
项目归属先看目录,但用户选择优先
Ankita 把项目定义为根路径,以及关联的笔记、决策、待办和工作日志,由 src/memory/projects.mjs 中的 ProjectStore 管理。会话开始时,src/memory/project-resolver.mjs 中的 detectProject 用当前工作目录匹配已保存的项目根路径。
匹配不能直接用字符串包含关系。C:\foo 不应该匹配 C:\foobar,因此实现通过相对路径检查保证目录边界,只在 Windows 上忽略大小写。遇到嵌套项目,最长的匹配根路径获胜;如果两个匹配根路径长度相同,则判为歧义,不选任何一个。已归档项目直接跳过。
这些规则解决的是一个实际问题:助手拿到上下文之前,先确定上下文属于谁。
即使发现当前目录是 Git 仓库,系统也只询问用户是否追踪,不会直接创建项目。用户通过 /project 显式选择了项目,目录检测就让位;否则,检测结果只影响本次会话的提示词,不修改持久保存的活动项目。
当前目录可以决定本次会话的项目上下文,但不能悄悄覆盖用户保存的选择。
src/core/cli.mjs 明确写下了这个优先级。src/core/agent.mjs 中的 resolvePathProject 还用 try/catch 包住解析过程,避免项目存储损坏导致整个会话无法启动。
自动维护先记事实,理解交给可选步骤
每轮结束后,src/memory/project-maintenance.mjs 中的 maintainTurn 负责更新项目记录。默认流程不调用模型,只处理能确定的信息。
如果本轮修改了文件,就追加且只追加一条工作日志,例如 done · 3 file(s) · a.mjs, b.mjs。单条最多 400 个字符,最多列出前 5 个文件名。会话检查清单也会同步:待处理或进行中的事项进入项目待办,每轮最多新增 5 条,每条最多 500 个字符;已完成或已取消的事项,则关闭匹配的开放待办。
每项自动写入都带来源标记,例如 { kind: "auto-worklog", sessionId, turnId }。appendWorkLog 会拒绝重复记录同一轮,因此重试或重放不会反复追加同一条日志。这里保证的是工作日志去重,不能顺势理解成所有操作都有完整的幂等保证。
它还给静默写入划了几条边界:
- 没有文件变化,也没有检查清单变化,就返回
no-work,不写记录。 - 维护过程持有文件锁;锁被占用时跳过本轮,不进入队列。
- 维护失败通过返回值报告,不影响本轮回复。
- 工作日志最多保留 100 条,超出后丢弃最旧记录。
真正需要模型理解的部分默认关闭。设置 PROJECT_INLINE_CONSOLIDATION=on 后,系统才会在会话日志达到 400 个字符后,后台提取决策和待办。它在回复交付后运行,用户不用等待,但提取结果可能在用户已经切换任务之后才落入项目。
因此,「零模型调用」只适用于默认的日志和待办同步,不能覆盖这个可选提取步骤。
撤销靠来源标记,不靠猜测时间范围
/project undo 调用 removeAutoEntries,使用当前 sessionId 找到来源类型以 auto 开头的记录,再删除本次会话自动新增的工作日志、笔记、决策和待办。被本次会话自动关闭的待办会重新打开,用户手动写入的内容则保留。
命令还会报告具体数量。原文示例是撤销 7 项自动变动:工作日志 3 条、待办 2 条、重新打开 1 条、笔记 1 条、决策 0 条。
能可靠撤销的前提,是写入时就记录来源,而不是事后判断哪些内容像是 AI 写的。
不过,这个命令撤销的是整个会话的自动维护记录,不会撤销代码文件修改,也不能只撤回其中一轮。作者明确把会话粒度当作设计选择,并追问:既然已经保存 turnId,为什么不支持按轮次撤销?
原文没有给出最终答案。可以推测,难点会出现在待办等共享状态上:某轮新增事项,后续轮次又更新或关闭它,单独撤销前一轮该恢复到什么状态?标签能帮助定位来源,但不会自动解决后续依赖。这是对设计成本的推断,并非作者已经验证的故障。
我的判断
我认可它把确定性规则放在模型提取之前。目录归属、文件变更数量、重复轮次识别,都可以用程序判断,没必要让模型参与。相比把所有对话交给模型总结,这样更容易控制开销,也更容易解释记录从哪里来。
但原文提供的是实现机制和作者经验,没有跨项目误匹配率、维护遗漏率或用户对撤销粒度的对照数据,不能据此断言它比其他方案更有效。
几个代价也很明确。目录改名后,项目会失去匹配,直到更新根路径;锁竞争时跳过维护,原文没有说明是否补写;100 条日志只是一个有界记录窗口;文件列表能说明改了什么,不能解释为什么改。
Ankita 是 MIT 开源、local-first 的桌面伴侣,这套设计更贴近个人工作流。一般团队若要多人共享项目记忆,还需要额外处理成员归属和并发更新。原文没有证明现有机制足以覆盖这些需求。
能拿来做什么
- 开发前端或 Node AI 编程助手时,可以先实现「显式项目选择优先、目录检测只作用于当前会话」,并验证嵌套目录、同前缀目录和同长度歧义,减少上下文串项目。
- 给助手增加项目记忆时,可以先记录文件变更和检查清单状态,再决定是否加入模型提取。界面应区分事实日志与提取出的决策,避免把文件名列表包装成项目理解。
- 只要工具会自动修改持久记录,就应在写入时保存来源类型、
sessionId和turnId,同时定义撤销新增记录、恢复已有状态的规则。若使用/project undo这样的命令,应明确告诉用户它撤销的是自动记录。