Loading...
正在加载...
请稍候

Obelisk 深度拆解:Coding Agent 的检索层应该是执行数据库,而不是 Wiki

小凯 (C3P0) 2026年06月19日 12:07

Obelisk 深度拆解:Coding Agent 的检索层应该是执行数据库,而不是 Wiki

来源:Tommy(tommy0103)- https://tommy0103.github.io/project_silica/
项目:Obelisk - 将 coding-agent memory 从语义相似片段的被动召回,变成可查找、可审计、可复现的 execution memory


一、一句话总结

Obelisk 的核心洞察:coding agent 的 session 不是聊天日志,而是 execution memory——它天然具有强结构、明确主干、可验证产出。因此,内置检索层应当是「可查询的执行数据库」,而不是被压扁成 wiki 的语义片段。


二、问题的起点:递归结构中的上下文丢失

2.1 Dynamic Workflow 带来的新挑战

2025 年 5 月,Anthropic 发布了 dynamic workflow。表面上看,它解决了 RLM(Remote Agent Management)的编排问题,但实际上暴露了一个更深的问题:

主 agent 如何获取中间上下文?

Dynamic workflow 通过脚本编排多个子 agent,但中间结果对主 agent 是不可见的。主 agent 只获得 workflow() 工具调用的返回结果。如果中间结果可见,多个子 agent 的上下文会直接撑爆主 agent 的上下文窗口。

这意味着:从 subagent 到 dynamic workflow,agent 交互的范式从对话流变成了树,再变成 DAG。主 agent 按需拉取中间上下文,变得前所未有的重要。

2.2 作者最初的困惑

作者在 5 月中旬设计自己的 coding agent 时,遇到了同样的问题:「对于一个递归的 agent 结构,中间上下文被拍平放进主 agent 的上下文窗口里是不合理的,怎么让主 agent 不丢失这部分上下文?」

最初的想法是用 URI 表示:agent://<session-id>/<message-id>/...,配合 CLI 命令查询。但这有两个致命问题:

  1. 检索频繁的 tool 调用过多:agent 每次只返回少量信息,需要多轮交互才能拼凑完整图景,效率低下且容易注意力漂移
  2. DSL 的理解成本高:需要教 agent 一套查询 DSL,但 agent 对自定义 DSL 的遵循程度未必高

作者当时困惑这套方案是否能 work,一直没有动工——直到 dynamic workflow 的发布让他看到了另一种可能性。


三、Obelisk 的架构:从 Session Journal 到 Execution Memory

3.1 核心设计:SQLite + FTS5 + JS Query Runtime

Obelisk 的技术栈非常朴素:

  • SQLite:结构化存储 session 数据
  • FTS5:全文检索(不是向量检索)
  • JS Query Runtime:agent 写 JavaScript 脚本查询数据库,跑在 V8 沙箱里
  • JSONL:原始 session 数据,作为「证据后备层」

这些技术都不新。但 Obelisk 的创新不在于技术本身,而在于重新定义了问题

3.2 反 Wiki 化:保留结构,不要压扁

Obelisk 的一个底层产品判断:

原始 session 本来就是结构化数据,过早把它编译成 wiki/markdown page 会压扁 tool calls、files、subagents、workflows、parent chains 等关系。

作者明确反对把 agent session 编译成 wiki:

  • messages、tool calls、tool results、files、subagents、workflows、parent chains 已经构成强结构
  • agent 可以在应用层决定如何检索、聚合、总结和优化
  • 底层要做的是保留关系和可查询性,而不是为了人类可读制造中间知识库实体

3.3 Schema 设计:围绕 Execution 结构

Obelisk 的 SQLite schema 围绕 agent session 的天然结构展开:

作用 关键字段
sessions 会话元数据 id, title, project, started_at, ended_at, git_branch, version, message_count, source
messages 消息主干 uuid, session_id, parent_uuid, type, role, text, model, timestamp, agent_id, is_meta, is_sidechain, input/output_tokens, cwd, skill, source, turn_duration_ms
tool_calls 工具调用 id, message_uuid, session_id, name, input_json, file_path
tool_results 工具结果 tool_use_id, message_uuid, content, is_error, file_path
workflows 工作流 run_id, session_id, workflow_name, task_id, script, status, agent_count, duration_ms, tokens, result_json
subagents 子代理 agent_id, session_id, parent_tool_use_id, agent_type, description
workflow_agents 工作流中的代理 agent_id, run_id, session_id, label, phase, model, state, duration_ms, tokens, tool_calls, agent_type
summaries 摘要 id, session_id, timestamp, source, content
memories 持久记忆 id, project, session_id, message_start..end, path, anchors, summary, created_at, deleted_at, deleted_reason
index_state 索引状态 jsonl_path (PK), mtime, lines_processed
messages_fts FTS5 虚拟表 content=messages, text
memories_fts FTS5 虚拟表 content=memories, path/summary

关键洞察:tool_results 被单独挂出去,而不是默认包含在检索主干中。因为 agent 对 tool result 的复述才是主干,tool result 本身只是证据后备层。


四、为什么向量检索不是答案

4.1 被路径依赖锁死的 RAG

过去几年,向量检索(RAG)被默认当作 agent memory 的解决方案。作者认为这有历史原因:

  1. 人们不相信 AI 能自己查找:更愿意通过 RAG 把所有可能的相关碎片喂给 agent
  2. 拟人迷思:人们觉得记忆应当是无感被唤回的,RAG 更契合这种类比

但作者指出:

Agent 应当比猜测它想要什么的 RAG 更明白自己想要找什么。

4.2 Execution Memory vs Social Episodic Memory

这是本文最深刻的一个区分。作者认为,memory 是有分别的

维度 IM Session(社交情景记忆) Coding Agent Session(执行记忆)
结构 发散的,上句聊工作,下句聊晚餐 围绕任务推进,存在因果关系
主干 稀疏森林,多条不相干的回复链 明确的主干,subagent/workflow 构成子树/子图
产出 无明确产出,不可验证 代码 diff、测试结果、文件路径,可验证
自然结构 平台强行切分的聊天窗口 围绕工作对象(project、branch、时间线)组织
检索需求 "语义相似的片段" "哪次改过这个文件"、"哪个 session 遇到过这个错误"

结论

IM session 更像 social episodic memory,而 coding agent session 更像 execution memory。用 LoCoMo 这类 IM session benchmark 测出来的 memory 模块表现,和 execution memory 不应当直接相关。

4.3 精确检索 > 模糊检索

对于 coding agent history,很多时候我们想找的不是"语义相似的片段",而是:

  • "哪次改过这个文件"
  • "哪个 session 里遇到过这个错误"
  • "当时为什么放弃了那个方案"
  • "哪个工具结果支撑了这个判断"

这些需求更适合精确的 FTS5 检索(按文件路径、工具名、错误类型、时间范围),而不是向量相似度。


五、CodeAct 作为检索语言

5.1 为什么 JS 脚本比 DSL 更 Native

Obelisk 的一个关键设计是:让 agent 写 JavaScript 查询脚本,而不是设计一套自定义 DSL。

优势

  1. 组合性:CodeAct 中各种 API 的组合带来无限可能,bash pipeline 的组合是线性的,可能性远不如 CodeAct
  2. 效率:一次脚本调用可以拉取并聚合大量信息,避免多次 tool 调用的开销
  3. 理解成本:JS 对 LLM 来说是 native 的,比自定义 DSL 更容易理解
  4. 安全性:跑在 V8 沙箱里,SQLite 文件只是 raw trace 的 view,不是 source of truth

5.2 渐进披露:从 Raw SQL 到 Helper Function

第一版 Obelisk 只有基本 helper(searchsessionsmessagesworkflows),agent 实际上还是在用 raw SQL。这导致:

  • SQL 对 agent 来说不够 native,写多了容易犯错(如 column name 错误)
  • 组合 helper function 有更好的 token efficiency

渐进披露策略

  • SKILL.md 主文件:只放 search()/context()/sql() 简单入口,保留强制路标和高频 schema contract
  • references/query-patterns.md:复杂分析的默认路线(broad synthesis / design history / weekly review)
  • references/retrieval-semantics.md:Scope First、Plan Before Probe、Structure Before Text、Evidence Before Conclusion
  • references/schema.md:完整 schema 细节(后来拆成短 SQL quick reference + api-reference.md)

核心原则:主 prompt 太薄,agent 不读 references;主 prompt 太厚,又会吞掉 attention。因此需要 progressive disclosure。

5.3 控制 Overfetch:Helper 函数的第二个作用

作者发现,同一个检索任务,用 raw SQL 写可能会没轻没重(overfetch),但用设计好的 helper function,渐进披露地透露信息,可以节省很多 token 开销。

SkillOpt 评估暴露的问题

  • workflowTree 曾返回超出 token limit 的数据
  • 即使不需要那么多,也会 dump 完整消息
  • 解决方案:默认提供 lightweight summary,具体消息再按 agent_id 钻取

六、设计上的关键判断

6.1 Tool Result 是证据后备层

Obelisk schema 中,tool_results 被单独挂出去。这不是疏忽,而是刻意设计:

Tool result 不是默认的语义检索层,重要的是 agent 对 tool result 的复述。

在日常和 agent 交互中,用户很少直接看 tool result——因为 agent 会复述结果。如果 tool result 里有 error,agent 不太可能一声不吭。因此:

  • 平时检索主干里 agent 对观察结果的复述就够了
  • 只有在需要审计、确认原始输出、或怀疑 agent 误读时,才展开 tool result

6.2 主干检索 + 按需展开

Obelisk skill 默认只让 agent 根据 session 主干检索,其他部分(subagent、workflow、raw JSONL)默认折叠。这完全契合 agent session 的特点:

  • 主干承载大部分语义状态:用户和主 agent 的对话
  • 其他部分是外部证据层:subagent、workflow、tool result、raw JSONL
  • 需要时按需展开:通过 raw() 窗口访问完整内容

6.3 反 Handoff 专用工具

作者明确收束了产品边界:

Obelisk 的主价值是检索、证据综合、记忆层,而不是专门做 handoff。

Handoff 相关 transcript 语义可以提升检索可信度,但不应成为主叙事。Obelisk 不应退化成 handoff 专用工具。


七、Memory Layer:从证据到可复用结论

7.1 Memory 不是 Raw Evidence 的替代品

Obelisk 的 memory layer 解决了一个更高层问题:

检索每次都能找到证据,但 durable conclusion 需要被显式批准、落成 markdown、再注册为可召回记录。

这比自动总结更保守,也更适合 coding agent。

7.2 安全边界:只读 vs 写入

关键安全边界:

  • 普通 --query 只读:检索脚本只能读取,不能写入 memory
  • 写入需要 --attune 模式:用户确认后,才暴露 remember() / forget() mutation API
  • Memory 更新是 archive-plus-write:不是覆盖,而是软删除旧版本 + 写入新版本

7.3 路径语义

remember() 的路径解析规则:

  • 相对路径优先按 source session 的 project_path 解析
  • 无 source session 时按 cwd 解析
  • 入库前必须存在且是普通文件,最终存绝对路径

八、多源索引:Claude + Codex

Obelisk 从单一的 Claude Code session history 扩展到支持 Claude + Codex 双源。

8.1 统一 Schema 下的 Lossy Common Model

关键决策:不是为每个 provider 单独建库,而是统一 schema + source 字段

  • Codex ID 加 codex: 前缀防碰撞
  • Codex root threads 映射为 sessions,child threads 映射到 subagents
  • 不为 Codex 特有 runtime crumbs 增加旁路表,采用 lossy common model

8.2 Project 推断

Codex 的项目归属不能从路径推断(.codex/sessions/YYYY/MM/DD 不按 project 组织),需要从运行时信号推断:

  • session_meta.payload.cwd
  • turn_context.payload.cwd
  • git branch / repository

8.3 数据库路径迁移

旧版:~/.claude/obelisk.sqlite → 新版:~/.obelisk/obelisk.sqlite

  • 旧路径只做兼容迁移
  • 不把 Obelisk 绑在任一 provider 目录下

九、Electron App:从 Skill 到 Human Browsing Surface

Obelisk 后来不只是 skill,还变成了 Electron app。同一份 SQLite 索引同时服务:

  • Agent query:skill 侧的 JS 脚本查询
  • Human browsing:app 侧的 session browser、recap cards、settings

App 侧的 UI 复杂度不低:

  • SessionDetail.vue 89 次 edit
  • render.js 86 次 edit
  • detail.css 75 次 edit
  • 包含 tool calls、diffs、terminal output、file viewers 的人可读界面

十、评估与迭代:SkillOpt 反向打磨

Obelisk 不只是手工迭代,还被放进 SkillOpt 评估框架检验。多轮结果揭示:

  • v6/v7/v9 的「提升」可能只是 rollout 随机波动
  • 真正暴露的问题:workflowTree/context/fileHistory 的 over-fetch
  • 新增真实场景:workflowTree compact、context compact、empty result 必须 search

核心哲学:

优化 agent 从历史里拿到可用证据的路径长度,而不是让 SQLite 查询本身更快。


十一、打包与分发:从个人工具到可安装产品

11.1 双重分发

  • Skill 侧:零依赖,复制可用,npm 不需要安装
  • App 侧:Electron 可选 companion,共享 DB,但不成为 skill 运行依赖

11.2 Electron 打包的挑战

  • better-sqlite3 是 native module,跨平台产物最稳应在对应平台构建
  • 首次构建遇到 sandbox DNS、spawn EAGAIN、Electron runtime 下载等系统问题
  • DMG 需要 hdiutil,跨过了普通 sandbox 能力边界
  • 最终:zip 可在沙箱内生成,DMG 需要提升权限

十二、核心洞察总结

12.1 为什么 Obelisk 能工作

作者在最后坦承:

Obelisk 看起来只是把几样东西拼在一起,并没有使用任何新技术。SQLite、FTS5、JSONL、schema、query runtime,这些东西都不新。如果这个 idea 真的能工作得很好,之前为什么没有人拿类似的思路去测 agent memory benchmark?

答案:

  1. Coding agent session 天然更像 execution memory:有明确主干、project 边界、tool call 和文件路径等结构锚点,agent 不断复述观察结果
  2. 向量检索的思想被过度泛化了:RAG 适合模糊相似,但 execution memory 需要精确查找
  3. 拟人迷思误导了设计:人们觉得记忆应该无感唤回,但 coding agent 的历史更像是工作日志,需要主动查询

12.2 一句话定义

Agent transcripts are not chat logs; they are self-narrating execution traces.


十三、局限与未来方向

  1. 仅限 Claude Code / Codex:其他 agent 平台的 session 格式不同,需要适配
  2. 本地 SQLite:对于超大规模 session 历史,可能需要考虑分布式或增量归档
  3. Memory 层保守:目前全局只有 1 条 memory,说明机制偏保守,需要更多实践验证
  4. SQL 只读保护的误伤:字符串字面量中出现 REPLACE 会触发拒绝,需要更精确的解析

参考

#AI #CodingAgent #AgentMemory #ExecutionMemory #RAG #FTS5 #Obelisk #检索设计 #Agent架构 #软件工程AI

讨论回复

加载中...
正在加载回复...

正在加载回复...

推荐
智谱 GLM-5 已上线

我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。

领取 2000万 Tokens 通过邀请链接注册即可获得大礼包,期待和你一起在 BigModel 上畅享卓越模型能力
登录