aimem-core 0.3.1

Core library for AiMem — give your AI a memory. Vector search + knowledge graph over Turso.
Documentation

aimem-core

aimem-core 是 AiMem 的 Rust 核心库:负责把记忆存进一个 Turso 数据库里,并提供项目挖掘、会话导入、关键词/语义搜索、知识图谱和 wake-up context 这些基础能力。

它是库,不是 CLI。CLI 在 aimem-cli,MCP server 在 aimem-mcp。

安装

[dependencies]
tokio = { version = "1", features = ["full"] }
cargo add aimem-core

aimem-core 自己会依赖 Turso;你通常不需要手工再加一份 turso,除非你要直接操作底层连接。

API 稳定性

aimem-core 建议优先使用两种高层入口:

1. aimem_core::prelude::*

use aimem_core::prelude::*;

适合快速接入、示例代码、内部工具和绝大多数应用层集成。

2. crate 根导出

use aimem_core::{Config, AimemDb, Embedder, Miner, ConvoMiner, Searcher, KnowledgeGraph, MemoryStack};

适合你想显式控制导入面的时候。

不要直接依赖更深层的模块路径,例如:

use aimem_core::miner::Miner;

原因很简单:直接依赖更深层模块路径会把你的代码绑到更宽的 API 面上。新接入方最好把依赖面收敛到 prelude、crate 根 re-export 和少数核心数据类型上。

什么时候该用它

  • 你想把 AiMem 嵌入自己的 Rust agent / daemon / backend
  • 你想直接控制 drawer、entity、triple 的落库方式
  • 你想自己实现 UI、CLI 或 MCP server,但复用现成的存储与搜索逻辑

核心概念

  • Store:整个 AiMem 记忆库,一个 Turso DB 文件
  • Wing:项目或领域,例如 my_app
  • Room:主题,例如 backend、decisions
  • Drawer:一段原文切片
  • L0/L1/L2/L3:唤醒文本、核心故事、按需回忆、深度搜索

用法总览

1. 打开/创建一个 AiMem DB

use aimem_core::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = AimemDb::open("./aimem.db").await?;
    println!("drawers = {}", db.drawer_count().await?);
    Ok(())
}

2. 手工写入 drawer(不生成 embedding 也可以)

use aimem_core::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = AimemDb::memory().await?;

    let drawer = Drawer::new(
        "drawer_demo_001",
        "demo_app",
        "decisions",
        "We chose Rust and Turso for the memory layer.",
        "example",
    ).with_source_file("DECISIONS.md");

    db.insert_drawer(&drawer, None).await?;
    Ok(())
}

3. 只做关键词搜索(离线、无 embedding、最稳)

如果你导入时用了 Miner::new(db, None),那 drawer 没有 embedding,但仍然可以做关键词搜索。

use aimem_core::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = AimemDb::memory().await?;
    let drawer = Drawer::new(
        "drawer_demo_001",
        "demo_app",
        "backend",
        "The backend uses Turso and Rust.",
        "example",
    );
    db.insert_drawer(&drawer, None).await?;

    let searcher = Searcher::keyword_only(db);
    let hits = searcher.keyword_search("Turso", Some("demo_app"), None, 5).await?;
    assert_eq!(hits.len(), 1);
    Ok(())
}

4. 做语义搜索(需要 embedding 模型)

第一次 LocalEmbedder::new() 会下载 all-MiniLM-L6-v2 模型缓存。

use std::sync::Arc;
use aimem_core::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = AimemDb::memory().await?;
    let embedder = Arc::new(LocalEmbedder::new()?);

    let content = "We moved the memory backend to Turso for local vector search.";
    let embedding = embedder.embed_one(content).await?;

    let drawer = Drawer::new("drawer_demo_001", "demo_app", "backend", content, "example");
    db.insert_drawer_with_profile(
        &drawer,
        Some(&embedding),
        embedder.provider_name(),
        embedder.model_name(),
    ).await?;

    let searcher = Searcher::new(db, embedder);
    let hits = searcher.vector_search("local vector database", Some("demo_app"), None, 5).await?;
    assert!(!hits.is_empty());
    Ok(())
}

5. 挖掘一个项目目录

项目根目录需要 aimem.yaml:

wing: demo_app
rooms:
  - name: backend
    description: backend code and docs
    keywords: [router, handler, database]
  - name: decisions
    description: design decisions
    keywords: [decided, chose, tradeoff]

然后:

use aimem_core::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = AimemDb::open("./aimem.db").await?;

    // None = 只入文本,不生成 embedding
    let miner = Miner::new(db, None);
    let stats = miner.mine("./demo-app", None, "example", 0, false).await?;

    println!("drawers added = {}", stats.drawers_added);
    Ok(())
}

6. 导入对话导出文件

use aimem_core::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = AimemDb::open("./aimem.db").await?;
    let miner = ConvoMiner::new(db, None);

    let stats = miner
        .mine("./chat-exports", "team_memory", "conversations", "example", 0, false)
        .await?;

    println!("conversation drawers = {}", stats.drawers_added);
    Ok(())
}

7. 生成 wake-up context

use std::sync::Arc;
use aimem_core::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let cfg = Config::load()?;
    let db = AimemDb::open(&cfg.db_path).await?;
    let embedder = Arc::new(LocalEmbedder::new()?);
    let stack = MemoryStack::new(db, embedder, &cfg);

    let wakeup = stack.wake_up(None).await?;
    println!("{wakeup}");
    Ok(())
}

8. 知识图谱

use aimem_core::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let db = AimemDb::memory().await?;
    let kg = KnowledgeGraph::new(db);

    kg.add_triple("Alice", "works_on", "AiMem", Some("2026-01-01"), None).await?;
    let facts = kg.query_entity("Alice", None, "outgoing").await?;

    assert_eq!(facts.len(), 1);
    Ok(())
}

Example 程序

这个 crate 附带了可编译示例:

  • cargo run -p aimem-core --example basic_aimem
  • cargo run -p aimem-core --example semantic_search
  • cargo run -p aimem-core --example knowledge_graph

测试

cargo test -p aimem-core
cargo test -p aimem-core --test usage_flows
cargo test -p aimem-core --test performance_smoke -- --ignored

实用注意事项

  • AimemDb::open() 会自动建表。
  • Miner::new(db, None) / ConvoMiner::new(db, None) 适合离线、无模型、先把文本塞进去。
  • 没有 embedding 的 drawer 依然可以被 keyword_search() 找到,但不会出现在 vector_search() 结果里。
  • 如果你要做服务端封装,优先把 AimemDb clone 出去复用;它是轻量句柄。