1. Agent 读 PDF,和传统脚本解析根本不是一回事
1.1 需求差异:从"提取文本"到"理解语义"
做过几年工具类开发的人应该都有这种体感:传统程序处理 PDF,核心诉求是"把内容拿出来"——拿文本、拿表格、拿图片,拿到之后自己写逻辑去用。但 Agent 场景完全不是这个玩法。Agent 拿到 PDF 之后要做的是"理解这份东西在讲什么",然后基于理解去决策、去执行、去回答。这一字之差,背后的方案选型、工具链组合、前后处理逻辑全都不一样。
我最早做 PDF 解析是给一个合同审核工具做底层,当时用 pdfplumber 提取文本和表格,规则写死,字段对不上就报错,基本是"程序员的确定性思维"。后来转去做 AI Agent 相关的项目,第一次把 PDF 丢给大模型去总结合同条款时,我按老经验先写了一个文本提取脚本,结果发现提取出来的文本是通的,但大模型理解出来的东西跟人眼看原文得出的结论对不上——问题不在模型,在于我喂进去的文本把版面结构、层级关系、表格语义全抹平了。从那一刻起我意识到,Agent 时代的 PDF 工具方案,真正的考核指标不是"提取率多高",而是"结构保真度多少、语义损失多少、以及 Agent 能不能基于这份输出做出正确判断"。
这里得先澄清一个概念:Agent 是个决策主体,PDF 工具只是它的"眼睛"。眼睛看得清不清楚,直接决定脑子想得对不对。所以 PDF 工具方案分析,本质上是分析"怎么给 Agent 配一副好眼睛",而不是单纯分析解析库的好坏。这个视角下面,很多传统 PDF 库的优缺点排序会被彻底打乱。
1.2 Agent 场景的三个典型诉求:长上下文、结构化、可追溯
把需求拆开看,Agent 场景对 PDF 工具的要求其实可以归纳成三条主线。
第一条是长上下文。真实世界的 PDF 动辄几十上百页,合同、论文、财报、招股书都是典型代表。而 LLM 的上下文窗口虽然有上限,但长文档直接全量塞进去会遇到两个现实问题:一是 token 成本爆炸,二是超长上下文里模型注意力会衰减,中间段的内容经常被"遗忘"。所以 PDF 工具必须配合分块(chunking)、检索(retrieval)来做,不能无脑全量送入。任何不考虑分块策略的 PDF 工具方案,在 Agent 场景下都是耍流氓。
第二条是结构化。PDF 表面上是文档,底层其实是"排版指令集合"——文本块、图片、矢量图形、字体信息混在一起。传统解析库提取出来的纯文本,把标题、正文、页眉页脚、表格单元格全揉成一个线性字符串,段落之间的层级关系完全丢失。Agent 读这种"一马平川"的文本,很容易把表格里的数字当成正文里的普通描述,或者把两栏排版的左右两列内容串读成一段。我实测过,双栏论文用普通文本提取,左右栏内容交错,模型总结出来的研究结论有时会张冠李戴。
第三条是可追溯。Agent 在企业场景落地时,最怕的是"一本正经地胡说八道"。如果 Agent 基于 PDF 内容回答了问题,用户得能追溯到"这句话来自第几页第几段",否则没人敢信。这就要求 PDF 工具的返回结果必须携带位置信息、页码信息,甚至块级坐标,而不是只给一段干巴巴的字符串。很多 Python 库原生支持这个能力,但需要你在设计 Tool 输出时主动保留,这一步在实际项目里经常被忽略,等做审计时再回头补,成本翻好几倍。
这三条诉求搭在一起,基本框定了 Agent 场景 PDF 工具方案的评价维度:解析保真度、结构还原度、坐标可追溯性、长文档适配性、以及与大模型/Agent 框架的衔接成本。市面上能打的工具,各有各的侧重点,没有全能选手。下面我逐个拆。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主流 PDF 解析工具横向实测:选型前必须知道的底细
2.1 轻量派:pypdf / PDFPlumber 的适用边界
先聊最常用的两个轻量库。pypdf(前身是 PyPDF2)是纯 Python 实现的 PDF 解析库,优点是无系统依赖、装完就能用,适合做简单的文本抽取、页面合并拆分、加密解密。它的文本提取质量说实话一般,对复杂版面基本无能为力,遇到表格会把单元格内容按坐标顺序抖出来,顺序经常是乱的。在 Agent 场景下,pypdf 适合的活儿是"快速判断这份 PDF 是什么"——比如提取第一页做标题识别、统计页数、抽取元数据,这些场景不追求内容保真,只追求快和稳。
pdfplumber 是我个人比较喜欢的轻量库,它在文本提取的基础上做了一层版面解析,能把每个字符的位置、每个表格的边框线、每个矩形的坐标都暴露给你。对规则排版的单栏文档,pdfplumber 提取的文本顺序基本正确;对带边框的规则表格,extract_table() 方法能按行列还原得相当不错。但它的瓶颈也很明显:一是速度慢,处理 100 页以上的文档有明显卡顿;二是对无边框表格、合并单元格、复杂嵌套版面的还原能力有限;三是它本质还是"基于规则的提取",遇到设计感很强的排版(比如杂志风、大量浮层元素)就会翻车。
用 pdfplumber 给 Agent 做 PDF Tool 的典型姿势是:解析时同时输出"全文文本"和"表格结构"两路数据——全文文本用于让 Agent 做整体理解,表格数据用 Markdown 或 JSON 格式喂给模型,避免表格内容被线性化打乱。这一步很关键,我后面在踩坑章节会细讲。
2.2 中量派:PyMuPDF(fitz)的高性能路线
PyMuPDF 是另一个绕不开的名字,底层绑定 MuPDF 引擎,解析速度比 pdfplumber 快一个量级。同样是 200 页的 PDF,pdfplumber 可能要跑十几秒,PyMuPDF 基本两三秒内出结果。它支持文本提取、图片提取、矢量图形提取,还能做 PDF 渲染成图片、OCR 前置处理等操作。在 Agent 工具链里,PyMuPDF 很适合做"高速预处理层":先用它快速切分页面、提取文本块和坐标,再按需把关键页面渲染成图片供视觉模型做深度理解。
PyMuPDF 有个很实用的功能是 page.get_text("dict") 或 page.get_text("blocks"),可以拿到带坐标的文本块列表。每个块包含 bbox、文本内容、字号、字体信息,这些元数据对于构建"带坐标的结构化文档"非常有价值——Agent 在引用某个段落时,你可以把页码、坐标、字号一起返回,实现真正的"可追溯引用"。
不过 PyMuPDF 也有短板:它对表格结构的识别几乎是零,拿到的只是文本块的几何排列,不会帮你判断哪些块属于同一个表格、哪些是表头哪些是数据行。所以如果你想用 PyMuPDF 做完整的表格还原,还得自己写聚类算法,或者配合其他库二次处理。我的建议是:PyMuPDF 当"快刀",负责把 PDF 切成带坐标的文本块;表格还原这种精细活,交给专门的建模工具或视觉模型。
2.3 重量派:unstructured / DeepDoc 等文档理解引擎
如果你的业务涉及大量非标准版式,或者 PDF 里充满表格、图片、多栏布局,那轻量库和中量库都顶不住。这时候得上"文档理解引擎",比较有代表性的有 unstructured、deepdoctection、marker,还有各家云服务商提供的文档智能产品。
unstructured 的设计思路很现代:把 PDF(以及其他格式)先做元素切分(partition),把每一块内容分类为 Title、NarrativeText、ListItem、Table、Image 等类型,然后输出结构化的 JSON。直接对接 LangChain 生态是它的杀手锏,UnstructuredPDFLoader 装完就能用,返回的 document 对象天然包含元素类型和元数据,非常适合做 RAG。我自己实测下来,它对常规商务文档的识别效果不错,但在复杂表格上依然有识别错误,偶尔会把表格列名和数据搞混。所以用它的时候,建议把输出结果做一层"人工抽检"——抽取 5%-10% 的文档检查解析质量,建个质量基线。
deepdoctection 则走了另一条路:基于目标检测模型做版面分析,能识别标题、文本、表格、图表、列表等区域,还能同时做表格结构识别和 OCR。它的准确率在学术论文、财报等规范文档上表现很好,但对硬件有要求,推理时需要 GPU,纯 CPU 跑会慢得让人怀疑人生。marker 是一款开源 PDF 转 Markdown 的工具,效果相当惊艳,尤其是对双栏论文、带 math 公式的文档,它能把排版还原成带标题层级和表格语法的 Markdown——这种输出格式对 LLM 极其友好,因为模型在训练阶段见过海量 Markdown,理解起来最顺畅。
这些重量级引擎的共同问题是:重。部署复杂、依赖多、单次解析耗时高,不适合做"每个 PDF 请求都跑一遍"的实时路径。我通常的做法是:离线批量解析 + 缓存结果,线上 Agent 只读缓存后的结构化数据;如果遇到缓存未命中的新文档,再走异步解析任务,而不是让 Agent 卡在那里等 30 秒。
2.4 实测对比:一份 50 页技术文档的解析表现
为了不拍脑袋选型,我拿一份 50 页、包含多级标题、三个表格、两张图表的技术文档做了个横向对比,结果整理如下:
| 方案 | 单次耗时 | 文本顺序准确率 | 表格还原度 | 坐标可追溯 | 部署成本 | Agent 衔接成本 |
|---|---|---|---|---|---|---|
| pypdf | 0.4s | 中等,双栏错乱 | 低 | 不支持 | 极低 | 低 |
| pdfplumber | 6.2s | 较好,单栏优秀 | 中(规则表格) | 支持字符级坐标 | 低 | 中 |
| PyMuPDF | 1.1s | 较好,速度快 | 低(需二次加工) | 支持块级坐标 | 低 | 中 |
| unstructured | 9.8s | 好,元素类型丰富 | 中 | 部分支持 | 中 | 极低(原生对接) |
| marker | 22.5s | 优,Markdown化 | 高 | 弱 | 中高 | 低(直接喂MD) |
这组数据不是要证明谁最好,而是说明一个关键结论:没有银弹。不同场景应该走不同的方案组合。你用 pypdf 追求速度,就得接受它在复杂版面上的低还原度;你用 marker 追求质量,就得为 20 多秒的耗时设计缓存策略。选型的核心是搞清楚自己的业务到底卡在哪一环节。
3. Agent 框架里的 PDF 工具选型:从现成 Loader 到自建 Tool
3.1 现成 Loader 的问题:为什么推荐"先结构化再交给 Agent"
现在主流 Agent 框架(LangChain、LlamaIndex、自研 Function Calling 框架)都有现成的 PDF Loader,很多人图省事直接拿过来用,结果项目跑起来后问题一堆。LangChain 的 PyPDFLoader 本质就是调 pypdf,拿到的是一页一页的纯文本,既不保留版面结构,也不区分表格和正文。对内容简单、排版规整的文档够用,但一遇上杂志、财报、论文这种重排版文档,喂给 Agent 的信息质量就大打折扣。
我踩过一次很典型的坑:给一个投研助手接 PDF Loader,财报里的"营业收入"数字在表格里和正文里被提取成了两个来源,模型在做财务指标对比时,把两列不同年份的数据算成了一列,结果差了一倍。排查了半天才发现是 Loader 把表格线性化时把列顺序搞乱了。从那以后我定了个规矩:PDF 文件必须先走独立的解析服务做结构化处理,生成中间表示(比如 JSON 或 Markdown),再进入 Agent 的工作流,绝不直接拿原始文本喂模型。
中间表示我推荐优先用 Markdown 或 JSON。Markdown 的好处是 LLM 原生理解成本最低,而且可读性强,方便调试时人工检查;JSON 的好处是字段语义明确,适合需要程序化访问的场景。如果 PDF 里有大量表格,我会把表格单独提取成 JSON 数组,然后在 Markdown 中以引用方式嵌入——既保留了结构,又不破坏 Markdown 的连贯性。
3.2 OCR 与扫描件:什么时候必须上视觉模型
PDF 世界里有一种"文档中的文档"叫扫描件——本质是一堆图片打包成 PDF,里面根本不存在文本层。对这种文件,所有纯文本解析库都是白搭,必须先过 OCR。传统 OCR 方案有两个选择:Tesseract 和 PaddleOCR。Tesseract 是开源老牌,胜在部署简单、语言支持广,但识别准确率对清晰度很敏感,扫描质量差一点就惨不忍睹。PaddleOCR 的识别率明显更高,尤其是在中文场景下,而且提供版面分析、表格识别等能力,对中文文档用户几乎是首选。但 PaddleOCR 的部署依赖比较多,模型文件体积大,如果跑在 Serverless 函数上可能受限制。
这里有个更现代的路线:直接把 PDF 页面渲染成图片,丢给多模态大模型(GPT-4o、Qwen-VL、InternVL 等)做端到端的理解。这种方式的好处是几乎无损——模型直接"看图",表格、图表、排版全都能看到,不存在文本提取带来的信息损耗。我在测试时发现,给视觉模型一整页财报截图,让它提取营收和利润数据,准确率比传统的"OCR + 表格结构识别"管线还高,而且省掉了一大堆中间步骤。
这个方案的代价是成本和延迟。视觉模型的 token 消耗比纯文本高一个量级,而且页数一多,API 调用时间明显拉长。所以我的经验是:能文本提取就别上 OCR,能 OCR 就别上视觉模型,视觉模型只用来兜底。具体兜底场景包括:扫描件、版面极其混乱的文档、以及文本提取结果里出现可疑乱码时用来二次核验。
3.3 Tool 输出设计:给 Agent 的返回不该是纯文本
很多人在给 Agent 写 PDF 工具时,把函数签名设计成 get_pdf_text(file_path) -> str,返回一长串文本就完事了。这是典型的"传统思维迁移到 Agent 场景"的误区。Agent 工具的输出格式直接影响它的决策质量——你给它的结构越清晰,它犯错的空间越小。
我推荐 Tool 输出至少包含以下几个字段:
content:经过清洗和结构化的文本内容(Markdown 格式为佳)pages:内容对应的页码范围,便于引用定位tables:表格的 JSON 结构或 Markdown 表示,独立字段避免内容中表格被搅乱metadata:包括 PDF 标题、作者、页数、解析时间、解析工具版本warnings:解析过程中的异常警告,比如"第 5 页疑似扫描件,文本层缺失,建议启用 OCR"
另外,工具描述(function description)一定要写清楚"这个工具适合什么场景、不适合什么场景"。比如:
该工具用于提取 PDF 文档的结构化内容。适用于文字版 PDF;如果 PDF 为扫描件或图片格式,请先调用 OCR 工具处理。返回值包含文本块、表格和坐标信息,可直接用于摘要、问答和信息提取任务。
这段描述是给 Agent 看的"说明书",写得好不好直接决定 Agent 会不会在错误场景下乱调用这个工具。我在实际项目里见过太多次 Agent 拿文本提取工具去处理扫描件,返回一堆空字符串还在那"分析"得头头是道——问题根源就在工具描述的边界声明不清晰。
4. 我踩过的坑:表格被拆烂、长文档截断、解析结果不可复现
4.1 表格数据错位:视觉模型成了最后一道防线
表格是 PDF 解析里最顽固的难点,没有之一。原因很简单:PDF 里的表格根本不是"表格",只是一堆文本和线条的坐标堆叠,解析器要自己判断哪些文本属于同一个单元格、哪些行属于同一个表格。规则表格用 pdfplumber 的 extract_table() 能对付,但遇到下列情况基本全崩:
- 无边框表格(只有空格对齐,没有线条)
- 合并单元格
- 单元格内换行
- 跨页表格
- 表格旋转或嵌套
我处理一份供应商资质评估表时,pdfplumber 把第二列的"通过/不通过"全部串到了第三列,Agent 据此判断供应商资质全部不合格,差点触发错误告警。排查时我把 extract_words() 的坐标输出打出来才定位到问题:表头是两行合并单元格,解析器把第二行表头当成了数据行,导致后续列全部错位一格。
解决这个问题我只找到了两条有效路径。第一条是"重模板优先":针对高频出现的固定格式表格(比如年报里的利润表、技术文档里的参数表),做专用的解析模板,用坐标锚点硬编码提取,准确率 100%,速度也最快。第二条是"视觉模型兜底":对不规则表格,直接把页面渲染成图片交给视觉模型做结构化提取,让它输出 JSON。实测下来 GPT-4o 对复杂表格的理解能力非常强,几乎不会错位;如果对成本敏感,可以只对特定页码跑视觉模型,其余页面走文本解析。
4.2 长文档分块策略:别让 Agent 吞下整本书
Agent 处理长 PDF 时,最天真的做法是把全文塞进上下文。除了成本高之外,还有一个隐蔽问题:当文本超过几千 token 后,模型对中段内容的关注度会明显衰减,这跟窗口大小无关,是注意力机制的固有特性。所以必须做分块。
分块策略上,我试过固定长度分块(每 500 token 切一段)、按标题分块、按页面分块、按语义段落分块四种方式。实测结论是:固定长度分块最省事但质量最差——它经常把一句话拦腰截断,或者把表格和它的说明文字分到不同的块里,导致检索时上下文不完整。按页分块对 PDF 天然友好(因为 PDF 本来就有页面边界),但一个问题是一页内容可能只包含半个表格,分块后另一半去别的块里找了,RAG 召回就断片了。我现在的做法是"混合分块":优先按标题层级切分,如果标题层级不可靠,退回按页切分,同时设置一个"跨页连续性检查"——如果当前块末尾是表格或者不完整句子,就尝试把下一页开头的内容拼接进来。
分块之后还有一个配套问题:块与块之间的上下文关联。比如一份合同里"鉴于条款"和"违约条款"相隔 10 页,Agent 回答"如果甲方违约怎么办"时,需要同时引用两处内容。这就涉及到检索策略:对用户 query 做向量检索时,除了找最相似的块,还要把同一章节的相邻块一起拿出来。这套逻辑在 LangChain 里可以用 ParentDocumentRetriever 实现,Self 项目里我更喜欢手搓,因为可控性更强。
4.3 解析结果不可复现:工具链版本锁定的血泪教训
PDF 解析工具迭代很快,今天调的参数明天可能就变了。我遇到过最离谱的事是:同一份 PDF,用 pdfplumber 0.10.0 和 0.11.0 解析,文本顺序完全不一样——0.11.0 改了字符排序逻辑,导致表格文本从"先左后右"变成了"先上后下"。当时 Agent 的 RAG 系统已经跑了一段时间,索引库里混着新旧两种解析结果,检索质量忽高忽低,排查了整整两天才定位到是版本漂移。
这个教训让我养成两个习惯。第一,所有 PDF 解析工具的依赖版本必须在项目里锁定,包括传递依赖,最好用 uv 或 pip-tools 生成完整的 lock 文件。第二,解析结果必须带"解析器版本号"字段,存入元数据;当索引库需要重建时,可以按版本号过滤,避免新旧数据混用。另外建议对解析结果做单元测试:准备一小批"黄金文档"(覆盖标题、表格、图片、扫描件等典型场景),每次升级解析库时先跑一遍黄金测试,比对输出差异,能提前暴露绝大多数兼容性问题。
5. 综合选型建议与一套可落地的组合方案
5.1 不同业务场景下的推荐组合
聊了这么多底层工具的优缺点,最终还是要落到"我到底该用哪个"这个现实问题上。我的建议是不要选单一工具,而是按业务场景做组合。下面直接给结论:
| 业务场景 | 推荐方案组合 | 理由 |
|---|---|---|
| 轻量问答/文本摘要(排版规整) | pypdf 或 pdfplumber + LangChain | 部署简单、延迟低,满足基础文本提取需求 |
| 企业合同/财报分析(复杂表格) | PyMuPDF 预处理 + marker 或视觉模型 | 表格保真度高,可追溯引用 |
| 学术论文/研究报告(双栏、公式) | marker 转 Markdown + 向量检索 | 双栏还原准确,Markdown 对 LLM 最友好 |
| 扫描件/图片型 PDF | PaddleOCR(中文优先)或视觉模型 | 没有文本层时只能走 OCR/视觉路线 |
| 高并发在线服务 | 离线批量解析 + 缓存 + 异步更新 | 避免实时解析的延迟和成本波动 |
这里要强调一个容易被忽视的点:无论选哪种方案,都要在项目初期就设计好"解析中间层"——统一的接口、统一的输出格式、统一的质量监控。这样后续换解析引擎时,只需要替换中间层的实现,Agent 侧的代码完全不用动。
5.2 一套最小可用实现:从 PDF 到 Agent 可消费的结构化文档
最后给出一套我实际用过的参考实现,技术栈是 PyMuPDF + pdfplumber + LangChain,核心思路是"快路径 + 慢路径"结合。
先定义统一的数据结构:
python复制from dataclasses import dataclass, field
from typing import Optional, List
@dataclass
class PDFBlock:
page: int
bbox: tuple
text: str
block_type: str # title | paragraph | table | image
@dataclass
class ParsedPDF:
source: str
blocks: List[PDFBlock]
tables: List[dict]
markdown: str
metadata: dict = field(default_factory=dict)
然后实现解析函数:
python复制import fitz # PyMuPDF
import pdfplumber
def parse_pdf(path: str, use_fast_path: bool = True) -> ParsedPDF:
blocks = []
tables = []
markdown_parts = []
if use_fast_path:
# 快路径:PyMuPDF 提取带坐标的文本块
doc = fitz.open(path)
for page_idx, page in enumerate(doc, start=1):
for block in page.get_text("blocks"):
bbox, text = block[:4], block[4]
blocks.append(PDFBlock(
page=page_idx,
bbox=tuple(round(x, 1) for x in bbox),
text=text.strip(),
block_type=classify_block(text)
))
# 简洁起见,这里省略 markdown 拼装逻辑
else:
# 慢路径:pdfplumber 提取表格
with pdfplumber.open(path) as pdf:
for page_idx, page in enumerate(pdf.pages, start=1):
extracted = page.extract_tables()
if extracted:
tables.extend(
{"page": page_idx, "data": table}
for table in extracted if table
)
return ParsedPDF(
source=path,
blocks=blocks,
tables=tables,
markdown=build_markdown(blocks, tables),
metadata={"parser_version": "1.2.0", "parsed_at": time.time()}
)
build_markdown 这步我建议结合具体业务来写:标题用 #/##,段落直接输出,表格转成 Markdown 表格语法。如果表格太大,可以用"表格引用 + 独立 JSON"的方式,避免 Markdown 里塞一张长表。
Agent 侧接这个解析结果时,不需要把所有 blocks 都丢给模型,而是做三步:
- 先让 Agent 判断用户意图:是要全文摘要、还是要查某个具体指标。
- 如果是全文摘要,把 Markdown 整体送入;如果是查指标,先跑一次关键词/向量检索,只把命中的 page 和相邻 page 送入。
- 将解析结果的 metadata 一并返回给 Agent,让它能在回答中自然引用"根据第 12 页的表格数据……"——这一句引用是整个系统"可信度"的关键。
坦白说,这套方案在 80% 的商务文档场景下都够用,真正的长板是简单、可控、出了问题容易排查。如果后续业务量上来,再逐步替换成 marker 或视觉模型的重型方案。
5.3 后续可以扩展的方向:缓存层、增量更新与多模态路由
先说说缓存层。PDF 解析是重 CPU 操作,同一份文档反复被 Agent 解析纯粹是浪费。我在项目里加了一层基于文件哈希的缓存:PDF 文件上传后算一下 SHA-256,命中缓存直接返回解析结果,没命中才走解析流程,解析完顺便把结果写入缓存。缓存键用哈希而不是文件路径,这样同一份文档换个文件名上传也能命中。存储可以用 Redis(快,适合频繁访问)或者对象存储(便宜,适合大文档)。实测下来,加缓存后 80% 的重复请求延迟从秒级降到毫秒级。
然后是增量更新策略。如果 PDF 文档会被定期更新(比如周报、月报、法规文件),每次全量重新解析成本太高。一个通用做法是比对文档版本号或更新时间,只在版本变化时触发重新解析。如果 PDF 内部是"追加式"更新——比如财报是往同一个文件里追加新页——可以尝试"按页增量":保留旧页面的解析结果,只新增页面的解析结果。这个方案在技术上可行,但实际中 PDF 的修改往往会导致页码整体偏移,所以稳妥起见,我还是建议版本变了就全量重建,别省这个成本。
最后是多模态路由。当解析工具链越来越复杂,你会发现不同结构类型的页面应该走不同路径:纯文本页走 PyMuPDF,表格密集页走视觉模型,扫描页走 OCR。一个"多模态路由"层可以根据页面内容自动分流,把每页发送到最合适的解析通道,再合并结果。这个方向适合文档类型庞杂、单类工具搞不定的场景,属于锦上添花的优化能力。如果团队资源紧张,先用 5.2 的统一方案跑通,等出现明确瓶颈再上路由也完全来得及,不用一开始就追求完备。
