前言¶
在 DSH(DeepSeek Harness,一切皆插件)里,给智能体接一个数据库,最常见的做法是把连接信息交给它,让它自己用 SHOW TABLES、DESCRIBE 去摸结构。这条路有两个代价:一是每次探结构和每次猜错都要消耗一轮模型回合,一轮就是几秒;二是结构信息没有边界,几百行 SHOW/DESCRIBE 结果很容易把上下文烧掉。
dbgraph 的思路不同:插件先把远程数据库的 catalog——表、列、注释、主键、索引、枚举字面量的有界采样——写进本地 SQLite 索引,智能体读索引(以及直接塞进 system prompt 的摘要)就知道结构,真正取数据只发一次网络调用。
下面介绍 CC19990113/dsh-plugin-dbgraph。
这是什么¶
- 名称:
CC19990113/dsh-plugin-dbgraph - 维护者:CC19990113
- 分类:记忆
- GitHub 星标:1
- 许可证:MIT
- 一句话定位:为 DeepSeek Harness 索引数据库结构,让智能体在写查询前知道每一张表和每一列,然后用一次工具调用拿到答案。
结构本地,数据在线¶
| 放哪 | 理由 | |
|---|---|---|
| 表、列、注释、主键、索引、枚举采样、行数估计 | 本地索引 | 零网络,可离线,问「这列是什么意思」不需要任何凭证 |
| 实际行值 | 始终走服务端 | 缓存密码不是快密码,是错的密码 |
真正来自业务数据的东西不会被持久化。唯一的、带数据形态的字段是 enum_values_json,它是一次 SELECT DISTINCT … LIMIT n 的采样,用来告诉模型 status 存的是 'paid' 还是 1。
核心功能¶
- 索引远程数据库的 catalog,覆盖每张表、每列、注释、主键、索引,以及有界的枚举字面量样本。
- 结果写入本地 SQLite 索引,位置在
~/.dsh/dbgraph。 - 模型读这个索引,外加一段直接放进 system prompt 的摘要,而不是反复发
SHOW TABLES和DESCRIBE。 - 真正取值时只发一次网络调用。
status.json 和索引放在一起,里面的 digest 被 system-prompt 段落同步读取。原因很实际:每个模型请求都会拼这一段,在这里打开 SQLite会是每一步都要付的成本,插件不接受这个代价。
索引格式¶
每个连接一个 SQLite 文件,每次运行整体替换(先写临时文件 <index>.<pid>-<nonce>.tmp,再改名)。nonce 每次运行不同,所以两次运行不会共用临时文件。这一个决定就去掉了增量 diff、删除和半更新索引的问题。
meta key/value: engine, database, schemaVersion, l1IndexedAt, l2IndexedAt
objects one row per table / view / collection / key pattern
columns ordinal, type, type_class, nullability, default, comment, pk/unique, enum sample
edges fk | index | view_ref | code_ref
terms normalized words + Chinese bigrams + alias expansion, for semantic lookup
search FTS5 (contentless) over object and column names and comments
两个层级¶
| Level | 内容 | 位置 |
|---|---|---|
| L1 | 对象和列名、声明的类型 | 内联,几秒内,立即可用 |
| L2 | 注释、外键、索引、行数估计、枚举样本 | 后台任务 |
L1 充实失败不会丢掉结构快照:能回答「有哪些列」的索引已经值回大部分成本。
工具¶
| 工具 | 超时 | 做什么 |
|---|---|---|
dbgraph |
30 s | connections schema objects columns find sql impact refs |
dbgraph_index |
300 s | 建立或刷新一个连接(l1 / l2 / full);大的运行交给后台任务 |
find 是最关键的一个:它离线把词解析到表和列,然后跑一条参数化 SELECT。遇到不确定时返回 ambiguous 并给出排好序的候选,而不是猜;遇到「这个用户」这种没有谓词的说法时返回 missing_predicate,而不是去扫一张表。
敏感列掩码¶
敏感列(password、passwd、pwd、secret、token、salt、id_card 等)默认返回 [REDACTED]。没有这层保护,一个粗心的问题就会把密码送进 transcript,而这个 harness 会持久化 transcript 并对它们做索引。按连接设 sensitiveColumns: 'show' 可以关掉。
凭证不会写进 connections.json:一个连接存的是凭证引用,实际凭证不落库。
写路径为什么用 Rust¶
node:sqlite 是同步 API。通过它写一万个列,事件循环会阻塞到写完为止。crates/dbgraph-indexer 把规范化、term 生成和 SQLite 写入都放到 libuv 线程池上,重建索引就不会拖住宿主。
README 给出同机、同目录、同固定时钟下的测量(500 张表 / 10,000 列):
| 写入方 | 写入耗时 | 索引文件 | 最长事件循环阻塞 |
|---|---|---|---|
| JavaScript | 1992 ms | 8.46 MiB | 1992 ms(整段写入) |
Rust(AsyncTask) |
218 ms | 8.41 MiB | 33.6 ms |
两个写入方互相校验,而不是各自信任:给定同一个目录和同一个固定 now,objects、columns、edges、terms、search(含 rowid)必须逐行一致,只有 meta.writer 可以不同。夹具期望是跑一遍 JavaScript 写入方生成的,不是手写的,所以任一侧改动没镜像到另一侧,构建就会失败。
JavaScript 写入方因此不是优化回退,而是一致性参考和降级路径(没有 Rust 工具链、没有 addon、调用抛错、报告读不出来都会回退到它,且只回退一次)。已知差异只有一处:JavaScript 写入方会把 dangling edge(指向快照里不存在的表)计入 report.warnings,native 写入方静默丢弃它;存储的数据完全相同,只有这一行 warning 不同。
选型与落盘细节¶
dbgraph_index 接受 writer: 'auto' | 'js' | 'rust':
js始终用 JavaScript 写入方,不碰 addon。rust只用 native 写入方;缺产物或调用抛错会让整次运行失败,而不是悄悄产出更慢的索引。auto(默认)在可用时用 native;缺产物、调用被拒、报告读不出来时,每个进程只记一次日志,然后回退到 JavaScript。
DSH_DBGRAPH_RUST_ADDON 显式指向某个产物:既是降级路径的测试钩子,也是「addon 放在 crates/ 之外」这种部署的逃生口。默认指向 crates/dbgraph-indexer/artifacts/dbgraph-indexer.<platform>-<arch>.node。
两个实测结论写进了 README,属于踩过的坑:
node:sqlite的 contentless FTS5 存的内容读不出来:每个列(包括UNINDEXED的)都返回NULL,只有rowid和rank活下来。所以索引用 contentful 表,其中object_id UNINDEXED能逐字读回。另外 FTS5 的列语法是column-name [UNINDEXED],前面带类型名会直接 parse error。unicode61把一段中文当成一个 token:原样存广告点击,MATCH '广告'什么都找不到。所以text存的是预分词后的流——ASCII 按 camel/数字边界切,CJK 切 bigram——查询也走同一个分词器,用OR连接(空格表示短语,不是「任一」)。
典型用法¶
README 给的对照很直接。没有索引时,回答「张三的密码是什么」要走五步,四次模型往返、四次网络往返、两次猜错,还会往 transcript 里塞 2,000 行;有 dbgraph 后:
dbgraph find {entity:'用户', fields:['密码'], where:{name:'张三'}}
一行拿到结果,回答里会说明它用了哪张表、哪一列。
结构层面的命令入口是 dbgraph(查询)和 dbgraph_index(建立或刷新、分 l1 / l2 / full、大任务进后台)。
安装与启用¶
这个项目发布形态是一套 bundle:packages/bundle/cordis.patch.yml 把各模块挂成一个 profile 层,packages/service 提供 ctx.dbgraph 的词汇表、方言注册表和索引协议,packages/catalog 每种引擎一个 DatabaseDialect(已实现 SQLite、MySQL;PostgreSQL/MongoDB/Redis 在计划中)。
README 没有给出独立的一键安装命令,也没有写 dsh plugin add github:... 这类用法。要启用,需要把它作为 bundle / patch 挂进你的 DSH profile。仓库根目录的 package.json 里,dsh.bundle.patch 指向 ./packages/bundle/cordis.patch.yml,这是它的挂载入口。请以仓库当前内容为准确认具体启用方式,不要凭插件名猜测命令。
适用场景与注意¶
适合给需要读数据库结构的 DSH 智能体用:结构探索零网络、可离线,取值只发一次调用;中文列名和中文注释靠预分词 + bigram 做语义检索;敏感列默认掩码。
注意三点:
- 插件以当前 DSH 进程的权限运行,挂进 profile 前先读一遍源码。
- 许可证是 MIT,仓库当前为公开仓库。
- 仓库还很新(创建于 2026-11-05,写作时为 1 星),大规模使用前建议自己跑一遍
dbgraph_index并核对status.json里的 digest,确认索引和真实 catalog 一致再放开。
写在最后¶
dbgraph 要解决的不是「数据库慢」,而是「模型回合贵」:把结构咨询从每次请求里拿出来,只保留一次必要取值。目录页在 https://www.skillhub.cn/plugins/CC19990113/dsh-plugin-dbgraph ,代码在 https://github.com/CC19990113/dsh-plugin-dbgraph 。