Sign in

    MoonRetrieve

    纯 MoonBit 零 FFI 的本地全文检索与 RAG 检索库:中英日分词、BM25 倒排索引、向量索引、RRF 混合检索与 LLM 上下文组装,可编译为 wasm / wasm-gc / js / native 多后端。

    moonbit
    search
    bm25
    rag
    tokenizer
    wasm
    local-first
    hybrid
    rrf
    Download zip
    Version
    0.4.1
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    33

    #MoonRetrieve

    mooncakes.io CI

    纯 MoonBit 零 FFI 的本地全文检索与 RAG(检索增强生成)检索库。文档在本地完成分词、分块、索引、检索和 LLM 上下文组装,数据不出本地;核心库可编译到 wasm / wasm-gc / js / native 多后端,适合浏览器、边缘设备与隐私敏感场景。

    #与现有 MoonBit 检索项目的差异

    • 不重复造“搜索引擎内核”:区别于 Lucius646/MoonSearch(嵌入式全文检索内核,多 Segment 持久化、Phrase/Boolean 查询),MoonRetrieve 聚焦轻量索引 + 混合检索 + RAG 上下文组装,提供 Engine 一站式 API 和开箱即用的 CLI。
    • 纯库、零 FFI:区别于 houjie/rag-mbt(RAG 管线,native 侧依赖 Python/bge 嵌入与 FFI),MoonRetrieve 核心库无任何 FFI,同一套代码跑 wasm-gc / js / native,适合浏览器与受限边缘环境。
    • 内置多语言分词:中/英/日文,中文单字 + bigram(单字查询也能命中),英文停用词与可选词干化,不依赖外部分词服务。
    • 混合检索 + 上下文组装:BM25 倒排索引与余弦向量索引双路检索,RRF 融合排序;ContextBuilder 按 token 预算把命中片段拼成带来源编号的 LLM 提示词。

    #功能

    • 分词器 Tokenizer:英文/中文/日文、N-gram、停用词、词干化
    • 分块器 Chunker:按段落切分、块间重叠、超长段落硬切
    • BM25 索引 SearchIndex:增量建索引、Top-K 检索、JSON 持久化
    • 短语检索 search_phrase:查询短语按原文顺序连续出现
    • 布尔检索 search_boolean:AND / OR / -term 排除
    • 前缀检索 search_prefix:按词条前缀匹配
    • 向量索引 VectorIndex:余弦相似度检索
    • 混合融合 rrf_fuse:Reciprocal Rank Fusion
    • 上下文组装 ContextBuilder:token 预算控制、来源标注
    • 结果高亮 highlight:查询词自动标记 **term**
    • 文档删除与统计:SearchIndex::remove / stats、Engine::remove_document / stats
    • 一站式引擎 Engine:文档 → 分块 → 索引 → 检索
    • CLI:index / query / context / stats / phrase / boolean / prefix

    #快速开始

    环境要求:MoonBit 0.1.20260807+;native 目标需要 C 编译器(Windows 用 MSVC)。

    # 添加依赖 moon add niuniu513-ask/MoonRetrieve

    库用法:

    let engine = @lib.Engine::new()
    engine.add_document("guide", "MoonBit 是面向云边计算的编程语言。")
    engine.add_document("faq", "RAG 是检索增强生成。")

    let results = engine.search("RAG 是什么", top_k=3)
    let cb = @lib.ContextBuilder::new(max_tokens=500)
    println(cb.build("RAG 是什么", results))

    CLI 用法(native 目标):

    # 建索引 moon run cmd/main --target native -- index examples/notes demo-index.json # 检索 moon run cmd/main --target native -- query demo-index.json "MoonBit 黑客松" -k 3 # 生成 LLM 上下文 moon run cmd/main --target native -- context demo-index.json "黑客松奖励" -k 3 # 索引统计 moon run cmd/main --target native -- stats demo-index.json # 短语检索(可加 --highlight) moon run cmd/main --target native -- phrase demo-index.json "MoonBit 黑客松" -k 3 # 布尔检索 / 前缀检索 moon run cmd/main --target native -- boolean demo-index.json "MoonBit AND RAG" -k 3 moon run cmd/main --target native -- prefix demo-index.json "moon" -k 3

    #项目结构

    MoonRetrieve.mbt 包说明 tokenizer.mbt 分词器 chunker.mbt 分块器 index.mbt BM25 倒排索引 vector.mbt 向量索引与 RRF 融合 context.mbt LLM 上下文组装 engine.mbt 一站式引擎 cmd/main/ CLI examples/notes/ 示例文档 docs/ 使用教程、申报书、差异化说明与自查清单

    #测试与构建

    moon test # 44 个测试(native / wasm-gc / js 由 CI 覆盖) moon check moon build --target wasm-gc moon bench # 3 项基准(分词 / 建索引 / 检索)

    #许可证

    Apache-2.0

    Chunk

    pub struct Chunk {
    doc_id : String
    index : Int
    text : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    文档分块结果。

    Chunk::new

    fn Chunk::new(doc_id : String, index : Int, text : String) -> Chunk

    Chunker

    pub struct Chunker {
    max_chars : Int
    overlap : Int
    } derive(ToJson,
    FromJson
    )

    文档分块器:按段落切分,块之间保留重叠内容。

    Chunker::chunk

    fn Chunker::chunk(self : Chunker, doc_id : String, text : String) -> Array[Chunk]

    将文档切成带编号的分块。

    Chunker::new

    fn Chunker::new(max_chars? : Int, overlap? : Int) -> Chunker

    ContextBuilder

    pub struct ContextBuilder {
    max_tokens : Int
    tokenizer : Tokenizer
    } derive(ToJson,
    FromJson
    )

    LLM 上下文组装器:把检索结果按 token 预算拼成可直接喂给大模型的提示词。

    ContextBuilder::build

    fn ContextBuilder::build(self : ContextBuilder, query : String, results : Array[SearchResult]) -> String

    组装上下文:先放问题,再按相关性放入带编号的片段,超出预算即停止。

    ContextBuilder::new

    fn ContextBuilder::new(max_tokens? : Int, tokenizer? : Tokenizer) -> ContextBuilder

    Engine

    pub struct Engine {
    chunker : Chunker
    index : SearchIndex
    } derive(ToJson,
    FromJson
    )

    一站式本地检索引擎:文档 -> 分块 -> BM25 索引 -> Top-K 检索。

    Engine::add_document

    fn Engine::add_document(self : Engine, doc_id : String, text : String) -> Unit

    添加一篇文档(自动分块后逐块建索引),返回更新后的引擎。

    Engine::add_documents

    fn Engine::add_documents(self : Engine, docs : Array[(String, String)]) -> Unit

    批量添加文档。

    Engine::count

    fn Engine::count(self : Engine) -> Int

    索引中的分块总数。

    Engine::highlight

    fn Engine::highlight(self : Engine, text : String, query : String) -> String

    对文本执行查询词高亮。

    Engine::new

    fn Engine::new(chunker? : Chunker, index? : SearchIndex) -> Engine

    Engine::remove_document

    fn Engine::remove_document(self : Engine, doc_id : String) -> Int

    按原始文档 ID 删除一篇文档及其全部分块,返回删除的分块数。

    Engine::save

    fn Engine::save(self : Engine) -> String

    序列化为 JSON 字符串。

    Engine::search

    fn Engine::search(self : Engine, query : String, top_k? : Int) -> Array[SearchResult]

    检索 Top-K 分块。

    Engine::search_boolean

    fn Engine::search_boolean(self : Engine, query : String, top_k? : Int) -> Array[SearchResult]

    布尔检索 Top-K 分块(AND / OR / -term)。

    Engine::search_phrase

    fn Engine::search_phrase(self : Engine, query : String, top_k? : Int) -> Array[SearchResult]

    短语检索 Top-K 分块。

    Engine::search_prefix

    fn Engine::search_prefix(self : Engine, prefix : String, top_k? : Int) -> Array[SearchResult]

    前缀检索 Top-K 分块。

    Engine::stats

    fn Engine::stats(self : Engine) -> IndexStats

    返回索引统计信息。

    Engine::try_load

    fn Engine::try_load(json : String) -> Result[Engine, String]

    从 JSON 字符串恢复引擎。

    IndexStats

    pub struct IndexStats {
    doc_count : Int
    term_count : Int
    total_tokens : Int
    avg_doc_len : Double
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    索引统计信息。

    IndexStats::new

    fn IndexStats::new(doc_count : Int, term_count : Int, total_tokens : Int, avg_doc_len : Double) -> IndexStats

    Posting

    pub struct Posting {
    doc_id : Int
    tf : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    倒排索引中的一条 posting:文档编号 + 词频。

    ScoredDoc

    pub struct ScoredDoc {
    id : String
    score : Double
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    向量检索或混合检索的排序结果。

    ScoredDoc::new

    fn ScoredDoc::new(id : String, score : Double) -> ScoredDoc

    SearchIndex

    pub struct SearchIndex {
    ids : Array[String]
    texts : Array[String]
    lens : Array[Int]
    postings : Map[String, Array[Posting]]
    total_docs : Int
    sum_len : Int
    avg_dl : Double
    k1 : Double
    b : Double
    tokenizer : Tokenizer
    } derive(ToJson,
    FromJson
    )

    BM25 倒排索引。

    支持增量添加文档、Top-K 检索,以及 JSON 序列化/反序列化。 设计为不可变更新风格:add 返回添加后的新索引。

    SearchIndex::add

    fn SearchIndex::add(self : SearchIndex, id : String, text : String) -> Unit

    向索引添加一篇文档,返回新索引。id 可以是文档路径或分块 ID。

    SearchIndex::count

    fn SearchIndex::count(self : SearchIndex) -> Int

    当前索引中的文档/分块数量。

    SearchIndex::new

    fn SearchIndex::new(k1? : Double, b? : Double, tokenizer? : Tokenizer) -> SearchIndex

    SearchIndex::remove

    fn SearchIndex::remove(self : SearchIndex, id : String) -> Bool

    按文档 ID 删除一篇文档,返回是否删除成功。

    SearchIndex::remove_if

    fn SearchIndex::remove_if(self : SearchIndex, pred : (String) -> Bool) -> Int

    按条件删除文档,返回删除数量。删除后内部编号重建,复杂度 O(N)。

    SearchIndex::save

    fn SearchIndex::save(self : SearchIndex) -> String

    序列化为 JSON 字符串。

    SearchIndex::search

    fn SearchIndex::search(self : SearchIndex, query : String, top_k? : Int) -> Array[SearchResult]

    对查询执行 BM25 检索,返回按分数降序排列的 Top-K 结果。

    SearchIndex::search_boolean

    fn SearchIndex::search_boolean(self : SearchIndex, query : String, top_k? : Int) -> Array[SearchResult]

    布尔检索:支持 AND / OR 连接词与 -term 排除。

    示例:
    • "moonbit AND rag":同时包含 moonbit 与 rag
    • "moonbit OR rag":包含任一即可
    • "moonbit -old":包含 moonbit 且不含 old

    SearchIndex::search_phrase

    fn SearchIndex::search_phrase(self : SearchIndex, query : String, top_k? : Int) -> Array[SearchResult]

    短语检索:查询短语必须按原文顺序连续出现,结果仍按 BM25 分数排序。

    SearchIndex::search_prefix

    fn SearchIndex::search_prefix(self : SearchIndex, prefix : String, top_k? : Int) -> Array[SearchResult]

    前缀检索:匹配以给定前缀开头的词条,结果按 BM25 分数排序。

    SearchIndex::stats

    fn SearchIndex::stats(self : SearchIndex) -> IndexStats

    返回索引统计信息。

    SearchIndex::try_load

    fn SearchIndex::try_load(json : String) -> Result[SearchIndex, String]

    从 JSON 字符串恢复索引。

    SearchResult

    pub struct SearchResult {
    doc_id : String
    score : Double
    text : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    检索结果:命中的分块/文档及其 BM25 分数。

    SearchResult::new

    fn SearchResult::new(doc_id : String, score : Double, text : String) -> SearchResult

    Tokenizer

    pub struct Tokenizer {
    min_token_len : Int
    bigram_cjk : Bool
    stem_english : Bool
    stopwords : Array[String]
    } derive(ToJson,
    FromJson
    )

    分词器配置。

    默认行为:
    • 英文/数字按字母数字串切分并转小写,过滤长度小于 min_token_len 的词
    • 中日韩文字按单字 + 相邻二字 bigram 切分
    • 可配置停用词与英文词干化

    Tokenizer::estimate_tokens

    fn Tokenizer::estimate_tokens(self : Tokenizer, text : String) -> Int

    估算文本的 token 数(按当前分词结果计数,供上下文预算使用)。

    Tokenizer::new

    fn Tokenizer::new(min_token_len? : Int, bigram_cjk? : Bool, stem_english? : Bool, stopwords? : Array[String]) -> Tokenizer

    Tokenizer::tokenize

    fn Tokenizer::tokenize(self : Tokenizer, text : String) -> Array[String]

    对文本分词,返回词条数组。

    VectorIndex

    pub struct VectorIndex {
    items : Array[ScoredDoc]
    vectors : Array[Array[Double]]
    } derive(ToJson,
    FromJson
    )

    稠密向量索引(暴力检索,适合中小规模本地 RAG)。

    VectorIndex::add

    fn VectorIndex::add(self : VectorIndex, id : String, vector : Array[Double]) -> Unit

    添加一个带向量的文档/分块,返回更新后的索引。

    VectorIndex::count

    fn VectorIndex::count(self : VectorIndex) -> Int

    向量数量。

    VectorIndex::new

    VectorIndex::search

    fn VectorIndex::search(self : VectorIndex, query : Array[Double], top_k? : Int) -> Array[ScoredDoc]

    按余弦相似度检索 Top-K。

    cosine

    fn cosine(a : Array[Double], b : Array[Double]) -> Double

    计算两个向量的余弦相似度。维度不一致或零向量返回 0。

    default_stopwords

    fn default_stopwords() -> Array[String]

    常用中文停用词。

    highlight

    fn highlight(text : String, query : String, tokenizer? : Tokenizer) -> String

    在文本中用 **term** 标记查询命中的词,返回高亮后的文本。

    长词优先、区间不重叠,避免中文单字与 bigram 同时命中时重复标记。

    rrf_fuse

    fn rrf_fuse(a : Array[ScoredDoc], b : Array[ScoredDoc], k? : Int, top_k? : Int) -> Array[ScoredDoc]

    用 RRF(Reciprocal Rank Fusion)融合两路检索结果。