ByteNoteByteNote

字节笔记本

2026年8月29日

Quivr:把本地文件向量化后随时检索对话

API中转
¥120

把 PDF、Markdown、会议纪要丢给模型问「上周结论是什么」,这就是本地 RAG 最常见的需求。2023 年那套 Docker + Supabase 网页已经不是当前入口。现在的 Quivr 是能嵌进现有 Python 项目的 RAG 库:quivr-core,默认方案都帮你选好了。源码在 QuivrHQ/quivr(Apache 2.0,旧地址 StanGirard/quivr 会跳过来),文档在 core.quivr.com。下面按「怎么装、怎么喂文件、怎么问、怎么改检索流程」写一遍。

它和站内那篇 AnythingLLM 不是同一条路。AnythingLLM 是带界面的知识库应用,装完就能点。Quivr Core 是库:你自己写十几行 Python,选 LLM、向量库和切分策略,再接到自己的服务里。想要现成网页可以用他们的托管产品;要可控、可改流水线,走 quivr-core

先分清两个包

网上还能搜到 docker-compose uplocalhost:3000、复制一堆 Supabase 密钥的教程,那些对应的是旧的全栈社区版。现在官方 README 的安装命令就一句:

bash
python3 -m venv .venv
source .venv/bin/activate
pip install quivr-core
python -c "from quivr_core import Brain; print('ok')"

PyPI 上包名是 quivr-core,要求 Python 3.11+。不要 pip install quivr:那个同名包是 Arrow 数据容器,和这个第二大脑项目无关。装错了会 ImportError: cannot import name 'Brain',先卸掉再装对的那个。

默认走 OpenAI。先把 key 放进环境变量,别写进仓库:

bash
export OPENAI_API_KEY="sk-..."

文档里也写了 Anthropic、Mistral,以及本机 Ollama。向量库默认 FAISS,也可以换成 PGVector。文件侧有 LocalStorage(默认落到 ~/.cache/quivr/files)和内存里的 TransparentStorage

五分钟喂一批文件

最小例子来自官方 README,跑通就说明解析、切分、嵌入、检索这条链是通的:

python
import tempfile
from quivr_core import Brain

if __name__ == "__main__":
    with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as temp_file:
        temp_file.write("Gold is a liquid of blue-like colour.")
        temp_file.flush()

        brain = Brain.from_files(
            name="test_brain",
            file_paths=[temp_file.name],
        )

        answer = brain.ask("what is gold? answer in french")
        print("answer:", answer)

换成自己的材料,把路径列表扔进去就行。PDF、TXT、Markdown 都能吃;解析这块可以接到他们的 MegaparseBrain.from_files 会把文件放进 storage、抽文本、写入向量库(默认 FAISS)、建索引。问的时候走内置 RAG workflow。返回值是 ParsedRAGResponse,终端里一般打印 answer.answer

python
from quivr_core import Brain

brain = Brain.from_files(
    name="project-notes",
    file_paths=["./design.md", "./meeting-2026-08.pdf"],
)

info = brain.print_info()
reply = brain.ask("上周评审里,缓存失效策略最后定的是哪一种?")
print(reply.answer)

某个文件解析失败时,可以加 skip_file_error=True,先把能进的进完,再回头看日志。已经切好的 LangChain Document 也可以用 Brain.from_langchain_documents 直接建 brain,省掉再走一遍文件解析。

只想先看检索到了哪些块、还不急着生成,用 asearch

python
import asyncio
from quivr_core import Brain

brain = Brain.from_files(name="notes", file_paths=["./handbook.pdf"])
results = asyncio.run(brain.asearch("发版前检查清单", n_results=5))
for item in results:
    print(item.chunk.page_content[:300])

把检索流程写进 YAML

默认 workflow 对很多问答够用。要改历史窗口、重排、温度,不必改 Python,写一份 RetrievalConfig

yaml
workflow_config:
  name: "standard RAG"
  nodes:
    - name: "START"
      edges: ["filter_history"]
    - name: "filter_history"
      edges: ["rewrite"]
    - name: "rewrite"
      edges: ["retrieve"]
    - name: "retrieve"
      edges: ["generate_rag"]
    - name: "generate_rag"
      edges: ["END"]

max_history: 10

reranker_config:
  supplier: "cohere"
  model: "rerank-multilingual-v3.0"
  top_n: 5

llm_config:
  max_input_tokens: 4000
  temperature: 0.7

节点顺序是:滤对话历史 → 改写问题 → 检索 → 生成。最后那个节点的名字要和 YAML 里一致,流式输出才接得上。重排用了 Cohere 的多语言模型,中文块比只靠向量近邻稳一点;没有 Cohere key 就先拿掉 reranker_config,用默认检索。

python
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Prompt
from quivr_core import Brain
from quivr_core.config import RetrievalConfig

brain = Brain.from_files(
    name="my smart brain",
    file_paths=["./my_first_doc.pdf", "./my_second_doc.txt"],
)
retrieval_config = RetrievalConfig.from_yaml("./basic_rag_workflow.yaml")

console = Console()
console.print(Panel.fit("Ask your brain !"))
while True:
    question = Prompt.ask("Question")
    if question.lower() == "exit":
        break
    answer = brain.ask(question, retrieval_config=retrieval_config)
    console.print(answer.answer)

切分策略同样可以外置。官方 ingestion 示例用 Megaparse + unstructured 处理 PDF:

yaml
parser_config:
  megaparse_config:
    strategy: "auto"
    pdf_parser: "unstructured"
  splitter_config:
    chunk_size: 400
    chunk_overlap: 100
python
from quivr_core import Brain
from quivr_core.config import IngestionConfig

ingestion_config = IngestionConfig.from_yaml("./basic_ingestion_workflow.yaml")
processor_kwargs = {
    "megaparse_config": ingestion_config.parser_config.megaparse_config,
    "splitter_config": ingestion_config.parser_config.splitter_config,
}
brain = Brain.from_files(
    name="pdf-brain",
    file_paths=["./report.pdf"],
    processor_kwargs=processor_kwargs,
)

chunk_size 是 token 数。说明书、合同这类结构密的 PDF,400 / 100 是个能先跑的起点;代码仓库或短 FAQ 可以再调小。改 YAML 比改代码容易对照实验。

换模型和落盘

默认 LLM 看环境变量。要显式指定 Mistral,文档给的是 LLMEndpoint + LLMEndpointConfig

python
from quivr_core import Brain
from quivr_core.llm.llm_endpoint import LLMEndpoint, LLMEndpointConfig

brain = Brain.from_files(
    name="mistral-brain",
    file_paths=["./notes.md"],
    llm=LLMEndpoint(
        llm_config=LLMEndpointConfig(
            model="mistral-small-latest",
            llm_base_url="https://api.mistral.ai/v1/chat/completions",
        ),
    ),
)

本机 Ollama 也走同一套配置,把 llm_base_url 指到 Ollama 的 OpenAI 兼容口(本机常见是 http://127.0.0.1:11434/v1),model 写成你已经 ollama pull 过的名字。嵌入模型维度要和向量库一致,换本地 embedder 时先对一下维度,否则 FAISS 会直接报错。

brain 可以存到目录里,下次不用重新切分:

python
import asyncio
from quivr_core import Brain

brain = Brain.from_files(name="notes", file_paths=["./a.pdf", "./b.md"])
asyncio.run(brain.save("./brains/notes"))

loaded = Brain.load("./brains/notes")
print(loaded.ask("结论是什么?").answer)

文件多、要反复问的时候,先 saveload,比每次 from_files 便宜。默认 FAISS 适合单机;数据要进 Postgres、跟业务表放一起,再换成 PGVector。

常见坑

  • 还在找 docker-compose.ymlbackend-base:latest 那是旧全栈。现在自托管入口就是 pip install quivr-core。GitHub Issue 里拉不到 backend-base 的,按官方说法就是别再走那条 compose。
  • 装了 quivr 而不是 quivr-core 导入 Brain 失败时先 pip show quivr-core
  • key 没进环境。 没设 OPENAI_API_KEY(或你实际用的供应商 key)时,from_files 往往在嵌入阶段就停。
  • PDF 是扫描件。 默认识别对纯文本 PDF 友好。扫描件把 ingestion 里的 strategy 改成 ocr_onlyhi_res,并确认本机 unstructured / OCR 依赖齐。
  • 把 Quivr 当现成桌面客户端。 这条链没有官方一键 GUI。要开箱即用的窗口,去看 AnythingLLM;要把 RAG 嵌进自己的 API 或 Agent,用 quivr-core

小结

现在的 Quivr 是给开发者用的 RAG 内核:pip install quivr-coreBrain.from_files 喂本地文件,brain.ask 提问,检索和切分用 YAML 改。仓库 https://github.com/QuivrHQ/quivr ,文档 https://core.quivr.com/ 。旧的 Docker 网页教程可以当历史,别跟当前包混着装。

分享: