@ diepagents/ text2sql
AI动力自然语言到SQL. 用简单的英语提问,获得可执行查询.
特征
- SQL 的自然语言 - 将问题转换为已验证、可执行的查询
- 数据库适配器 - PostgreSQL, SQLite, DuckDB, SQL 服务器, MySQL/MariaDB, BigQuery, ClickHouse, 以及 PostHog HogQL 适配器
- Schema 反思 - 自动回顾表格、关系、指数和制约因素
- 域知识 - 通过碎片输入业务术语、护栏和查询模式
- 对话 - 多回合对话与持续的背景
- 默认安全 - 仅读查询、验证和可配置的护栏
安装
npm install @deepagents/text2sql
安装匹配适配器的数据库驱动程序或客户端库 :
npm install pg # PostgreSQL
npm install mssql # SQL Server
npm install mysql2 # MySQL / MariaDB
npm install @duckdb/node-api # DuckDB
npm install @google-cloud/bigquery # BigQuery
npm install @clickhouse/client # ClickHouse, or use your preferred client
# PostHog uses native fetch and needs no additional client package
电子表格适配器需要 xlsx 用于Excel、CSV和TSV文件。 没错
可选对等依赖; 仅在使用时安装
@deepagents/text2sql/spreadsheet。用 npm 12,把这个添加到您的项目中
.npmrc 允许直接声明的 URL 依赖性 :
allow-remote=root
然后安装工作表JS xlsx 发文日期:
npm install https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
需要节点.js LTS.
快开始
import { groq } from '@ai-sdk/groq';
import pg from 'pg';
import { Text2Sql, toSql } from '@deepagents/text2sql';
import {
Postgres,
columnValues,
constraints,
indexes,
info,
rowCount,
tables,
views,
} from '@deepagents/text2sql/postgres';
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
});
const adapter = new Postgres({
execute: async (sql) => {
const result = await pool.query(sql);
return result.rows;
},
grounding: [
tables(),
views(),
info(),
indexes(),
constraints(),
rowCount(),
columnValues(),
],
});
const text2sql = new Text2Sql({ adapters: { main: adapter } });
const fragments = await text2sql.index({ names: ['main'] });
const { sql } = await toSql({
input: 'Show me the top 10 customers by revenue',
adapter,
fragments,
model: groq('openai/gpt-oss-20b'),
});
console.log(sql);
index() 内观数据库的图案,并将其作为上下文片段返回.
toSql() 将这些碎片交给模型并验证该模型的SQL:
模型写作.
适配器- 映射键( E)main 此处为适配器名. 重用同样的密钥
text2sql.index({ names: ['main'] }) 在任何情况下 sql validate <db> "..." 编号 :
sql run <db> "..." 从安装 CLI 软件包的沙盒中调用.
这是配置的连接名, 而不是 SQL 级数据库或计划
名称( 例如, SQLite 默认值) main 计划.
适配器名称必须匹配 /^[A-Za-z_][A-Za-z0-9_]*$/。如果您构建适配器
动态地图, 使用 isValidAdapterName(name) 检查一个密钥或
validateAdapterNames(names) 在构建前快速失败 Text2Sql 或者说
沙盒边适配器模块 sql CLI(英语:CLI).
AI 模型提供者
Text2SQL 与任何模型提供者合作,由该模型支持 维塞尔 AI SDK包括OpenAI,Anthropic,Google,Groq等等.
搭建谈话代理
Text2Sql 拥有SQL生成和计划索引,但您拥有聊天回路.
通过编组一个 ContextEngine一个沙盒,和 agent + 键
chat 从 @deepagents/context数字 :
import {
ContextEngine,
type ContextFragment,
InMemoryContextStore,
agent,
chat,
createBashTool,
createDockerSandbox,
errorRecoveryGuardrail,
npm,
user,
} from '@deepagents/context';
import {
createSqlCommandHooks,
instructions,
sqlValidateReminder,
} from '@deepagents/text2sql';
const store = new InMemoryContextStore();
const context = new ContextEngine({
store,
chatId: 'chat-123',
userId: 'user-456',
});
const backend = await createDockerSandbox({
installers: [npm('@deepagents/text2sql', { ensureRuntime: true })],
volumes: [
{
type: 'bind',
hostPath: process.cwd(),
containerPath: '/workspace',
readOnly: true,
},
],
env: {
TEXT2SQL_ADAPTERS: '/workspace/text2sql-adapters.ts',
TEXT2SQL_OUT_DIR: '/tmp/text2sql',
},
});
const sandbox = await createBashTool({
sandbox: backend,
...createSqlCommandHooks({ adapters: { main: adapter } }),
});
const indexResult = await sandbox.sandbox.executeCommand('sql index');
if (indexResult.exitCode !== 0) throw new Error(indexResult.stderr);
const manifest = JSON.parse(indexResult.stdout) as { fragmentsPath: string };
const fragments = JSON.parse(
await sandbox.sandbox.readFile(manifest.fragmentsPath),
) as ContextFragment[];
context.set(...instructions(), ...fragments);
context.set(sqlValidateReminder());
const ai = agent({
name: 'sql-assistant',
sandbox,
model: groq('openai/gpt-oss-20b'),
context,
guardrails: [errorRecoveryGuardrail],
maxGuardrailRetries: 3,
});
await context.continue(user('Show me the top 10 customers by revenue'));
const stream = await chat(ai);
for await (const chunk of stream) {
// handle streaming response
}
这个 /workspace/text2sql-adapters.ts 模块必须存在于沙盒和
默认导出您的适配器映射 。 将您的工程挂入 /workspace (同上)
在调用前上传或写入该模块 sql index与远程 Docker 连接
守护进程,绑起的挂载 hostPath 必须在守护进程的主机上存在.
TEXT2SQL_OUT_DIR 将图案和查询文物保存在可写作的沙盒中
/tmp/text2sql 目录。 默认 ./sql 将在只读的
/workspace 挂载.
instructions() 返回 SQL 味的系统碎片(政策、工作流程,
澄清、样式指南)——将其推广到 context.set() 与
计划碎片由 sql index。索引输出起始于
available_databases 块列出精确配置的适配器名称
模型必须传递给 sql validate <db> 财务报告和审定财务报表 sql run <db>中添加或替换
指令,并按需要使用自己的域片段.
sqlValidateReminder() 返回由
SQL_VALIDATE_REMINDER。在成功之后 sql run <db> "...",它增加了一个
在没有前款时提醒下一代之前只使用模型的工具输出
sql validate <db> "..." 使用相同的配置适配器名称和精确的 SQL
语句。 原始 SQL 命令结果保持不变 。 流出期间 chat()
返回时,提醒会一直作为用于重放的合成用户信息
快速缓存; 赤裸 createPrepareStep() 一体化和
agent.generate() 自己坚持他们创造的助理历史.
高级: SQL CLI 在沙盒中
sql validate <db> "..." 财务报告和审定财务报表 sql run <db> "..." 是来自
@deepagents/text2sql 软件包. <db> 是已配置的适配器名称。 与一个
单个配置的适配器,错误的数据库选择器将路由到该唯一
适配器和命令打印注释; 多个适配器, 名称未知
失败,并打印可用的列表. sql index 写入 available_databases
片段、 计划片段和聊天设置的进度事件, 将全部索引
默认配置适配器(与 --all除非适配器名称是
提供.
在沙盒内安装软件包并设定 TEXT2SQL_ADAPTERS 到模块
默认导出为 Record<string, Adapter>缺少 sql 表示
沙盒准备不当 .
sql index 输出细节 :
stdoutJSON 显示fragmentsPath, (中文(简体) ),eventsPath,适配器,以及 碎片计数.--verbose pretty或者说--verbose json: 镜像进度事件stderr保留stdout以表报.--out-dir <path>:在该路径下写出文物(默认:$TEXT2SQL_OUT_DIR或者说./sql) (中文(简体) ).
这个 sql CLI 缓存仅在您通过 env 选择进入时进行内向跳动: set
TEXT2SQL_INDEX_CACHE_DIR (缓存文件所在)和/或
TEXT2SQL_INDEX_VERSION (一个无效的代号——当计划时撞出它)
页:1 没有设定,每个 sql index 内观出新. 见
Schema 索引缓存 页:1
根本的可注射原始生物.
对于进程内或虚拟沙盒的使用,安装 just-bash 并覆盖已存在的
Text2Sql 实例为自定义命令 :
import { InMemoryFs } from 'just-bash';
import { createVirtualSandbox } from '@deepagents/context';
import {
type CreateSqlCommandOptions,
type CreateSqlCommandResult,
Text2Sql,
createSqlCommand,
} from '@deepagents/text2sql';
const text2sql = new Text2Sql({ adapters: { main: adapter } });
const commandOptions: CreateSqlCommandOptions = {
outputDir: '/sql-artifacts',
};
const sqlCommand: CreateSqlCommandResult = createSqlCommand(
text2sql,
commandOptions,
);
const sandbox = await createVirtualSandbox({
fs: new InMemoryFs(),
customCommands: [sqlCommand.command],
});
await sandbox.executeCommand('sql validate main "SELECT 1"');
CreateSqlCommandOptions 配置命令默认值( 目前) outputDir) (中文(简体) ).
CreateSqlCommandResult 返回命令加一 repair(raw) 帮助
执行前将模型生成的 argv 恢复正常 .
展开 createSqlCommandHooks({ adapters }) 输入 createBashTool() 页:1
模型驱动的点击呼叫。 钩子之前保留
对常见的 LLM 引用错误的旧虚拟命令容忍度, 重写 SQL
标识符回击,所以击打不会运行它们作为命令替换,以及
区块 原始数据库访问, 仅读, 范围检查留后
sql validate 编号 : sql run后钩恢复隐藏 formattedSql
来自主机适配器映射的元数据,而不将这种关切放入真实
CLI(英语:CLI).
仅读执行解析单曲 SELECT编号 :WITH 语句即使当它
从空白或 SQL 注释开始( S)-- ..., (中文(简体) ), /* ... */) (中文(简体) ). 在此之前
适配器验证器或执行器运行, 方言政策拒绝写入
语句,多报表批次,解析失败, SELECT INTO,锁定
读取/内容、任务、已知状态/文件/扩展/远程访问功能,
大查询外部查询, 合格的大查询持续常规
点击之家表格函数或不安全的 UDF 来源 .
范围检查比较参考基准表和对各实体的看法
生产 tables() 财务报告和审定财务报表 views() 地盘。 无实体查询, 如
SELECT 1 不要仅仅为了解决空许可列表和元数据而运行地面
诸如 indexes(), (中文(简体) ), constraints(), (中文(简体) ), rowCount(), (中文(简体) ),
columnStats(),以及 columnValues() 不扩展注解方案背景
验证许可列表.
此解析策略是一个安全层, 而不是数据库权限边界 .
静态分析不能证明任意的不附带条件的用户定义函数是
无副作用, 和以其他方式有效的 SELECT 还能消耗过多
CPU,内存,字节,锁,或时间. 生产连接必须使用专用的
最低特权身份 SELECT 仅针对预期关系; 禁用或
保留扩展、文件、远程清空和例行执行能力;以及
配置适合方言的语句超时、字节/结果限制,以及
计算/计算限制.
DuckDB 适配器使用 DuckDB 本地的 JSON AST 而不是共享的 SQL
parser),因此DuckDB特定语法在其真实语法中被分析. 它决定
引用的每个引用关系 catalog.schema.table拒绝写道,
多报表批次、副作用或用户定义的函数和宏,
和任意的表格函数,然后将这些关系与表格进行比较
和通过定位产生的观点。 生产 DuckDB 连接必须仍然是
仅读,禁止外部访问和自动/社区扩展加载,
设置资源限制,设置后锁定配置.
PostHog适配器不会通过本地的SQL parser通过HogQL. 它问
PostHog 对每个查询进行解析 HogQLMetadata,检查返回的基础表
名称与被禁用的计划相对应 HogQLQuery 用于执行.
直接 adapter.execute(sql, values) 调用已命名的 HogQL 值
,用于外部添加的应用程序自有过滤器
模型循环。 外壳- 逃出美元前缀 PostHog 属性, 如
properties['\$device_type'] 在验证和执行前解码.
碎片
通过设置碎片来输入域知识 ContextEngine 你建起来
为代理。 这些碎片影响到每一个 chat() 转 用于直接 SQL
生成,给予 toSql({ fragments }) 碎片从 index() 还有你的
拥有碎片.
import {
ContextEngine,
InMemoryContextStore,
example,
guardrail,
hint,
term,
} from '@deepagents/context';
const store = new InMemoryContextStore();
const context = new ContextEngine({
store,
chatId: 'chat-123',
userId: 'user-456',
});
context.set(
term('MRR', 'monthly recurring revenue'),
hint('Always exclude test accounts with email ending in @test.com'),
guardrail({
rule: 'Never expose individual salaries',
reason: 'Confidential HR data',
action: 'Aggregate by department instead',
}),
example({
question: 'show me churned customers',
answer: `SELECT * FROM customers WHERE status = 'churned' ORDER BY churned_at DESC`,
}),
);
域块 (11类): term, (中文(简体) ), hint, (中文(简体) ), guardrail, (中文(简体) ), example, (中文(简体) ), explain, (中文(简体) ), clarification, (中文(简体) ), workflow, (中文(简体) ), quirk, (中文(简体) ), styleGuide, (中文(简体) ), analogy, (中文(简体) ), glossary。 。 。 .
用户碎片 (5类): identity, (中文(简体) ), persona, (中文(简体) ), alias, (中文(简体) ), preference, (中文(简体) ), correction。 。 。 .
见 @ diepagents/ context 用于完整文件.
设地
控制AI接收的元数据计划:
| 函数 | 说明 |
|---|---|
tables() | 表格、列和主键 |
views() | 数据库视图 |
info() | 数据库版本和信息 |
indexes() | 用于性能提示的索引信息 |
constraints() | 外国钥匙和其他制约因素 |
rowCount() | 表格大小(小、中、大、大) |
columnStats() | 列的最小/最大/最小分布 |
columnValues() | 具有样本值的等号和低心率列 |
对话
chat() (从) @deepagents/context(a) 持续的历史经过
ContextEngine 你为经纪人建造。 重新使用同一个商店, chatId,以及
userId 继续同样的线程。 在每次转弯之前,仅附加新的
正在发送信件到引擎; 请不要重放先前已经打开的转折
存储在上下文存储处 :
await context.continue(user('Show me orders from last month'));
const stream = await chat(ai);
for await (const chunk of stream) {
// handle streaming response
}
// Continue the same conversation
await context.continue(user('Now filter to only completed ones'));
const followUp = await chat(ai);
for await (const chunk of followUp) {
// handle streaming response
}
检查点文件
Checkpoint 从 @deepagents/text2sql 保存已完成的计算
管道可以恢复而不重复。 装入文件为
Checkpoint.load({ path, configHash, clock: globalThis }),然后使用 run() 页:1
一个计算或 each() 为一系列输入。 两者都需要一个编码器
编码输出并在恢复时予以验证; jsonCodec(schema) 处理平面
JSON价值. 可选 configHash 当两个结果同时保存时无效
文档和调用者提供不同之处的非空散列.
clock 需要控制文件替换重试之间的延迟
当文件忙的时候。 过 globalThis 用于使用Node.js计时器.
从一个写 Checkpoint 实例运行顺序。 每个字都捕捉到
启动时的最近状态, 所以较早的快照不能覆盖以后的快照
一个 检查站使用 durableWrite 在替换文件前冲出文件
同步支持的父目录 。 等着 run(), (中文(简体) ), each(),或
point().through() 离开前。 单独实例或进程不
坐标写入同一文件 .
Schema 索引缓存
司马入想是索引的昂贵部分. Text2Sql 财务报告和审定财务报表
AdapterIndexer 拥有没有存储。 你可以给他们一个缓存。 缓存密钥是
适配器名称 :
export interface IndexCache {
read(key: string): Promise<ContextFragment[] | null>;
write(key: string, fragments: ContextFragment[]): Promise<void>;
}
- 没有
cache: 每个index()调用内存数据库. - A 类
cache数字 :index()先读取缓存。 错的时候,它会回顾 数据库,然后将结果写入缓存.
软件包发送一个文件备份缓存 :
import { FileIndexCache, Text2Sql } from '@deepagents/text2sql';
const text2sql = new Text2Sql({
adapters: { main: adapter },
cache: new FileIndexCache({
clock: globalThis,
dir: '/var/cache/text2sql',
namespace: 'v1',
}),
});
FileIndexCache 写入临时文件并在缓存文件中重命名 。 A 类
缺少文件, 无效的 JSON, 或无法进行上下文分解验证的数据
缓存丢失, 所以下一个 index() 再次调用内向. clock 需要时间
并控制文件替换重试延迟; 通过 globalThis 要使用节点.js
计时器. dir 默认为 OS 临时目录。 可选 namespace
区分缓存世代; 当方案改变时更改它.
对同时打电话者的一次回顾
index() 不使召唤者互相等待。 当两个打电话的人错过了
同时缓存,两者都对数据库进行内置. 结果是留下的
正确, 因为每次写入都会替换整个缓存文件 .
只让一位来电者回顾一下, 获得一个钥匙周围 index()。此示例
用途 @zukhruf/mutex数字 :
import { Mutex } from '@zukhruf/mutex';
import { SqliteStore } from '@zukhruf/mutex/sqlite';
const mutex = new Mutex(
new SqliteStore('/var/lib/my-app/locks', { clock: globalThis }),
);
const fragments = await mutex.acquire('text2sql:main', () =>
text2sql.index({ names: ['main'] }),
);
呼叫者必须共享缓存目录,命名空间,以及适配器名称. 第一个
调用者对数据库进行回顾并写入缓存。 当每个等候者
获得相同的锁键,它的 index() 电话读取了缓存计划.
SqliteStore 在一个主机上共享进程之间的密钥。 舰队:
主机,从所有主机共享的服务获取密钥,例如 a
Postgres 咨询锁定, 并给予所有主机相同的缓存 .
关于存储对象的卷 (GCS/S3 FUSE)
rename这不是原子。 这个FileIndexCache解析行为容忍撕裂的文字:下一个index()再次调用内向.
指数的流化进展
AdapterIndexer#index({ onProgress }) 在温暖或读取时发布进步事件
用于主机侧索引的缓存。 沙盒 CLI 写着同样的事件
形状 eventsPath 返回的文件由 sql index数字 :
import { createUIMessageStream } from 'ai';
import {
AdapterIndexer,
TEXT2SQL_INDEX_PROGRESS_CHUNK,
type Text2SqlIndexProgressEvent,
} from '@deepagents/text2sql';
const indexer = new AdapterIndexer({ adapters });
await context.continue(user('Show me top 10 customers'));
const stream = createUIMessageStream({
execute: async ({ writer }) => {
const head = await context.headMessage();
if (!head || head.name !== 'assistant') {
throw new Error(
'expected head to be an assistant message — call context.continue() before chat()',
);
}
writer.write({ type: 'start', messageId: head.id });
const fragments = await indexer.index({
onProgress: (event) =>
writer.write({
type: TEXT2SQL_INDEX_PROGRESS_CHUNK,
data: event,
}),
});
context.set(...instructions(), ...fragments);
writer.merge(await chat(ai));
},
});
每个进度事件包括一个通用 Unix 时代 timestampMs 这样客户就可以
计算不包含文本2SQL的每个索引、每个适应器或每个相位持续时间
实行一个期限政策.
toSql() 无国籍。 它读取了目前的计划片断,但它没有
将信件、标题或用法写入上下文库.
带有额外碎片的直接 SQL 生成
这个低级的帮助仍然需要一个 adapter 因为它绕过
多适应器 Text2Sql 完全包装.
import { term } from '@deepagents/context';
import { toSql } from '@deepagents/text2sql';
const result = await toSql({
input: 'Show ARR by plan',
adapter,
model: groq('openai/gpt-oss-20b'),
fragments: [
...(await adapter.introspect()),
term('ARR', 'annual recurring revenue'),
],
});
console.log(result.sql);
文档
备有文件全文 (原始内容存档于2013-10-12) (日语). januarylabs.github.io/deepagents数字 :