Obelisk 深度拆解:Coding Agent 的检索层应该是执行数据库,而不是 Wiki
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://,配合 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 数据,作为「证据后备层」
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 |
---
四、为什么向量检索不是答案
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 里遇到过这个错误"
- "当时为什么放弃了那个方案"
- "哪个工具结果支撑了这个判断"
---
五、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(search、sessions、messages、workflows),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)
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.cwdturn_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
SessionDetail.vue89 次 editrender.js86 次 editdetail.css75 次 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 会触发拒绝,需要更精确的解析
---
参考
- Tommy. "Obelisk: Coding Agent 内置检索层应该是执行数据库,而不是 Wiki." https://tommy0103.github.io/project_silica/
- Obelisk GitHub: https://github.com/tommy0103/obelisk(推测)
- Anthropic Dynamic Workflow: https://www.anthropic.com/news/dynamic-workflows(发布于 2025-05-29)
🌟 智谱 GLM-5 已上线
我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。
🎁 领取 2000万 Tokens