AnswrSe3kr/pdfsearchable
GitHub: AnswrSe3kr/pdfsearchable
一款离线的 PDF 知识库构建与搜索工具,支持 OCR、全文与语义检索、取证分析与 AI 增强,适用于本地安全审计与合规场景。
Stars: 0 | Forks: 0
# pdfsearchable
[](https://github.com/AnswrSe3kr/pdfsearchable/actions/workflows/ci.yml)
[](https://www.python.org/downloads/)
[](tests/)
[](LICENSE)
[](https://github.com/PyCQA/bandit)
[](https://github.com/astral-sh/ruff)
**在离线状态下对 PDF 集合进行编录、搜索与浏览。**
一个命令行工具,可将文件夹中的 PDF 转换为可搜索、可浏览的知识库,并提供一个类似 Apple 的交互式 Web 应用界面。无需云端支持。内置 OCR。支持通过本地 Ollama 进行 AI 增强。
## 功能特性
| 功能 | 详情 |
|---|---|
| **编录** | PyMuPDF 文本提取 + 每页 Tesseract OCR |
| **OCR 流水线** | OSD 方向校正、Otsu 二值化、自动去偏、历史模式(CLAHE、Sauvola、形态学清理) |
| **全文搜索** | SQLite FTS5,支持 `AND / OR / NOT` 和精确短语匹配 |
| **语义搜索** | Ollama 嵌入(`nomic-embed-text`),纯 Python 余弦相似度 |
| **交互式 SPA** | 类似 Apple 的三列 Web 应用:侧边栏、文档列表、详情面板 |
| **文档查看器** | 全页查看器,内嵌 PDF、元数据侧边栏、注释、RAG 聊天 |
| **词云** | 基于文档类型或全集的交互式 Canvas 词频可视化 |
| **位置地图** | Leaflet 地图展示提取的位置(可选 Nominatim 地理编码) |
| **知识图谱** | 力导向 D3 实体关系图 |
| **时间线** | 按时间排序的文档视图,支持日期范围过滤 |
| **AI 增强** | 文档类型分类、摘要、标签、当事人、金额 —— 通过 Ollama 实现 |
| **RAG 聊天** | 终端中多文档问答(`pdfsearchable chat`) |
| **注释** | 为已编录文档添加、列出和管理每页注释 |
| **合同分析** | 合同条款检测、到期提醒、摘要仪表盘 |
| **取证分析** | PDF 取证分析:元数据、隐藏文本、嵌入文件 |
| **红action检测** | 检测 PDF 中的红action内容 |
| **表格提取** | 将结构化表格导出为 CSV 或 JSON |
| **MCP 服务器** | 通过 MCP stdio 向 Claude Desktop、Cursor、Zed 暴露索引 |
| **监控模式** | 自动为新出现或修改的 PDF 编录索引 |
| **导出** | JSON(完整编录)、JSONL(LLM/RAG 流水线)、CSV(表格)、Markdown(每文档一文件)、Obsidian/Logseq YAML 前置元数据 |
| **备份/校验** | `.tar.gz` 归档 + 哈希漂移完整性检查 |
| **实时更新** | SSE 推送 —— 编录变更时 SPA 显示横幅 |
| **离线优先** | 预先下载 Leaflet、wordcloud2;运行时无 CDN 依赖 |
| **认证** | 可选的 Bearer 令牌(`PDFSEARCHABLE_AUTH_TOKEN`)保护 HTTP 服务器 |
| **CORS** | 可配置的 CORS 头以支持跨域 API 访问 |
| **审计日志** | 所有操作追加至 `.pdfsearchable/audit.jsonl` |
| **分类器反馈** | 记录校正以改进未来文档分类 |
| **自动快照** | 每次保存前旋转索引快照(`PDFSEARCHABLE_AUTO_SNAPSHOT=1`) |
| **Schema 迁移** | `pdfsearchable migrate` 强制 v1/v2 → v3 并提供干运行预览 |
| **干运行编录** | `pdfsearchable add --dry-run` 在写入前预览新/重新处理/忽略项 |
| **增量嵌入** | `pdfsearchable add --embed` 仅对新/变更文档生成嵌入 |
| **语义去重** | `pdfsearchable dedup-semantic` 通过余弦相似度 ≥ 阈值查找近似重复(翻译、版本) |
| **检查命令** | `pdfsearchable inspect` 显示任意文档的 5 个 Rich 面板或 JSON |
| **Ollama 长连接** | 模型在调用间保持活跃(`PDFSEARCHABLE_OLLAMA_KEEP_ALIVE=30m`)——每文档节省 2–5 秒 |
| **公式保留** | 检测 LaTeX(`$…$`、`$$…$$`、`\[…\]`、`equation/align` 环境)**和** Unicode 数学簇(∫ ∑ ∏ √ ∂ π θ …)。通过 `PDFSEARCHABLE_DETECT_FORMULAS=1` 启用。存储在 `metadata.formulas` 中,并在 Markdown 导出时以 `$$ … $$` 块重新输出。 |
| **Markdown 基准** | `pdfsearchable benchmark-markdown ` 测量 PDF→Markdown 速度提升(对比朴素 PyMuPDF 重新解析)。在 20 页合成 PDF 上实测 **7.7× 更快**(10 次迭代,macOS arm64);提速源于读取缓存的预提取文本而非重新解析。 |
## 安装
**要求:** Python 3.10+、[Tesseract](https://github.com/tesseract-ocr/tesseract)(用于 OCR)、[Ollama](https://ollama.com)(可选,用于 AI 功能)
```
pip install pdfsearchable
```
或从源码安装:
```
git clone https://github.com/AnswrSe3kr/pdfsearchable.git
cd pdfsearchable
pip install -e .
```
可选扩展:
```
pip install "pdfsearchable[ai]" # OpenAI classification
pip install "pdfsearchable[htr]" # HTR (TrOCR) for handwritten text
pip install "pdfsearchable[tables-ocr]" # Table extraction from scanned PDFs
pip install "pdfsearchable[dev]" # pytest, ruff
```
## 快速开始
```
# 1. 索引 PDF 文件夹
pdfsearchable add ~/Documents/contracts/
# 2. 打开交互式 Web 应用程序
pdfsearchable serve
# → 打开 http://127.0.0.1:8000/app.html
# 3. 从终端搜索
pdfsearchable search "rescisão contratual"
# 4. 获取文档详情
pdfsearchable info contrato-aluguel
```
Web 应用(`app.html`)是一个单页面应用,通过 REST API 动态加载所有数据。它提供:
- **侧边栏** — 文档类型筛选、标签筛选、统计信息
- **文档列表** — 可搜索、可排序的已编录 PDF 列表
- **详情面板** — 元数据、提取文本、注释、内嵌 RAG 聊天
- **词云视图** — `/wordcloud.html` — 交互式词频 Canvas
- **位置地图** — `/map.html` — 带地理编码位置的 Leaflet 地图
- **知识图谱** — `/graph.html` — 力导向实体关系图
- **时间线** — `/timeline.html` — 带日期范围过滤的文档浏览器
静态的 `report.html` 可通过 `pdfsearchable report` 单独生成 —— 它是一个独立的快照,无需运行服务器即可在任意浏览器中打开。
## 命令参考
### 编录
```
pdfsearchable add FILE_OR_DIR [OPTIONS]
--workers N Parallel workers (0=auto up to 16; 1=sequential; 2+=multiprocessing)
--batch-size N Process in batches of N files (gc between batches)
--order-by size|mtime|name Order files before processing
--recursive / --no-recursive Include PDFs in subfolders (default: recursive)
--resume Resume from interrupted run (Ctrl+C)
--continue Skip already indexed and continue on errors
--reprocess Re-index even if content hash matches
--extract-mode text|blocks|dict PyMuPDF extraction mode
--compress Compress stored text (gzip)
--password TEXT PDF password (or env PDF_PASSWORD)
--confirm-type Confirm AI-classified type interactively
--embed Generate semantic embeddings (incremental) after indexing
--dry-run Preview what would be indexed without writing anything
```
OCR 通过环境变量配置:`PDFSEARCHABLE_OCR_LANG`(默认 `por`)、`PDFSEARCHABLE_OCR_DPI`(默认 `300`)、`PDFSEARCHABLE_OCR_HISTORICAL`(`off` / `auto` / `on`)。
```
pdfsearchable remove FILE_OR_ID [--yes] # Remove from index
pdfsearchable status # Project status (indexed files, totals)
pdfsearchable info ID_OR_NAME # Detailed metadata for one document
pdfsearchable inspect ID_OR_NAME [--json] # 5-panel view (identification, enrichment, entities, detections, pdf meta)
pdfsearchable migrate [--dry-run] # Force index schema migration (v1/v2 → v3)
pdfsearchable dedup-semantic [--threshold 0.98] [--model nomic-embed-text] # Near-duplicates by cosine similarity
```
### 搜索
```
pdfsearchable search QUERY [OPTIONS]
--type TYPE Filter by document type
--language LANG Filter by language (e.g. pt-BR, en)
--date-from DATE Indexed since (YYYY-MM-DD)
--date-to DATE Indexed until (YYYY-MM-DD)
--semantic Semantic search via embeddings (run `pdfsearchable embed` first)
--ollama Expand query terms via Ollama
```
FTS 操作符:`"精确短语"`、`term1 AND term2`、`term1 OR term2`、`NOT term`
### Web 服务器
```
pdfsearchable serve [OPTIONS]
--host HOST Bind address (default: 127.0.0.1)
--port PORT Port (default: 8000)
--open Open browser automatically
```
`serve` 将所有 SPA 模板文件复制到 `.pdfsearchable/` 并启动 HTTP 服务器。访问 `http://host:port/` 会重定向到 `/app.html`。
**服务器暴露的 API 端点:**
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/` | 重定向到 `/app.html` |
| GET | `/api/health` | 健康检查(编录 + Ollama 状态) |
| GET | `/api/index` | SPA 启动用的完整编录 JSON |
| GET | `/api/search` | FTS + 语义搜索(`?q=&type=`) |
| GET | `/api/text` | 指定文档的完整提取文本(`?id=`) |
| GET | `/api/page` | 指定页面的文本(`?id=&page=`) |
| GET | `/api/annotations` | 列出文档的注释(`?id=`) |
| GET | `//wordcloud` | 词频(`?type=&limit=`) |
| GET | `/api/locations` | 位置(可选地理编码,`?geocode=0\|1`) |
| GET | `/api/graph` | 知识图谱节点与边 |
| GET | `/api/timeline` | 时间线条目与统计 |
| GET | `/api/events` | 实时编录更新的 SSE 流 |
| GET | `/arquivos-processados/` | 提供 PDF 文件 |
| POST | `/api/meta/update` | 更新文档类型、标签、主题 |
| POST | `/api/annotations` | 添加新注释 |
| POST | `/api/ask` | 通过 Ollama 进行 RAG 问答(速率限制) |
**服务器特性:** Bearer/基础认证(`PDFSEARCHABLE_AUTH_TOKEN`)、CORS(带 `Max-Age` 预检缓存)、`/api/ask` 速率限制、大响应 gzip 压缩、SSE 连接超时(5 分钟)、地理编码缓存、所有端点的 JSON 错误响应、禁用目录列表。
### 报告(静态、独立快照)
```
pdfsearchable report [--output PATH]
```
生成当前编录的自包含 `report.html` 快照。该文件可在无运行服务器的情况下在任意浏览器中打开。它独立于 `serve` 和实时 SPA。
### AI 功能(Ollama)
```
# 使用 AI 元数据丰富所有已索引文档
PDFSEARCHABLE_AI=ollama pdfsearchable add docs/
# 为向量搜索生成语义嵌入
pdfsearchable embed
# 与文档集合对话
pdfsearchable chat
pdfsearchable chat --doc DOCUMENT_ID # Single-document focus
# 语义搜索
pdfsearchable search --semantic "quem assinou o contrato"
```
### 文档分析
```
# 检测 PDF 中的红acted内容
pdfsearchable redactions [PDF]
# 取证分析(元数据、隐藏文本、嵌入文件)
pdfsearchable forensics [PDF]
# 将表格导出为 CSV 或 JSON
pdfsearchable tables [PDF]
# 合同摘要和到期提醒
pdfsearchable contracts
# 终端中的文档时间线
pdfsearchable timeline
# 生成知识图谱 HTML
pdfsearchable knowledge-graph
```
### 注释与反馈
```
# 管理文档注释(列出、添加、删除)
pdfsearchable annotations [OPTIONS]
# 记录分类器修正用于未来训练
pdfsearchable feedback
```
### MCP 服务器(Claude Desktop / Cursor / Zed)
```
pdfsearchable mcp
```
添加到 `claude_desktop_config.json`:
```
{
"mcpServers": {
"pdfsearchable": {
"command": "pdfsearchable",
"args": ["mcp"],
"cwd": "/path/to/your/pdf/folder"
}
}
}
```
暴露的工具:`list_documents`、`search_documents`、`get_document_text`、`ask_document`、`ask_all_documents`、`index_document`、`get_redaction_report`、`get_forensics_summary`
### 自动化
```
pdfsearchable watch [DIR] # Auto-index new/modified PDFs
pdfsearchable watch --interval 5 # Check every 5 seconds
pdfsearchable backup [OUTPUT] # Create .tar.gz of the index
pdfsearchable verify # Check hash integrity of all PDFs
pdfsearchable export --format jsonl --output colecao.jsonl
pdfsearchable export --format markdown --output ./docs_md/
pdfsearchable export --format csv --output metadados.csv
pdfsearchable export --format obsidian --output-dir ~/vault/PDFs
pdfsearchable doctor # Diagnose environment (PyMuPDF, Tesseract, Ollama, disk…)
```
## 配置
所有设置均以 `PDFSEARCHABLE_` 为前缀的环境变量,或保存在 `.pdfsearchable/config.json` 中(通过 `pdfsearchable init` 创建)。
| 变量 | 默认值 | 描述 |
|------|--------|------|
| `PDFSEARCHABLE_AI` | `heuristic` | `heuristic` / `ollama` / `openai` |
| `PDFSEARCHABLE_OLLAMA_URL` | `http://localhost:11434` | Ollama 基础 URL |
| `PDFSEARCHABLE_OLLAMA_MODEL` | `llama3.2` | 聊天/分类模型 |
| `PDFSEARCHABLE_OLLAMA_CLASSIFY_MODEL` | (无) | 仅用于分类的小型模型(例如 `llama3.2:1b`) |
| `PDFSEARCHABLE_OLLAMA_KEEP_ALIVE` | `5m` | 在调用间保持模型加载(`-1`=永久,`0`=卸载) |
| `PDFSEARCHABLE_AUTO_SNAPSHOT` | `0` | `1` 表示在每次保存前对编录进行快照 |
| `PDFSEARCHABLE_SNAPSHOT_KEEP` | `5` | 保留的旋转快照数量 |
| `PDFSEARCHABLE_DETECT_REDACTIONS` | `0` | `1` 表示在编录期间检测红action区域 |
| `PDFSEARCHABLE_FORENSICS` | `0` | `1` 表示在编录期间运行 PDF 取证分析 |
| `PDFSEARCHABLE_CONTRACTS` | `0` | `1` 表示在编录期间检测合同条款 |
| `PDFSEARCHABLE_OCR_WORKERS` | 自动(3·CPU/4,2–8) | 每页并行 OCR 工作线程数 |
| `PDFSEARCHABLE_DETECT_FORMULAS` | `0` | `1` 表示在编录期间检测 LaTeX / Unicode 数学公式 |
| `PDFSEARCHABLE_AUTH_TOKEN` | (无) | HTTP 服务器的 Bearer 令牌 |
| `PDFSEARCHABLE_CORS` | `0` | `1` 表示启用 CORS 头 |
| `PDFSEARCHABLE_CORS_ORIGIN` | `*` | 允许的 CORS 源 |
| `PDFSEARCHABLE_ASK_RATE_LIMIT` | (无) | `/api/ask` 的请求/分钟(0 = 无限制) |
| `PDFSEARCHABLE_HTTP_LOG` | `0` | `1` 表示记录 HTTP 请求 |
| `PDFSEARCHABLE_WEBHOOK_URL` | (无) | 每次编录运行后 POST JSON |
| `PDFSEARCHABLE_OCR_LANG` | `por` | Tesseract 语言代码 |
| `PDFSEARCHABLE_OCR_DPI` | `300` | OCR 渲染 DPI |
| `PDFSEARCHABLE_OCR_CORRECT` | `0` | `1` 表示通过 LLM 修复 OCR 错误 |
| `PDFSEARCHABLE_OCR_HISTORICAL` | `off` | `off` / `auto` / `on` —— 历史文档流水线(CLAHE + Sauvola + 形态学清理 + 历史 HTR) |
| `OPENAI_API_KEY` | (无) | 当 `PDFSEARCHABLE_AI=openai` 时需要 |
## 存储布局
```
your-project/
├── .pdfsearchable/
│ ├── index.json # Document registry
│ ├── fts.sqlite # Full-text search index (FTS5)
│ ├── embeddings.sqlite # Semantic embeddings (optional)
│ ├── audit.jsonl # Action log
│ ├── assets/ # Offline JS/CSS assets
│ ├── config.json # Project config
│ ├── app.html # Main SPA (copied by serve)
│ ├── document-view.html # Full-page document viewer (copied by serve)
│ ├── wordcloud.html # Word cloud view (copied by serve)
│ ├── map.html # Location map view (copied by serve)
│ ├── graph.html # Knowledge graph view (copied by serve)
│ ├── timeline.html # Timeline view (copied by serve)
│ └── report.html # Static report (generated by pdfsearchable report)
└── arquivos-processados/
├── .pdf # Symlink / copy of original
└── / # Extracted text per page
├── page_001.txt
└── …
```
## 开发
```
git clone https://github.com/AnswrSe3kr/pdfsearchable.git
cd pdfsearchable
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# 预提交钩子(安全、代码规范、格式化)
pip install pre-commit && pre-commit install
# 完整测试金字塔
pytest tests/unit/ -q # unit (white-box)
pytest tests/integration/ -q # integration (grey-box)
pytest tests/system/ -q # system / E2E (black-box)
pytest tests/acceptance/ -q # UAT
pytest tests/security/ -q # security (OWASP)
pytest tests/regression/ -q # regression (bug guards)
pytest tests/performance/ -q # load & throughput
# 一次性覆盖率测试
pytest tests/ --cov=pdfsearchable --cov-report=term-missing
# Lint + 格式化
ruff check src/ && ruff format --check src/
# SAST 安全扫描
bandit -r src/ -c pyproject.toml
# 依赖漏洞扫描
pip-audit
```
## 许可证
MIT
标签:AI风险缓解, Apple风格UI, Bandit, CI, CIDR输入, CLAHE, D3, Leaflet, LLM评估, Nominatim, OCR, Ollama, PDF索引, PyMuPDF, Python, Python 3.11, RAG聊天, Ruby, Ruff, Sauvola, SEO, SQLite FTS5, Tesseract, Webhook, 三栏布局, 交互式Web应用, 代码风格, 余弦相似度, 侧边栏, 全文本搜索, 关键词优化, 力导向图, 单页应用, 历史HTR, 向量嵌入, 地图可视化, 地理编码, 安全扫描, 形态学清理, 文档查看器, 无云部署, 无后门, 时序注入, 时间线视图, 本地AI, 本地RAG, 注释, 测试, 知识库, 离线文档处理, 网络安全, 词云, 语义搜索, 逆向工具, 隐私保护