1. 这个项目解决的究竟是谁的痛
先说结论:我们做了一个叫mdskill的开源项目,核心能力是把 PDF、Word、PPT、Excel、图片、网页、EPUB 这些乱七八糟的文件格式,在毫秒级内转成干净的 Markdown,并且自带一套标准 Skill 声明,Agent 框架只要按约定去调用就能用。项目在 GitHub 开源两周,拿到了 20.4k 星星,前两天刚过 21k。
这个数量级对于工具类项目来说不算小,但你要问我最得意的是什么,还不是 star 数,而是终于把“文档转 Markdown”这件事从“能用”做到了“好用”。过去大家用 Pandoc 也好,用各种在线转换器也好,最大的痛点是格式一复杂就翻车:PDF 扫描件识别成乱码、Word 里的多级列表转出来只剩缩进、Excel 合并单元格直接碎掉、PPT 里的文本框位置全乱。这些问题在普通文档场景顶多是难看,但在 AI 场景里就是致命的——你把一堆乱糟糟的文本喂给大模型,Retrieval 检索出来的片段没法看,Agent 读文档读出来的全是残缺信息,后面的分析、总结、问答全被污染了。
所以这个项目从一开始就不是冲着“做一个转换器”去的,而是奔着“给 RAG 和 Agent 生态补一块最底层的基础设施”去的。适合谁看?三类人:
- 正在做知识库、RAG、Agent 应用的开发者,需要稳定的文档解析链路;
- 经常处理多格式文档、想统一成 Markdown 管线做自动化的工程师;
- 对开源项目冷启动、增长策略感兴趣的同学。
我尽量把技术选型、踩坑过程和增长逻辑都写明白,你拿去能直接用,遇到问题也知道去哪查。
2. 整体架构:一条“解析-标准化-渲染”的主链路
2.1 四层架构设计
项目整体分了四层,这一点从开源第一天就没变过,后面所有迭代都是在各层内部替换实现,外层接口一直很稳定。
第一层是输入层,也就是接入方式。我们提供了三个入口:Python SDK、CLI 命令行、HTTP API。SDK 面向开发者在代码里集成,CLI 面向日常命令行操作,HTTP API 面向部署成独立服务。三个入口共享同一套核心逻辑,只是包了一层不同的壳。早期的版本只有 SDK,后来社区里有人提需求说想在服务端跑,我们就加了 FastAPI 封装,结果这个 HTTP 接口成了很多 Agent 项目接入的首选方式。
第二层是解析层,这也是最重的一层。每种文件格式对应一个解析适配器,PDF 有 PDF 适配器,Word 有 Word 适配器,以此类推。适配器的职责是读源文件、抽取内容、填充统一的中间表示。你可以把这一层理解成“翻译官”——每种语言配一个翻译,但翻译完之后都写成同一种文字。
第三层是格式化层,负责处理中间表示里的内容结构和语义关系。比如判断这个段落是不是标题、标题是几级、这个表格的表头在哪一行、代码块的语言标注应该是什么。这层做的是“理解内容”的工作,也是纯规则处理和 LLM 处理的分界线。
第四层是渲染层,把格式化后的中间表示输出成标准 Markdown。这一层相对薄,但涉及的细节非常多:Markdown 的标题语法、表格的管道符对齐、代码块的围栏、图片引用的相对路径,都需要按规范处理干净。
2.2 为什么必须引入中间表示(IR)
很多同类项目第一步就走错了,直接在做“Pandoc 式”的格式对格式硬转,PDF 解析到一半就开始拼 Markdown 字符串。这种做法的问题在于:一旦某个格式的解析逻辑要改,或者要新增一种输出格式(比如从 Markdown 改成 JSON),就得把整条链路重写。
我们引入了一个叫IRDocument的中间表示,内部是一个层级化结构,包含block、heading、paragraph、table、image、code、list、quote这些元素。每个元素都带元数据,比如标题层级、表格行列坐标、图片地址、代码语言。解析层负责往这个结构里塞内容,渲染层负责把它倒腾成 Markdown,两边互不干扰。
这个设计带来的实际好处很直接:我们后来想加对 HTML 的输出支持,只写了一个新的 renderer,两百行代码就搞定了,解析层完全没动。另外,调试的时候你可以把任意文件解析后 dump 成 JSON,漏了什么一目了然,不用靠肉眼去猜 Markdown 哪里不对。
2.3 选技术栈时的取舍
后端语言选了 Python,我知道有人看到 Python 就会担心性能。但坦白讲,在这个场景下 Python 不是性能瓶颈,解析库才是。Python 生态里恰好有最好用的一批解析库:PDF 用 PyMuPDF,DOCX 走 python-docx 遍历 XML,PPTX 用 python-pptx,XLSX 用 openpyxl,HTML 用 BeautifulSoup,图片 OCR 用 PaddleOCR。这些底层全是 C/C++ 实现,Python 只是胶水层。
真正的性能瓶颈在 IO 和复杂格式的解析逻辑上,而不是 Python 解释器本身。我们做过压测,一个 2MB 的 PDF,文本层完整、结构不算复杂,解析加渲染 300 到 500 毫秒搞定。而一个 40 页全部是扫描图片的 PDF,OCR 单页就得 1 到 2 秒,谁来了都一样。所以我们在首页就写清楚了性能边界:纯文本类文档,毫秒级;扫描版和复杂排版文档,秒级。别吹牛,用户实测后失望比什么都伤。
3. 核心解析链路:不同格式怎么吃掉
3.1 PDF 的两种路径:文本层提取与 OCR 兜底
PDF 是文档转换里最复杂的格式,没有之一。因为它本质上是一堆图形指令的集合,所谓“文本”在 PDF 里可能只是一个绘制字符的操作,没有语义信息,没有段落结构,没有表格边界。
我们的 PDF 适配器采用双路径策略。第一路径尝试从页面提取文本层,用 PyMuPDF 把每个页面上的文字块、字体大小、位置坐标抓出来,然后根据坐标聚类重建段落和标题。这个方案对数字化生成的 PDF(比如 LaTeX 编译、Word 另存的 PDF)效果非常好,能够保留标题层级、列表缩进和大致的段落关系。第二路径是 OCR 兜底,检测到页面没有文本层,或者文本层内容过少(比如一张图片占满整页),就自动截图交给 OCR 处理。
两条路径的结果都会映射到同一套 IR。这里有个关键细节:OCR 结果的坐标信息比文本层的坐标信息更精确,因为我们可以拿到每个字的包围盒。利用这些坐标,把位于同一水平线的文字合并成段落,把垂直方向上对齐的多行合并成表格区域,准确率能提升一大截。很多人 OCR 出来是一坨纯文本,就是没做坐标聚类这一步。
3.2 DOCX/PPTX/XLSX 的“解包”思路
Office 系列格式其实都是 zip 压缩包,里面装着一堆 XML 文件。所以处理 DOCX 的思路不是“读文件”,而是“解压然后遍历 XML 节点树”。
DOCX 的 Word 文档里最麻烦的是分节符和样式层级。很多 Word 文档的标题不是用“标题 1”“标题 2”样式写的,而是手动加粗、放大字号、居中排的,这种情况下我们就得根据字体大小和是否加粗来判断标题级别。具体规则是:字体大小超过正文 2pt 以上且加粗,记为标题;然后用字体大小数值排序,确定各级别之间的级差。这套启发式规则在测试集上准确率有 86%,剩下的 14% 基本是人为排版混乱的文档,我们默认降级为普通段落。
PPTX 的难点在版式坐标。幻灯片上的文本框是绝对定位的,我们可以拿到每个文本框的 left、top、width、height,然后按从上到下、从左到右的顺序排列文本块。但如果同一个页面上有主标题、副标题、正文、注释,纯粹靠坐标排序会乱。我们的做法是先按 top 值分组,同一行内多个文本框合并为一个区块,再按 left 值排序;同时判断字号,页面上最大的字号作为 slide title,其余作为内容。
XLSX 则要简单一些,按工作表拆分成多个表格块,每张表的第一行作为表头,后面的行作为数据行。合并单元格是个大坑,openpyxl 读到的合并区间只在左上角有值,其余位置为空。我们的处理方法是把合并区间内所有单元格都填充左上角的值,并且记录一个colspan和rowspan属性,渲染成 Markdown 的时候虽然丢了合并信息,但至少不会出现表格错位。
3.3 图片和扫描件怎么处理
单张图片的转换,我们直接走 PaddleOCR 做文字检测和识别,输出的文本块同样做坐标聚类,然后根据文本块的布局塞进 IR。这个功能看起来简单,实际上挺多人刚需——经常有人拿一张截图、一张商品图、一张发票照片让你转成可检索的 Markdown。
对于扫描版 PDF,我们做了一个小优化:先用 PyMuPDF 把每一页渲染成高分辨率图片,然后在 OCR 之前做一个预处理,包括灰度化、二值化、去噪。PaddleOCR 自己也能做图像预处理,但我们发现先手动做一遍可以显著降低文字检测的漏检率,尤其是带背景色的页面。
图片里如果有表格,OCR 出来是纯文本流的。我们试过各种方案——从简单的基于制表符对齐的启发式,到用目标检测模型定位表格区域——最终线上跑的还是启发式为主,因为速度快且对常见表格够用。具体做法是:检测文本块之间的垂直间距和水平对齐关系,相邻且水平对齐的文本块构成一列,多列组合成一个表格。对于复杂的跨行跨列表格,这个方案会翻车,但在“毫秒级”这个约束下是合理取舍。
3.4 表格识别:最难的骨头
在整个项目里,表格识别消耗的开发时间最多。Markdown 的表格语法是扁平的管道符结构,行列必须完全对齐,一旦解析出错,渲染出来就乱七八糟。
我们的表格识别分成两条路径。数字化文档(Word、PPT、网页)走的路径是“读取结构”:查看 XML 里是否自带表格定义,如果有就直接按行列读取。PDF 则复杂得多,我们依赖坐标聚类来判断表格区域。一个页面上有一组文本块,它们的垂直坐标呈规则的等距分布,水平坐标也有明显的分列边界,就可以猜测这是一个表格。接下来,列边界线会根据多个行的坐标取中位数来对齐,这样能容忍个别单元格的内容过长。
对于跨页表格,我们做了一个后处理步骤:检测到连续两页上有相同的表头内容时,自动把后一页的表格数据合并到前一个表格里,同时丢弃重复的表头。这个功能在 PDF 格式的财报、学术论文里非常实用。但不得不承认,遇到无边框、内容重叠、单元格内容自由漂浮的表格,规则系统就无能为力了。所以我们也保留了接口,允许用户在参数里指定use_llm=True,把表格区域截图发送给视觉模型做结构化抽取。代价是慢,但准确率从 60% 提到 95% 以上。这个选项给需要极致准确率的场景兜底。
4. 毫秒级转换背后的工程手段
4.1 性能目标是怎么定的
项目立项的时候,我们给性能定的目标不是“尽量快”,而是“快到感觉不到”:绝大多数 10MB 以下的文档,从提交到拿到结果,应该在 1 秒以内;常见的纯文本类文件(Markdown、HTML、纯文本 TXT)应该控制在 300 毫秒以内。为什么这么定?因为我们发现 Agent 场景对耗时极其敏感——一次 Agent 任务可能需要同时检索十几个文档,如果每个文档转换耗费 5 秒,整体延迟就会爆炸。
在实践里,这也是一个定位问题:我们不想做“高质量离线批处理”工具,那种场景用 Pandoc 或者大的商业解析器就够。我们想做的是“在线服务内可调用的实时转换器”,所以延迟必须压到用户无感的水平。
4.2 缓存策略:同一文件绝不解析两遍
毫秒级的一个关键手段是缓存。我们做了两层缓存:
第一层是内容寻址缓存。每个入站文件先算一个 SHA-256 哈希,如果这个哈希在缓存库里存在,直接返回上一次的 Markdown 结果。这个缓存对重复提交同一文件的情况极其有效,知识库更新场景里 90% 以上的重复请求都能在这里命中。缓存的存储可以选内存(适合单机部署)、Redis(适合多实例)或者磁盘(适合持久化)。
第二层是局部缓存。对于大文件,我们按章节或页码切分,每个片段单独计算哈希。如果文件中间只有一页更新,只重新解析那一页,其余片段直接复用。这个优化的场景是:客户经常拿着同一个 PDF 的修订版本来跑,只是改了某几页的措辞,全量重新解析就是纯浪费。
缓存还有一个细节:键值要带上解析参数。同样的 PDF,是否开启 OCR、是否使用 LLM 增强,结果完全不同,所以哈希键要把配置参数序列化进去,否则会命中错误的结果。
4.3 并行调度与资源隔离
Python 的全局解释器锁(GIL)在 CPU 密集场景是个大问题。虽然 PyMuPDF 等底层库在解析时能释放 GIL,但 OCR 和图像处理不会。我们的做法是使用进程池而非线程池来处理大文件,每个 worker 进程独立持有解析器实例,进程间不共享内存。
资源隔离方面,我们单独分了三条队列:小文件队列(100KB 以下)、常规文件队列、OCR 大文件队列。小文件优先处理,因为它们的延迟最敏感;OCR 队列单独限制并发数为 2,防止一次性把 CPU 吃满导致其他请求全部超时。
实际上线后,我们发现每个进程的常驻内存大概在 300 到 500MB,主要是因为 PyMuPDF 和 OCR 模型会缓存字体资源和模型权重。所以部署指南里我写了一条:单机建议 4GB 内存起,每个 worker 配置 512MB 内存限制。这个数字是压测压出来的,不是随便拍的。
4.4 落地效果数据
写这篇文字的时候,我翻了一下压测记录。测试环境是 4 核 8GB 的轻量云服务器,单 worker 进程:
- 一个 224KB 的 HTML 文章,解析加渲染 120ms;
- 一个 1.8MB 的 DOCX(包含 32 个表格),450ms;
- 一个 6.5MB 的 PPTX(50 页),800ms;
- 一个纯文本层的 10MB PDF(120 页),1.2s;
- 一个 40 页扫描 PDF 开启 OCR,2 分 20 秒——这个数据没法看,但 OCR 本身就是重资源操作,无法苛责。
这些数字在 README 里都有,我们没有做任何夸大。用户自己跑一遍,发现和文档描述一致,信任度就建立起来了。开源项目最怕货不对板,第一波用户的差评能毁掉一个项目。
5. Skill 机制:让 Agent 像调工具一样调文档转换
5.1 什么是 Skill,和 Function Calling 有什么区别
Agent 开发这两年火得不行,但很多人被“Agent 框架”这个概念绕晕了。本质上,Agent 和大模型的区别就是:Agent 可以调工具、读文件、写文件、执行动作。而 Skill(技能)就是封装好的工具单元,它告诉大模型:“嘿,我这里有一个功能,你什么时候需要就调用我。”
和 Function Calling 相比,Skill 的概念更宽泛一些。Function Calling 是 OpenAI、Anthropic 这类大模型 API 层面的结构化函数声明,而 Skill 更多是 Agent 框架层面的能力封装。一个 Skill 可以包含多个 Function,可以带配置文件、提示词模板、甚至脚本逻辑。你可以把 Skill 理解成“带说明书的功能包”,Agent 框架通过解析 Skill 的声明文件,决定在哪些场景下触发这个技能。
项目叫mdskill本身就是这个定位——它不是一个普通的库,而是可以直接挂到 Agent 上的文档转换技能。
5.2 mdskill 的 Skill 声明长什么样
Skill 的声明文件是一个 JSON 格式的 manifest,我们这样设计:
{ "name": "doc_to_markdown", "description": "将任意支持的文件格式转换为结构化Markdown,适用于文档解析、知识库入库、RAG检索前的预处理", "version": "1.0.0", "parameters": { "file_path": { "type": "string", "description": "待转换的本地文件路径或URL", "required": true }, "ocr": { "type": "boolean", "description": "是否启用OCR识别扫描件", "default": false, "required": false }, "use_llm": { "type": "boolean", "description": "是否启用大模型增强表格识别", "default": false, "required": false } }, "execution": { "entry": "tool.py", "callable": "convert_to_markdown" }, "when_to_use": [ "用户上传或提供了文档文件", "需要从非结构化文档中提取文本内容", "RAG知识库入库前的文件预处理", "需要将PDF转成可编辑的文本格式" ] }when_to_use字段是我特别加的设计。传统 Function Calling 声明里只有 description,大模型靠这个描述自己悟;但实际测试中,模型经常会在不该用的时候调函数。加了when_to_use之后,Agent 框架可以用一种轻量级的规则匹配做一次“要不要调”的预判,减少无效调用。这个思路后来被好几个 Agent 框架吸收,算是我们在开源社区里做的一点小贡献。
tool.py内部再对参数做校验和映射,调用 SDK 的核心转换逻辑。这里有个细节:我们强制要求返回结果里带上source_type和confidence字段——前者告诉 Agent 这是什么源格式转换的,后者说明解析置信度。Agent 可以在后续处理中根据置信度做判断,如果置信度低于 0.6 就提示用户“文档较复杂,建议人工检查”。
5.3 接入主流 Agent 框架的两种方式
第一种方式是作为 MCP 工具接入。MCP(Model Context Protocol)现在几乎是 Agent 工具的标准协议了,我们把整个转换能力封装成一个 MCP server,任何支持 MCP 的客户端(很多主流 Agent 框架都已经原生支持)直接配置一下地址就能用。
第二种方式是作为插件包导入。如果 Agent 框架支持自定义插件或者 Actions,可以直接把mdskill作为本地包安装,在代码里显式注册工具。这种方式适合对延迟要求更苛刻的场景,不需要走网络请求。
社区里已经有几篇文章在写怎么把它接到不同的 Agent 框架上了。常见的做法是:让 Agent 在检测到用户上传文件时,先调用doc_to_markdown把文件转成文本,然后交给向量化流程做 embedding,存进知识库;问答阶段,Agent 检索到相关片段后,再回传原始 Markdown 给大模型做答案生成。
5.4 一个完整的 Agent 调用链路示例
我举个例子。假设你搭了一个知识库 Agent,用户上传了一个 PDF 格式的行业报告,然后问:“报告里关于 AI 基础设施的投入预算,2025 年比 2024 年增长了多少?”
传统做法是:先用 PDF 库瞎抽一段文本,做 embedding,再让大模型看检索结果,很可能因为 PDF 里的表格数据被抽烂,模型根本找不到那张对比表。
用mdskill的处理链路是:
- Agent 收到上传文件,调用 Skill:
convert_to_markdown(file_path="report.pdf", ocr=false); - 转换器返回结构化的 Markdown,表格部分被干净地转换为 Markdown 表格;
- 切片器按照标题层级切块,把表格单独切片;
- 向量化并入库;
- 用户提问时,检索系统召回包含“AI基础设施”“预算”“2025”“2024”的表格片段;
- 大模型直接阅读 Markdown 格式的表格数据,准确算出同比增长率。
在这个链路里,mdskill做的事情最基础但最关键——如果这一步输出的是垃圾,后面所有步骤都会受到污染。这也解释了为什么 Agent 开发者会成为这个项目最忠实的用户群体。
6. 开源冷启动:20.4k Star 不是凭空掉下来的
6.1 发布前的准备
很多人以为开源项目就是把代码扔到 GitHub 然后等星标,实际完全不是。发布前两周,我们做了一个清单:
第一,README 必须能把项目卖出去。我们把核心亮点放第一屏:支持哪些格式、转换效果对比、性能数据、一两张真实的转换前后对比截图。开发者见多了夸大宣传,所以对比截图也很关键,我们特意挑了 PDF 和 PPTX 里比较“难看”的例子放上去——文档里有公式、有复杂表格、有多列排布,让用户看到几个场景下行不行。
第二,demo 必须能跑起来。README 里的安装命令、示例代码,我们逐条在干净环境里测试过。如果你发布后第一个 issue 就是“README 的命令跑不了”,那热度直接减半。
第三,准备一套 issues 模板。Bug 报告模板、功能请求模板、帮助模板,引导用户提交有效反馈。这点容易被忽略,但没有模板的仓库很容易被灌水,高质量的讨论出不来。
6.2 两周的传播节点
发布第一天,我们把项目推到了几个技术社区和开发者群,当天涨到 800 星。这个阶段的主要来源是朋友圈和微信群,一帮做 AI 应用的人正好被文档解析折磨过,看到“毫秒级转 Markdown”完全对上了需求。
第二三天是转折点。有一位做 RAG 工具的技术博主写了一篇测评文章,标题大概是“我试了一下最近刷屏的文件转 Markdown 项目”,文章里对比了几个主流项目,列举了转换效果和性能差异,结果那条帖子的流量直接把我们带到了 3k 星。第三天开始,陆续有海外用户和 AI Agent 开发者加入,GitHub 趋势榜把我们推到了 Python 分类的日榜第一,接下来一周基本是每天 1k 到 2k 的增长。两周整的时候冲上了 20.4k。
坦白讲,这个数字有运气的成分——时间点对了,正好赶上 Agent/RAG 这波热度;功能对了,解决的是真实痛点。但运气来的时候你得接得住,否则流星很快就滑过去了。我们那两周几乎每天发两三个 release,修 issue 的速度维持在 24 小时以内,新功能建议被快速采纳。这种节奏让社区觉得“这个项目是活着的”,参与感一强,口碑就会滚动起来。
6.3 社区贡献和它带来的正反馈
开源项目最稀缺的不是代码,是“有人愿意帮你发现问题”。从发布到现在,我们收到了 210 多个 issue,其中真正的 bug 大概占 60%,剩下的是需求建议和性能反馈。社区贡献者的 Pull Request 截至目前有 34 个,合并了 27 个,涵盖了 EPUB 解析适配器、MCP server 封装、Windows GUI 前端等我们原计划里排期很靠后的功能。
社区带来的另一个意想不到的收获是生态联动。有人把mdskill接入了自己的知识库项目,有人用它在 Coze 里做工作流,还有人把它和 Markdown 编辑器配合做成了一条“PDF→Markdown→Typora”的输入管线。这些用例反过来启发了我们的路线图——新增了对 EPUB 的支持、增强了 Markdown 表格输出的兼容性、提供更细粒度的切片回调。开源就是这样,你做的东西被更多人用,更多人的需求会引导你往更好的方向走。
7. 常见问题与排查实录
7.1 PDF 转出来是空白内容?
这几乎是新手必踩的坑。如果你拿到一个 PDF,转出来是空字符串或者只有一页空白,大概率是:这个 PDF 是个纯扫描版,没有文本层,而你忘记开启 OCR 参数了。
排查方法:先用 PDF 阅读器打开文件,试着用鼠标选择文字;如果选不中三五个字符,说明没有文本层。此时调用接口时传入ocr=true就可以了。如果是批量任务,可以在代码里自动检测文本层的字符数并决定是否回退 OCR,这样比每次强制 OCR 高效很多。
还有一种情况是 PDF 设置了权限密码,禁止复制文字。这种文件我们目前的做法是直接返回错误,提示用户解除密码保护。因为绕过权限保护涉及合规问题,不做为妙。
7.2 表格跨页破行怎么处理?
同一个表格被 PDF 分页切断,是文档转换的高频次难点。转出来的 Markdown 里,表格被硬拆成两半,数据行可能少了几行,表头可能只出现了一次。我们在 3.4 节说过,跨页表头识别能解决大部分情况。如果你转换后仍然出现表格切断,可以先检查源 PDF 的分页位置,如果表格中间恰好有页眉或页脚,自动合并会失败。
临时方案是把 PDF 先用工具拆分成单页,然后手动拼接分页处的表格。但说实话,这个方案体验很差,我们自己也还在持续优化这个场景。如果你遇到的是无边框表格,建议直接开启use_llm=true,让大模型根据语义把断开的表格拼回来,效果比规则硬拼好得多。
7.3 Agent 调用超时怎么办?
Agent 框架调用外部工具通常有超时设置,mdskill的 HTTP API 默认超时是 30 秒。如果你的文档特别大,或者开了 OCR,30 秒确实不够用。
排查从两个方向走:一是检查调用方超时参数,调大到 60 秒以上;二是检查服务端并发情况,如果同时有多个 OCR 任务在跑,新请求会被排队,每个请求实际等待时间可能远超预期。建议在服务端配置里限制 OCR 并发数,并把队列的等待时间计入调用方超时。更进一步,可以把超时策略改成“轮询任务状态”:HTTP 接口先返回一个 task_id,客户端再去查任务结果,这样避免长连接占用资源。这个模式我们也放在 README 的高级用法里了。
7.4 内存占用过高
有些用户在内存只有 2GB 的小机器上跑,遇到 OOM(内存溢出)是正常的。mdskill的默认配置是按 8GB 内存的机器调优的,PyMuPDF 对大型 PDF 会缓存页面对象,OCR 模型权重也占内存不小。
建议按这个配置内存限制:
| 场景 | 推荐内存 | worker 数 | 说明 |
|---|---|---|---|
| 纯文本类转换 | 1GB | 4 | 不加载 OCR 模型 |
| 混合文档转换 | 4GB | 2 | 按需加载 OCR |
| OCR 重度使用 | 8GB+ | 1-2 | 预留模型权重和批量图像缓存 |
另外可以开启环境变量MDSKILL_LOW_MEMORY=1,这会关闭 PyMuPDF 的页面缓存并降低 OCR 批处理大小。代价是速度下降 30% 左右,但能保住服务稳定性。
8. 后续路线图与我的几点心得
这个项目已经稳定运行了几个月,Star 数还在涨,但我心里清楚真正要做的还有很多:更好的公式识别、更稳定的原生化表格结构预测、更轻量的模型部署方案、以及跟更多 Agent 框架的官方适配。这些方向我会继续投入,也会把每一阶段的落地结果同步到开源社区。
回到开头那句话:mdskill本质上解决的不是“格式转换”这个表层问题,而是“让非结构化信息变得可用”这个底层问题。在 AI 应用越来越依赖高质量文档输入的今天,文档解析的准确率和效率会成为 Agent 能力上限的一部分。
我个人在实际开发中最大的体会是:规则、启发式和模型能力之间,不能一味追求用模型解决所有问题。能用规则解决的,绝对不动用模型——不仅因为速度和成本,更因为规则是可解释的,出了问题你知道怎么改。模型是拿来兜底的,不是拿来当主力的。这个取舍原则,无论做开源项目还是做企业内部系统,都值得反复掂量。
最后再分享一个小技巧:如果你要在一个新环境里快速部署mdskill,不要把ocr和use_llm两个高级参数默认打开。先跑通主链路,再按数据情况逐步启用增强选项。这个思路也适用于任何复杂系统——先把最小闭环跑通,再谈优化。