数据库
FoxCore 使用嵌入式数据库,零外部服务依赖。
技术选型
| 库 | 用途 | 说明 |
|---|---|---|
| SQLite(SeaORM) | 关系数据 | 消息记录、用户画像、情感状态 |
| Moka | 内存缓存 | 热数据缓存,带 TTL 自动过期 |
| LanceDB | 向量搜索 | 记忆系统的 embedding(计划中) |
| Grafeo | 图数据库 | 知识图谱、实体关系(计划中) |
存储隔离
data/
├── foxcore.db # 核心数据(+ .db-wal / .db-shm)
└── plugins/
└── <adapter_name>/ # 每个插件独立子目录
└── <adapter_name>.db # 插件独立 SQLite(+ WAL 辅助文件)- 核心 DB:由核心独占。消息自动入库、缓存、未来的用户/情感数据。插件通过
BusContext只读查询,写入约定仅核心自己做。 - 插件 DB:每个插件(以 dylib 文件名命名)拥有自己的子目录 + SQLite 文件。WAL 三件套锁在子目录内,互不干扰。插件开、插件关、插件跑 migration。
- 独立 SQLite 文件 = 独立写锁,核心和插件写入互不阻塞。
核心 DB:通过 BusContext 访问
查询消息
rust
use foxcore_api::bus::db_request::*;
let resp = ctx.db_query_messages(QueryMessagesRequest {
group_id: Some("67890".into()),
sender_id: None,
limit: 20,
before_timestamp: None,
}).await?;按 row_id 取单条
rust
if let Some(msg) = ctx.db_get_message(42).await? {
println!("{}", msg.content);
}缓存
rust
ctx.cache_set("key", "value", None).await?;
let val = ctx.cache_get("key").await?;Moka 内存缓存,进程重启即清空。TTL 由 [database.cache] ttl_secs 全局控制(默认 300s),cache_set 的 ttl_secs 参数当前被忽略。
插件 DB:一行打开
插件 dylib 入口拿到 PluginInit,保存 name 和 data_dir,在 start() 的 rt.spawn 里调 PluginDb::open 即可:
rust
use foxcore_api::adapter::{Adapter, PluginInit};
use foxcore_api::database::PluginDb;
#[unsafe(no_mangle)]
pub extern "Rust" fn foxcore_create_adapter(init: PluginInit<'_>) -> Box<dyn Adapter> {
let config: MyConfig = toml::from_str(init.config_toml).unwrap_or_default();
Box::new(MyAdapter::new(
config,
init.name.to_string(),
init.data_dir.to_path_buf(),
))
}
// 在 start() 的 rt.spawn 里(必须在插件自己的 runtime 中)
let pdb = PluginDb::open::<MyPluginMigrator>(&name, &data_dir).await?;
let conn = pdb.conn(); // &DatabaseConnection,随便 CRUDPluginDb::open 自动做:create_dir_all 子目录 → sqlite:...?mode=rwc 连接 → PRAGMA journal_mode=WAL; busy_timeout=5000 → Migrator::up。
插件只管定义 Entity + Migrator。完整 CRUD 示例见插件开发者文档(FoxCoreApi/skill/database-guide.md)。
为什么插件要自己打开而不是核心传连接
SeaORM 的 DatabaseConnection 基于 sqlx 连接池,池绑定创建时的 tokio runtime。插件是 dylib、自带 runtime,跨 dylib 传连接对象会 panic。所以 API 在 foxcore-api 里统一实现,执行动作在插件 runtime 里发生——调用方式仍然是一行。
配置
toml
[database.sqlite]
# 最大连接数(核心)
max_connections = 5
[database.cache]
# 最大缓存条目数
max_capacity = 10000
# 默认 TTL(秒)
ttl_secs = 300数据库路径硬编码(核心 data/foxcore.db,插件 data/plugins/<name>/<name>.db),不可配置。
Phase 2 预留
ctx.vector_search() / ctx.graph_query() API 表面已冻结,当前返回「未启用」错误。LanceDB / Grafeo 真接入后,插件代码无需改动。