


















LiteParse 是由 LlamaIndex 团队(run-llama 组织)开发和维护的一个开源、本地、轻量级的文档解析器**。它的官方定位是 LlamaParse(LlamaIndex 云端文档解析 SaaS)的本地/离线开源替代品。
一句话定义:"一个专注于快、轻的开源文档解析器,提供高质量的空间文本提取和精确的边界框(bounding box),完全本地运行,无需云依赖。"
项目解决的核心问题:
项目命名中的 "lite" 强调:二进制小、零运行时依赖、极简 API、仅关注文本 + 边界框(不做表格语义分割、图像识别等复杂任务)。
| 项目 | 内容 |
|---|---|
| GitHub 仓库 | https://github.com/run-llama/liteparse |
| 组织 | run-llama(LlamaIndex 官方组织) |
| 主要维护者 | Logan Markewich 及 LlamaIndex 团队 |
| 当前版本 | v2.0.7(截至 2026-06-09) |
| 许可证 | Apache-2.0 |
| 首次提交 | 约 2025 年 5 月 |
| Release tag 数 | 62 个 |
| 分析日期 | 2026-06-09 |
| 渠道 | 地址/包名 | 说明 |
|---|---|---|
| GitHub | https://github.com/run-llama/liteparse | 主仓库 |
| Crates.io | liteparse |
Rust 包 |
| PyPI | liteparse |
Python 包(同时安装 lit CLI) |
| npm (Node) | @llamaindex/liteparse |
Node.js/TypeScript 包 |
| npm (WASM) | @llamaindex/liteparse-wasm |
浏览器 WebAssembly 包 |
| 中文 README | README.zh-CN.md | 官方中文文档 |
┌────────────────────────────────────────────────────────────────────┐
│ LiteParse 架构总览 │
└────────────────────────────────────────────────────────────────────┘
用户输入文件 调用方式
┌────────────────────────┐ ┌─────────────────────────┐
│ PDF / DOCX / PPTX / │ │ Python: liteparse │
│ XLSX / ODT / PNG / ... │ │ Rust: liteparse crate │
└───────────┬────────────┘ │ Node: @llamaindex/... │
│ │ WASM: @llamaindex/... │
│ │ CLI: lit parse │
▼ └────────────┬────────────┘
┌───────────────────────────────────────────────┼──────────────────┐
│ LiteParse 核心引擎(Rust) │ │
│ │ │
│ Step 1: 输入格式检测与转换 │ │
│ ┌────────────────────────────────────┐ │ │
│ │ infer() —— 文件类型嗅探(infer crate) │ │ │
│ │ conversion.rs —— 非 PDF → 调用外部工具 │ │ │
│ │ Office → LibreOffice --headless│ │ │
│ │ Image → ImageMagick convert │ │ │
│ └──────────────────┬─────────────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ Step 2: 原生文本提取(Google PDFium) │ │
│ ┌────────────────────────────────────────────┐ │ │
│ │ pdfium crate —— Rust 高层封装 │ │ │
│ │ pdfium-sys crate —— FFI 绑定到 C PDFium │ │ │
│ │ extract.rs —— FPDFTextPage → TextItem │ │ │
│ │ 每个 TextItem 包含: │ │ │
│ │ text / x / y / width / height / rotation │ │ │
│ │ font_name / font_size / font_height / │ │ │
│ │ font_weight / fill_color / confidence │ │ │
│ └──────────────────────┬──────────────────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ Step 3: OCR 通道(可选) │ │
│ ┌─────────────────────────────────────────────────┐ │ │
│ │ render.rs —— 将页面渲染为位图(dpi 控制分辨率) │ │ │
│ │ ocr.rs —— 选择 OCR 引擎: │ │ │
│ │ ① ocr_engine_override(Rust trait 注入) │ │
│ │ ② HTTP OCR Server(reqwest 并发请求)│ │ │
│ │ ③ Tesseract(默认,随库捆绑) │ │ │
│ │ ocr_merge.rs —— OCR 结果与原生文本合并 │ │ │
│ │ (坐标反投影回 PDF 坐标系统) │ │ │
│ └──────────────────────┬───────────────────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ Step 4: 网格投影(Projection —— LiteParse 关键创新) │ │
│ ┌────────────────────────────────────────────────────┐ │ │
│ │ projection.rs —— 栅格化到虚拟列/行网格 │ │ │
│ │ Snap 机制: Left / Right / Center 对齐 │ │ │
│ │ Anchor 机制: 解决多列、页边、列表编号等排版问题 │ │ │
│ │ 生成保留视觉阅读顺序的纯文本 page.text │ │ │
│ │ 同时保留每个 TextItem 的精确坐标 (x, y, w, h) │ │ │
│ └──────────────────────┬────────────────────────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ Step 5: 输出格式化 │ │
│ ┌────────────────────────────────────────────────────┐ │ │
│ │ output.rs —— OutputFormat::Text 或 Json │ │ │
│ │ Text: 拼接各页纯文本(保留布局) │ │ │
│ │ Json: ParsedPage → TextItem 结构化序列化 │ │ │
│ │ │ │ │
│ │ render.rs —— ScreenshotResult (PNG 截图) │ │ │
│ │ 多模态 LLM 使用:页面图像 + 文本边界框 │ │ │
│ └──────────────────────┬────────────────────────────────────┘ │ │
│ │ │ │
└─────────────────────────┼────────────────────────────────────────────────┘ │
│ │
▼ │
┌────────────────────────────────────────────┐ │
│ ParseResult 输出结构 │ │
│ │ │
│ { │ │
│ pages: [ │ │
│ { │ │
│ page_num, page_width, page_height, │ │
│ text, // 纯文本(保留布局) │ │
│ text_items: [ │ │
│ { text, x, y, width, height, │ │
│ font_name, font_size, confidence } │ │
│ ] │ │
│ } │ │
│ ], │ │
│ text: String // 全文拼接 │ │
│ } │ │
└────────────────────────────────────────────┘ │
│
┌───────────────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 下游 RAG / 多模态 / Agent 应用 │
│ │
│ • LlamaIndex: 替换 SimpleDirectoryReader 的 PDFReader │
│ • 基于边界框做区域切分(识别表格、标题、段落) │
│ • 页面截图 + bbox → 多模态 LLM(Claude/GPT-4o) │
│ • 搜索 search_items() → 合并边界框高亮关键字 │
│ • AI Agent 工具流:npx skills add run-llama/llamaparse-agent-skills │
└─────────────────────────────────────────────────────────────────────────┘
| 亮点 | 说明 |
|---|---|
| 速度 | Rust + PDFium 核心,比传统 Python 解析库快 10–100× |
| 空间信息 | 每个文字项带精确 (x, y, width, height) 边界框 |
| 多语言 API | Rust / Python / Node.js (TypeScript) / WebAssembly,四种语言原生绑定 |
| 本地、离线、零云依赖 | 完全在本地运行,无 API Key,无网络请求 |
| OCR 可插拔 | 内置 Tesseract + HTTP OCR 服务规范 + 自定义引擎注入 |
| 极简 API | parse() / screenshot() 两个主方法 + 一个 Config 结构体 |
| 多格式支持 | PDF + 所有主流 Office + 所有主流图像格式 |
| 网格投影算法 | Snap/Anchor 机制重建阅读顺序,解决多列排版问题 |
| 二进制分发 | Python wheel 随附 prebuilt PDFium,pip install 即可用 |
| 截图能力 | 高质量页面 PNG,供多模态 LLM 直接消费 |
LiteParse 的核心速度来自两个设计决策:
实际速度表现(社区 benchmark 报告):
这是 LiteParse 与传统文本提取工具(pypdf、markitdown 等)最大的区别之一:
传统提取(pypdf):
"第三章 实验方法 ... "(只拿到文本,不知道"第三章"在页面哪个位置)
LiteParse:
TextItem { text: "第三章", x: 72.0, y: 90.5, width: 35.2, height: 14.3, ... }
TextItem { text: "实验方法", x: 112.3, y: 90.5, width: 50.1, height: 14.3, ... }
为什么空间信息重要?
search_items() 返回合并边界框,可直接在 UI 中高亮OCR 引擎选择优先级(从高到低):
① ocr_engine_override
→ Rust trait 注入:with_ocr_engine(Arc<dyn OcrEngine>)
→ WASM 端通过 JS ocrEngine 回调注入
→ 最灵活,支持任何引擎(PaddleOCR/EasyOCR/TrOCR/自建模型)
② ocr_server_url(HTTP OCR)
→ 并发 num_workers 个 POST /ocr 请求
→ 返回 { text, bbox, confidence } JSON
→ 遵循 OCR_API_SPEC.md 规范
→ 适合部署 GPU OCR 服务,客户端保持轻量
③ Tesseract(默认)
→ feature = "tesseract",随库捆绑
→ 通过 TESSDATA_PREFIX 支持离线语言包
→ 支持 eng / chi_sim / jpn / kor / fra / deu / ... 上百种
架构优势:默认够用(Tesseract),同时保留升级通道(HTTP),还允许完全自定义(trait 注入),兼顾 "开箱即用" 和 "生产部署" 两种场景。
这是 LiteParse 在"保留阅读顺序"方面的核心创新:
输入:TextItem 列表(每个有 x, y, w, h, text)
算法流程:
Step 1: 行聚类(y 坐标分组)
┌────────────────────────────────────┐
│ "第三章 实验方法" │ ← y = 90.5,同一行合并
│ "本章节描述了三种实验方法..." │ ← y = 120.3
│ "方法 A:..." │ ← y = 150.1(多列情况) "方法 B:..."
└────────────────────────────────────┘
Step 2: Snap 机制(对齐吸附)
每个 TextItem 计算左/中/右三个可能的吸附点
通过 HashMap 聚类相同 x 坐标的吸附点
→ 识别出页面中"隐含的列边界"
Step 3: Anchor 机制
将 Snap 点锚定到页面全局网格
解决:
• 多列文档(每列独立阅读顺序)
• 页边空白(过滤 margin noise)
• 列表编号/项目符号(单独吸附为独立列)
Step 4: 阅读顺序重建
按 行 主序(先从上到下)
在同一行内按 Snap 列顺序(从左到右)
→ 生成保留视觉布局的纯文本
输出:page.text(保留行间距与缩进的纯文本)
+ 完整 TextItem 列表(仍保留精确坐标)
算法复杂度:O(n log n),n 为 TextItem 数量
与传统 PDF 文本提取的区别:传统方法通常是"按 PDF 内部存储顺序"输出文本,经常出现列错位、文字顺序错乱(尤其是多列布局的论文)。LiteParse 的投影算法从几何层面重建阅读顺序,显著提升文本可读性。
以 Python 为例,整个库的 API 可以浓缩为:
class LiteParse:
def __init__(self, **config) -> None: ... # 配置(全部可选)
def parse(self, path_or_bytes) -> ParseResult: ... # 解析文档 → 结构化结果
def parse_bytes(self, bytes) -> ParseResult: ... # 直接解析字节
def screenshot(self, path, page_numbers) -> list[ScreenshotResult]: ...
# 搜索(返回带合并边界框的匹配项)
def search_items(text_items, keyword, case_sensitive=False): ...
class ParseResult:
pages: list[ParsedPage] # 每页结构化数据
text: str # 全文拼接(保留布局)
class ParsedPage:
page_num: int
page_width: float
page_height: float
text: str # 本页纯文本(保留布局)
text_items: list[TextItem] # 细粒度文本项
class TextItem:
text: str
x, y, width, height: float # PDF 72 DPI 坐标(左上原点)
rotation: float
font_name: str
font_size: float
font_height: float
font_weight: int
fill_color: str
confidence: float
配置项仅有 10 个左右,全部有合理默认值,用户通常不需要修改。
┌────────────────────────────────────┐
│ Rust 核心 (crates/) │
│ liteparse —— 核心库 + CLI │
│ pdfium —— PDFium 高层封装 │
│ pdfium-sys —— PDFium FFI 绑定 │
└────────────┬───────────────────────┘
│
┌─────────────────┼─────────────────┬──────────────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ PyO3 绑定 │ │ napi-rs 绑定 │ │ wasm-bindgen │ │ Rust 原生 │
│crates/ │ │ crates/ │ │ crates/ │ │ crates/ │
│liteparse-│ │ liteparse-napi │ │ liteparse- │ │ liteparse │
│python │ │ │ │ wasm │ │ │
│ │ │ │ │ │ │ │
│ → packages/│ │ → packages/ │ │ → packages/ │ │ → Crates.io │
│ python/ │ │ node/ │ │ wasm/ │ │ │
│ │ │ │ │ │ │ │
│ PyPI: │ │ npm: │ │ npm: │ │ 直接 cargo │
│ liteparse │ │ @llamaindex/│ │ @llamaindex/ │ │ use │
│ │ │ liteparse │ │ liteparse- │ │ │
│ │ │ │ │ wasm │ │ │
└────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
优势:
| 工具 | 纯本地 | 空间信息/bbox | OCR | 速度 | 依赖 | 云调用 |
|---|---|---|---|---|---|---|
| LiteParse | ✅ | ✅ 原生 | ✅ Tesseract/HTTP | 极快 | Rust + PDFium | ❌ |
| LlamaParse | ❌ | ✅ | ✅ | 快 | SaaS | ✅ |
| pypdf | ✅ | 有限 | ❌ | 中 | 纯 Python | ❌ |
| PyMuPDF (fitz) | ✅ | ✅ | ❌ | 快 | C | ❌ |
| unstructured.io | 半 | ✅ | 可选 | 中 | 重依赖 | 可选 |
| markitdown | ✅ | ❌ | ❌ | 中 | 纯 Python | ❌ |
LiteParse 在"本地 + 空间信息 + OCR + 速度"的组合上是目前最均衡的开源方案。
| 组件 | 最低要求 | 推荐 |
|---|---|---|
| 操作系统 | Linux (x86_64, aarch64) / macOS 11+ (Intel/ARM) / Windows 10+ | Linux x86_64 |
| 内存 | 256 MB RAM(小文档) | 2 GB+ RAM(OCR 大文档) |
| 磁盘空间 | 30–50 MB(核心库 + PDFium) | 100 MB+(含 tesseract 语言包) |
| CPU | 任何支持 AVX 的 x86 CPU 或 ARM64 | 4 核+(OCR 并发受益) |
| 可选工具 | LibreOffice(解析 Office 文档时需要) | LibreOffice 7+ |
| 可选工具 | ImageMagick(解析图像文件时需要) | ImageMagick 7+ |
| 语言 | 角色 |
|---|---|
| Rust | 核心库 + CLI + 原生文本提取逻辑 + PDFium 封装 |
| Python | PyO3 绑定层 + 薄封装 |
| TypeScript / JavaScript | Node.js 绑定(napi-rs)+ WASM 浏览器端 |
| Python(辅助) | OCR 服务器示例(EasyOCR/PaddleOCR)、数据集评估 |
| C++ (FFI) | PDFium 动态库(Google PDFium 项目提供,随包分发) |
# 核心安装
pip install liteparse
# 已包含:Rust 扩展(_liteparse)+ PDFium 动态库 + lit CLI
# 零 Python 运行时依赖
# 验证
python -c "from liteparse import LiteParse; p = LiteParse(); print('OK')"
# CLI
lit parse document.pdf
# Cargo.toml
[dependencies]
liteparse = "2.0"
# 启用 OCR(默认已开)
# liteparse = { version = "2.0", features = ["tesseract"] }
use liteparse::LiteParse;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let parser = LiteParse::default();
let result = parser.parse("document.pdf")?;
println!("{}", result.text);
Ok(())
}
npm install @llamaindex/liteparse
import { LiteParse } from "@llamaindex/liteparse";
const parser = new LiteParse({ ocrEnabled: true });
const result = await parser.parse("./document.pdf");
console.log(result.text);
npm install @llamaindex/liteparse-wasm
import init, { LiteParse } from "@llamaindex/liteparse-wasm";
await init();
const parser = new LiteParse({ ocrEnabled: false, outputFormat: "json" });
const bytes = new Uint8Array(await file.arrayBuffer());
const result = await parser.parse(bytes);
# 标准镜像(仅 Rust/Python 核心 + PDFium)
docker pull ghcr.io/run-llama/liteparse:latest
docker run -v ./docs:/docs ghcr.io/run-llama/liteparse:latest \
lit parse /docs/report.pdf
# 完整版(含 LibreOffice + ImageMagick)
docker pull ghcr.io/run-llama/liteparse:full
| 配置项(Python/Rust/JS 命名可能为 snake_case/camelCase) | 默认值 | 说明 |
|---|---|---|
ocr_enabled |
true |
是否启用 OCR(扫描/图片 PDF 必需) |
ocr_language |
"eng" |
Tesseract 语言代码,多语言用 + 连接(如 "eng+chi_sim") |
ocr_server_url |
None/null |
自定义 HTTP OCR 服务地址(启用后跳过 Tesseract) |
tessdata_path |
None/null |
离线 tessdata 目录路径 |
max_pages |
1000 |
最大处理页数(防超大文档) |
target_pages |
None/null |
要处理的页范围(如 "1-5,10,15-20"),空=全部 |
dpi |
150 |
OCR / 截图渲染分辨率(越高越慢,越清晰) |
output_format |
"text" |
"text" 或 "json" |
preserve_very_small_text |
false |
是否保留极小字体(通常是噪音) |
password |
None/null |
加密 PDF 口令 |
quiet |
false |
静默模式(关闭 [liteparse] 日志输出) |
num_workers |
CPU 核心数 - 1 | OCR 并发 workers 数 |
.pdf).doc / .docx / .docm / .odt / .rtf / .pages.ppt / .pptx / .pptm / .odp.xls / .xlsx / .xlsm / .ods / .csv / .tsv.png / .jpg / .jpeg / .gif / .bmp / .tiff / .webp / .svg文件类型嗅探:使用 infer crate 基于文件魔数(magic bytes)检测,不依赖扩展名。
支持上百种语言,常用代码:
| 语言 | 代码 | 语言 | 代码 |
|---|---|---|---|
| 英语 | eng |
简体中文 | chi_sim |
| 繁体中文 | chi_tra |
日语 | jpn |
| 韩语 | kor |
法语 | fra |
| 德语 | deu |
西班牙语 | spa |
| 俄语 | rus |
阿拉伯语 | ara |
| 印地语 | hin |
葡萄牙语 | por |
多语言识别:ocr_language="eng+chi_sim+fra"
liteparse/ 【项目根目录】
│
├── Cargo.toml Rust workspace 根
│ [workspace]
│ members = ["crates/*"]
│
├── Cargo.lock
│
├── README.md / README.zh-CN.md 英文/中文文档
├── OCR_API_SPEC.md HTTP OCR 服务规范
├── CHANGELOG.md / SECURITY.md / CONTRIBUTING.md
├── LICENSE Apache-2.0
├── AGENTS.md / CLAUDE.md 给 AI Agent 的提示
│
├── Dockerfile / full.Dockerfile 标准/完整容器镜像
│
├── .github/workflows/ CI / Release / 多平台打包
│
├── crates/ 【Rust crates 工作区】
│ │
│ ├── liteparse/ ★ 核心库 + CLI
│ │ ├── Cargo.toml (v2.0.7)
│ │ └── src/
│ │ ├── lib.rs 对外 API(LiteParse struct)
│ │ ├── parser.rs 调度核心 parse_input()
│ │ ├── types.rs 类型定义(TextItem/ParsedPage)
│ │ ├── extract.rs PDFium 文本提取
│ │ ├── projection.rs ★ 网格投影算法(核心创新)
│ │ ├── ocr.rs OCR 引擎选择 + 并发调度
│ │ ├── ocr_merge.rs OCR 结果与原生文本合并
│ │ ├── output.rs Text/Json 输出格式化
│ │ ├── conversion.rs Office/Image → PDF 转换
│ │ ├── render.rs 页面渲染为 PNG 截图
│ │ ├── config.rs LiteParseConfig
│ │ ├── error.rs LiteParseError (thiserror)
│ │ └── search.rs search_items() 搜索工具
│ │
│ ├── liteparse-python/ PyO3 Python 绑定
│ │ ├── Cargo.toml
│ │ └── src/lib.rs #[pyclass] struct LiteParse
│ │
│ ├── liteparse-napi/ napi-rs Node.js 绑定
│ │ ├── Cargo.toml
│ │ └── src/lib.rs
│ │
│ ├── liteparse-wasm/ wasm-bindgen 浏览器绑定
│ │ ├── Cargo.toml
│ │ └── src/lib.rs
│ │
│ ├── pdfium/ ★ Rust 对 PDFium 的高层封装
│ │ ├── Cargo.toml
│ │ └── src/
│ │
│ └── pdfium-sys/ PDFium FFI 底层绑定(C→Rust)
│ ├── Cargo.toml
│ ├── build.rs 构建时定位 PDFium 库
│ └── src/
│
├── packages/ 【语言包发布目录】
│ │
│ ├── python/ PyPI 包
│ │ ├── pyproject.toml maturin 配置
│ │ └── liteparse/
│ │ ├── __init__.py 包入口
│ │ ├── parser.py Python 侧薄包装
│ │ ├── types.py dataclass 类型定义
│ │ └── cli.py `lit` CLI 入口
│ │
│ ├── node/ npm @llamaindex/liteparse
│ │
│ └── wasm/ npm @llamaindex/liteparse-wasm
│
├── ocr/ 【示例 OCR 服务】
│ ├── easyocr/ EasyOCR HTTP 服务端示例
│ └── paddleocr/ PaddleOCR HTTP 服务端示例
│
├── dataset_eval_utils/ 数据集质量评估(Python)
│
├── demo/docs/ 示例文档(benchmark 用)
│
├── integration_tests_data/ 集成测试数据
│
├── scripts/ 构建脚本、musl 交叉编译
│
├── docs.config.mjs 文档站点配置
│
└── wasm-demo-site/ WASM 在线 demo
crates/liteparse/src/lib.rs —— 对外 API定义了对外暴露的核心类型和 API:
pub struct LiteParse {
config: LiteParseConfig,
}
impl LiteParse {
/// 用默认配置构造
pub fn new() -> Self { ... }
/// 带自定义配置构造
pub fn with_config(config: LiteParseConfig) -> Self { ... }
/// 从文件路径解析(同步)
pub fn parse(&self, path: impl AsRef<Path>) -> Result<ParseResult, LiteParseError> {
self.parse_input(PdfInput::Path(path.as_ref().into()))
}
/// 从内存字节解析(适合从网络下载后直接解析)
pub fn parse_bytes(&self, bytes: &[u8]) -> Result<ParseResult, LiteParseError> {
self.parse_input(PdfInput::Bytes(bytes.to_vec()))
}
/// 生成页面截图
pub fn screenshot(&self, path: impl AsRef<Path>, page_numbers: &[u32])
-> Result<Vec<ScreenshotResult>, LiteParseError> { ... }
/// ★ 核心调度方法
fn parse_input(&self, input: PdfInput) -> Result<ParseResult, LiteParseError> {
// Step 1: 输入格式检测与转换(conversion.rs)
// Step 2: 文本提取(extract.rs)
// Step 3: OCR 通道(ocr.rs + ocr_merge.rs)
// Step 4: 网格投影(projection.rs)
// Step 5: 输出格式化(output.rs)
}
}
pub struct ParseResult {
pub pages: Vec<ParsedPage>,
pub text: String,
}
pub struct ParsedPage {
pub page_num: u32,
pub page_width: f32,
pub page_height: f32,
pub text: String,
pub text_items: Vec<TextItem>,
}
pub struct TextItem {
pub text: String,
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
pub rotation: f32,
pub font_name: String,
pub font_size: f32,
pub font_height: f32,
pub font_weight: i32,
pub fill_color: String,
pub confidence: f32,
// ... 内部字段:snap, anchor, num_spaces, is_dup, is_margin_line_number
}
pub struct ScreenshotResult {
pub page_num: u32,
pub width: u32,
pub height: u32,
pub image_bytes: Vec<u8>, // PNG
}
设计洞察:LiteParse 是一个无状态结构体(仅保存配置)。parse() 和 parse_bytes() 都是同步方法(Python/Node 端在绑定层做了 async 包装)。parse_input() 是统一的调度核心,接收 PdfInput::Path 或 PdfInput::Bytes 两种输入。
crates/liteparse/src/parser.rs —— 核心调度parse_input() 的五阶段流程是整个项目的骨架:
Phase 1 ── 输入检测与格式转换
├── infer::get_kind() —— 基于 magic bytes 判断文件类型(PDF/Image/Office)
├── 如果是 PDF:直接进入 Phase 2
├── 如果是 Image:调用 ImageMagick `convert` → 临时 PDF(tempfile::TempPath)
└── 如果是 Office:调用 LibreOffice `--headless --convert-to pdf` → 临时 PDF
Phase 2 ── 原生文本提取(见 extract.rs + pdfium crate)
├── pdfium::PdfDocument::open(path_or_bytes)
├── 按页循环 for page in document.pages() {
│ let text_page = page.text_page()?; // FPDFTextPage
│ for char_index in 0..text_page.count_chars() {
│ 提取字符 → 合并为 TextItem(按 word/line 聚合)
│ }
│ }
└── 生成 Vec<ParsedPage>(还未做投影,只是原始 text_items)
Phase 3 ── OCR 通道(见 ocr.rs + ocr_merge.rs)
├── 如果 config.ocr_enabled:
│ ① 将页面渲染为位图(dpi 控制)
│ ② 选择引擎:override > HTTP > Tesseract
│ ③ 并发 num_workers 个 OCR 任务
│ ④ 将 OCR 结果的像素坐标反投影回 PDF 坐标
│ ⑤ 与 Phase 2 的原生文本合并
│ (替换没有有效文本的区域,保留有文本的区域)
└── 否则:跳过
Phase 4 ── 网格投影(见 projection.rs,★ 核心算法)
├── for page in pages:
│ ① 行聚类(y 坐标分组)— y 相近的合并为同一行
│ ② Snap 吸附(左右中三种吸附点)— HashMap 聚类 x 坐标
│ ③ Anchor 锚定 — 将 Snap 点锚定到全局虚拟网格
│ ④ 重建阅读顺序 — 先 y 后 x,多列布局自动检测
│ ⑤ 生成 page.text(保留布局的纯文本)
│ ⑥ 过滤 is_dup / is_margin_line_number 噪音
└── 结果:更新 pages 中的每个 ParsedPage
Phase 5 ── 输出格式化(见 output.rs)
├── match config.output_format:
│ OutputFormat::Text → 拼接 pages.iter().map(|p| p.text).collect()
│ OutputFormat::Json → serde_json::to_string(&pages)
└── 返回 ParseResult { pages, text }
核心流程:
/// 从 PDFium 的 FPDFTextPage 提取 TextItem 列表
fn extract_text_items(
page: &PdfPage,
text_page: &FPDFTextPage,
page_num: u32,
) -> Result<Vec<TextItem>, LiteParseError> {
// Step 1: 获取页面尺寸
let (page_width, page_height) = page.size();
// Step 2: 遍历字符 → 合并为词/行
let char_count = text_page.count_chars();
let mut items: Vec<TextItem> = Vec::new();
// 遍历字符,按"空格"切分为词,按"换行"切分为行
let mut current_word_chars: Vec<FPDFChar> = Vec::new();
for i in 0..char_count {
let ch = text_page.get_char(i)?;
// 获取字符在 PDF 中的矩形区域
let rect = text_page.get_char_box(i)?;
// rect: { left, top, right, bottom } —— PDF 坐标(左下原点)
// 注意:后续会规范化到 top-left 原点
if ch.is_whitespace() || ch == '\n' {
if !current_word_chars.is_empty() {
// 合并 current_word_chars 为一个 TextItem
let item = merge_chars_to_item(¤t_word_chars, page_width, page_height)?;
items.push(item);
current_word_chars.clear();
}
if ch == '\n' {
// 行边界标记
}
} else {
current_word_chars.push(ch);
}
}
// Step 3: 坐标规范化
// PDF 原生坐标系:原点在左下角,Y 向上递增
// LiteParse 输出坐标系:原点在左上角,Y 向下递增
// 这与大多数 UI/图像处理库一致
for item in &mut items {
item.y = page_height - item.y - item.height;
}
// Step 4: 附加字体信息
// font_name / font_size / font_height / font_weight / fill_color
// 这些从 PDFium 的 FPDF_Font 对象读取
// Step 5: confidence 标记
// 原生提取的文本 confidence = 1.0(确定)
// OCR 提取的文本 confidence = OCR 引擎返回值
Ok(items)
}
关于 PDFium:
pdfium-sys crate 做 FFI 绑定,pdfium crate 做高层封装PDFIUM_LIB_PATH 环境变量可以指定自定义 PDFium 动态库位置libpdfium.{so,dylib,dll}crates/liteparse/src/projection.rs —— 网格投影算法这是 LiteParse 区别于其他 PDF 解析器的核心创新。
问题背景:传统 PDF 文本提取按照 PDF 内部的"绘制顺序"输出文字。对于多列布局的文档(如论文、报纸、双栏书籍),输出的文本顺序常常错乱:
真实文档布局:
┌──────────┬──────────┐
│ 左栏第1段 │ 右栏第1段 │
│ 左栏第2段 │ 右栏第2段 │
└──────────┴──────────┘
传统提取结果(错乱):
"左栏第1段 右栏第1段 左栏第2段 右栏第2段"
(左右栏交错,无法阅读)
LiteParse 投影算法输出(正确阅读顺序):
"左栏第1段
左栏第2段
右栏第1段
右栏第2段"
算法详细说明:
数据结构:
struct TextItem { text, x, y, width, height, ... }
enum Snap { Left, Right, Center } // 三种吸附方式
struct Anchor { col_index, snap_point_x }
Step 1 —— 行聚类(Y 坐标分组)
原理:同一行的文字 y 坐标接近
方法:对所有 TextItem 按 y 排序,使用容差(如 y ± 0.5 * font_height)
分组,同组视为同一行
伪代码:
let rows: Vec<Vec<&TextItem>> = [];
let sorted_by_y: Vec<&TextItem> = sort(items, key=|it| it.y);
for item in sorted_by_y:
if rows.last().is_some_and(|r|
abs(r.last().unwrap().y - item.y) < item.font_height * 0.5
):
rows.last_mut().unwrap().push(item); // 加入当前行
else:
rows.push(vec![item]); // 开新行
Step 2 —— Snap 吸附(检测列边界)
原理:同列的文字左边界/右边界/中心应该对齐
方法:对每个 TextItem 计算三个潜在吸附点:
snap_left = item.x
snap_center = item.x + item.width / 2
snap_right = item.x + item.width
用 HashMap<f32_with_tolerance, Count> 统计每个吸附点出现次数:
let mut snap_points: HashMap<RoundTo2dp, usize> = HashMap::new();
for item in items:
*snap_points.entry(round(item.x)).or_insert(0) += 1;
*snap_points.entry(round(item.x + item.width/2.0)).or_insert(0) += 1;
*snap_points.entry(round(item.x + item.width)).or_insert(0) += 1;
出现次数多的吸附点 → 隐含的"列边界"
例:如果 snap_x = 72.0 出现 80 次,说明页面左侧 x=72 处是一个列边界
Step 3 —— Anchor 锚定(分配到虚拟列网格)
原理:将每个 TextItem 的 Snap 点"锚定"到最接近的列边界
方法:
let columns: Vec<f32> = top_n_snap_points(snap_points, n=6); // 最多6列
for item in items:
let nearest_col = columns.iter()
.min_by_key(|c| abs(c - item.x))
.unwrap();
item.internal_anchor = nearest_col.index;
item.internal_snap = nearest_col.snap_type;
Step 4 —— 重建阅读顺序
先按行排序(y 坐标) → 在每行内按列索引排序(anchor 索引)
结果:
第 1 行的所有 TextItem 按 列1 → 列2 → ... → 列N 的顺序
第 2 行的所有 TextItem 按 列1 → 列2 → ... → 列N 的顺序
...
对于多列文档:
先输出完左列所有行,再输出右列所有行(而非左右交错)
(因为左列 y=90 的项 anchor=列0,y=120 的项 anchor=列0 ...
右列所有项 anchor=列1,在 anchor 排序中排在列0之后)
Step 5 —— 生成纯文本 page.text
按行遍历,每行内部按 Snap 列顺序输出 TextItem.text
保留缩进(不同 Snap 列之间的空格)
保留空行(不同 y 行之间的空行)
额外过滤:
• is_dup —— 去重(PDF 有时会重复绘制相同文字)
• is_margin_line_number —— 去除页码/边注
复杂度:O(n log n)(排序主导)
内存:O(n)(items 拷贝一份带 internal_* 字段)
为什么这是"关键创新":
crates/liteparse/src/ocr.rs —— OCR 引擎系统OCR 引擎选择优先级:
1. ocr_engine_override → 自定义 Rust OcrEngine trait
2. ocr_server_url → HTTP OCR 服务
3. Tesseract → 默认(feature = "tesseract")
并发模型:
┌─────────────────────────────────────────────────────────┐
│ 主线程:LiteParse::parse_input() │
│ Phase 2 完成后,pages 已包含原生文本 TextItem │
│ │
│ for page in pages { │
│ if page.has_any_text() → skip OCR (已有原生文本) │
│ else → push to OCR queue │
│ } │
│ │
│ tokio::spawn(async move { │
│ 并发 num_workers 个任务: │
│ ① render page to bitmap (depend on config.dpi) │
│ ② call OCR engine (trait impl / HTTP / Tesseract) │
│ ③ deserialize OCR result + bbox + confidence │
│ }) │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ OCR 结果格式(通用) │
│ │
│ struct OcrResult { │
│ text: String, │
│ bbox: { x, y, width, height }, // 像素坐标 │
│ confidence: f32, │
│ } │
│ │
│ HTTP 协议(见 OCR_API_SPEC.md) │
│ POST /ocr │
│ Body: multipart/form-data (image: PNG/JPEG) │
│ Response: { "items": [{ "text": "...", │
│ "bbox": [x,y,w,h], │
│ "confidence": 0.95 }] } │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ ocr_merge.rs —— 坐标反投影与合并 │
│ │
│ OCR 返回的是图像像素坐标(0..width, 0..height) │
│ 需要投影回 PDF 72 DPI 坐标(0..page_width, │
│ 0..page_height) │
│ │
│ scale_x = page_width / render_width │
│ scale_y = page_height / render_height │
│ text_item.x = ocr.bbox.x * scale_x │
│ text_item.y = ocr.bbox.y * scale_y │
│ text_item.width = ocr.bbox.width * scale_x │
│ text_item.height = ocr.bbox.height * scale_y │
│ text_item.confidence = ocr.confidence │
│ │
│ 合并策略:只对 page.has_any_text() = false 的页面使用 OCR │
│ 即:优先使用 PDF 中已编码的真实文本, │
│ 仅当页面是扫描图(无编码文本)时才走 OCR 路径 │
└──────────────────────────────────────────────────────────┘
OcrEngine trait 定义:
pub trait OcrEngine: Send + Sync {
fn ocr(&self, image_bytes: &[u8]) -> Result<Vec<OcrResult>, LiteParseError>;
fn name(&self) -> &str;
}
// 实现示例:TesseractEngine(feature = "tesseract")
pub struct TesseractEngine {
language: String,
tessdata_path: Option<String>,
}
// 实现示例:HttpOcrEngine(内置)
pub struct HttpOcrEngine {
url: String,
client: reqwest::Client,
}
// 用户自定义:任何 Rust 类型只要实现 OcrEngine trait,
// 就可以通过 with_ocr_engine() 注入
let custom: Arc<dyn OcrEngine> = Arc::new(MyCustomEngine::new());
let parser = LiteParse::with_config(config)
.with_ocr_engine(custom);
性能提示:num_workers 默认是 CPU 核心数 - 1。对于小文档可以设为 1 降低开销;对于大扫描文档可以设为 CPU 核心数 × 2(I/O 密集型)。
crates/liteparse/src/output.rs —— 输出格式化pub enum OutputFormat {
Text, // 纯文本(保留布局)
Json, // 完整 ParsedPage → TextItem 结构
}
fn format_output(pages: &[ParsedPage], format: OutputFormat) -> String {
match format {
OutputFormat::Text => {
// 简单拼接:每页文本之间用 "\n\n" 分隔
pages.iter()
.map(|p| p.text.clone())
.collect::<Vec<_>>()
.join("\n\n")
}
OutputFormat::Json => {
// serde_json 序列化完整结构
serde_json::to_string_pretty(&pages).unwrap()
}
}
}
JSON 输出示例(简化):
[
{
"page_num": 1,
"page_width": 612.0,
"page_height": 792.0,
"text": "第三章 实验方法\n\n本章节描述了三种实验方法...",
"text_items": [
{
"text": "第三章",
"x": 72.0, "y": 90.5,
"width": 35.2, "height": 14.3,
"font_name": "SimSun-Bold",
"font_size": 14.0,
"confidence": 1.0
},
...
]
}
]
crates/liteparse/src/conversion.rs —— 格式转换/// 将非 PDF 输入转换为临时 PDF 文件
fn convert_to_pdf(input: &InputFile) -> Result<TempPath, LiteParseError> {
let kind = infer::get_kind(input.bytes())?;
match kind {
infer::Kind::PDF => {
// 已是 PDF,无需转换
Ok(TempPath::from_bytes(input.bytes())?)
}
infer::Kind::Image(_) => {
// 调用 ImageMagick convert
let tmp = TempFile::new()?;
let status = std::process::Command::new("convert")
.arg(input.path())
.arg(tmp.path())
.status()?;
if !status.success() {
return Err(LiteParseError::ConversionFailed("ImageMagick".into()));
}
Ok(tmp.into_temp_path())
}
_ => {
// 假定是 Office 文档,调用 LibreOffice headless
let tmp_dir = TempDir::new()?;
let status = std::process::Command::new("libreoffice")
.arg("--headless")
.arg("--convert-to")
.arg("pdf")
.arg("--outdir")
.arg(tmp_dir.path())
.arg(input.path())
.status()?;
if !status.success() {
return Err(LiteParseError::ConversionFailed("LibreOffice".into()));
}
// 在 tmp_dir 中找生成的 pdf
let pdf_path = tmp_dir.path().join(
input.path().file_stem().unwrap().to_string_lossy() + ".pdf"
);
Ok(TempPath::from_file(pdf_path)?)
}
}
}
设计权衡:
crates/liteparse/src/render.rs —— 页面截图pub fn render_page_to_png(
page: &PdfPage,
dpi: u32,
) -> Result<Vec<u8>, LiteParseError> {
// Step 1: PDF page → 位图
// PDF 默认 72 DPI → dpi 参数缩放
let scale = dpi as f32 / 72.0;
let width = (page.width() * scale) as u32;
let height = (page.height() * scale) as u32;
// Step 2: PDFium 渲染到内存位图(BGRA)
let bitmap = pdfium::render_page_to_bitmap(page, width, height)?;
// Step 3: BGRA → PNG 编码(image crate)
let img = image::RgbaImage::from_raw(width, height, bitmap.into_bytes())
.ok_or(LiteParseError::RenderFailed)?;
// Step 4: PNG 压缩输出
let mut buf = Vec::with_capacity((width * height * 4) as usize / 10);
img.write_to(&mut buf, image::ImageFormat::Png)?;
Ok(buf)
}
用途:
crates/liteparse/src/config.rs —— 配置#[derive(Clone, Debug)]
pub struct LiteParseConfig {
pub ocr_enabled: bool,
pub ocr_language: String,
pub ocr_server_url: Option<String>,
pub tessdata_path: Option<String>,
pub max_pages: u32,
pub target_pages: Option<String>,
pub dpi: u32,
pub output_format: OutputFormat,
pub preserve_very_small_text: bool,
pub password: Option<String>,
pub quiet: bool,
pub num_workers: usize,
// 自定义 OCR 引擎(依赖注入)
pub ocr_engine_override: Option<Arc<dyn OcrEngine>>,
}
impl Default for LiteParseConfig {
fn default() -> Self {
Self {
ocr_enabled: true,
ocr_language: "eng".into(),
ocr_server_url: None,
tessdata_path: None,
max_pages: 1000,
target_pages: None,
dpi: 150,
output_format: OutputFormat::Text,
preserve_very_small_text: false,
password: None,
quiet: false,
num_workers: num_cpus::get().saturating_sub(1).max(1),
ocr_engine_override: None,
}
}
}
合理默认值的设计:默认值被精心选择,使大多数用户无需修改。例如 num_workers = CPU - 1 保证不会占满机器。
crates/liteparse/src/error.rs —— 错误类型#[derive(thiserror::Error, Debug)]
pub enum LiteParseError {
#[error("Failed to open PDF: {0}")]
PdfOpen(String),
#[error("Page {page_num} extraction failed: {reason}")]
PageExtraction { page_num: u32, reason: String },
#[error("OCR failed: {0}")]
Ocr(String),
#[error("Format conversion failed: {0}")]
ConversionFailed(String),
#[error("Render failed: {0}")]
RenderFailed(String),
#[error("Invalid page range: {0}")]
InvalidPageRange(String),
#[error("PDF is password-protected and no password provided")]
PasswordRequired,
#[error("PDF password is incorrect")]
PasswordIncorrect,
#[error("Page limit exceeded: {actual} > {max}")]
PageLimitExceeded { actual: u32, max: u32 },
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
}
Python/Node 绑定层:将这些错误统一转换为各自语言的原生异常类型(Python → RuntimeError,Node → Error)。
crates/liteparse/src/search.rs —— 搜索工具/// 在 TextItem 列表中搜索关键词,
/// 返回匹配项(合并相邻命中的边界框)
pub fn search_items(
items: &[TextItem],
keyword: &str,
case_sensitive: bool,
) -> Vec<SearchedItem> {
// Step 1: 收集包含关键词的 TextItem
let mut matches: Vec<&TextItem> = Vec::new();
for item in items {
let hay = if case_sensitive { item.text.clone() }
else { item.text.to_lowercase() };
let needle = if case_sensitive { keyword.to_string() }
else { keyword.to_lowercase() };
if hay.contains(&needle) {
matches.push(item);
}
}
// Step 2: 按 y 坐标聚类(同一行的匹配合并边界框)
// merge by row → 合并 x, y, width, height
// 返回合并后带完整包围矩形的 SearchedItem
...
}
用途:给 UI 提供"搜索高亮"功能——用户搜索一个词,返回匹配项的合并边界框,用于在页面截图上绘制高亮框。
packages/python/liteparse/parser.py —— Python 薄封装from ._liteparse import ffi # PyO3 生成的扩展模块
class LiteParse:
"""Python 侧的薄封装,所有实现在 Rust 层"""
def __init__(
self,
ocr_enabled=True,
ocr_language="eng",
ocr_server_url=None,
tessdata_path=None,
max_pages=1000,
target_pages=None,
dpi=150,
output_format="text",
preserve_very_small_text=False,
password=None,
quiet=False,
num_workers=None,
):
# 转发到 Rust 侧的构造函数
self._parser = ffi.LiteParse(
ocr_enabled=ocr_enabled,
ocr_language=ocr_language,
ocr_server_url=ocr_server_url,
tessdata_path=tessdata_path,
max_pages=max_pages,
target_pages=target_pages,
dpi=dpi,
output_format=output_format,
preserve_very_small_text=preserve_very_small_text,
password=password,
quiet=quiet,
num_workers=num_workers,
)
def parse(self, path_or_bytes):
if isinstance(path_or_bytes, (bytes, bytearray)):
return self._parser.parse_bytes(bytes(path_or_bytes))
return self._parser.parse(str(path_or_bytes))
def parse_bytes(self, data):
return self._parser.parse_bytes(bytes(data))
def screenshot(self, path, page_numbers=None):
return self._parser.screenshot(str(path), page_numbers or [])
设计洞察:Python 层几乎没有逻辑,全量转发给 Rust 扩展。这保证了:
packages/python/liteparse/cli.py —— lit 命令行# 核心命令
lit parse <FILE> # 解析,输出纯文本
lit parse <FILE> --format json # JSON 输出
lit parse <FILE> --target-pages "1-10" # 指定页
lit parse <FILE> --no-ocr # 关闭 OCR
lit parse <FILE> --ocr-server-url URL # 自定义 OCR 服务
lit screenshot <FILE> -o ./out # 生成页面 PNG 截图
lit batch-parse <INPUT_DIR> <OUTPUT_DIR> # 批量处理
lit search <FILE> "keyword" # 搜索 + 返回 bbox
curl URL | lit parse - # 从 stdin 解析
| 依赖 | 版本(估计) | 用途 |
|---|---|---|
pdfium |
动态 | PDFium 高层封装(项目内 crate) |
pdfium-sys |
动态 | PDFium FFI 绑定(项目内 crate) |
image |
0.25+ | PNG 读写 / 图像操作 |
tempfile |
3+ | 格式转换临时文件管理 |
reqwest |
0.12 | HTTP OCR 客户端(rustls + multipart + json) |
serde / serde_json |
1.0 | 类型与 JSON 序列化 |
tokio |
1.0+ | 异步任务(OCR 并发、子进程管理) |
thiserror |
1.0 | LiteParseError 错误类型派生 |
web-time |
1.0 | WASM 兼容的 Instant |
ordered-float |
4.0 | 稳定浮点排序(用于 y 聚类) |
infer |
0.15 | 文件类型魔数嗅探 |
url |
2.5 | OCR URL 解析 |
clap |
4.0 | CLI 参数解析(derive feature) |
tesseract-rs |
0.15 | Tesseract 绑定(feature = "tesseract") |
pyo3 |
0.21 | Python 绑定(仅 liteparse-python crate) |
napi |
2.16 | Node.js 绑定(仅 liteparse-napi crate) |
wasm-bindgen |
0.2 | WASM 绑定(仅 liteparse-wasm crate) |
Python 包运行时依赖:无(纯 Rust wheels,零 Python 代码运行时依赖)
用户需求:
构建一个不依赖任何云服务的本地知识库问答系统,
需要解析 10,000+ 篇 PDF 学术论文。
为什么选 LiteParse:
✅ 完全本地,无需上传文档到云端
✅ 速度快(10,000 篇文档的解析时间从小时级降到分钟级)
✅ 空间信息支持基于区域的智能切分
✅ OCR 能力处理扫描版论文
✅ 零 Python 依赖,部署简单
集成方式:
from liteparse import LiteParse
from llama_index.core import Document
parser = LiteParse(ocr_language="eng+chi_sim")
result = parser.parse("paper.pdf")
# 方式 A:纯文本 → 简单切分
doc = Document(text=result.text,
metadata={"source": "paper.pdf"})
# 方式 B:基于边界框做区域切分(更智能)
docs = []
for page in result.pages:
# 按 y 坐标聚类为段落
paragraphs = cluster_by_y_coordinate(page.text_items)
for para in paragraphs:
docs.append(Document(
text=para.text,
metadata={"source": "paper.pdf",
"page": page.page_num,
"bbox": [para.x, para.y, para.w, para.h]}
))
# 后续走常规 LlamaIndex 索引/检索流程
用户需求:
让 LLM "看" 文档的版式和图表来回答问题。
LiteParse 的作用:
parser = LiteParse(ocr_enabled=True, dpi=300)
screenshots = parser.screenshot("report.pdf", page_numbers=[5, 6, 7])
parsed_pages = parser.parse("report.pdf").pages
# 同时发送给多模态 LLM:
# ① 页面截图(PNG bytes)
# ② OCR/原生文本
# ③ 每个文本项的精确坐标
#
# LLM 可以:
# • "看图"理解图表和布局
# • "读坐标"知道文字在页面的位置
# • 回答"图 3 中显示的数据在哪个章节讨论?"这类问题
prompt = """
下图是报告第 5 页的截图,以及页面中文字的坐标:
{text_items_with_coordinates}
请描述图 3 的数据趋势,并找出正文中讨论此图的段落。
"""
用户需求(金融/医疗/法律团队):
文档包含敏感数据,绝对不能上传到任何云端。
同时需要高质量的文本提取和搜索。
LiteParse 的优势:
✅ 零网络请求(除非你配置了 HTTP OCR,
但那也是你自己控制的服务)
✅ 没有 API Key / 帐号 / 用量限制
✅ 处理过程完全在你的服务器/笔记本内存中
✅ 临时文件通过 tempfile::TempPath 自动清理
部署方式(企业内网):
Docker:
docker run -d --name liteparse-service \
-p 8000:8000 \
-v ./data:/data \
ghcr.io/run-llama/liteparse:full
然后在内部 HTTP API 中调用:
POST /parse form-data: file=@report.pdf
GET /screenshot?file=report.pdf&pages=1,2,3
AI Agent 需要读取一份 PDF 来回答问题。
LiteParse Agent Skill 集成:
npx skills add run-llama/llamaparse-agent-skills --skill liteparse
Agent 内部流程:
1. 用户:"帮我总结这篇 50 页的技术文档"
2. Agent 调用:lit parse document.pdf --format json
3. 获取 ParsedPage 列表 + TextItem 坐标
4. 基于 bbox + 字体大小识别"标题/段落/表格区域"
5. 分章节交给 LLM 总结
6. 合并章节总结 → 输出给用户
lit batch-parse ./incoming_docs ./parsed_texts
# 递归处理 incoming_docs 下的所有文档
# 结果写入 parsed_texts,保留目录结构
# 每个 .pdf → 生成同名 .txt + .json
# 每个 .docx/.xlsx → 先转 PDF 再解析
适合数据管道:
S3/GCS → 本地临时目录 → LiteParse → 向量数据库
(或增量处理 + 缓存已解析结果)
num_workers 控制并发,扫描文档处理速度随 CPU 核心线性扩展(x, y, width, height, font_name, font_size, confidence)parse() / screenshot()pip install liteparse 即可,不需要编译OcrEngine trait 注入(完全控制)ocr/ 目录)npx skills add run-llama/llamaparse-agent-skills)| 问题 | 说明 |
|---|---|
| 不做语义表格识别 | LiteParse 只提取文字和坐标,不区分"这是一张表格" vs "这是正文" |
| 表格效果依赖下游 | 需要调用方基于 bbox 和对齐启发式自行重建表格(或配合云端 LlamaParse) |
| 对比 LlamaParse | LlamaParse 有专门的表格识别模型,可以输出 Markdown 表格;LiteParse 无此能力 |
推荐做法:如果你的用例大量涉及表格解析:
| 问题 | 说明 |
|---|---|
| 不做图像理解 | LiteParse 提取的是文字,不识别图片内容(流程图、照片、示意图等) |
| 依赖多模态 LLM | 需要配合多模态 LLM + screenshot() 才能"看懂"图片内容 |
| 无 OSD/文本方向检测 | 旋转了 90 度的扫描页面可能识别失败,需要调用方预处理 |
| 问题 | 说明 |
|---|---|
| LibreOffice 依赖 | 解析 .docx/.xlsx/.pptx 需要系统安装 LibreOffice |
| ImageMagick 依赖 | 解析图像文件需要 ImageMagick |
| 子进程调用开销 | 每次非 PDF 文档都要启动一个 LibreOffice 进程,启动时间数秒 |
| Docker 镜像体积 | :full 标签含 LibreOffice,体积大(几百 MB) |
| Windows 上的路径问题 | 某些 Windows 环境下 LibreOffice 的安装路径检测可能出问题 |
改进方向:社区正在讨论在 Rust 中实现轻量级 Office 文本提取(特别是 .docx,它本质上是 ZIP + XML),以减少对 LibreOffice 的依赖。
| 问题 | 说明 |
|---|---|
| Tesseract 对中文不完美 | 中文手写体、艺术字、低分辨率扫描的识别率仍不高 |
| OCR 是性能瓶颈 | 启用 OCR 后,解析速度从"毫秒/页"降级到"秒/页" |
| WASM 端默认无 OCR | 浏览器端使用 WASM 时,需要额外处理 OCR(通过 JS 回调注入) |
推荐做法:
ocr_server_urlocr_language="chi_sim",安装对应的 tessdata| 问题 | 说明 |
|---|---|
| PDFium 错误透传 | PDFium 的错误码是 C 风格的整数,Rust 侧将其转换为字符串,但信息可能不够详细 |
| LibreOffice 失败静默 | 格式转换失败时错误信息可能只有"LibreOffice 失败",难以调试 |
| 缺少调试模式 | 没有 verbose 或 log_level 参数来查看详细处理日志 |
改进方向:增加 verbose 配置 + 更细粒度的错误分类 + 日志系统(如 tracing crate)。
| 问题 | 说明 |
|---|---|
| OCR 限制 | WASM 端默认无 OCR(浏览器内做 OCR 需要额外的 JS 库如 tesseract.js) |
| 文件大小 | WASM 包 + PDFium WASM 构建体积较大(数 MB),在移动端加载慢 |
| 浏览器兼容性 | 需要较新的浏览器(支持 WebAssembly、SIMD 可选) |
LiteParse 提供"文字 + 坐标",但不做更高层次的语义推断:
这意味着:
某些 PDF 使用:
在这些边缘案例上,PDFium 可能无法提取文字,只能 fallback 到 OCR。OCR 本身对非标准字体也不完美。
password 参数)parse() 是同步阻塞的(Rust 内部不涉及 Python GIL,但调用仍在当前线程运行)改进方向:在 Python 绑定层暴露 parse_async() 方法(内部使用 pyo3 的 #[pyo3(async_fn)])。
| 你的场景 | 推荐方案 | 说明 |
|---|---|---|
| 隐私敏感 / 不能上传 | LiteParse | 本地、离线、零云依赖 |
| 需要表格识别 | LlamaParse(云端) | LiteParse + LlamaParse 混合 |
| 只需要纯文本、简单 | pypdf | 轻量,纯 Python |
| 浏览器内解析 PDF | LiteParse WASM | 唯一能在浏览器内做 bbox 解析的方案 |
| Rust 项目 + 需要 PDF | LiteParse | 原生 Rust API,无外部依赖 |
| 大批量 ETL 处理 | LiteParse + 并发调用 | 速度优势明显 |
| 复杂格式(CAD/签名等) | Adobe 官方工具 / LlamaParse | 开源方案在复杂场景有短板 |
| 多模态文档 QA | LiteParse(截图 + 文本 + 坐标) | 配合 GPT-4o / Claude 3 Opus |
LiteParse 是一个定位清晰、实现精良的开源文档解析工具。它不是要做"世界上最好的 PDF 解析器",而是要做"本地、快速、带空间信息的 PDF 解析器"——在这个定位上,它目前是开源生态中的最佳选择。
它的核心价值在于:
LiteParse.parse() 一个方法解决 99% 的场景它的主要不足在于:表格识别能力弱(需配合云端 LlamaParse)、Office 文档依赖外部工具、语义层次需要调用方自行重建。但在其定位范围内("本地文本+空间信息提取"),它是目前最均衡的方案。
推荐使用场合:
不推荐使用场合:
报告生成时间:2026-06-09
分析基于:GitHub 仓库 run-llama/liteparse (v2.0.7)
参考资料:README、OCR_API_SPEC.md、项目源码、LlamaIndex 文档
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。