前言¶
在 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 。