cgraphx 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.claude-template/skills/db-query/SKILL.md +75 -42
- package/dist/.claude-template/skills/db-query/agent-prompt.md +12 -13
- package/dist/.claude-template/skills/export-table-ddl/SKILL.md +167 -0
- package/dist/.claude-template/skills/export-table-ddl/closed-loop.html +163 -0
- package/dist/.claude-template/skills/export-table-ddl/evals/evals.json +26 -0
- package/dist/.claude-template/skills/export-table-ddl/scripts/export-ddl.mjs +163 -0
- package/dist/dbquery/cli.d.ts.map +1 -1
- package/dist/dbquery/cli.js +20 -3
- package/dist/dbquery/cli.js.map +1 -1
- package/dist/dbquery/executor.d.ts.map +1 -1
- package/dist/dbquery/executor.js +15 -23
- package/dist/dbquery/executor.js.map +1 -1
- package/dist/dbquery/format.d.ts +15 -1
- package/dist/dbquery/format.d.ts.map +1 -1
- package/dist/dbquery/format.js +86 -23
- package/dist/dbquery/format.js.map +1 -1
- package/dist/dbquery/identifiers.d.ts +19 -0
- package/dist/dbquery/identifiers.d.ts.map +1 -0
- package/dist/dbquery/identifiers.js +37 -0
- package/dist/dbquery/identifiers.js.map +1 -0
- package/dist/dbquery/index.d.ts +1 -1
- package/dist/dbquery/index.d.ts.map +1 -1
- package/dist/dbquery/index.js.map +1 -1
- package/dist/dbquery/queries.d.ts +23 -3
- package/dist/dbquery/queries.d.ts.map +1 -1
- package/dist/dbquery/queries.js +46 -27
- package/dist/dbquery/queries.js.map +1 -1
- package/dist/dbquery/types.d.ts +2 -15
- package/dist/dbquery/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -20,7 +20,7 @@ cgraphx db <subcommand> [options]
|
|
|
20
20
|
|
|
21
21
|
输出格式:所有命令支持 `--format json|table|csv`(schema 命令固定 json)。
|
|
22
22
|
|
|
23
|
-
**`ddl`
|
|
23
|
+
**`ddl` 模式输出**:直接打印该表真实的 `CREATE TABLE ...` DDL 文本到 stdout(非 JSON)。MySQL 走 `SHOW CREATE TABLE`(含 ENGINE/CHARSET/外键/二级索引/列注释/表注释);PostgreSQL 由 `pg_get_constraintdef` + `pg_get_indexdef` + 列/表注释拼装(含 `COMMENT ON COLUMN` / `COMMENT ON TABLE` / `CREATE INDEX`)。**这是判断表关系 + 字段语义的首选信息源** —— 数据库外键 + 业务专家写的注释 + 索引都在这,输出可直接回灌给同方言 DB。
|
|
24
24
|
|
|
25
25
|
## 配置位置
|
|
26
26
|
|
|
@@ -36,7 +36,7 @@ profiles 文件结构:每个 profile 含 `type`(mysql/postgres)、连接参数
|
|
|
36
36
|
1. **必须先确定目标库避免连错** — 先 `cgraphx db profiles` 列出,确认目标 profile
|
|
37
37
|
2. **所有查询只读**(铁律,不可破) — 子系统内置禁止 DELETE/UPDATE/INSERT/DROP/ALTER,只读永不改变数据库
|
|
38
38
|
3. **必须带 LIMIT** — `cgraphx db query` 自动 LIMIT,但自定义 SQL 仍建议显式 `LIMIT N`
|
|
39
|
-
4.
|
|
39
|
+
4. **表结构优先读本地知识库,不主动连库** — 先 `Read docs/schema-knowledge/<project>/ddl/<table>.sql`(含 DDL + DISCOVERED 块,比 DB 多团队沉淀的隐式关系/含义/陷阱);文件不存在才 `cgraphx db schema --mode ddl` 兜底。DB 留给"查数据"和"知识库没有的表"
|
|
40
40
|
5. **陌生 schema 探索"先问后试"** — 不确定表关系或字段含义时,**先问业务专家**,不要埋头反复试错(试错既浪费 token,也可能拿错数据误导后续判断)
|
|
41
41
|
6. **复用 DAO 时验证参数** — 修改 Mapper XML 中的查询条件前,先用真实数据确认字段值分布
|
|
42
42
|
|
|
@@ -44,8 +44,9 @@ profiles 文件结构:每个 profile 含 `type`(mysql/postgres)、连接参数
|
|
|
44
44
|
|
|
45
45
|
```
|
|
46
46
|
0. cgraphx db profiles → 确认连接哪个库哪个 profile
|
|
47
|
-
1.
|
|
48
|
-
|
|
47
|
+
1. Read docs/schema-knowledge/<project>/ddl/work_order.sql → 看字段、注释、外键 + DISCOVERED 块(本地,首选)
|
|
48
|
+
(文件不存在 → cgraphx db schema --mode ddl --table work_order 兜底)
|
|
49
|
+
2. cgraphx db query --sql "SELECT * FROM ... LIMIT 5" → 用 LIMIT 查样本数据(只有查数据才连库)
|
|
49
50
|
3. 确认字段值分布 → 编写/修改代码
|
|
50
51
|
```
|
|
51
52
|
|
|
@@ -54,27 +55,54 @@ profiles 文件结构:每个 profile 含 `type`(mysql/postgres)、连接参数
|
|
|
54
55
|
按以下优先级链尝试,**每步失败才进入下一步**:
|
|
55
56
|
|
|
56
57
|
```
|
|
57
|
-
1.
|
|
58
|
-
|
|
59
|
-
→
|
|
60
|
-
→
|
|
58
|
+
1. Read docs/schema-knowledge/<project>/ddl/<table>.sql
|
|
59
|
+
→ export-table-ddl skill 导出的 DDL + 团队补充的 DISCOVERED 块(隐式关系/字段含义/陷阱/流程)
|
|
60
|
+
→ 找 <project> 目录:在 docs/schema-knowledge/*/ddl/ 下找 <table>.sql;唯一命中直接读,多个问用户
|
|
61
|
+
→ 这是首选:比 DB 多 DISCOVERED 块,且不连库
|
|
61
62
|
|
|
62
|
-
2.
|
|
63
|
-
→
|
|
64
|
-
→
|
|
63
|
+
2. 知识库没有 → cgraphx db schema --mode ddl --table A(再 B)
|
|
64
|
+
→ 看 DDL 里的外键(MySQL: SHOW CREATE TABLE 内联 / PG: FOREIGN KEY ... REFERENCES)+ 列注释
|
|
65
|
+
→ 兜底,且拿到后应沉淀回知识库(见步骤 4)
|
|
65
66
|
|
|
66
67
|
3. 都没有 → 问用户(不要试错)
|
|
67
68
|
→ "这两个表你想通过哪个字段关联?"
|
|
68
69
|
→ "你期望这条 SQL 返回什么样的业务实体?"
|
|
69
70
|
→ 用户也不知道时,**每一步试错都报告用户**,让用户决定继续还是换思路
|
|
70
71
|
|
|
71
|
-
4. 探索完成(用户告知或谨慎试错得到结论)
|
|
72
|
-
a.
|
|
73
|
-
b.
|
|
74
|
-
c.
|
|
72
|
+
4. 探索完成(用户告知或谨慎试错得到结论)后,沉淀到 per-table DDL 文件:
|
|
73
|
+
a. 定位文件 docs/schema-knowledge/<project>/ddl/<table>.sql(不存在 → 先跑 export-table-ddl 建基线)
|
|
74
|
+
b. append 到文件末尾的 DISCOVERED 块(格式见下)
|
|
75
|
+
c. 若是 DB 注释缺失导致的含义不明 → 另生成 COMMENT SQL 提示用户执行(执行后 DB 注释固化,DISCOVERED 块里那条可删)
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
**长期目标**:DB 注释逐渐健全(用户/DBA 执行了 COMMENT SQL)后,`schema --mode ddl`
|
|
78
|
+
**长期目标**:DB 注释逐渐健全(用户/DBA 执行了 COMMENT SQL)后,`schema --mode ddl` 直接返回业务含义,DISCOVERED 块逐渐退役。DB 自带注释是单一事实源,任何工具/任何人查 DDL 都受益。
|
|
79
|
+
|
|
80
|
+
## DISCOVERED 块格式(append 到 ddl/<table>.sql 末尾)
|
|
81
|
+
|
|
82
|
+
```sql
|
|
83
|
+
-- === DISCOVERED (db-query 探索补充,非 DB 原生) ===
|
|
84
|
+
-- [关系] assignee_id → users.staff_code (处理人工号,业务语义非 DB 外键)
|
|
85
|
+
-- [关系] region_id → region.id (地域)
|
|
86
|
+
-- [字段含义] status: 0=待派单 1=处理中 2=已完成 3=已取消
|
|
87
|
+
-- [陷阱] email 不唯一(同 email 多账号),用 phone 关联更稳
|
|
88
|
+
-- === END DISCOVERED ===
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- 每行 `-- [类别] 内容`,类别:`关系` / `字段含义` / `陷阱` / `流程`(跨表业务流程)。
|
|
92
|
+
- 块在 `-- === DISCOVERED` 和 `-- === END DISCOVERED ===` 之间 —— `export-table-ddl` 重 dump 时会保留这个块(不会覆盖)。
|
|
93
|
+
- 一张表可多次 append;重复的行不重复加。
|
|
94
|
+
- 这是 SQL 注释,不影响 DDL 可执行性。
|
|
95
|
+
|
|
96
|
+
### 跨表知识往哪张表写 —— 枢纽表原则(重要)
|
|
97
|
+
|
|
98
|
+
agent 的访问路径是 `INDEX.md → ddl/<table>.sql`,**基本不会主动读独立的 NOTES.md / 项目级笔记文件**。所以跨表知识不能单独建文件,必须塞进 agent 必然落地的表的 DISCOVERED 块:
|
|
99
|
+
|
|
100
|
+
- **关系(`[关系]`)双向标注**:`A.x → B.y` 在 `A.sql` 和 `B.sql` 的 DISCOVERED 块各写一行。agent 无论从哪头查表,都能看到这条关联。
|
|
101
|
+
- **跨表业务流程(`[流程]`)**:塞进该流程的**枢纽表** —— 即流程中心、agent 落地概率最高的那张表。例如"派单→处理→回单"流程,枢纽是 `work_order`(流程主表),写进 `work_order.sql`;不要散到 `dispatch_log`/`receipt` 各一份,更不要建 `派单流程.md`。
|
|
102
|
+
- **枢纽表判断**:流程主表 / 被外键指向最多的表 / agent 问"X 业务"时最先被点名的表。多个候选时选那个,其余表用 `[关系]` 双向指回枢纽。
|
|
103
|
+
- **绝不建** `NOTES.md` / `<project>-notes.md` / `跨表知识.md` 这类独立文件 —— 那等于把知识埋了,agent 不会主动读。
|
|
104
|
+
|
|
105
|
+
工程级 meta(profile 对应哪个服务、整体架构一句话)可以放 `INDEX.md` 顶部(agent 进目录第一件事就是读它),但不要沉淀到表级以下的独立文件。
|
|
78
106
|
|
|
79
107
|
## 何时派子 agent(让上下文更干净)
|
|
80
108
|
|
|
@@ -88,7 +116,7 @@ profiles 文件结构:每个 profile 含 `type`(mysql/postgres)、连接参数
|
|
|
88
116
|
|
|
89
117
|
| 场景 | 不派(直接调工具) | 派(派子 agent) |
|
|
90
118
|
|---|---|---|
|
|
91
|
-
| 看表结构 | "work_order 表结构" → `cgraphx db schema --mode ddl
|
|
119
|
+
| 看表结构 | "work_order 表结构" → `Read docs/schema-knowledge/*/ddl/work_order.sql`(没有再 `cgraphx db schema --mode ddl`) | "梳理这个库的所有表关系" |
|
|
92
120
|
| 看索引 | "users 表的索引" → `cgraphx db schema --mode indexes --table users` | "系统理解 X 业务的数据模型" |
|
|
93
121
|
| 查数据 | "查 work_order 里 status=1 的前 5 条" → `cgraphx db query --sql "SELECT ... LIMIT 5"` | "调研 work_order / payment / users 这套流程涉及的表关系" |
|
|
94
122
|
|
|
@@ -112,35 +140,40 @@ profiles 文件结构:每个 profile 含 `type`(mysql/postgres)、连接参数
|
|
|
112
140
|
- **派的代价**:子 agent 本身耗 token,简单查询派反而更贵
|
|
113
141
|
- **R4 是平衡点**:只在"模糊意图 + 主 agent 无法精确判断目标"时派
|
|
114
142
|
|
|
115
|
-
## schema-knowledge
|
|
116
|
-
|
|
117
|
-
每个 profile 一个 markdown 文件:`docs/schema-knowledge/<profile>.md`(项目业务数据目录,跨工具版本保留,不会被 cgraphx 升级覆盖)
|
|
118
|
-
|
|
119
|
-
```markdown
|
|
120
|
-
# <profile> schema knowledge
|
|
121
|
-
|
|
122
|
-
## 表关系(DB 未定义 FK 的隐式关联)
|
|
123
|
-
- `work_order.assignee_id` → `users.staff_code`(处理人)
|
|
124
|
-
- `payment.order_id` → `work_order.id`(订单的支付记录)
|
|
143
|
+
## schema-knowledge 目录结构(由 export-table-ddl 建基线,db-query 探索补充)
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
145
|
+
```
|
|
146
|
+
docs/schema-knowledge/
|
|
147
|
+
<工程A>/
|
|
148
|
+
INDEX.md # 表名 + 表注释,一眼扫完
|
|
149
|
+
ddl/
|
|
150
|
+
users.sql # 完整 DDL + 末尾 DISCOVERED 块(db-query 探索补充)
|
|
151
|
+
work_order.sql
|
|
152
|
+
<工程B>/
|
|
153
|
+
...
|
|
154
|
+
```
|
|
134
155
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
156
|
+
**两张表的 DDL 文件示例**(work_order.sql):
|
|
157
|
+
|
|
158
|
+
```sql
|
|
159
|
+
CREATE TABLE `work_order` (
|
|
160
|
+
`id` bigint NOT NULL AUTO_INCREMENT,
|
|
161
|
+
`assignee_id` varchar(64) NOT NULL COMMENT '处理人工号',
|
|
162
|
+
`status` tinyint NOT NULL,
|
|
163
|
+
PRIMARY KEY (`id`)
|
|
164
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
165
|
+
-- === DISCOVERED (db-query 探索补充,非 DB 原生) ===
|
|
166
|
+
-- [关系] assignee_id → users.staff_code (处理人工号,关联 users.staff_code)
|
|
167
|
+
-- [字段含义] status: 0=待派单 1=处理中 2=已完成 3=已取消
|
|
168
|
+
-- [陷阱] 跨 schema 查询时 schema 前缀必须显式
|
|
169
|
+
-- === END DISCOVERED ===
|
|
138
170
|
```
|
|
139
171
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
-
|
|
143
|
-
|
|
172
|
+
**闭环工作流**:
|
|
173
|
+
1. `export-table-ddl` 跑一次 → 建基线(DDL + 注释 + 索引,无 DISCOVERED 块)
|
|
174
|
+
2. 日常 `db-query` 查数据踩坑 → 发现隐式关系/含义/陷阱 → append 到对应 `ddl/<table>.sql` 的 DISCOVERED 块
|
|
175
|
+
3. 重跑 `export-table-ddl`(schema 变了)→ DISCOVERED 块保留,新 DDL 接上
|
|
176
|
+
4. DB 注释健全后 → DISCOVERED 块里对应的行可删(DB 已固化)
|
|
144
177
|
|
|
145
178
|
## 期望错误(CLI 退出码语义)
|
|
146
179
|
|
|
@@ -12,22 +12,21 @@
|
|
|
12
12
|
|
|
13
13
|
| 工具 | 用途 |
|
|
14
14
|
|---|---|
|
|
15
|
+
| `Read docs/schema-knowledge/*/ddl/<table>.sql` | **首选**:本地知识库,含 DDL + DISCOVERED 块(团队沉淀的隐式关系/含义/陷阱/流程) |
|
|
16
|
+
| `Read docs/schema-knowledge/*/INDEX.md` | 工程级表清单 + 注释,定位要读哪些表 |
|
|
15
17
|
| `cgraphx db profiles` | 列已配置 profiles,确认连哪个库 |
|
|
16
|
-
| `cgraphx db schema --mode
|
|
17
|
-
| `cgraphx db schema --mode
|
|
18
|
-
| `cgraphx db
|
|
19
|
-
| `cgraphx db schema --mode table-comment --table <name>` | 看表注释 |
|
|
20
|
-
| `cgraphx db query --sql "SELECT ... LIMIT N" [--profile <name>]` | 查样本数据(自动 LIMIT) |
|
|
21
|
-
| `Read` | 读 `docs/schema-knowledge/<profile>.md` 找团队沉淀 |
|
|
18
|
+
| `cgraphx db schema --mode ddl --table <name> [--profile <name>]` | **兜底**:知识库没有该表时,看字段、注释、外键 |
|
|
19
|
+
| `cgraphx db schema --mode indexes --table <name>` | 看索引(知识库的 DDL 通常已含) |
|
|
20
|
+
| `cgraphx db query --sql "SELECT ... LIMIT N" [--profile <name>]` | 查样本数据(只有查数据才连库) |
|
|
22
21
|
|
|
23
22
|
## 探索策略(对齐主 SKILL.md 的"陌生 schema 探索流程")
|
|
24
23
|
|
|
25
|
-
1. **`
|
|
26
|
-
2. 对涉及的表逐个 **`
|
|
27
|
-
3.
|
|
28
|
-
4.
|
|
29
|
-
5.
|
|
30
|
-
6.
|
|
24
|
+
1. **`Read docs/schema-knowledge/*/INDEX.md`** 找目标表所在工程目录(唯一命中直接进,多个问主 agent)
|
|
25
|
+
2. 对涉及的表逐个 **`Read docs/schema-knowledge/<project>/ddl/<table>.sql`** —— DDL + DISCOVERED 块,这是首选信息源(比 DB 多团队沉淀,且不连库)
|
|
26
|
+
3. 知识库没有该表文件 → **`cgraphx db schema --mode ddl --table <name>`** 兜底,看外键 + 列注释
|
|
27
|
+
4. **`cgraphx db query`** 只在需要看样本数据确认字段值分布时用,**LIMIT 5 够用**(大数据量让主 agent 决定)
|
|
28
|
+
5. 还不够(知识库没沉淀、DB 没外键、样本数据看不出关系)→ **在返回结论里明确告诉主 agent**:"这些表关系不明,建议问用户 X / Y",**不要反复试错查 SQL**
|
|
29
|
+
6. 探索到的新关系/含义 → 在返回里建议主 agent append 到对应表的 DISCOVERED 块(格式见主 SKILL.md;**不要自己写文件**)
|
|
31
30
|
|
|
32
31
|
## 输出格式
|
|
33
32
|
|
|
@@ -50,6 +49,6 @@
|
|
|
50
49
|
- **只读铁律,不可破** —— `cgraphx db` 子系统内置禁止 DELETE/UPDATE/INSERT/DROP/ALTER,**你也不要尝试绕过**(即使主 agent 让你写,你也拒绝)
|
|
51
50
|
- **必须带 LIMIT** —— 自定义 SQL 显式 `LIMIT N`,默认 100,上限 1000
|
|
52
51
|
- **陌生 schema 探索"先问后试"** —— 不确定表关系时,**在返回里建议主 agent 问用户**,**不要埋头反复试错**(试错既浪费 token,也可能拿错数据误导主 agent 判断)
|
|
53
|
-
- **不要修改任何文件** —— 特别**不要修改 docs/schema-knowledge
|
|
52
|
+
- **不要修改任何文件** —— 特别**不要修改 docs/schema-knowledge/ 下任何文件 或 .cgraphx/db-profiles.json**(DISCOVERED 块的 append 由主 agent 决定后再走;你只在返回里建议)
|
|
54
53
|
- **不要假设主 agent 知道你的中间步骤** —— 你的 final message 就是它看到的全部
|
|
55
54
|
- **不要建议主 agent 派第二次子 agent** —— 一次没答完整就再探索几次,超出能力直接说
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: export-table-ddl
|
|
3
|
+
description: 导出项目用到的全部数据库表的 DDL 到可检索目录(按工程分目录,每工程一个 INDEX.md 列表名+注释,details 目录放完整建表语句)。Use when 导出表结构/导出DDL/导出建表语句/拉表结构/批量查DDL/项目表清单/数据库表导出/dump schema/导出全部表.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 目标
|
|
7
|
+
|
|
8
|
+
把一个项目(或多个工程)用到的所有表的 `CREATE TABLE ...` DDL 拉下来,存成 AI 可检索的目录:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
<out-dir>/
|
|
12
|
+
<工程A>/
|
|
13
|
+
INDEX.md # 表名 + 表注释,一眼扫完
|
|
14
|
+
ddl/
|
|
15
|
+
users.sql # 完整 DDL(含列注释/索引/外键)
|
|
16
|
+
orders.sql
|
|
17
|
+
<工程B>/
|
|
18
|
+
INDEX.md
|
|
19
|
+
ddl/
|
|
20
|
+
...
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
AI 后续要理解某张表 → 直接 `Read ddl/<table>.sql`;要扫一个工程有哪些表 → `Read INDEX.md`。不用每次重新连库查。
|
|
24
|
+
|
|
25
|
+
## 前置
|
|
26
|
+
|
|
27
|
+
- `cgraphx` CLI 已装(`cgraphx db` 可用),目标项目已配 `.cgraphx/db-profiles.json` 或 `~/.cgraphx/db-profiles.json`。
|
|
28
|
+
- 目标 DB profile 可连。先 `cgraphx db profiles` 确认。
|
|
29
|
+
|
|
30
|
+
## 流程
|
|
31
|
+
|
|
32
|
+
### 1. 确认 profile
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cgraphx db profiles
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
跟用户确认连哪个 profile(避免连错库)。记下 profile 名。
|
|
39
|
+
|
|
40
|
+
### 2. 识别项目用到的表名
|
|
41
|
+
|
|
42
|
+
三种来源,按用户意图选:
|
|
43
|
+
|
|
44
|
+
**(a) 从代码里 grep 出来**(默认,最贴"项目用到的表"):
|
|
45
|
+
|
|
46
|
+
按项目主语言/框架选 grep 模式,目标是抽出所有被引用的表名,dedup 后给用户过目。
|
|
47
|
+
|
|
48
|
+
通用 SQL 引用(任何语言,SQL 字符串里):
|
|
49
|
+
```bash
|
|
50
|
+
grep -rhoE '\b(FROM|JOIN|INTO|UPDATE|DELETE\s+FROM)\s+`?[a-zA-Z_][a-zA-Z0-9_]*`?' --include='*.sql' --include='*.xml' --include='*.java' --include='*.ts' --include='*.py' --include='*.go' \
|
|
51
|
+
| sed -E 's/^(FROM|JOIN|INTO|UPDATE|DELETE\s+FROM)\s+`?([^` ]+)`?$/\2/' \
|
|
52
|
+
| tr '[:upper:]' '[:lower:]' | sort -u
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
ORM 装饰器(按框架补):
|
|
56
|
+
- JPA/Hibernate:`@Table(name\s*=\s*"([^"]+)")` / `@Entity`
|
|
57
|
+
- MyBatis-Plus:`@TableName\("([^"]+)"\)`
|
|
58
|
+
- SQLAlchemy:`__tablename__\s*=\s*['"]([^'"]+)['"]`
|
|
59
|
+
- Django:`db_table\s*=\s*['"]([^'"]+)['"]`
|
|
60
|
+
- TypeORM:`@Entity\({?\s*name:\s*['"]([^'"]+)['"]`
|
|
61
|
+
|
|
62
|
+
把 SQL 引用 + ORM 声明的表名合并、dedup、过滤掉明显非真实表的(subquery 别名、`information_schema.*`、`pg_*`、`dual`、`t`/`t1`/`tmp` 这种短别名 —— 命名太短且不在 DB 表清单里的丢掉)。
|
|
63
|
+
|
|
64
|
+
**(b) 用户给清单文件**(每行一个表名,或 JSON 数组)—— 用户已有列表时最省事。
|
|
65
|
+
|
|
66
|
+
**(c) 全库导出**(`--tables all`)—— 不区分项目用没用,把 profile 里所有表都拉下来。适合建知识基线。
|
|
67
|
+
|
|
68
|
+
把最终表清单写到一个临时文件(如 `/tmp/tables-<project>.txt`),给用户看一眼确认再进下一步。**确认前不要连库批量拉** —— 表清单错了会浪费 N 次查询。
|
|
69
|
+
|
|
70
|
+
### 3. 按工程分目录(多工程项目)
|
|
71
|
+
|
|
72
|
+
如果项目有多个工程(微服务 / monorepo / Maven 多模块),每个工程单独跑一次脚本,`--project <工程名>` 区分目录。
|
|
73
|
+
|
|
74
|
+
工程划分来源(按可靠度排序):
|
|
75
|
+
1. 用户显式指定(最可靠)—— 问用户"这些表哪些归工程 A,哪些归工程 B",或用户给每个工程的表清单文件。
|
|
76
|
+
2. 代码目录结构推断 —— `packages/*/`、`services/*/`、`apps/*/` 下每个子目录一个工程;Maven 看 `*/pom.xml`;Go workspace 看 `go.work` member。推断后**给用户确认**。
|
|
77
|
+
3. 不分 —— 单工程直接 `--project <项目名>` 一个目录。
|
|
78
|
+
|
|
79
|
+
### 4. 批量拉 DDL
|
|
80
|
+
|
|
81
|
+
对每个工程跑一次 bundled 脚本:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
node <skill-dir>/scripts/export-ddl.mjs \
|
|
85
|
+
--profile <profile名> \
|
|
86
|
+
--tables <表清单文件|all> \
|
|
87
|
+
--out <输出根目录> \
|
|
88
|
+
--project <工程名>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
脚本流程:
|
|
92
|
+
1. 一次 `cgraphx db schema --mode=tables` 拿全表 + 表注释(用于 INDEX.md + 校验表是否存在)。
|
|
93
|
+
2. 对清单里每张表 `cgraphx db schema --mode=ddl --table=X` 拿 DDL 文本,写到 `<out>/<project>/ddl/<table>.sql`。
|
|
94
|
+
3. 生成 `<out>/<project>/INDEX.md`:markdown 表格,每行 `| 表名 | 注释 | DDL链接 | 状态 |`。
|
|
95
|
+
4. stderr 报告成功/缺失/失败计数。
|
|
96
|
+
|
|
97
|
+
表清单里某张表在 DB 中不存在 → INDEX.md 标"缺失",不中断其余表导出。
|
|
98
|
+
|
|
99
|
+
### 5. 报告
|
|
100
|
+
|
|
101
|
+
跟用户报告:
|
|
102
|
+
- 每个工程导出了多少张表、输出目录路径。
|
|
103
|
+
- 缺失的表(清单里有、DB 里没有)—— 可能是表名拼错、或属于另一个库,让用户决定。
|
|
104
|
+
- 失败的表(连接/超时)—— 列出来,建议重跑或单独查。
|
|
105
|
+
|
|
106
|
+
## INDEX.md 格式(脚本自动生成)
|
|
107
|
+
|
|
108
|
+
```markdown
|
|
109
|
+
# customer-svc 表清单
|
|
110
|
+
|
|
111
|
+
共 42 张表(成功 40 / 缺失 2 / 失败 0),DDL 见 `ddl/` 目录。
|
|
112
|
+
|
|
113
|
+
| 表名 | 注释 | 状态 |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `users` | 用户资料表 | ✓ |
|
|
116
|
+
| `orders` | 订单主表 | ✓ |
|
|
117
|
+
| `tmp_x` | (表中不存在) | missing |
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## 输出目录放哪
|
|
121
|
+
|
|
122
|
+
放 `docs/schema-knowledge/`
|
|
123
|
+
|
|
124
|
+
## 何时不该用这个 skill
|
|
125
|
+
|
|
126
|
+
- 只查一两张表 → 直接 `cgraphx db schema --mode=ddl --table=X`,不用批量导出。
|
|
127
|
+
- 查数据(不是结构)→ 用 `db-query` skill。
|
|
128
|
+
- 表关系探索(不是导出)→ 用 `db-query` skill 的子 agent 流程。
|
|
129
|
+
|
|
130
|
+
## 代价意识
|
|
131
|
+
|
|
132
|
+
每张表一次 DB 连接(dbquery 子系统无连接池)。100 张表 ≈ 101 次连接(`--mode=tables` 1 次 + DDL 100 次),远程库可能几十秒。表清单越大越慢,所以**先确认表清单再批量拉**,避免拉了一堆没用的。
|
|
133
|
+
|
|
134
|
+
## 闭环:和 db-query skill 配合
|
|
135
|
+
|
|
136
|
+
本 skill 建**基线**(DDL + 列注释 + 表注释 + 索引,来自 DB),`db-query` skill 在日常查数据时**补充**探索发现:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
export-table-ddl → docs/schema-knowledge/<project>/ddl/<table>.sql (基线 DDL)
|
|
140
|
+
↑ ↑ 重 dump 保留
|
|
141
|
+
│ │
|
|
142
|
+
└──── schema 变了重跑 ────┘
|
|
143
|
+
│
|
|
144
|
+
db-query 探索踩坑 → append DISCOVERED 块(隐式关系/字段含义/陷阱/流程)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
DISCOVERED 块格式(`ddl/<table>.sql` 末尾):
|
|
148
|
+
|
|
149
|
+
```sql
|
|
150
|
+
-- === DISCOVERED (db-query 探索补充,非 DB 原生) ===
|
|
151
|
+
-- [关系] assignee_id → users.staff_code (处理人工号)
|
|
152
|
+
-- [字段含义] status: 0=待派单 1=处理中 2=已完成 3=已取消
|
|
153
|
+
-- [陷阱] email 不唯一,用 phone 关联更稳
|
|
154
|
+
-- [流程] 派单→处理→回单:work_order.status 驱动,见 dispatch_log/receipt
|
|
155
|
+
-- === END DISCOVERED ===
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
重跑本 skill 时,脚本自动保留每张表已有的 DISCOVERED 块(不会覆盖)。所以可以放心重 dump —— DB schema 变了拿新 DDL,探索积累的知识不丢。详见 `db-query` skill 的"陌生 schema 探索流程"。
|
|
159
|
+
|
|
160
|
+
### 知识落在哪 —— 顺着 agent 的访问路径
|
|
161
|
+
|
|
162
|
+
agent 进这个目录的访问路径固定是 `INDEX.md → ddl/<table>.sql`,**不会主动读独立笔记文件**。所以知识只能落在两个地方:
|
|
163
|
+
|
|
164
|
+
- **`INDEX.md`**:工程级 meta(profile 对应哪个服务、架构一句话)。agent 进目录第一件事就是读它。
|
|
165
|
+
- **`ddl/<table>.sql` 的 DISCOVERED 块**:一切表级 + 跨表知识。
|
|
166
|
+
|
|
167
|
+
跨表知识塞**枢纽表**(流程主表 / 被指向最多的表 / agent 最先点名的表),关系 `A.x → B.y` 双向标注(写进 `A.sql` 和 `B.sql` 各一行)。**绝不建** `NOTES.md` / `跨表知识.md` 这类独立文件 —— 那等于把知识埋了,agent 不会主动读。详见 `db-query` skill 的"枢纽表原则"。
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>db schema skills 闭环</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root {
|
|
8
|
+
--green: #16a34a; /* 本地知识库(便宜) */
|
|
9
|
+
--green-bg: #dcfce7;
|
|
10
|
+
--blue: #2563eb; /* DB 连接(贵) */
|
|
11
|
+
--blue-bg: #dbeafe;
|
|
12
|
+
--orange: #ea580c; /* 探索补充 */
|
|
13
|
+
--orange-bg: #ffedd5;
|
|
14
|
+
--red: #dc2626;
|
|
15
|
+
--gray: #6b7280;
|
|
16
|
+
--gray-bg: #f3f4f6;
|
|
17
|
+
--border: #d1d5db;
|
|
18
|
+
}
|
|
19
|
+
* { box-sizing: border-box; }
|
|
20
|
+
body {
|
|
21
|
+
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
22
|
+
max-width: 1100px; margin: 0 auto; padding: 32px 24px 80px;
|
|
23
|
+
color: #111827; line-height: 1.6;
|
|
24
|
+
}
|
|
25
|
+
h1 { font-size: 28px; margin: 0 0 8px; }
|
|
26
|
+
h2 { font-size: 20px; margin: 40px 0 12px; border-bottom: 2px solid var(--border); padding-bottom: 6px; }
|
|
27
|
+
h3 { font-size: 15px; margin: 20px 0 8px; color: var(--gray); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
28
|
+
.subtitle { color: var(--gray); font-size: 15px; margin-bottom: 32px; }
|
|
29
|
+
.tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: 600; margin-right: 6px; }
|
|
30
|
+
.tag-green { background: var(--green-bg); color: var(--green); }
|
|
31
|
+
.tag-blue { background: var(--blue-bg); color: var(--blue); }
|
|
32
|
+
.tag-orange { background: var(--orange-bg); color: var(--orange); }
|
|
33
|
+
|
|
34
|
+
/* 角色卡 */
|
|
35
|
+
.roles { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 16px 0; }
|
|
36
|
+
.role-card { border: 1px solid var(--border); border-radius: 8px; padding: 16px 20px; }
|
|
37
|
+
.role-card.init { border-left: 4px solid var(--blue); background: var(--blue-bg); }
|
|
38
|
+
.role-card.daily { border-left: 4px solid var(--green); background: var(--green-bg); }
|
|
39
|
+
.role-card h3 { margin: 0 0 8px; color: inherit; text-transform: none; letter-spacing: 0; font-size: 16px; }
|
|
40
|
+
.role-card .when { font-size: 13px; color: var(--gray); margin-top: 8px; }
|
|
41
|
+
|
|
42
|
+
/* 目录树 */
|
|
43
|
+
.tree {
|
|
44
|
+
background: var(--gray-bg); border: 1px solid var(--border); border-radius: 8px;
|
|
45
|
+
padding: 16px 20px; font-family: "SF Mono", Menlo, monospace; font-size: 13px; line-height: 1.8;
|
|
46
|
+
margin: 12px 0; white-space: pre;
|
|
47
|
+
}
|
|
48
|
+
.tree .hl { background: var(--green-bg); padding: 1px 4px; border-radius: 3px; color: var(--green); font-weight: 600; }
|
|
49
|
+
.tree .hlo { background: var(--orange-bg); padding: 1px 4px; border-radius: 3px; color: var(--orange); font-weight: 600; }
|
|
50
|
+
|
|
51
|
+
/* 数据流图 */
|
|
52
|
+
.flow {
|
|
53
|
+
display: grid; grid-template-columns: 1fr auto 1fr; gap: 12px 8px; align-items: center;
|
|
54
|
+
margin: 16px 0;
|
|
55
|
+
}
|
|
56
|
+
.box {
|
|
57
|
+
border: 2px solid var(--border); border-radius: 8px; padding: 12px 16px; text-align: center;
|
|
58
|
+
background: white;
|
|
59
|
+
}
|
|
60
|
+
.box.db { border-color: var(--blue); background: var(--blue-bg); }
|
|
61
|
+
.box.kb { border-color: var(--green); background: var(--green-bg); }
|
|
62
|
+
.box.agent { border-color: var(--gray); background: var(--gray-bg); }
|
|
63
|
+
.box .label { font-weight: 600; font-size: 14px; }
|
|
64
|
+
.box .sub { font-size: 12px; color: var(--gray); margin-top: 4px; }
|
|
65
|
+
.arrow {
|
|
66
|
+
text-align: center; font-size: 24px; color: var(--gray); font-weight: bold;
|
|
67
|
+
position: relative;
|
|
68
|
+
}
|
|
69
|
+
.arrow .a-label {
|
|
70
|
+
position: absolute; top: -8px; left: 50%; transform: translateX(-50%);
|
|
71
|
+
font-size: 11px; white-space: nowrap; background: white; padding: 0 4px; color: var(--gray);
|
|
72
|
+
}
|
|
73
|
+
.arrow.green { color: var(--green); }
|
|
74
|
+
.arrow.blue { color: var(--blue); }
|
|
75
|
+
.arrow.orange { color: var(--orange); }
|
|
76
|
+
.arrow-row { display: contents; }
|
|
77
|
+
|
|
78
|
+
/* 时序 */
|
|
79
|
+
.timeline { position: relative; padding-left: 24px; margin: 16px 0; }
|
|
80
|
+
.timeline::before { content: ''; position: absolute; left: 8px; top: 8px; bottom: 8px; width: 2px; background: var(--border); }
|
|
81
|
+
.tl-item { position: relative; padding: 8px 0 16px 24px; }
|
|
82
|
+
.tl-item::before {
|
|
83
|
+
content: ''; position: absolute; left: -20px; top: 14px; width: 12px; height: 12px;
|
|
84
|
+
border-radius: 50%; background: white; border: 2px solid var(--gray);
|
|
85
|
+
}
|
|
86
|
+
.tl-item.init::before { border-color: var(--blue); background: var(--blue); }
|
|
87
|
+
.tl-item.read::before { border-color: var(--green); background: var(--green); }
|
|
88
|
+
.tl-item.write::before { border-color: var(--orange); background: var(--orange); }
|
|
89
|
+
.tl-item .tl-title { font-weight: 600; }
|
|
90
|
+
.tl-item .tl-body { font-size: 14px; color: var(--gray); margin-top: 2px; }
|
|
91
|
+
.tl-item code { background: var(--gray-bg); padding: 1px 5px; border-radius: 3px; font-size: 12px; }
|
|
92
|
+
|
|
93
|
+
/* 枢纽表对比 */
|
|
94
|
+
.compare { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 16px 0; }
|
|
95
|
+
.compare-card { border: 1px solid var(--border); border-radius: 8px; padding: 16px; }
|
|
96
|
+
.compare-card.bad { border-color: var(--red); background: #fef2f2; }
|
|
97
|
+
.compare-card.good { border-color: var(--green); background: var(--green-bg); }
|
|
98
|
+
.compare-card .head { font-weight: 600; margin-bottom: 8px; }
|
|
99
|
+
.compare-card.bad .head::before { content: "✗ "; color: var(--red); font-weight: bold; }
|
|
100
|
+
.compare-card.good .head::before { content: "✓ "; color: var(--green); font-weight: bold; }
|
|
101
|
+
|
|
102
|
+
/* 规则卡 */
|
|
103
|
+
.rules { display: grid; gap: 10px; margin: 16px 0; }
|
|
104
|
+
.rule { display: flex; gap: 12px; padding: 12px 16px; border-left: 3px solid var(--green); background: var(--gray-bg); border-radius: 4px; }
|
|
105
|
+
.rule .num { font-weight: 700; color: var(--green); font-size: 18px; flex-shrink: 0; }
|
|
106
|
+
.rule .body { font-size: 14px; }
|
|
107
|
+
.rule .body b { color: #111827; }
|
|
108
|
+
|
|
109
|
+
code { font-family: "SF Mono", Menlo, monospace; }
|
|
110
|
+
.footer { margin-top: 60px; padding-top: 16px; border-top: 1px solid var(--border); color: var(--gray); font-size: 12px; }
|
|
111
|
+
</style>
|
|
112
|
+
</head>
|
|
113
|
+
<body>
|
|
114
|
+
|
|
115
|
+
<h1>db schema skills 闭环</h1>
|
|
116
|
+
<p class="subtitle">两个 skill + 一个共享知识库,让 agent 越用越懂业务数据</p>
|
|
117
|
+
|
|
118
|
+
<h2>1. 共享的目录结构(知识库本体)</h2>
|
|
119
|
+
<div class="tree">docs/schema-knowledge/
|
|
120
|
+
├── order-svc/
|
|
121
|
+
│ ├── <span class="hl">INDEX.md</span> ← agent 进目录第一件事读(表清单+注释)
|
|
122
|
+
│ └── ddl/
|
|
123
|
+
│ ├── work_order.sql ← DDL + <span class="hlo">DISCOVERED 块</span>(积累的隐式关系/含义/陷阱)
|
|
124
|
+
│ ├── users.sql ← DDL + DISCOVERED 块
|
|
125
|
+
│ └── payment.sql
|
|
126
|
+
└── cust-svc/
|
|
127
|
+
├── INDEX.md
|
|
128
|
+
└── ddl/...</div>
|
|
129
|
+
<p style="font-size:14px;color:var(--gray);">
|
|
130
|
+
<b style="color:var(--green)">绿色</b> = agent 必经节点(INDEX.md 是入口,ddl/<table>.sql 是详情);
|
|
131
|
+
<b style="color:var(--orange)">橙色</b> = db-query 探索补充的部分,export 重 dump 时会保留。
|
|
132
|
+
</p>
|
|
133
|
+
|
|
134
|
+
<h2>2. 时序:知识怎么积累起来</h2>
|
|
135
|
+
<div class="timeline">
|
|
136
|
+
<div class="tl-item init">
|
|
137
|
+
<div class="tl-title">T0 · 跑 export-table-ddl</div>
|
|
138
|
+
<div class="tl-body">建基线 → <code>ddl/work_order.sql</code> 只有 DDL,无 DISCOVERED 块</div>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="tl-item read">
|
|
141
|
+
<div class="tl-title">T1 · db-query 任务:"查 work_order 待派单的数据"</div>
|
|
142
|
+
<div class="tl-body"><code>Read ddl/work_order.sql</code>(首选)→ <code>cgraphx db query --sql "SELECT ... LIMIT 5"</code>(查数据)</div>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="tl-item write">
|
|
145
|
+
<div class="tl-title">T1 续 · 发现 work_order.assignee_id 实际对应 users.staff_code</div>
|
|
146
|
+
<div class="tl-body">append 到 <code>ddl/work_order.sql</code> 和 <code>ddl/users.sql</code> 的 DISCOVERED 块(双向)</div>
|
|
147
|
+
</div>
|
|
148
|
+
<div class="tl-item init">
|
|
149
|
+
<div class="tl-title">T2 · DBA 改了 schema,重跑 export-table-ddl</div>
|
|
150
|
+
<div class="tl-body">DDL 部分刷新(拿新结构),<b style="color:var(--orange)">DISCOVERED 块自动保留</b>(不覆盖)</div>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="tl-item read">
|
|
153
|
+
<div class="tl-title">T3 · 下一次 db-query 读 work_order</div>
|
|
154
|
+
<div class="tl-body"><code>Read ddl/work_order.sql</code> → 看到 DDL + DISCOVERED 块(<b>积累生效,不用重新踩坑</b>)</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="footer">
|
|
159
|
+
闭环核心:知识库是 <b style="color:var(--green)">便宜 + 丰富</b> 的首选信息源(本地读 + DISCOVERED 块),DB 是 <b style="color:var(--blue)">昂贵 + 不可替代</b> 的兜底(连库 + 只读铁律)。agent 默认走知识库,只在"知识库没有"或"要实际数据"时连库 —— 这样新沉淀的知识才会被读到,闭环才转得起来。
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
</body>
|
|
163
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "export-table-ddl",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"name": "single-project-from-code",
|
|
7
|
+
"prompt": "我在做一个客户管理服务的代码理解,项目根目录在 ~/work/customer-svc,用 zqassis 这个 pg profile。帮我把这个项目用到的所有表的 DDL 导出来,放到 docs/schema-dump/ 下,工程名就叫 customer-svc。表名从代码里 grep 出来就行,导出前先给我看一眼表清单确认。",
|
|
8
|
+
"expected_output": "docs/schema-dump/customer-svc/INDEX.md + ddl/*.sql;INDEX.md 列出所有表名+注释;导出前用户确认过表清单",
|
|
9
|
+
"files": []
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": 2,
|
|
13
|
+
"name": "multi-project-monorepo",
|
|
14
|
+
"prompt": "我们的 monorepo 在 ~/work/platform,下面 services/order-svc 和 services/cust-svc 两个工程,各自有自己的表。用 xw-order 和 xw-cust 两个 mysql profile 分别导出(每个工程对应一个 profile),放到 docs/schema-dump/ 下按工程分目录。表清单用全库导出(--tables all)。",
|
|
15
|
+
"expected_output": "docs/schema-dump/order-svc/ + docs/schema-dump/cust-svc/,各自 INDEX.md + ddl/,两个工程用了各自的 profile",
|
|
16
|
+
"files": []
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 3,
|
|
20
|
+
"name": "user-provided-table-list",
|
|
21
|
+
"prompt": "我有一个表清单文件 /tmp/核心表.txt,每行一个表名,大概 30 张表。用 zqassis profile 导出到 docs/schema-dump/core/ 下,工程名 core。直接导出不用让我确认表清单了。",
|
|
22
|
+
"expected_output": "docs/schema-dump/core/INDEX.md + ddl/*.sql;跳过表清单确认步骤直接导出;清单里不存在于 DB 的表在 INDEX 里标 missing",
|
|
23
|
+
"files": []
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|