接入数据源
把 Trove 接到你自己的库上。这一页讲两种接入方式(管理端 / 本地 CLI)、 六种数据源各自的注意点、以及决定准确率的那三件事—— 它们比选哪个模型重要得多。
整条路径长这样,后面每一节拆开讲其中一步:
flowchart TD
A["装驱动(extras 按需懒加载)"]:::entry --> B["注册:连接探测通过才落库"]:::entry
B --> C["/kb init 建模"]:::gen
C --> D["通读 YAML、校准 semantics.yml"]:::gen
D --> E["lint_kb.py 自检(可加活库探测)"]:::exec
E -- "发现问题" --> D
E -- "干净" --> F["提问 → /kb learn 沉淀"]:::entry
classDef entry fill:#71717a14,stroke:#71717a59,stroke-width:1px;
classDef gen fill:#6366f114,stroke:#6366f199,stroke-width:1.5px;
classDef exec fill:#14b8a614,stroke:#14b8a699,stroke-width:1.5px;
linkStyle 4 stroke:#8b5cf6,stroke-width:1.5px
前置条件
| 项 | 要求 |
|---|---|
| Python | ≥ 3.12 |
| Node | 仅在改前端时需要(^22.13.0 || >= 24.0.0) |
| 模型凭证 | /kb init 起草注释时需要;没有凭证会走骨架降级路径 |
| 数据库账号 | 强烈建议只读账号——理由见下 |
第一步:装驱动
驱动是 extras,按目标库选着装。Doris 复用 MySQL 协议驱动,所以两个 extra 的内容是一样的:
uv sync --extra mysql --extra doris # MySQL + Doris
uv sync --extra postgres # PostgreSQL(业务库 + 向量)
uv sync --extra clickhouse # ClickHouse
uv sync --extra duckdb # DuckDB
uv sync --extra snowflake # Snowflake
uv sync --extra bigquery # BigQuery
pyproject.toml:55 doris registry.py:42 _ADAPTER_REGISTRY
导入注册表本身不需要任何驱动——适配器模块在 connect() 里才导入驱动,
缺包时报错信息里会直接给出该跑哪条 uv sync。
连接串
| scheme | 默认端口 | 示例 |
|---|---|---|
| mysql | 3306 | mysql://user:pw@host:3306/db |
| doris | 9030 | doris://user:pw@host:9030/db |
| postgres postgresql | 5432 | postgres://user:pw@host:5432/db |
| clickhouse | 8123 | clickhouse://user:pw@host:8123/db |
| snowflake | — | snowflake://user:pw@account/DB/SCHEMA?warehouse=wh |
| bigquery | — | bigquery://project/dataset?location=US |
| sqlite | — | sqlite:///path/to.db |
| duckdb | — | duckdb:///path/to.duckdb |
urls.py:215 parse_datasource_url
网络 URL 的数据源名 = 库名;文件型(SQLite / DuckDB)用 scheme 作名字,
所以它们都叫 sqlite / duckdb。云仓的名字是身份的两段:
Snowflake 取 <库>.<模式>,BigQuery 取 <项目>.<数据集>——
同一个库里两个模式(数据集)是两个可查单元,只拿库名当名字会让第二次注册撞上「已存在」。
这个名字决定了 KB 目录
(.trove/kb/<name>/),改 URL 的库名等于换一个 KB,注意不要无意中改掉。
第二步:注册(两种方式)
方式 A:管理端(推荐,适合平台化)
- admin 登录 → 数据源页面 → 注册(内置 demo 或填 URL)。
- 注册是事务性的:先连接探测,成功才落库、才进注册表。失败返回 400 并说明原因。
- 对该源执行
kb/init——LLM 起草 schema 注释 + 确定性生成词表与模板。 - 用户端下拉/列表里才出现这个源(非 admin 还需要 grants 授权)。
admin.py:824 datasources admin.py:879 prepare admin.py:861 409
同名但连接信息不同的注册会被拒绝(409)而不是静默覆盖—— 覆盖一个数据源等于让它背后的 KB 对错库,这个错误很难事后发现。
方式 B:本地 CLI(适合自己试)
uv run trove --datasource mysql://root:root@127.0.0.1:3306/financial
# 进 REPL 后
/kb init
本地方式不写 .trove/datasources.yml,适合一次性探索。
要长期用还是走管理端注册,这样重启后自动恢复。
Doris 的三个差异
Doris 适配器继承自 MySQL 适配器——协议相同,所以流程完全一样,只有三处不同:
| 差异 | 说明 |
|---|---|
| URL scheme | doris://(而不是 mysql://) |
| 默认端口 | 9030(FE 的 MySQL 协议端口),不是 3306 |
| 探活方式 | FE 不支持 COM_PING,所以用 SELECT 1 代替 |
doris.py:23 DorisAdapter doris.py:20 DEFAULT_PORT doris.py:51 _ping_reconnect
连接时会探测并上报能力(是否支持事务、CTE、窗口函数),方言声明为 doris。
这些能力位会影响生成策略——比如不支持 CTE 时不该生成 CTE。
云仓(Snowflake / BigQuery)的差异
两个云仓适配器和上面五个的流程完全一样:装 extra → 注册 → kb init → 授权。
差异不在流程,在连接与执行细节上:
| 差异 | 说明 |
|---|---|
| 驱动形状 | 两家的官方驱动都只有同步版——适配器把每个调用整体包进 asyncio.to_thread,事件循环不被阻塞(其余适配器走原生异步驱动) |
| 连接串形状 | Snowflake:账号 + /<库>/<模式>,?warehouse=…&role=…&private_key_file=…;BigQuery:项目 + /<数据集>,?location=…&service_account_file=… |
| 凭据 | Snowflake 用 user/password 或私钥文件;BigQuery 只认 service account 文件或 ADC——连接串里带 user:password 会被拒绝而不是静默忽略:一组无效凭据必须响亮地失败 |
| 超时 | Snowflake 把整秒超时折进登录参数(STATEMENT_TIMEOUT_IN_SECONDS,DB 侧强制,重连同样带着);BigQuery 没有会话级时长机制,能力位如实声明不支持——超时由客户端侧管理 + 主动终止承担 |
| 元数据内省 | BigQuery 走 metadata API(list_tables + 逐表 get_table,免费、不起查询 job),不查 INFORMATION_SCHEMA(计费表,查它就是起一次扫描) |
| EXPLAIN | Snowflake 支持;BigQuery 声明不支持(GoogleSQL 没有 EXPLAIN 语句)——请求 explain 会被能力位拒绝,而不是发出一条必然报错的 SQL |
两者都支持主动终止(supports_interrupt):云仓的查询跑在服务端,
客户端只是无状态 REST 包装,停下来只能靠服务端手段——Snowflake 按我们注入的
requestId 反查后 abort,BigQuery 直接用 job 句柄 job.cancel()。
snowflake.py:148 SnowflakeAdapter bigquery.py:117 BigQueryAdapter urls.py:133 _cloud_datasource_name
第三步:决定准确率的三件事
这是这一页最重要的部分。换模型带来的差异,通常小于下面三件事做得好不好:
① /kb init 是根基
它决定了系统对这个库的初始理解:每张表每个字段是什么意思、有哪些指标、哪些枚举取值。 做完之后通读一遍生成的 YAML——LLM 起草的注释会有想当然的地方, 而错误的字段注释比没有注释更糟,因为它会被当真。
② 校准 semantics.yml
指标口径必须由懂业务的人确认。特别是:
- 指标的表达式是不是表限定的(
loans.amount,不是amount)——否则数据集归属推导不出来。 - 关系边上的
fan_out有没有正确标注——标错会导致该拒绝的没拒绝。
③ 用 /kb learn 沉淀
每次得到一个好答案,把它固化成示例。这是准确率增长的主要来源, 而且它跟着使用增长,不需要重新训练任何东西。
系统还有一条自校验闭环会帮你发现错误答案:生成的 SQL 会被实际执行、结果会过规则链、 这一轮有没有进步会被版本比对判定。发现的问题会变成 pending 的教训或示例, 等你确认。详见知识库。
权限:只读账号怎么给
Trove 在执行层有多重只读保护,但真正的安全边界是数据库侧的只读角色。 代码里对此有明确说明,不要把它当成「有防火墙就够了」。
guard.py:50 check_readonly registry.py:416 _guard_read_only
MySQL / Doris 侧的典型做法:
CREATE USER 'trove_ro'@'%' IDENTIFIED BY '...';
GRANT SELECT ON financial.* TO 'trove_ro'@'%';
-- 可选:限制单条查询的扫描量与超时
SET GLOBAL max_execution_time = 30000; -- MySQL 8
SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY'; -- 让含糊的分组直接报错
另外建议给这个账号配资源组/查询审计,因为 Trove 会执行模型生成的 SQL——
这些都是 SELECT,但复杂度不可控。
即使 Trove 有三层写保护(关键词正则、AST 防火墙、执行器统一门), 给一个只会 SELECT 的账号仍然是最省心的防线:被绕过的代价是零。
表级白名单(allowed_tables)是另一道可选收紧,在管理端配置。
registry.py:453 set_allowed_tables intent.py:107 has_strong_write
其他方言怎么建这个账号
-- PostgreSQL:默认 PUBLIC 对所有表有 CONNECT,但没有 SELECT
CREATE ROLE trove_ro LOGIN PASSWORD '...';
GRANT USAGE ON SCHEMA public TO trove_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO trove_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO trove_ro;
-- ClickHouse:READ 是组别名(含 SELECT/SHOW/DESCRIBE 等),比逐条 SELECT 稳
CREATE USER trove_ro IDENTIFIED BY '...';
GRANT READ ON *.* TO trove_ro;
-- 引擎侧再兜一层:这个用户即使写出写语句,也会被配额/只读设置拦下
ALTER USER trove_ro SETTINGS readonly = 1;
PostgreSQL 那三行的关键在第三行:默认权限。少了它,今天建的表都只读,
明天新建的表默认给 PUBLIC 什么都不加、但 Trove 也就查不到——看起来像权限
没配好,实则是漏了这条。
配好之后:让它自己确认一遍
启动时 Trove 会自己查一遍权限表,确认这个账号确实写不了,并把结论挂在探活端点上
(readonly 字段)。它不会靠「试写一条语句」来验证——
那条语句「必然失败」的前提正是「账号只读」这个还没被证明的假设,账号其实可写时它会真的写进去。
三种取值与结论口径见 可观测性。
| 方言 | 自检查什么 |
|---|---|
| MySQL / Doris | SHOW GRANTS,看权限清单里有没有写权限(表名撞上关键字不算) |
| PostgreSQL | 是否超级用户、是否是表的属主(属主天然可写)、table_privileges 里的写权限 |
| ClickHouse | system.grants,展开角色继承后再看有没有写权限 |
| Snowflake / BigQuery | 暂未实现权限表探测——结论是「无从判断」。探测缺失不会被渲染成「已确认只读」:那等于替一道并不存在的边界背书 |
| SQLite / DuckDB | 查不了——没有角色概念,边界是文件权限,结论是「无从判断」 |
接入后自检
uv run python scripts/lint_kb.py --db-id <name>
# 加上活库探测(会比对枚举值与真实 DISTINCT)
uv run python scripts/lint_kb.py --db-id <name> --datasource <url>
lint 会挑出「不报错但一直悄悄降低准确率」的问题:术语指向不存在的列、 示例 SQL 解析不了、声明的枚举值漏了真实存在的取值等等。 完整清单见知识库 · lint。