根据阅读者身份变化的PDF

Hacker News Top 工具

摘要

本文介绍了一种技术,利用PDF规范中的替换文本属性,在PDF内部嵌入隐藏的Markdown结构,使得LLMs能够提取干净、结构化的数据,而人类看到的仍然是相同的视觉文档。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/12 17:55

# 自适应PDF 来源:https://sgaud.com/texts/pdf PDF 是一种视觉格式,它存储了如何在页面上绘制字符的指令。规范中其实支持“带标签 PDF”(Tagged PDF)——一种用结构树标记标题、段落、列表的方式。某些领域会使用它,比如政府的无障碍访问要求、企业出版流水线。但你实际遇到的大多数 PDF 都是未加标签的。LaTeX、Chrome 的「打印为 PDF」以及大多数导出工具都不会生成标签。所以你得到的只有坐标和字体大小。文本提取器从左到右、从上到下读取绘制指令,然后祈祷结果正确。 这在只有人类读者的时候无关紧要。但现在大多数 PDF 最终都流向了 LLM。我们把它上传到 ChatGPT,让 Claude 总结,或者扔给解析器处理。而每一款这样的工具都在与同一个问题作斗争:从一个从未承载过结构的格式中重建结构。LLM 看到的是 `Project Alpha\nLed a team of 5 engineers\nto deliver the`,然后得猜测标题在哪里结束、句子从哪里继续。有时它能猜对,更多时候则不能。 我想做一种 PDF:人类看到的是格式化文档,机器提取出的则是干净的 Markdown。同一个文件,没有新扩展名,就是一个 `.pdf`。 ## 它是如何工作的 PDF 规范中(自 PDF 1.4,2001 年起)有一个属性,允许你为标记内容定义替代文本。渲染器会忽略它——它们按照内容流绘制任何内容。但支持该属性的文本提取器会返回替代文本,而不是视觉文本。在我的测试中,PyMuPDF 和 Poppler 都支持它。不同工具和版本的支持情况有所差异,但主流开源提取器都能处理。 这个属性原本是为连字以及无法自然映射到 Unicode 的字符设计的。比如视觉连字 "fi" 应该被提取为两个字符 "f" 和 "i"。它从未被用于更大的范围。 我们将其用于文档级别。通过标记内容序列(marked-content sequences)为内容流附加替代文本,因此支持该属性的提取器会返回结构化的 Markdown,而不是原始的视觉文本。PDF 渲染效果完全一样——同一个文件,根据读取者的不同,产生两套完全不同的输出。 ## 提取器实际看到的内容 同一个 PDF,同一份视觉外观。以下是 PyMuPDF 分别从两种版本中提取出的内容。 **普通 PDF:** ``` Quarterly Infrastructure Report Overview Cloud migration completed ahead of sch edule. Three critical services were moved to the new cluster. Key Metrics Uptime: 99.97% Latency: 42ms avg (down from 68ms) Cost: $12,400/mo (down 34%) Action Items Migrate remaining batch jobs by Q3 Set up automated failover for db-west Review cost allocation per team ``` **Smart PDF:** ``` # Quarterly Infrastructure Report ## Overview Cloud migration completed ahead of schedule. Three critical services were moved to the new cluster. ## Key Metrics | Metric | Value | |---------|---------------------------| | Uptime | 99.97% | | Latency | 42ms avg (down from 68ms) | | Cost | $12,400/mo (down 34%) | ## Action Items - Migrate remaining batch jobs by Q3 - Set up automated failover for db-west - Review cost allocation per team ``` 两个文件在预览、Adobe 或任何 PDF 查看器中看起来完全相同。但普通提取没有层级结构,行会在句子中间断开,项目符号与段落无从区分,表格被压平成零散的行。智能提取则带有 `#` 标题、Markdown 表格、`-` 项目符号,以及不会在单词中间断开的句子。LLM 无需猜测「Key Metrics」是不是段落标题,也不需要猜测那三行是不是列表——这些都是显式给出的。 ## 基准测试 使用我们的工具将若干 PDF 转换为 Smart PDF,然后用 PyMuPDF 的 `get_text()` 以及 [pdf2go.com](https://www.pdf2go.com/seaparately) 分别从两个版本中提取文本,两者都返回了 Markdown。Token 计数使用 tiktoken(cl100k_base)。基准测试脚本在仓库中。 | 文档 | 页数 | 大小差异 | 普通 Token | Smart Token | |--------------------|------|----------|------------|-------------| | 简历 | 1 | +15.7% | 650 | 668 | | 教科书 | 417 | -8.5% | 193,064 | 195,858 | | 小说章节 | 38 | +4.7% | 16,472 | 15,958 | | 研究论文 | 18 | +2.5% | 8,082 | 7,897 | Token 计数大致相当。优势不在于更少的 token,而在于同样的 token 现在携带了结构。`## Overview` 与 `Overview` 代价相同,但前者告诉机器它看到的是什么。在不增加 token 数量的前提下,每个 token 的信息密度提升了。 对于大多数文件,体积开销在个位数百分比。教科书体积反而缩小了,这是因为 PyMuPDF 在保存时使用了 `garbage=3` 参数移除了无用的 PDF 对象——这是一种常规优化,并非特定于本技术。 将 Smart PDF 分别上传到 ChatGPT 和 Claude,要求它们逐字符复制它们看到的原始文本。两者都返回了 Markdown:`#`、`##`、`-` 项目符号。仅凭这个结果还不能完全下定论,因为 LLM 自身具备结构推断能力,像 Docling 这类工具也可以通过布局分析从普通 PDF 生成 Markdown。但输出与我们嵌入的层完全一致,包括一些格式选择——这些选择是任何布局启发式算法都无法精确复现的。 ## 自适应文档 最终你得到的是这样一种文档:它能根据读取者进行自适应。人类打开它,看到的是他们熟悉的格式化 PDF——字体、布局、间距,一切正常。机器读取它,得到的是干净的 Markdown——标题、列表、结构。同一个文件,不需要单独维护两个版本,不需要转换步骤。它只是根据谁在看而呈现不同的内容。 你不需要管理什么,也不需要维护两份副本。文档自身会根据被消费的方式决定呈现什么。 我正积极进一步探索这个方向,并计划为 Google Docs 开发一个扩展来简化流程。这是我对这个想法的第一次迭代。

相似文章

自适应 Markdown

Reddit r/artificial

自适应 Markdown 是一种开源文档格式/查看器,利用编码智能体使文档具有交互性,为学术阅读、笔记记录和自动化工作流等任务提供实时工作空间。