用自己的 OpenAI API 做 PDF 对话:路线对比与落地步骤
现成的 PDF 对话产品大多要单独订阅,用自己的 API Key 自建是更省钱也更可控的路子。本文对比「全文塞进上下文」和「切片加向量检索」两条技术路线,说明各自适合的文档类型,给出自建工具选型、最小可用脚本、成本估算方法和上线前的检查清单。
很多人手上已经有了 OpenAI 的 API 额度,却还在为读 PDF 另外订阅一个文档问答产品。市面上的 ChatPDF、Humata、ChatDoc、PandaGPT 这类工具体验确实成熟,但都要单独付费,而且文档要上传到别人的服务里。于是问题就变成:有没有办法直接用自己的 API Key,把 PDF 丢给模型问答?下面把路线、工具和落地细节拆开讲。
先分清两条技术路线
几乎所有 PDF 对话产品,底层无非两种做法,选型前必须搞清楚自己要哪一种。
路线 A:把全文或大段原文直接送进上下文
把 PDF 抽成文本,整篇塞进支持长上下文的模型。优点是模型看到的是完整原文,跨章节的关联、语气、前后矛盾都能捕捉到;缺点是输入 token 随文档长度线性增长,超长文档要么截断要么分批,成本和延迟都上去了。
路线 B:切片 + 向量检索(RAG)
把文档切成若干块,用 embedding 模型建索引,提问时先检索最相关的几块再交给模型。优点是文档再长也能处理、单次调用便宜、可以挂很多文档;缺点是召回有上限——切断了上下文、跨段落的信息容易丢,块切得不好还会答非所问。
| 路线 A 全文入上下文 | 路线 B 切片加检索 | |
|---|---|---|
| 适合文档 | 几十页以内、需要通读理解的合同/论文/报告 | 大量文档、只需定位某一处细节的知识库 |
| 成本结构 | 每次提问都重复付全文的输入费用 | 建库一次,后续按检索块付费 |
| 主要风险 | 超长截断、上下文窗口不够 | 切块丢信息、召回不准 |
| 实现复杂度 | 低,一个脚本就够 | 中,要维护解析、切分、向量库、重排 |
关于「embedding 做 PDF 对话是不是鸡肋」,两种说法都有道理:对一份要通读的文档,切片确实会把关键信息切散;但如果是几百份文档里找一句话,全文入上下文既贵又放不下。判断标准很简单——你是「读一份」还是「查一堆」。
自建方案怎么选
按从轻到重排:
- 直接写脚本调 API:最可控,适合单文档深读。用
pdftotext或 PyMuPDF 抽文本,拼进 prompt 发出去即可,不需要任何框架。 - 可视化编排平台:如 Dify 这类开源平台,配置模型供应商、上传文档、选切分策略,图形界面就能跑通一条 RAG 链路,适合不想写代码又要接自己 Key 的场景。
- 本地知识库客户端:AnythingLLM、Open WebUI 等同类工具,装上就能用,支持自定义 API 地址和 Key,适合个人长期使用。
需要提醒的是,这类平台的产品定位偏「知识库」:一次导入一批文档、做持续问答很顺,但针对单个 PDF 做通读、逐页追问、精确定位页码的体验往往不如专用阅读器。如果核心诉求是「把这一份文档吃透」,脚本路线反而更快出效果。
一个最小可用的做法
- 抽取文本:
pdftotext -layout input.pdf out.txt,保留版式能减少表格错乱;扫描件必须先 OCR。 - 估算长度:用分词工具实测 token 数,别凭页数猜。
- 组装请求:系统提示词明确「只依据给定文本回答,找不到就说找不到」,并要求标注页码或章节。
- 反复追问时,把原文和之前的问答一起放进对话历史,避免模型前后矛盾。
from openai import OpenAI
client = OpenAI() # 从环境变量读取 API Key
text = open("out.txt", encoding="utf-8").read()
resp = client.chat.completions.create(
model="<你账号可用的长上下文模型>",
messages=[
{"role": "system", "content": "你是文档助手,只依据给出的原文回答,并标注出处章节;原文没有的信息直接说明。"},
{"role": "user", "content": f"原文:\n{text}\n\n问题:这份文档的核心结论和适用条件是什么?"},
],
)
print(resp.choices[0].message.content)
模型名称、上下文窗口大小和计费方式各账号不同、也常调整,以官方文档和控制台的最新说明为准。文档明显超出窗口时,按章节切分后逐段提问,再让模型汇总各段结论,比硬截断可靠。
成本到底差多少
没法给出统一数字,但估算思路是确定的:
- 全文路线:成本 ≈ 文档 token 数 × 提问次数。同一份文档问十个问题,全文就要付十次输入费。
- RAG 路线:建库成本 = 解析 + 每块一次 embedding(很便宜)+ 向量存储;每次提问 = 检索出的几块 + 回答。文档多、提问多时优势明显。
- 隐性成本:解析质量差导致的无效重问、切块不当导致的答错,往往比 token 费用更贵。
先拿两三份真实文档跑一遍,记录 token 用量再决定,比看任何评测都准。
解析质量比模型更决定成败
多数「答得不好」的案例,问题出在 PDF 转文本这一步,而不是模型:
- 双栏排版被按行拼接,句子交错;
- 表格变成一串数字,行列关系丢失;
- 页眉页脚、参考文献混进正文,污染检索;
- 扫描件直接抽取得到空文本。
处理办法:优先选 -layout 或版面分析模式;对表格单独抽取、以 Markdown 形式保留;清洗掉重复的页眉页码;扫描件走 OCR。这一步多花十分钟,后面少踩很多坑。
部署在哪:本地脚本还是常驻服务
只做单次阅读,本机跑脚本就够。但如果你搭的是知识库服务、需要团队随时访问,或者本机性能有限、跑不动向量库和 OCR,就该考虑放到一台常开的机器上。这类场景下,把服务部署在一台长期在线的远程主机上(例如整台放在新加坡机房的 Mac mini,按月租用、远程连接、可长期在线)是可选做法之一,好处是不占本地资源、网络出口相对稳定,代价是多一份租用成本,用之前先确认自己的用量撑得起。
上线前检查清单
- PDF 是文本型还是扫描型?后者先 OCR
- 用分词工具实测 token 数,确认是否超出上下文窗口
- 需要页码引用吗?需要就从解析环节保留页码映射
- 文档含敏感信息吗?确认服务商的数据使用条款
- 提问是「读一份」还是「查一堆」?据此选全文或 RAG
- 切块大小与重叠试过两三种设置,对比召回效果
- 收集十来个真实问题做回归,观察答错是召回问题还是生成问题
小结
自建 PDF 对话并不复杂,真正的决策点只有三个:文档是精读还是检索、解析质量能不能保证、服务放在哪。精读用全文入上下文,一个脚本最快见效;批量检索上 RAG 平台,省 token 也便于扩展。别急着上框架,先把一份文档跑通,确认解析和提示词没问题再谈规模化。