Skip to content

Repository files navigation

ragkit

装配式中文本地 RAG 框架 —— 基于 LangChain / LangGraph 生态,文档加载 → 分块 → 向量化 → 混合检索 → 重排 → 问答,一个配置开箱即用。

npm version license node

特性

  • 中文本地:默认本地中文 embedding(bge-base-zh)+ 嵌入式 LanceDB(零服务),问答模型走 OpenAI 兼容端点(deepseek 等均可)
  • 评测驱动:内置 golden set + 检索指标(Recall@k / MRR)+ LLM-judge 生成指标(Faithfulness / Answer Relevance / 拒答正确率),每个效果改进都有量化验证
  • 装配式:一个 createKnowledgeBase(config) 开库,各环节「配置 / 已就绪实例 / 工厂」三选一
  • 可嵌入:对外只见生态标准接口(Document / Embeddings / VectorStore / BaseRetriever),RAG 图是 CompiledStateGraph 可作子图
  • 依赖纯净:只依赖 @langchain/* 生态 + 必要原生库,可直接嵌入你自己的 LangChain / LangGraph 项目

安装

pnpm add ragkit
# 或 npm i ragkit

@langchain/core 为 peer 依赖(生态惯例,避免版本重复实例);默认实现(LanceDB / 本地 embedding)随包安装,无需额外配置。

快速开始

# 复制 .env.example 为 .env,填入 OPENAI_API_KEY / OPENAI_BASE_URL(问答模型,可用 deepseek 兼容端点)
# 首次运行会下载 bge 中文 embedding 模型(约 98MB)

开一个知识库,全生命周期:

import "dotenv/config";
import { ChatOpenAI } from "@langchain/openai";
import { createKnowledgeBase } from "ragkit";

const model = new ChatOpenAI({
  model: "deepseek-v4-flash",
  apiKey: process.env.OPENAI_API_KEY,
  configuration: { baseURL: process.env.OPENAI_BASE_URL },
});

const kb = await createKnowledgeBase({
  source: ["./docs"],                          // 目录 / glob / loader 实例 / 工厂
  model,                                       // 必填:BaseChatModel
  mode: "hybrid",                              // 默认即 hybrid;simple 为低成本档(多库路由见下节)
});

await kb.index();                              // 入库(幂等:按内容 hash 判重,重跑不重复)
const { answer, citations } = await kb.ask("二手房交易按什么流程完成?");
// answer    → "挂牌 → 看房 → 议价 → 签约 → 贷款申请 → 过户 → 交房 [1]"
// citations → [{ index:1, title, source, updatedAt }]

for await (const ev of kb.stream("中介费怎么收?")) {
  // { type:"token", text } | { type:"done", citations }
}
await kb.remove("/abs/path/to/a.md");          // 文档级删除(同步内存索引)

对话演示:pnpm example:kb(灌入 examples/data/*.md 并问答)。

两种模式:一条配置切换

mode 检索管线 成本 适用
hybrid(默认) BM25 + 向量 RRF 融合 → 拒答 → 生成 开箱默认:词法 + 语义互补(默认叠加本地重排+分层,即「hybrid+rerank+hierarchical」)
simple 向量检索 → 拒答判断 → 生成 小库 / 快速验证

多知识库路由(agentic)是独立的 createAgenticKB 入口(见下节),不走单一 mode

const kb = await createKnowledgeBase({
  source: ["./docs"],
  model,
  // 默认即 hybrid + 本地重排 + 分层(评测最优);以下按需叠加增强
  retriever: { k: 4, multiQuery: true },   // ← 显式叠加 query 改写
});

检索质量工程

检索准 = 混合检索(词法+语义) → 重排(精排) → 查询改写(召回补全) → 多库路由(归位)
生成好 = 引用溯源(citations) + 知识边界(不足即拒答) + 结构化上下文
环节 实现 默认
分块 中文 RecursiveCharacter(chunkSize 400 / overlap 50
混合检索 字符二元组 BM25(零依赖,k1=1.5 / b=0.75)+ 向量,RRF 融合bm25ScoreScaling 用 BM25 原始分缩放 RRF 贡献,破除长文档对词法侧的排名压制 rrfK=30
融合去重 跨向量 / BM25 侧按 contentHash 合流(docKey 一致),融合后默认不折叠(同一来源多个 chunk 都保留,保证答案覆盖率)
重排 内置零依赖本地重排器(词法重叠 + 新鲜度信号updatedAt 越新越靠前);可注入 bge-reranker 等交叉编码器 rerank: true(默认开启)
查询改写 LLM 生成语义相同、措辞不同的查询变体 → 多路检索 → 去重合并(覆盖口语化 vs 书面化 gap) multiQuery: false
拒答 问句关键词(中文 bigram + 英文词)对检索上下文的分语言组覆盖率 ≥ 0.35 才作答;否则诚实拒答

评测基线(golden set 100 例,pnpm eval

100 例多行业非技术集实测(房产 / 销售 / 餐饮,含 LLM-judge 双采样)。hybrid+rerank+hierarchical 已是默认;下表对比展示该默认组合 vs 关闭增强的对照。

模式 Recall@4 MRR ContextPrecision ContextRecall Faithfulness Answer Relevance 拒答正确率
simple(纯向量) 0.932 0.735 0.734 0.932 0.998 0.990 0.870
hybrid 0.979 0.895 0.895 0.979 0.995 0.985 0.960
hybrid+rerank+hierarchical(默认) 1.000 0.924 0.921 1.000 0.995 0.984 0.950
agentic(3 行业子库路由,路由正确率 0.960) 0.948 0.874 0.874 0.948 0.984 0.973 0.960
  • hybrid 显著优于 simple(MRR 0.735→0.895、拒答正确率 0.870→0.960):BM25 词法兜住纯向量漏掉的精确命名与跨语言(SKU 编号 SP-1024、菜品英文名、楼盘名等)
  • 默认组合让检索指标最优(Recall@4 / ContextRecall 全 1.000):分层折叠父块使相关源排位最前、上下文完整 → 因此默认即 hybrid+rerank+hierarchical
  • 生成指标 LLM-judge 双采样取均值,抑制单次采样偶发噪声(详见 eval/metrics.ts);以本地复现为准
  • 拒答正确率非满分:各模式 4-5 个稳定误拒为覆盖率阈值边界(问句含较多虚词稀释关键词覆盖率,检索本身命中、Recall@4=1),按需可调 coverageThreshold;agentic 另有 4 处跨行业词汇重叠导致的路由偏移(如"投诉处理时限"在销售库的售后规范中同样出现,实测路由到零售销售)

统一装配 API

interface KnowledgeBase {
  index(): Promise<void>;                                        // 幂等入库
  remove(source: string): Promise<void>;                         // 文档级删除
  retrieve(question: string, filter?: MetadataFilter): Promise<Document[]>;
  ask(question: string, filter?: MetadataFilter): Promise<{ answer, sourceDocuments, citations }>;
  stream(question: string, filter?): AsyncGenerator<StreamEvent>;
  store(): Promise<VectorStore>;                                 // 底层向量库(懒加载)
  retriever: BaseRetriever;                                      // 标准检索器
  graph: CompiledQAGraph;                                        // 整张 RAG 图(可作子图)
}

扩展点契约:每环节「配置 / 实例 / 工厂」三选一

扩展点 默认值 注入方式
source loader loadDocuments(txt / md / json / glob,多编码 utf-8 / gbk,frontmatter 适配) loader 实例 / 工厂
splitter 中文 RecursiveCharacter(400 / 50) SplitterConfig / TextSplitter 实例 / 工厂
hierarchical 开启(默认) false 关闭 / HierarchicalSplitterConfig 自定义尺寸(分层检索,见下)
embedder 本地 bge-base-zh-v1.5(cls / q8,~98MB,query 侧自动套 BGE 检索指令前缀 EmbedderConfig / Embeddings 实例 / 工厂
store LanceDB 嵌入式(.lancedb/kb {type:"lancedb",uri,tableName} / VectorStore 实例 / 工厂
retriever k=4 参数(vectorK / bm25K / rrfK / multiQuery / rerank / collapseBySource…)
model 无(必填) BaseChatModel 实例

分层检索(parent-child,默认开启)

小块检索 / 父块生成:索引按 父块(800) → 小块(200) 切分,小块命中后自动折叠还原父块,生成上下文更完整、引用粒度更大。评测验证为检索指标最优(Recall@4 / ContextRecall 全 1.000,MRR / ContextPrecision 亦领先各模式)。

const kb = await createKnowledgeBase({
  source: ["./docs"],
  model,
  // hierarchical 默认开启,无需配置;也可自定义尺寸:
  hierarchical: { smallSize: 200, parentSize: 800 },
  retriever: { k: 4, rerank: true }, // 可与 hybrid / rerank / multi-query 组合
});
// 想退回单层(400/50):hierarchical: false

注意事项

  • 旧索引不兼容:旧 400 块无 parentHash,升级后需用新 tableName 或先 dropTable 重建(旧块会透传降级、混库污染结果)。
  • 传自定义 splitter 时自动退出分层(视为自定分块方案);除非再显式 hierarchical: true
  • 小块 metadata 内嵌父块全文(自包含还原),存储约 4 倍冗余、生成上下文约 2 倍 token 成本——本地嵌入式可接受,大语料需知悉。
  • 父块上下文变大 → 覆盖率升高,拒答边界会漂移;必要时按需调 coverageThreshold

存储解绑:默认 LanceDB 只是装配默认;传 Qdrant / pgvector / 内存库的 VectorStore 实例即可,库内不内置多种向量库实现

多知识库路由(agentic)

const kbHouse = await createKnowledgeBase({ source: ["./房产"], model, mode: "hybrid" });
const kbSales = await createKnowledgeBase({ source: ["./销售"], model, mode: "hybrid" });

const agentic = await createAgenticKB({
  model,
  knowledgeBases: [
    { name: "房产经纪", description: "二手房交易 / 门店运营 / 客户服务规范", kb: kbHouse },
    { name: "零售销售", description: "商品 SKU / 促销规则 / 售后处理规范", kb: kbSales },
  ],
});
await agentic.index();                       // 委托全部子库(幂等)
const { answer } = await agentic.ask("中介费怎么收?");   // 自动路由到 房产经纪

路由用 LLM 排序 + 证据竞争仲裁(不是盲信单次路由):

  1. LLM 给子库排序(按 name + description 相关度)
  2. 依次检索候选库并计算其对问句的覆盖率(同拒答信号)
  3. 覆盖率 ≥ 0.7 直接胜出(强证据早停);否则取覆盖率最高的库
  4. 全部覆盖率极低 → 回退首候选,拒答交给下游统一判断

好处:模型单次路由偶发选错库(尤其子库间词汇重叠时)不会致命——词汇重叠的错库覆盖率通常远低于真答案库,被仲裁掉。可配 route: { maxCandidates, strongCoverage }

描述覆盖 = 路由可见性description 是 LLM 路由的"目录"——库里有的主题没写进描述,路由就不知道去那个库找(评测曾因此把"系统入口"类问题路由错库,补齐描述即修)。给子库描述尽量枚举库内实际主题,是路由质量里性价比最高的一步。另注意:一个答案横跨多个子库的组合题目前超出"单库路由"能力(只保证命中首源目录),这类题请放进同一子库。

嵌入外部 LangChain 项目

// ① 只要标准检索器,塞进自己的检索节点
const retriever = kb.retriever;              // BaseRetriever

// ② 整张 RAG 图作子图,嵌进自己的 LangGraph 主图
builder.addNode("rag", kb.graph);            // CompiledStateGraph

// ③ 项目已有 Qdrant / pgvector,只借用问答能力
const kb2 = await createKnowledgeBase({ store: myQdrantStore, model });

评测体系

pnpm eval                        # simple 基线
RAG_EVAL_MODE=hybrid pnpm eval   # 混合检索对比
RAG_EVAL_MODE=hybrid+rerank pnpm eval
RAG_EVAL_MODE=hybrid+multi pnpm eval
RAG_EVAL_MODE=hybrid+rerank+multi pnpm eval
RAG_EVAL_MODE=hybrid+rerank+hierarchical pnpm eval  # 分层检索(父块上下文)
RAG_EVAL_MODE=agentic pnpm eval  # agentic(多库路由)

检索指标含 Context Precision / Context Recall(RAGAS 口径,无模型);报告落盘 eval/report-<mode>.json(只增不改,旧报告可比)。

  • golden seteval/dataset.ts,100 例 (question, expectedAnswer, relevantSources),含 4 例拒答边界样本
  • 检索指标:Recall@k / MRR(无模型、快)
  • 生成指标:Faithfulness(忠实于检索上下文)/ Answer Relevance(切题)—— LLM-judge,双采样取均值
  • 拒答正确率:应拒答且实际拒答 / 应作答且实际作答
  • 报告落盘 eval/report-<mode>.json

开发脚本

命令 作用
pnpm typecheck / pnpm build 类型检查 / 打包(tsup JS + tsc d.ts)
pnpm test 单元测试(96 例;e2e 默认跳过)
RAG_E2E=1 pnpm test 含真实 embedding + 向量库的端到端
pnpm eval 评测(见上表)
pnpm example:kb 最小闭环演示(灌入 examples/data 并问答)

注意

  • LanceDB 版本锁定:嵌入式零服务。⚠️ LanceDB 0.23+ 停止发布 Intel Mac (darwin-x64) 原生二进制,本包锁定 0.22.3(最后支持 x64 版本)
  • Embedding 镜像:本地 embedding 从 HuggingFace 下载,国内需 HF_ENDPOINT=https://hf-mirror.com。换 embedding 模型需重建索引
  • 拒答阈值:覆盖率 0.35 可经 config.confidence 调整(coverageThreshold / distanceThreshold

文档

License

MIT

About

装配式中文本地 RAG 框架:基于 LangChain/LangGraph,本地 bge embedding + 嵌入式 LanceDB,一个配置完成 加载→分块→混合检索→重排→问答,评测驱动

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages