lorebox:基于 Ollama 与 ChromaDB 的本地离线文档问答项目

lorebox 是一个本地私密文档问答工具,基于 Ollama 本地模型与 ChromaDB 向量检索实现。用户可将自己的 Markdown 或纯文本笔记导入本地索引,随后用自然语言提问,答案仅依据本地文档生成,全程离线运行,数据不出本机,无需任何 API Key,不产生网络请求。项目面向笔记管理、课程资料整理、个人知识库检索等场景,帮助开发者在保护隐私的前提下,快速查询和复用自己积累的资料。技术栈为 Python,代码结构清晰,包含分块、向量索引、检索与问答四个模块,适合学习 RAG 原理或二次开发。

分支1Tags1

lorebox

A private, offline document Q&A tool. Drop your Markdown / text notes in, ask questions in natural language, and get answers grounded in your own files — nothing leaves your machine.

一个本地私密文档问答工具。把你的 Markdown / 纯文本笔记丢进去,用自然语言提问, 答案只基于你自己的文件生成——数据不出本机。

It uses Ollama for embeddings and generation, and chromadb for local vector search. No cloud, no API key, no telemetry. If you would rather not run a model server, --embed-backend onnx computes the embeddings with chromadb's bundled model instead — indexing and search then need no Ollama at all, only ask does.

它用 Ollama 做嵌入和生成,用 chromadb 做本地向量检索。没有云服务、没有 API Key、 没有遥测上报。不想跑模型服务的话,加 --embed-backend onnx 改用 chromadb 自带的 模型算嵌入——这样索引和 search 完全不需要 Ollama,只有 ask 需要。

Repository / 仓库地址:https://atomgit.com/NJPJI_JK/lorebox (同一项目同时发布在 https://gitcode.com/NJPJI_JK/lorebox)

Why this exists / 为什么做这个

I had a growing pile of notes and course material I wanted to query without uploading any of it to a third-party service. Most RAG demos assume a paid API key and a cloud vector DB. This is the offline version of that idea.

我积攒了一堆笔记和课程资料,想查询它们又不愿上传到任何第三方服务。市面上大多数 RAG demo 都假设你有付费 API Key 和云端向量库。这个项目就是那个想法的离线版本。

Requirements / 环境要求

  • Python 3.9+
  • Ollama running locally (ollama serve) — required for the default embedding backend and for ask; indexing and search do not need it when you pass --embed-backend onnx / 本地运行 Ollama(默认嵌入后端与 ask 需要;用 --embed-backend onnx 时 索引与检索不需要)
  • Pull the models you want to use, e.g. / 拉取你想用的模型,例如:
ollama pull nomic-embed-text
ollama pull qwen2.5

Not sure whether your setup is ready? / 不确定环境是否就绪:

lorebox doctor

Install / 安装

From PyPI / 从 PyPI 安装:

pip install lorebox

Add the local embedding backend if you want indexing and search to work without a model server / 如果希望索引与检索不依赖模型服务,再装上本地嵌入后端:

pip install "lorebox[local-embed]"

From source, if you want to work on the code / 想改代码就从源码装:

git clone https://atomgit.com/NJPJI_JK/lorebox.git
cd lorebox
pip install -e .

Usage / 使用

Index your notes — Markdown, plain text and PDF are supported / 索引你的笔记(支持 Markdown、纯文本和 PDF):

lorebox add ./notes
lorebox add notes/a.md --overlap 200 --max-chars 800
lorebox add ./notes --dry-run          # list what would be indexed / 先看会处理哪些文件

--ext .org adds a suffix the default set doesn't cover; the default set is .md .markdown .txt .rst .pdf. / --ext .org 可以追加默认集合之外的后缀;默认集合是 .md .markdown .txt .rst .pdf。

Ask a question / 提问:

lorebox ask "what did I write about the final exam?"
lorebox ask "我笔记里写了期末考试考什么?"

The answer is printed along with the source chunks it used / 答案后面会列出用到的来源片段:

lorebox ask "..." --no-sources         # hide the source list / 不显示来源列表
lorebox ask "..." --path notes/course  # search one folder only / 只在这个目录里检索
lorebox ask "..." --min-score 0.35     # drop unrelated chunks / 丢掉不相关的片段
lorebox ask "..." --json               # {question, answer, sources} / 结构化输出
lorebox ask "..." --stream             # print as it generates / 边生成边打印

--min-score sets a similarity floor: chunks scoring below it are dropped instead of padding the answer with unrelated text. Scores run from 0 to 1 (higher is closer) and the default 0 keeps everything, so leaving it out changes nothing. Matching chunks are also reranked so that one paragraph and its copies cannot fill every slot. / --min-score 是相似度下限:低于它的片段直接丢掉, 而不是拿去凑数。score 取值 0 到 1(越大越接近),默认 0 表示不过滤,不加这个 选项行为不变。检索结果还会做重排序,防止同一段落的多份重复副本占满全部位置。

--json and --stream cannot be combined: structured output needs the complete answer. / --json 与 --stream 不能同时使用,结构化输出需要拿到完整答案。

Look at the index / 查看索引:

lorebox list                   # one line per file / 每个文件一行
lorebox list --chunks          # one line per chunk / 每个片段一行
lorebox stats                  # file / chunk / character counts / 统计信息
lorebox stats --files --top 5  # biggest files first / 按字符数从大到小排,只看前 5 个
lorebox doctor                 # check Ollama, the models and the store / 自检本机环境

Move an index between machines / 在机器之间迁移索引:

lorebox export --out dump.json --with-embeddings
lorebox import dump.json       # no re-embedding needed / 不需要重新嵌入

import only accepts a dump made with --with-embeddings; a plain dump carries no vectors, so it is refused rather than half-imported. / import 只接受 --with-embeddings 导出的 dump;普通 dump 不含向量,会被直接拒绝, 不会只导入一半。

Search without a model / 不用模型直接检索:

lorebox search "what did I write about the final exam?"
lorebox search "..." --path notes/course
lorebox search "..." --min-score 0.35

search only retrieves: it prints the closest chunks with their score and distance, and never calls a language model. With --embed-backend onnx it needs no Ollama at all. / search 只做检索:打印最相近的片段及其 score 与距离,不调用 语言模型。配合 --embed-backend onnx 时完全不需要 Ollama。

Embedding backends / 嵌入后端:

lorebox add ./notes --embed-backend onnx
lorebox search "..." --embed-backend onnx

onnx runs all-MiniLM-L6-v2 locally through chromadb, so no model server is needed. Two things worth knowing: the model is downloaded once on first use (after that it really is offline), and an index can only be queried with the backend that built it — the two produce vectors of different width, so lorebox records which backend an index used and refuses to mix them. The embedding model is recorded for the same reason: --embed-model nomic-embed-text and bge-m3 both run on Ollama but do not share a vector space, so pointing an existing index at the other one is an error with a message instead of silently returning noise. / onnx 通过 chromadb 在本机运行 all-MiniLM-L6-v2,不需要模型服务。两点需要注意: 模型会在首次使用时下载一次(之后才是真正离线);一个索引只能用建它时的那个后端 查询——两者向量宽度不同,lorebox 会记录索引用的是哪个后端并拒绝混用。嵌入模型同样 会被记录下来:--embed-model 的 nomic-embed-text 与 bge-m3 都跑在 Ollama 上, 但向量不在同一个空间,拿另一个模型去查已有索引会得到明确报错,而不是静默返回噪声。

Maintain the index / 维护索引:

lorebox remove notes/a.md      # drop one file / 删掉某个文件

--store picks the index directory (default .lorebox/); --model, --embed-model, --top-k, --min-score, --overlap, --max-chars and --out apply to the commands where they make sense. Set OLLAMA_HOST if Ollama is not on http://localhost:11434. / --store 指定索引目录(默认 .lorebox/); --model、--embed-model、--top-k、--min-score、--overlap、--max-chars、 --out 按各命令的用途适用。Ollama 不在默认地址时用 OLLAMA_HOST 指定。

Development / 开发与测试

The unit tests need chromadb (installed together with the package) and do not need a running Ollama instance. 单元测试需要 chromadb(装包时会一起装上),但不需要本地跑着 Ollama。

python -m unittest discover -s tests

They cover the chunker, the store and the command line. Please keep them green before opening a pull request. 用例覆盖分块、索引存储和命令行三部分。提 PR 前请保持全绿。

How the pieces fit together is described in docs/architecture.md; what a good change looks like is in CONTRIBUTING.md. 各模块如何拼在一起见 docs/architecture.md;什么样的改动好评审见 CONTRIBUTING.md。

Layout / 目录结构

  • ollama.py — thin client for the local Ollama HTTP API / 本地 Ollama HTTP 客户端
  • chunker.py — paragraph-based chunking / 按段落分块
  • store.py — chromadb persistent index / chromadb 持久化索引
  • retrieve.py — embed a query and fetch top-k chunks / 查询嵌入 + top-k 检索
  • ask.py — prompt assembly and generation / prompt 组装与生成
  • cli.py — the command line: add / ask / list / remove / export / import / stats / doctor / 命令行入口:add / ask / list / remove / export / import / stats / doctor
  • tests/ — unit tests; no running Ollama needed / 单元测试(不需要本地跑着 Ollama)
  • docs/architecture.md — module map, index layout, design trade-offs / 模块结构、索引布局与设计取舍

Current limitations / 当前限制

  • Retrieval reranks what it fetches but still ranks by vector similarity alone: no keyword or hybrid matching. / 检索虽然会重排序,但排序依据仍然只有向量 相似度,没有关键词匹配,也没有混合检索。
  • Incremental indexing works at file granularity: a file is skipped only when its contents are unchanged, so editing one paragraph re-embeds the whole file. / 增量索引以文件为单位:只有内容完全没变才会跳过,改一个段落就会让整个文件重新嵌入。
  • Only Markdown, plain text and PDF are read; docx and epub are not. / 目前只读 Markdown、纯文本和 PDF,docx 与 epub 尚不支持。
  • The onnx embedding backend downloads its model on first use, so the very first run needs network access. / onnx 嵌入后端首次使用时会下载模型,因此第一次 运行需要联网。

Design boundaries / 设计边界

These are deliberate choices, not gaps waiting to be closed. Listing them separately from the limitations above, because "not implemented yet" and "decided not to do" are different things.

这些是有意的选择,不是待补的缺口。与上面的限制分开列,因为「还没做」和「决定不做」 是两回事。

  • ask requires a local LLM. Generating an answer needs a model; there is no way around that. What this project does is keep that model on your own machine rather than remove it. If you would rather not run one at all, use search with --embed-backend onnx — indexing and retrieval then need no model service. ask 需要本地 LLM。 生成答案离不开模型,这一点绕不开。项目做的是把这个 模型限制在你自己机器上,而不是消灭它。如果完全不想跑模型服务,用 search 配合 --embed-backend onnx——索引与检索都不需要模型服务。
  • No cloud fallback. There is no "use a hosted model when the local one is unavailable" path. / 没有云端兜底。 本地模型不可用时不会自动转到云端。
  • The index is a derived artifact. To switch embedding backend or model, delete .lorebox/ and index again; there is no migration script. / 索引是派生产物。 想换嵌入后端或模型,删掉 .lorebox/ 重新索引即可,不提供 迁移脚本。

Maintenance plan / 维护计划

Three passes are done — the known bugs, the usability gaps (streaming, structured output, scoped search, import/export, batch embeddings, self-check), and the local embedding backend. What changed, per release, is in CHANGELOG.md.

三轮已完成:已知问题、体验缺口(流式输出、结构化输出、范围检索、导入导出、批量 嵌入、环境自检),以及本地嵌入后端。每次改动见 CHANGELOG.md。

What is left, roughly in order / 后续方向,大致按优先级:

  1. Only re-embed what changed. One edited paragraph currently costs a full file re-embed. 只重算变更部分。 现在改一个段落会让整个文件重新嵌入。
  2. Rerank the retrieved chunks and add a similarity threshold. ✓ done on the rerank-threshold branch, waiting to be merged. 检索结果重排序,并加上相似度阈值。✓ 已在 rerank-threshold 分支完成,待验收。
  3. More formats: docx and epub. 支持更多格式:docx、epub。
  4. Ship on PyPI ✓ done in 0.2.0 — pip install lorebox now works. 发布到 PyPI ✓ 0.2.0 已完成,现在可以直接 pip install lorebox。
  5. A server-free path for ask too. Generating an answer still needs Ollama; the embedding side already has a local option. 给 ask 也提供无服务端的路径。 生成答案目前仍需 Ollama,而嵌入侧已经 有了本地选项。

Contributions are welcome at any point — see CONTRIBUTING.md.

任何阶段都欢迎参与贡献,见 CONTRIBUTING.md。

Contributing / 参与贡献

See CONTRIBUTING.md. / 详见 CONTRIBUTING.md。

License / 许可证

MIT

项目介绍

lorebox 是一个本地私密文档问答工具,基于 Ollama 本地模型与 ChromaDB 向量检索实现。用户可将自己的 Markdown 或纯文本笔记导入本地索引,随后用自然语言提问,答案仅依据本地文档生成,全程离线运行,数据不出本机,无需任何 API Key,不产生网络请求。项目面向笔记管理、课程资料整理、个人知识库检索等场景,帮助开发者在保护隐私的前提下,快速查询和复用自己积累的资料。技术栈为 Python,代码结构清晰,包含分块、向量索引、检索与问答四个模块,适合学习 RAG 原理或二次开发。

定制我的领域