需求场景
做科研最怕“文献山”。老板一句“把近五年综述补齐”,往往意味着通宵达旦地下 PDF、开 Word、贴引用。传统做法里,人工扫摘要、做笔记、归主题,一篇 200 篇的综述常常要两周;更尴尬的是,第二天老板换方向,前面功夫全打水漂。能不能让机器先跑一遍,把“哪篇讲了啥、哪段能回答我的问题”自动标好,再让人聚焦判断?本文的实战目标就是:用 ChatGPT 系列模型搭一套“自动化文献综述系统”,输入一个文件夹的 PDF,输出可直接引用的段落摘要与问答定位,把调研时间砍掉 80%。
技术架构
整体链路分三层:
- 数据层:负责 PDF 解析、清洗、分块、向量化入库。
- 语义层:基于 OpenAI Embeddings 做向量索引,结合 Chroma/FAISS 实现召回。
- 生成层:用 GPT-3.5-turbo 做多轮 Prompt Chaining,先定位文献、再生成综述、最后输出带引用的回答。
核心流程:PDF → Raw Text → Chunks → Embeddings → VectorDB → Query → Context → LLM → Answer。
技术选型:
- Python 3.10+,类型标注 + Pydantic
- 解析:PyPDF2、pdfplumber、textract 三选一,按文档类型动态 fallback
- 向量化:text-embedding-ada-002,1536 维,便宜且统一
- 框架:LangChain 负责链式调用,Tenacity 做重试,tiktoken 预估 token 数
- 部署:Colab Pro 即可跑通 500 篇规模,本地 Docker 方案文末给出
核心实现
1. PDF 解析陷阱与对比
学术 PDF 的坑集中在“双栏+公式+扫描件”。实测 100 篇 IEEE 论文:
- PyPDF2:轻量、纯文本,遇到公式直接丢,双栏 顺序错乱。
- pdfplumber:保留坐标,可写启发式规则还原栏序,但对扫描件完全失效。
- textract(底层调 Apache Tika):OCR 一体化,扫描件可救,速度×0.3,本地需装 Java。
代码策略:先 pdfplumber,抛异常再 fallback 到 textract,扫描件自动走 OCR。
from pathlib import Path import pdfplumber, textract, logging def extract_text(p: Path) -> str: try: with pdfplumber.open(p) as pdf: text = "\n".join(page.extract_text() or "" for page in pdf.pages) if len(text) < 200: # 可能扫描件 raise ValueError("Text too short") return text except Exception as e: logging.warning("plumber failed, fallback to textract: %s", e) return textract.process(p, encoding='utf-8').decode()2. 向量化与语义索引
文本分块用“句子窗口+重叠”策略,每 512token 滑 128,保证引用定位不截断。入库前用 tiktoken 计算,超长的按句号再切。
import openai, chromadb, tiktoken from typing import List EMB = openai.Embedding.create enc = tiktoken.get_encoding("cl100k_base") def chunk_text(text: str, maxlen: int = 512) -> List[str]: tokens = enc.encode(text) chunks = [tokens[i:i+maxlen] for i in range(0, len(tokens), maxlen-128)] return [enc.decode(c) for c in chunks] def embed_and_store(pdf_id: str, chunks: List[str], collection): for idx, c in enumerate(chunks): emb = EMB(input=c, model="text-embedding-ada-002")["data"][0]["embedding"] collection.add(documents=[c], embeddings=[emb], ids=[f"{pdf_id}_{idx}"])Chroma 本地启动,磁盘持久化,500 篇约 1.2 G,检索 10 条 <100 ms。
3. 问答链设计(Prompt Chaining)
单轮 Prompt 容易“胡说八道”,采用三链:
- 召回链:query → 向量库 → 返回 5 段最相关原始段落。
- 判断链:让 LLM 给每段打 0-1 分,过滤低于 0.6 的,减少干扰。
- 综述链:把高分段落拼接,指令生成“带引用标记”的综述。
SUMMARY_PROMPT = """ You are an academic assistant. Based solely on the provided passages, write a concise literature review in Chinese. Cite each passage with its ID like [1], [2]. Do not hallucinate external knowledge. Passages: {context} Review: """LangChain 的 SequentialChain 把三链串起,代码仅 60 行,日志可回溯每一步的中间结果,方便 debug。
4. Temperature 调优与学术术语
学术写作要求“准”而非“花”。实验发现:
- 召回/判断链:Temperature=0,最稳定。
- 综述链:Temperature=0.3 时,术语保留率最高;>0.7 容易把“BERT”翻译成“双向编码表征”。
自定义 stop_tokens=["\n\n", "References:"],防止模型自己编引用。
5. 异常处理与抄袭规避
- 所有 LLM 输出落地前走一遍 查重 API(iThenticate 或 CNKI 开放接口),相似度>15 % 自动标红。
- 强制在 Prompt 里加指令:“Do not copy sentences verbatim longer than 10 words.”
- 返回结果附带 chunk_id,用户可点击跳转原文,确保“机器只是指路,引用责任在人”。
生产考量
1. 成本控制
- 嵌入阶段:ada-002 每 1k token $0.0001,一篇 10k 论文约 $0.001,500 篇 $0.5。
- 生成阶段:gpt-3.5-turbo $0.002/1k token,一次综述平均 3k 输入+1k 输出 ≈ $0.008。
- 预算公式:总美元 ≈ 0.001×N + 0.008×Q (N 论文数,Q 查询次数)。
2. API 限流与重试
官方 3/min 免费层不够用,付费后 3500 RPM 仍可能被打满。用 Tenacity 的 AsyncRetrying,指数回退 + 抖动,把批量请求拆成 500 token/次,峰值延迟可压到 2 s 以内。
3. 本地缓存
对每篇 PDF 的原始 chunks 与 embedding 做 SHA256 缓存,二次运行跳过已解析文件,开发阶段可省 70 % 时间。
4. 容器化部署
Dockerfile 里预装 tesseract 与 Java,镜像 1.8 G;docker-compose 把 Chroma 与 Web 服务分离,卷挂载本地论文库,重启不丢数据。
延伸思考
- 跨语言文献:当前链只接受英文嵌入,德/法/日论文需先翻译还是训练多语言 embedding?欢迎读者尝试 [sentence-transformers/multilingual] 并分享效果。
- 图表理解:模型目前只看文本,把图注+OCR 结果塞入上下文后,召回率涨 8 %,但公式 表格仍丢失,后续可接入 LayoutLM。
- 主动推荐:基于用户历史查询向量,做协同式文献推送,实现“机器先猜你要啥”。
完整可运行代码与 Colab 链接已放在 GitHub 仓库,一键复制即可跑自己的 PDF 文件夹。
如果你也想亲手把“读文献”这件苦差事交给 AI,又不希望踩坑浪费 API 额度,可以看看我在火山引擎上做的另一个小实验——从0打造个人豆包实时通话AI。里面同样用到了低延迟 ASR 和 LLM 的组合思路,对链式 Prompt 的优化策略一脉相承,我这种半吊子 Python 水平都能一次跑通,相信你也可以。