将近十年积累的个人文档从 Google Drive 文件夹体系迁移到 Paperless-ngx 的完整记录。 涵盖分类体系设计、从 Google Takeout 批量导入、ML 分类器训练,以及日常收件箱工作流。
为什么要迁移#
过去多年,我的"文档管理"是一棵手工维护的 Google Drive 文件夹树:
| |
归档时还算顺手,但检索很痛苦。想找"2022 年的保险表格",要翻六个文件夹,还得猜当时的命名。 Paperless-ngx 提供全文检索、OCR、以及会从你自己的标注中学习的 ML 分类器—— 对于横跨移民手续、税务申报、房产合同、医疗记录的文档库来说,这是本质性的提升。
系统架构#
| |
Paperless-ngx 通过 Synology Container Manager 运行在 Docker 中。
所有存储(文档、数据库、Redis)挂载到 /volume1/docker/paperless/。
分类体系设计#
批量导入之前把分类体系设计好很重要——它是 ML 分类器的训练信号。 400 份文档贴错标签,等于教错了东西。
文档类型#
目标是互斥且完备的类型——覆盖我实际拥有的文档,不多不少。 全部用中文命名,ML 分类器不管标签是什么语言都能学。
| ID | 名称 | 归入此类的内容 |
|---|---|---|
| 1 | 发票收据 | 已付款的发票、收据、付款确认(仅限完成交易) |
| 3 | 操作手册 | 产品说明书、用户指南、组装说明 |
| 4 | 活动通告 | 活动邀请函、公告(非预订类) |
| 5 | 参考资料 | 参考材料、价目表、宣传册 |
| 6 | 设备信息 | 设备规格、序列号、保修记录 |
| 7 | 日程课表 | 周期性日历、课程表(如音乐课日历) |
| 8 | 金融账单 | 银行/投资/券商账单 |
| 9 | 税务文件 | 报税表、W-2、1099、1098、HSA |
| 10 | 身份证件 | 护照、驾照、身份证 |
| 11 | 合同协议 | 合同、协议、租约、施工授权书 |
| 12 | 医疗记录 | 病历、处方、化验报告 |
| 13 | 证明证书 | 证明信、公证、各类证书 |
| 14 | 移民文件 | I-797、I-94、I-20、EAD、绿卡 |
| 15 | 签证申请 | 签证申请材料(美、中、加等) |
| 16 | 工资单 | 工资条 |
| 17 | 房产文件 | 贷款、建筑许可、房产税、HOA |
| 18 | 车辆文件 | 车辆租约、DMV、年检 |
| 28 | 行程预订 | 酒店/机票预订确认、活动门票、通行证、SNO-PARK 等许可证 |
| 29 | 报价估价 | 维修估价、施工报价、项目提案(付款前的询价阶段) |
设计决策说明:
- 发票收据 = 已付款交易:酒店确认单归 行程预订;维修估价归 报价估价;只有完成付款的文件归此类
- 行程预订 vs 发票收据:酒店确认单是预订凭证,不是收据;入住结账后的账单才是收据。门票和通行证也归此类
- 报价估价 vs 发票收据:未付款的估价/报价是采购前阶段;付款并开票后转为发票收据
- 设备信息 ≠ 参考资料:设备档案(序列号、保修)与参考材料(价目表、手册)结构不同,分开有意义
- 活动通告 ≠ 日程课表:前者是一次性通知,后者是反复查阅的参考文档
- 金融账单合并银行 + 投资:都是周期性账单,必要时用往来方(HSBC vs Vanguard)区分
- 税务文件涵盖所有税务文档:不需要在类型层面细分 W-2/1099/报税表,标签和往来方承担这个维度
- 移民文件 vs 签证申请:在途维持身份文件(I-797、EAD)与签证申请材料是不同的业务场景
- 医疗账单 → 发票收据 + #医疗 标签:医疗账单本质是收据,标签提供"医疗"维度,不需要单独的类型
标签#
标签处理不属于文档类型的横切维度:
| 标签 | 用途 |
|---|---|
| #保险 | 保险相关 |
| #医疗 | 医疗主题 |
| #教育 | 教育相关 |
| #财务 | 财务主题 |
| #房产 | 房地产 |
| #旅行 | 旅行 |
| #车辆 | 车辆 |
| #移民 | 移民主题 |
| #签证 | 签证主题 |
| #税务 | 税务主题 |
| #待处理 | 收件箱 / 待审阅 |
| #重要 | 重要、时效性强 |
| #归档 | 已归档,无需操作 |
年份标签经过考虑后放弃。 最初计划给每份文档加 #2016 到 #2026 的年份标签。 反思后发现:年份标签给 ML 训练信号增加噪音,而"文档年份"自定义字段能更干净地覆盖这个需求 (可按数值过滤和排序,不占用标签云空间)。
自定义字段#
| ID | 名称 | 类型 | 用途 |
|---|---|---|---|
| 1 | Amount | 浮点数 | 发票/账单金额 |
| 2 | Bill Period | 日期 | 账单周期截止日 |
| 3 | 到期日期 | 日期 | 文件到期日(证件、签证) |
| 4 | Document Year | 整数 | 税务年份、历史文档所属年份 |
| 5 | 保单/账号 | 字符串 | 保单号、账号 |
往来方#
设置了出现频率足以作为过滤条件的实体:
| ID | 名称 |
|---|---|
| 5 | IRS |
| 6 | California FTB |
| 7 | Google LLC |
| 8 | USCIS |
| 9 | US Dept of State |
| 10 | Vanguard |
| 11 | HSBC |
| 12 | County of Santa Clara |
导入前:关闭 ML 自动匹配#
批量导入 400 份文档之前,先关闭所有文档类型、标签、往来方的 Auto/ML 匹配。 如果自动匹配在导入过程中处于开启状态,Paperless 可能用半训练的模型尝试重新分类, 覆盖你精心指定的元数据。
| |
导入完成后,对需要 ML 建议的类型、标签、往来方重新设置 "matching_algorithm": 6。
例外:#待处理 应永久保持 0(无)。 它是由工作流指定的状态标签,不是内容类别—— ML 没有理由去猜测它。
迁移脚本#
migrate.py 一次性完成分类和上传,直接读取 Google Takeout .zip 而无需全部解压。
分类逻辑#
每个文件通过按优先级排列的 if 链根据文件夹路径进行分类:
| |
年份从路径中任意匹配 \b(20[12]\d)\b 的部分提取——
30 - Tax Filing/2022/W2.pdf 会自动得到 year=2022。
往来方先从文件名关键词推断(google、hsbc、vanguard、irs、ftb),
再由文件夹特定规则覆盖。
上传#
| |
标签必须以重复表单字段方式发送(不是 JSON 数组):
| |
自定义字段使用 JSON 编码的字典,键为字符串:
| |
文件类型过滤#
跳过混入 Takeout 的非文档文件:
| |
.doc/.docx 技术上受 Paperless 支持,但 OCR 效果不稳定;
如果需要全文检索,建议先导出为 PDF。
预演模式#
| |
输出示例:
| |
执行导入#
| |
--yes 跳过确认提示(通过 SSH 运行时 input() 会挂起,必须加此参数)。
Paperless 按内容哈希去重,重复运行是安全的。
导入结果#
358 份文档分布在 17 种文档类型中(后续分类体系扩展至 19 种,见下文)。含 OCR 处理时间,上传约耗时 25 分钟。
排除 .doc/.docx 后零错误。
ML 分类器训练#
所有文档完成 OCR 且批量清除 #待处理 标签后:
| |
~400 份已标注文档为分类器提供了扎实的训练集。 最常见的文档类型(税务文件、移民文件、金融账单各有 50-90 个样本)效果较好。 较少见的类型随着文档增加会持续改善。
训练前等 OCR 完成。 分类器在 OCR 文本上训练,不是原始文件。 过早运行意味着在空白或残缺的文本上训练。
收件箱工作流#
新文档(手动上传、移动端扫描、邮件导入)自动获得 #待处理 标签:
设置 → 工作流 → 添加工作流:
- 名称:
新文档收件箱 - 触发:文档添加(类型 2)
- 动作:指定标签 #待处理(动作类型 1)
无论来源如何,每份新文档都会触发工作流,给你一个可靠的收件箱视图。
#待处理 的 matching_algorithm 永久设为 0(无)——它只由工作流指定,ML 不会猜测它。
这保证了它作为状态信号的干净性。
审阅完文档后移除 #待处理,文档从收件箱移出。
日常使用方式#
文档来源#
| 来源 | 方式 |
|---|---|
| 纸质文档 | 手机扫描 App(如 Scanner Pro),上传到 Paperless |
| 邮件附件 | Paperless 邮件收件箱(IMAP 轮询,单独配置) |
| 下载的 PDF | 拖放到 Paperless UI 或消费文件夹 |
| 消费文件夹 | NAS 上的 SMB 共享,从 Windows/Mac 可访问 |
审阅流程#
- 打开保存的搜索:标签:#待处理
- 逐份文档:确认类型,补充缺失标签,修正标题
- 移除 #待处理——文档离开收件箱
- 对时效性强或即将到期的文档加 #重要
检索文档#
- 全文搜索处理大多数场景(如"EAD renewal 2023")
- 按文档类型 + 往来方过滤账单(金融账单 + Vanguard)
- 按往来方 + Document Year 自定义字段过滤税务文档
- 完整处理且无需后续操作的文档加 #归档
凭证备份#
将 Paperless 管理员密码和 API Token 存入密码管理器(Bitwarden)。 API Token 在 设置 → Tokens 中查看;如需轮换,更新所有脚本中的引用。
排障记录#
批量清除 #待处理 标签#
如果在禁用 Auto/ML 之前运行了分类器,它可能已经学会给所有文档打此标签。 通过 API 批量清除:
| |
文档类型被错误自动指定#
ML 在语料不均衡或有噪音时可能分错。手动纠正后重新训练:
| |
每次手动纠正都会反馈到训练集。
custom_fields 格式错误#
Paperless 期望自定义字段为以字符串为键的 JSON 编码字典:
| |
端口被占用 / 防火墙#
如果 Paperless 不可达,检查 NAS 上 5656 端口是否有冲突服务。 DSM 防火墙也可能拦截特定子网的访问——检查 控制面板 → 安全性 → 防火墙。
我的配置#
| 项目 | 值 |
|---|---|
| NAS | Synology DS1621+ |
| NAS IP | 10.0.10.10 |
| Paperless 端口 | 5656 |
| Docker 镜像 | ghcr.io/paperless-ngx/paperless-ngx:latest |
| 导入文档数 | 358 |
| 导入来源 | Google Takeout(单个 .zip,约 2 GB) |
| OCR 语言 | 中文 + 英文 |
| 训练语料 | ~400 份已标注文档,覆盖 19 种类型 |
备注#
post_document接口是异步的——Paperless 将文档加入 OCR 队列后立即返回{"result": "OK"},文档要等 OCR 完成后才可搜索(通常每份几秒到一分钟,取决于 NAS 负载)- Paperless 按文件内容的 SHA256 去重——重复运行导入脚本是安全的,重复文档会被静默跳过
- Google Takeout 默认导出 Google Docs/Sheets 为原生格式;如需 PDF, 创建 Takeout 时选择"导出格式: PDF"
document_create_classifier命令在训练集未变化时输出No updates since last training——这是正常现象,不是错误