Skip to content

Repository files navigation

Insight

CI License: MIT

Insight 是一个面向气象业务资料的 local-first RAG 应用。它支持在本地导入 PDF、Markdown 和 TXT 资料,使用 BM25 与向量检索进行混合召回,并返回带来源引用的问答结果。项目自带的气象资料为参考样例,用于验证检索与问答链路。

Insight is a local-first RAG application for meteorological operational documents. It imports PDF, Markdown, and TXT files locally, performs hybrid retrieval with BM25 and vector search, and returns answers with source citations. The bundled meteorological documents are reference samples for verifying the retrieval and QA pipeline.

功能概览

  • 在本地检索观测说明、预警信号说明和数据处理流程。
  • 关键词检索、语义检索、RRF 融合与可选重排序构成的混合召回链路。
  • 证据不足时明确拒答,避免模型自由补充事实。

架构

flowchart LR
  A[PDF / Markdown / TXT] --> B[解析与分块]
  B --> N[本地索引任务]
  N --> C[SQLite 文档目录]
  N --> D[BM25 索引]
  N --> E[Ollama Embedding]
  E --> F[Milvus / Milvus Lite]
  Q[用户问题] --> G[BM25 + 向量召回]
  F --> G
  D --> G
  G --> H[RRF 融合]
  H --> I[可选 Rerank]
  I --> R{相关性足够?}
  R -->|否| K[明确拒答 + 阶段状态]
  R -->|是| L[Ollama 生成]
  L --> M[回答 + 来源引用 + trace]
Loading

核心功能

  • 文档上传、列表、删除和重建索引。
  • PDF 页码、Markdown 标题和文本块元数据保留。
  • 可选扫描 PDF OCR:默认关闭,启用后只处理没有文本层的页面。
  • BM25 + 向量召回、向量分数归一化、RRF 融合、Top-K、双层阈值和可选 Rerank。
  • 可选 Ollama 模型重排:设置 RERANKER_MODEL 后按候选片段评分;模型失败自动保留 RRF 顺序。
  • /chat/chat/stream/search、文档管理和 /health
  • Ollama、Milvus 和 Rerank 均通过 adapter 隔离,测试可使用 fake/mock。
  • 索引任务支持后台执行、状态轮询、失败重试、内容指纹幂等和模型版本变更提示。
  • 文档支持来源/标签过滤,搜索支持分页;问答支持可选会话上下文和 SSE 事件流。
  • RAG 工作流返回 query analysis、retrieval、rerank、relevance check、generation/fallback 阶段信息。
  • 评估脚本输出实际运行得到的 hit rate、MRR、拒答准确性和平均延迟;README 不预填性能指标。
  • 内置无 Node 依赖的本地 Web Console,可直接完成资料导入、混合检索和流式问答。

技术栈

Python 3.11+、FastAPI、Pydantic、Uvicorn、Ollama HTTP API、Milvus/Milvus Lite、BM25、SQLite、pytest、Docker Compose 和 GitHub Actions。

项目目录

app/
├── api/          # FastAPI routes
├── core/         # environment settings
├── ingestion/    # parsers and chunking
├── models/       # domain models
├── retrieval/    # BM25, vector, hybrid fusion
├── schemas/      # API schemas
├── services/     # catalog, Ollama, ingestion, jobs, sessions, QA
├── web/          # dependency-free local browser console
└── workflows/    # explicit RAG stage state
data/
├── sample_docs/  # reference sample documents
└── uploads/      # local runtime uploads
scripts/evaluate.py
tests/
Dockerfile
docker-compose.yml
pyproject.toml

本地运行

需要 Python 3.11+。推荐使用虚拟环境和 uv:

uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
cp .env.example .env
uvicorn app.main:app --reload

启动后打开 http://localhost:8000/,即可使用本地 Web Console。页面不需要 Node.js、前端构建工具或外部 CDN;它与 FastAPI 使用同源请求,上传后自动轮询索引任务,并在问答区展示 SSE 片段、来源和阶段状态。也可以使用 /docs 查看完整 API。

不安装或不启动外部模型时,应用仍可启动并使用关键词检索;问答和向量召回会根据依赖状态返回明确结果。

上传和重建索引默认返回后台任务,不阻塞 HTTP 请求。可通过 /jobs/{job_id} 轮询状态;进程重启后未完成的任务会被标记为可重试失败。

Ollama 模型准备

安装 Ollama 后准备一个生成模型和一个 embedding 模型:

ollama pull llama3.2:3b
ollama pull nomic-embed-text

通过 .env 配置 LLM_BASE_URLLLM_MODELEMBEDDING_MODEL。模型名称、地址、Top-K、阈值和超时均通过配置注入,不硬编码在业务逻辑中。

可选扫描 PDF OCR

扫描版 PDF 没有文本层时,可以额外安装本机 OCR 工具并显式开启:

# macOS
brew install poppler tesseract

# Debian/Ubuntu
sudo apt-get install poppler-utils tesseract-ocr

中文资料还需要安装对应的 Tesseract 语言包,并确保 pdftoppmtesseract 位于 PATH。在 .env 中设置:

OCR_ENABLED=true
OCR_LANGUAGE=chi_sim+eng
OCR_TIMEOUT_SECONDS=30
# 可选:指定 OCR 临时目录的父目录
OCR_TEMP_DIR=

OCR 默认关闭,不会影响普通 PDF、Markdown 和 TXT。启用后系统仅 OCR 文本层为空的 PDF 页面;工具缺失、超时或命令失败会让索引任务失败并返回 ocr_unavailableocr_timeoutocr_failed 语义。启用 OCR 后建议对已有扫描资料执行一次重建索引。Docker 镜像不预装这些系统工具,需自行制作带 OCR 运行时的镜像。

Milvus

本地开发可以将 MILVUS_URI 指向 Milvus Lite 文件路径;使用 Docker Compose 时:

docker compose up -d milvus
uvicorn app.main:app --reload

完整 API 容器和 Milvus 依赖可一起启动:

docker compose up --build

首次启动或切换 embedding 模型后,建议调用重建索引接口。不同 embedding 模型的向量维度可能不同,不能直接复用旧集合。

导入文档

curl -X POST http://localhost:8000/documents/upload \
  -F "file=@data/sample_docs/typhoon-warning.md"

curl http://localhost:8000/documents
curl -X POST http://localhost:8000/documents/reindex

# 上传/重建响应中的 job_id
curl http://localhost:8000/jobs/<job_id>
curl -X POST http://localhost:8000/jobs/<job_id>/retry
curl -X POST http://localhost:8000/jobs/<job_id>/cancel

# 更新来源和标签
curl -X PATCH http://localhost:8000/documents/<document_id>/metadata \
  -H 'Content-Type: application/json' \
  -d '{"source":"reference","tags":["typhoon"],"description":"typhoon warning procedures"}'

搜索与问答

curl -X POST http://localhost:8000/search \
  -H 'Content-Type: application/json' \
  -d '{"query":"台风预警信号分为几级?","top_k":5}'

curl -X POST http://localhost:8000/chat \
  -H 'Content-Type: application/json' \
  -d '{"query":"台风预警信号分为几级?"}'

# 按标签过滤并分页
curl -X POST http://localhost:8000/search \
  -H 'Content-Type: application/json' \
  -d '{"query":"预警","tag":"typhoon","offset":0,"top_k":5}'

# SSE 流式问答
curl -N -X POST http://localhost:8000/chat/stream \
  -H 'Content-Type: application/json' \
  -d '{"query":"台风预警信号分为几级?","session_id":"demo-session"}'

问答响应包含 answersourcesretrieval_resultsquerylatency_msstatus。来源包括文件名、页码(可用时)、章节和文本块 ID。没有达到阈值的上下文时,系统返回"当前知识库中没有足够信息"语义的拒答。

完整响应还包含 trace_idstagesretrieval_status/chat/stream 使用 text/event-stream,事件包括 startretrievalsourcetokencomplete;配置了 Ollama 时会使用原生 NDJSON 流式片段,并在连接异常时标记 fallback。排队中的索引任务可以取消,运行中任务不会被强制终止。会话历史仅辅助当前问题理解,不替代当前轮次的检索证据。

/searchstages 返回 keywordvectorfusionrerankretrieval 五个阶段。每项包含 statuslatency_ms;未启用阶段的耗时为 null,向量或模型异常时会显示已有的 fallback:<异常类别> 状态。控制台在检索结果上方展示这些阶段信息。

可选模型重排

默认不调用重排模型。需要使用本地 Ollama 对混合召回候选进行相关性评分时,设置:

ENABLE_RERANK=true
RERANKER_MODEL=<本地重排模型名>
CANDIDATE_K=20

系统要求模型只返回 0..1 的单个数字;模型不可用、超时或输出无法解析时,保留重排前的 RRF 顺序,并在 retrieval_status.rerank 中记录 fallback。模型重排按候选逐条调用 Ollama,可能增加延迟,建议从较小的 CANDIDATE_K 开始。清空 RERANKER_MODEL 可回退到不需要模型的确定性关键词重排。

测试

pytest
ruff check app tests scripts
python -c "from app.main import app; print(app.title)"

默认测试不连接真实 Ollama、Milvus 或外部 API。API 测试在安装完整依赖后执行;如果 FastAPI 尚未安装,相关测试会被标记为 skipped,不会伪造通过。

评估

评估数据位于 data/eval_questions.json,包含 10~20 条问题、期望命中文件/关键内容,以及拒答样例。运行:

python scripts/evaluate.py --output data/eval-result.json

默认检索模式是 bm25,且重排 disabled:只运行本地 BM25 基线,不访问 Ollama、Milvus 或外部 API。可以用同一问题集比较不同检索和重排路径:

# 默认离线基线
python scripts/evaluate.py --reranker-mode disabled --output /tmp/insight-eval-disabled.json

# 确定性的关键词重排,不需要模型服务
python scripts/evaluate.py --reranker-mode keyword --output /tmp/insight-eval-keyword.json

# 显式启用 Ollama 重排;每个候选片段会产生一次本地模型请求
python scripts/evaluate.py \
  --reranker-mode ollama \
  --reranker-model "$RERANKER_MODEL" \
  --ollama-base-url "${LLM_BASE_URL:-http://localhost:11434}" \
  --top-k 5 \
  --output /tmp/insight-eval-ollama.json

# 向量-only:显式调用 Ollama Embedding,使用内存向量后端
python scripts/evaluate.py \
  --retrieval-mode vector \
  --vector-backend memory \
  --embedding-model "${EMBEDDING_MODEL:-nomic-embed-text}" \
  --ollama-base-url "${LLM_BASE_URL:-http://localhost:11434}" \
  --vector-score-threshold "${VECTOR_SCORE_THRESHOLD:-0.7}" \
  --output /tmp/insight-eval-vector.json

# 混合检索 + Milvus Lite:URI 和 collection 请按本次实验隔离
python scripts/evaluate.py \
  --retrieval-mode hybrid \
  --vector-backend milvus \
  --embedding-model "${EMBEDDING_MODEL:-nomic-embed-text}" \
  --ollama-base-url "${LLM_BASE_URL:-http://localhost:11434}" \
  --milvus-uri /tmp/insight-eval-milvus.db \
  --milvus-collection insight_eval_hybrid \
  --vector-score-threshold "${VECTOR_SCORE_THRESHOLD:-0.7}" \
  --output /tmp/insight-eval-hybrid.json

输出保留 hit_ratemrrrefusal_accuracyaverage_latency_ms,并新增 refusal_calibration(阈值、拒答样例数、误报回答数、误报率)、retrieval_modeprofilemodels.embedding、向量后端参数、average_stage_latency_ms 以及每条问题的 stage_status/stage_timings_ms。向量分数会归一化到 [0, 1]VECTOR_SCORE_THRESHOLD 默认 0.7,在向量/混合检索中于 RRF 前过滤弱候选,SCORE_THRESHOLD 仍用于融合结果。可以使用同一问题集重复运行不同阈值,比较 hit_rate/mrrrefusal_calibration.false_positive_rate;BM25 默认模式不启用向量阈值。null 表示阶段或指标不适用,不代表零毫秒;向量 profile 的模型、地址、后端、URI、collection 和实际延迟会写入结果。vector/hybrid profile 缺少 Embedding 模型或 Milvus URI 时会快速失败。本项目不预置或声称任何准确率、延迟或模型压缩指标;这些数值必须由本机按指定语料和配置重新测得。

已知限制

  • PDF 标题识别依赖文档文本层,扫描图片 PDF 需要 OCR 扩展。

  • 扫描 PDF OCR 依赖本机 Poppler、Tesseract 和相应语言包;复杂表格、手写文字和版面结构不保证识别质量。

  • Milvus 集合的向量维度必须与当前 embedding 模型一致,切换模型后需要重建索引。

  • 向量评估需要本地 Ollama Embedding;Milvus Lite 评估会写入指定 URI,重复实验应使用新的 collection 或临时数据库文件。不同 embedding 模型的分数分布可能不同,需要通过 VECTOR_SCORE_THRESHOLD 做本地校准;默认值不是准确率保证。

  • Web Console 面向本地单用户场景,不提供认证、权限、会话列表或复杂文档管理功能。

  • 索引任务由单进程本地 worker 执行,不提供跨机器任务调度;进程重启后的 running 任务需要重试。

  • 原生流式效果取决于 Ollama 服务和模型;流式连接异常时会退化为完整回答事件。

  • Rerank 目前作为可选 adapter;模型不可用时保留混合检索顺序并记录 fallback。

  • 附带的气象资料为参考样例,不能替代正式气象业务规范。

路线图

  • 增强 OCR 和更稳健的章节识别。
  • 增加可选 LangGraph 状态图和节点级追踪。
  • 增加真实公开资料的许可与来源管理。
  • 增加可选的跨编码器 Rerank 和更系统的离线评估集。

本项目由 Vibe Coding 辅助实现落地。Built with Vibe Coding.

About

洞察者 · 本地优先气象文档 RAG — BM25+向量混合检索、证据不足明确拒答、全链路可观测 | Local-first RAG: hybrid retrieval, explicit refusal & full-pipeline observability

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages