Skip to content

数据库

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_setttl_secs 参数当前被忽略。

插件 DB:一行打开

插件 dylib 入口拿到 PluginInit,保存 namedata_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,随便 CRUD

PluginDb::open 自动做:create_dir_all 子目录 → sqlite:...?mode=rwc 连接 → PRAGMA journal_mode=WAL; busy_timeout=5000Migrator::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 真接入后,插件代码无需改动。