适用场景
这套方案适合经常把 PDF 交给大模型处理、但又不方便把文件上传到外部服务的人:合同、标书、内部白皮书、未公开的研究稿,一旦上传就脱离了可控范围。它会用 PaperPilot 在本机把 PDF 拆成带页码和段落结构的 JSON,再把这套解析能力包装成一个 MCP Server,让 Claude Desktop、Cursor 这类客户端像调用普通工具一样调用它,全程只有本机的进程在读写磁盘。如果你的痛点不是“解析得准不准”,而是“文件不能外传”,这套组合基本对得上。
环境与前置条件
先说明一句:PaperPilot 的包名、子命令名和配置项在版本迭代中可能微调,下文命令的结构是通用的,具体名称请以官方文档当前版本为准。遇到命令不识别时,优先去看官方 README 的安装章节,不要硬试。
操作系统与运行时:
- macOS、Windows(建议用 WSL2)、主流 Linux 发行版都可以。
- Node.js 用当前 LTS 版本即可,官方文档通常会写明最低支持主版本,装之前对一眼。用
node -v确认。 - 要本地编译 Rust 解析引擎,需要 Rust 工具链(
rustc、cargo);如果直接下载官方预编译的引擎二进制,可以跳过编译。
硬件建议:
- 内存 8 GB 起步;处理 100 页以上的扫描件或大幅面图纸,建议 16 GB。
- 磁盘预留 2–3 GB,用来放引擎二进制、解析缓存和临时文件。单份几百页 PDF 的中间产物可能到几百 MB。
- 不需要独立显卡,纯 CPU 运行。首次安装需要联网下载依赖,装完之后解析全过程可以在断网状态下完成。
分步骤部署
第 1 步:确认运行时环境
```bash
node -v
npm -v
```
这步只是确认 Node 能被调用。输出里能看到版本号即算通过。如果提示 command not found,说明 Node 没装或者没进 PATH,先用系统包管理器或 Node 官方安装包补上。
第 2 步:安装 WASM 版本
PaperPilot 的解析内核以 WebAssembly 形式分发,好处是不依赖系统里的 PDF 库、跨平台行为一致,也不会在解析时触发任何外部请求。先建一个干净目录,避免污染全局环境:
```bash
mkdir -p ~/paperpilot-local && cd ~/paperpilot-local
npm init -y
npm install paperpilot
npx paperpilot --version
```
如果想全局可用,把 npm install paperpilot 换成 npm install -g paperpilot,之后直接敲 paperpilot 即可。
成功标志:npx paperpilot --version 打印出版本号,且 node_modules 下多出 PaperPilot 相关目录。安装过程如果卡在下载环节,检查 npm 源可达性,或临时切到国内镜像源再装。
第 3 步:准备 Rust 解析引擎
WASM 版本能跑通基础解析,但遇到双栏排版、复杂表格、页眉页脚混排这类结构还原时,原生引擎的稳定性和速度更好。两条路,任选其一。
方式 A:使用官方预编译引擎(推荐,免编译)
```bash
mkdir -p ~/.paperpilot/bin
从官方发布页下载对应平台的压缩包后解压到固定目录
tar -xzf paperpilot-engine-<平台>.tar.gz -C ~/.paperpilot/bin
chmod +x ~/.paperpilot/bin/paperpilot-engine
~/.paperpilot/bin/paperpilot-engine --version
```
方式 B:从源码编译(需要 Rust 工具链)
```bash
rustc --version && cargo --version
git clone <官方仓库地址>
cd paperpilot && cargo build --release
```
编译产物一般在 target/release/ 下,把它复制到 ~/.paperpilot/bin/ 并加执行权限。
成功标志:执行引擎二进制带 --version 能打印版本信息,不报 “cannot execute binary file”。后者通常意味着下载的包和你的 CPU 架构对不上(比如在 ARM 机器上拿了 x86 包),换对应架构的包即可。
第 4 步:跑通一次单文件解析
先从最简单的场景验证,再谈接入:
```bash
paperpilot parse ./sample.pdf --output ./out --format json
ls -lh ./out
```
这一步在做的事是:读取 PDF 字节流 → WASM 内核做初步解析 → 调用 Rust 引擎还原文档结构 → 把结果写成 JSON。
成功标志:./out 下出现与源文件同名的 .json。打开看一眼,结构大致长这样(字段名以实际输出为准):
```json
{
"file": "sample.pdf",
"pages": [
{
"page": 1,
"blocks": [
{ "type": "heading", "level": 1, "text": "第 3 章 市场结构" },
{ "type": "paragraph", "text": "……" }
]
}
]
}
```
重点检查三件事:页数对不对、标题层级有没有还原成 heading、正文有没有被页眉页脚污染。这三项符合预期,说明引擎工作正常,可以进入封装环节。
如果想强制指定引擎路径,用环境变量指向它(变量名以官方文档为准):
```bash
export PAPERPILOT_ENGINE=~/.paperpilot/bin/paperpilot-engine
```
第 5 步:以 MCP Server 方式启动
MCP Server 推荐用 stdio 传输,客户端拉起子进程,通过标准输入输出通信,不监听任何端口,也就不存在端口暴露的问题:
```bash
paperpilot mcp serve --transport stdio --workspace ~/papers
```
--workspace 指定允许访问的目录,解析和列目录都被限制在这个范围内,这个参数值得认真设,不要直接指向用户主目录。
注意:这条命令通常不需要你手动常驻执行——客户端启动时会自己把它拉起来。手动执行时终端会“卡住”不动,那是正常的等待输入状态,不是报错。
第 6 步:接入 Claude Desktop
先拿到可执行文件的绝对路径,图形界面应用启动的进程不一定继承你 shell 里的 PATH:
```bash
which paperpilot
```
编辑配置文件(macOS:~/Library/Application Support/Claude/claude_desktop_config.json;Windows:%APPDATA%\Claude\claude_desktop_config.json):
```json
{
"mcpServers": {
"paperpilot": {
"command": "/usr/local/bin/paperpilot",
"args": ["mcp", "serve", "--transport", "stdio", "--workspace", "/Users/yourname/papers"],
"env": {}
}
}
}
```
把 command 换成上一步 which 的结果,--workspace 换成你的真实目录。保存后完全退出 Claude Desktop(macOS 上要 Cmd+Q,关窗口不算退出),再重新打开。
第 7 步:接入 Cursor
在 ~/.cursor/mcp.json(全局)或项目根目录的 .cursor/mcp.json(仅本项目)里写入同样结构:
```json
{
"mcpServers": {
"paperpilot": {
"command": "/usr/local/bin/paperpilot",
"args": ["mcp", "serve", "--transport", "stdio", "--workspace", "/Users/yourname/papers"]
}
}
}
```
保存后在 Cursor 的 MCP 面板里确认状态变成已连接,必要时重启编辑器。
验证部署是否成功
分三层验证,任何一层不过都说明问题出在这一层。
第一层,命令行本身能工作:
```bash
paperpilot --version
~/.paperpilot/bin/paperpilot-engine --version
paperpilot parse ./sample.pdf --output ./out --format json
```
三条都不报错,且 ./out/sample.json 存在,第一层通过。
第二层,MCP 服务能被客户端识别:打开 Claude Desktop 或 Cursor 的工具/MCP 面板,工具列表里出现 paperpilot 及其提供的解析类工具,则第二层通过。这一层失败,九成是 command 路径写错或 Node 环境没被继承。
第三层,端到端可用。在对话框里说:
> 列出 /Users/yourname/papers 目录下的 PDF,挑一个解析出来,告诉我文档分几章、每章标题是什么。
预期结果是模型先调用列目录工具,再调用解析工具,最后返回带章节标题的回答。如果模型只是“假装”回答了内容而没显示工具调用记录,说明工具没挂上,回到第二层排查。
常见报错与解决
报错:客户端里 MCP Server 状态显示 failed,日志里是 spawn paperpilot ENOENT
→ 原因:图形界面应用启动时没有继承 shell 的 PATH,找不到 paperpilot。
→ 解决:用绝对路径。
```bash
which paperpilot
把输出结果原样填入配置里的 command 字段
```
报错:Cannot find module 或 WebAssembly.instantiate(): CompileError
→ 原因:Node 版本低于该版本包的要求,或 node_modules 安装不完整(下载中断)。
→ 解决:升级到 LTS 版本后重装依赖。
```bash
node -v
rm -rf node_modules package-lock.json
npm install paperpilot
```
报错:解析大文件时进程被 Killed,或系统明显卡顿
→ 原因:单批次页数过多,中间结果全部驻留内存,触发了系统 OOM 保护。
→ 解决:分批处理,或调低单次页数(参数名以官方文档为准),并给系统留出足够 swap。
```bash
paperpilot parse ./big.pdf --output ./out --format json --max-pages 50
```
报错:解析成功但正文大片空白,只有页码
→ 原因:这是扫描件,没有文本层,纯文本抽取拿不到内容。
→ 解决:确认是否需要启用 OCR 能力;如果只是要提取目录和章节位置,先用带文本层的原生 PDF 替代。
```bash
paperpilot parse ./scan.pdf --output ./out --format json --ocr
```
报错:客户端提示 Request timed out,工具调用无响应
→ 原因:一个大 PDF 的解析时间超过了客户端默认超时窗口,同步等待被截断。
→ 解决:先离线跑完解析产出 JSON,让 MCP 只负责读取结果文件,而不是每次对话都现场解析。
```bash
paperpilot parse ./big.pdf --output ~/papers/.cache --format json
```
后续维护
备份。真正值得备份的是三样:~/.paperpilot/ 下的配置、~/.paperpilot/bin/ 下的引擎二进制、以及你的解析输出目录。配置文件很小,建议直接纳入版本管理(注意剥离里面可能存在的绝对路径信息)。解析产出的 JSON 体积大,按项目归档即可,不必全量长期保留。
升级。升级前先跑一次 paperpilot parse ./sample.pdf 做基线,记录页数、块数、关键字段;升级后重跑同一条命令做对比。升级顺序建议是:先升 WASM 包,再换引擎二进制,两步之间各验证一次,出问题能立刻定位是哪一层引入的。引擎二进制保留上一版一份,方便回滚。
日志。CLI 一般有 --verbose 之类的详细输出开关,排查时打开;MCP 模式的日志走 stderr,不会污染 stdio 通信通道,客户端面板里能看到。建议定期看一眼日志里有没有反复重试、超时、解析失败的文件名。
监控。个人本地使用不需要上监控系统,两个习惯就够:一是定期看 ~/.paperpilot/bin/ 和缓存目录的磁盘占用,几百份文档下来很容易吃掉几个 GB;二是每月手动跑一次端到端验证(列目录 → 解析 → 提问),确认客户端升级或系统更新之后 MCP 连接还在。系统大版本升级后尤其要重跑一次,PATH 和沙箱权限的变动经常在这一步才暴露出来。
权限收口。--workspace 指向哪个目录,模型就能读哪个目录。放敏感材料的机器上,把这个参数限定到具体的项目子目录,别图省事指向整个用户目录。
