数据库
Flue 将智能体对话持久存储在数据库中,通过单个文件进行配置:db.ts。本指南介绍 Flue 存储的内容、db.ts 入口模块的工作方式、默认的内存存储及其限制、内置的 sqlite() 适配器,以及 Postgres、libSQL 和其他后端的生态系统适配器。
预先说明一点:数据库是 Node.js 的关注点。在 Cloudflare 目标上,每个智能体对话都是一个 Durable Object,拥有自己内置的 SQLite 存储——无需配置,db.ts 文件在构建时会被拒绝。请参阅 Cloudflare 了解那里的存储工作方式。
Flue 存储的内容
Flue 数据库存储运行时自身的持久状态——而非你的应用业务数据。其中有三种记录:
- 规范对话。 每个智能体对话是一条仅追加的记录流:用户消息、助手输出、工具调用和结果、压缩以及恢复事实。这条流是唯一的事实来源——后续的每一轮、每个重连的客户端、每次崩溃恢复都通过读取它来重建对话的画面。
- 已接受的提交。 当提示或
dispatch(...)输入被接受时,Flue 会在处理开始之前将其持久记录,同时记录声明和租约以跟踪哪个进程拥有该工作。这些行使得被接受的工作在中断后可恢复,而不是被静默丢失。 - 持久化状态。 每次
usePersistentState写入都会被记录在对话流中,这就是状态在对话生命周期内能够在重启后存活的原因。
附件负载(图片和其他二进制输入)作为不可变记录存储在对话旁边,规范流会引用它们。
Flue 不会 存储沙箱文件和已安装的依赖(参见 沙箱)、外部 API 副作用、提供商凭据或你应用自身的数据。持久数据库不会使沙箱持久化,持久工作区也不会保留对话历史——它们是不同的关注点。当工具写入你的应用数据库时,Flue 仅存储工具被调用及其返回值的记录。
db.ts 入口模块
要选择数据库,在项目的源码目录中创建一个 db.ts 文件,并默认导出一个持久化适配器:
import { sqlite } from '@flue/runtime/node';
export default sqlite('./data/flue.db');与 app.ts 一样,db.ts 入口通过约定发现——vite dev、vite build 和 flue run 都会从源码根目录(.flue/、src/ 或项目根目录)解析它并在启动时连接。要将其放在其他位置,请在配置文件中设置 db 路径;参见 配置。
因为该模块是普通的 TypeScript,适配器可以从环境变量读取连接字符串、构建驱动池并导出所需的内容。Flue 在启动时调用适配器的 migrate() 一次以创建或验证表,然后等待 connect()——因此不可达或配置错误的数据库会在启动时失败,而不是在第一次对话中间。
使用 start() 的独立脚本不经过构建,因此不会获取 db.ts——请通过 db 选项直接传递适配器。
默认的内存存储
没有 db.ts 时,Flue 使用内存中的 SQLite 运行。一切正常工作——对话、持久化状态、进程生命周期内的恢复——但 重启会丢失一切:每个对话、每个已接受的提交、每条状态。开发时没问题;在生产环境中,仅适用于真正可丢弃的智能体。
开发命令会软化这个默认行为:
| 命令 | 没有 db.ts 时的表现 |
|---|---|
vite dev | 使用缓存文件(node_modules/.cache/flue/dev.db)——历史在代码重载后保留,开发服务器冷启动时重置。 |
flue run | 使用缓存文件(node_modules/.cache/flue/run.db)——永不重置,因此 --id 可以跨调用继续对话。 |
vite build | 使用内存——部署的服务器仅在进程生命周期内保留状态。 |
有 db.ts 时,三者都使用你的适配器,因此开发运行与生产使用相同的存储结构。
内置的 sqlite() 适配器
sqlite() 适配器随运行时发布,无需额外依赖——它运行在 Node 内置的 node:sqlite 模块上。将其指向一个文件路径以获得在重启后存活的存储:
import { sqlite } from '@flue/runtime/node';
export default sqlite('./data/flue.db');适配器在首次启动时创建文件(以及任何缺失的父目录)并以 WAL 模式打开。不带参数调用 sqlite()——或传入 ':memory:'——会得到与默认相同的内存数据库。
基于文件的 SQLite 数据库适用于单主机部署:它能在进程重启和同一台机器上的重部署中存活,但无法在主机本身丢失时存活。当状态必须在主机丢失后存活,或多个副本需要共享状态时,请使用外部数据库。
生态系统适配器
Flue 为各大数据库生态系统发布了适配器,每个都以蓝图形式提供——一份由你的编码智能体执行的 Markdown 实现指南,而非包安装器。蓝图名称是后端的小写名称:
flue add database postgres
| 后端 | 适配器包 |
|---|---|
| Postgres | @flue/postgres |
| Supabase | @flue/postgres |
| libSQL | @flue/libsql |
| Turso | @flue/libsql |
| MySQL | @flue/mysql |
| MongoDB | @flue/mongodb |
| Redis | @flue/redis |
| Valkey | @flue/redis |
这些适配器共享自带驱动的设计:适配器实现 Flue 的存储契约,但它不选择、打包或配置数据库驱动。相反,你将配置好的驱动包装在一个小型运行器中——通常是一个 query 函数、一个 transaction 函数和一个 close 函数——然后交给适配器。你完全控制驱动选择、连接池、TLS 和凭据,Flue 与你应用现有的数据库通信方式共存:
import { postgres } from '@flue/postgres';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export default postgres({
query: async (text, params) => (await pool.query(text, params)).rows,
transaction: async (fn) => {
const client = await pool.connect();
try {
await client.query('BEGIN');
const result = await fn({
query: async (text, params) => (await client.query(text, params)).rows,
});
await client.query('COMMIT');
return result;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
},
close: () => pool.end(),
});每个蓝图为其生态系统的标准驱动生成一个完整的 db.ts,就像上面这样;每个生态系统页面记录了运行器契约和后端的持久化注意事项。
使用任何适配器都不需要手动运行迁移。migrate() 在首次启动时幂等地配置 Flue 的表,在重启时复用它们,并标记格式版本——不兼容的 Flue 版本写入的数据库会拒绝启动,而不是损坏状态。
共享数据库不支持主动 - 主动扩展。持久的外部数据库可以让替代进程恢复已接受的工作,可以让副本共享对话状态,但每个智能体对话仍然需要恰好一个活跃的 Node 所有者。请参阅 持久性 了解所有权规则和恢复实际回放的内容。
编写自定义适配器
如果你的后端不在目录中,可以自己实现存储契约。适配器是一个具有 connect()(返回三个存储——提交、对话流和附件)的对象,加上可选的 migrate() 和 close();类型定义在 @flue/runtime/adapter 中:
import type { PersistenceAdapter } from '@flue/runtime/adapter';
export default {
migrate() {
/* 创建或验证后备存储 */
},
connect() {
return { submissionStore, conversationStreamStore, attachmentStore };
},
close() {
/* 释放连接 */
},
} satisfies PersistenceAdapter;
该契约有严格的原子性和排序要求——幂等准入、围栏化生产者声明、仅追加流——因此请将数据持久化 API 作为规范,并使用 @flue/runtime/test-utils 中的契约测试套件来测试你的实现。它们是每个内置适配器都通过的验收测试。
选择数据库
| 场景 | 选择 |
|---|---|
| 本地开发 | 使用默认值——仅在需要针对生产存储开发时添加 db.ts |
| 单主机 Node 部署 | 基于文件的 sqlite() |
| 状态必须在主机丢失后存活,或有多个副本 | 生态系统适配器,每个对话路由一个活跃所有者 |
| Cloudflare 部署 | 无需配置——Durable Object SQLite 是自动的 |
| 目录中没有的后端 | 自定义 PersistenceAdapter |
后续步骤
- 持久性——中断后恢复回放的内容,以及单活跃所有者规则。
- 数据持久化 API——完整的适配器和存储契约。
- Postgres 和其他生态系统数据库页面——每个后端的设置、配置和注意事项。
- 在 Node.js 上部署智能体——在服务器旁配置数据库。
- 项目布局——
db.ts和其他入口模块的位置。