
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 用 PyMuPDFDOCX 走 python-docx 遍历 XMLPPTX 用 python-pptxXLSX 用 openpyxlHTML 用 BeautifulSoup图片 OCR 用 PaddleOCR。这些底层全是 C/C 实现Python 只是胶水层。真正的性能瓶颈在 IO 和复杂格式的解析逻辑上而不是 Python 解释器本身。我们做过压测一个 2MB 的 PDF文本层完整、结构不算复杂解析加渲染 300 到 500 毫秒搞定。而一个 40 页全部是扫描图片的 PDFOCR 单页就得 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_llmTrue把表格区域截图发送给视觉模型做结构化抽取。代价是慢但准确率从 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 的 PPTX50 页800ms一个纯文本层的 10MB PDF120 页1.2s一个 40 页扫描 PDF 开启 OCR2 分 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 工具接入。MCPModel 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 收到上传文件调用 Skillconvert_to_markdown(file_pathreport.pdf, ocrfalse)转换器返回结构化的 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 阅读器打开文件试着用鼠标选择文字如果选不中三五个字符说明没有文本层。此时调用接口时传入ocrtrue就可以了。如果是批量任务可以在代码里自动检测文本层的字符数并决定是否回退 OCR这样比每次强制 OCR 高效很多。还有一种情况是 PDF 设置了权限密码禁止复制文字。这种文件我们目前的做法是直接返回错误提示用户解除密码保护。因为绕过权限保护涉及合规问题不做为妙。7.2 表格跨页破行怎么处理同一个表格被 PDF 分页切断是文档转换的高频次难点。转出来的 Markdown 里表格被硬拆成两半数据行可能少了几行表头可能只出现了一次。我们在 3.4 节说过跨页表头识别能解决大部分情况。如果你转换后仍然出现表格切断可以先检查源 PDF 的分页位置如果表格中间恰好有页眉或页脚自动合并会失败。临时方案是把 PDF 先用工具拆分成单页然后手动拼接分页处的表格。但说实话这个方案体验很差我们自己也还在持续优化这个场景。如果你遇到的是无边框表格建议直接开启use_llmtrue让大模型根据语义把断开的表格拼回来效果比规则硬拼好得多。7.3 Agent 调用超时怎么办Agent 框架调用外部工具通常有超时设置mdskill的 HTTP API 默认超时是 30 秒。如果你的文档特别大或者开了 OCR30 秒确实不够用。排查从两个方向走一是检查调用方超时参数调大到 60 秒以上二是检查服务端并发情况如果同时有多个 OCR 任务在跑新请求会被排队每个请求实际等待时间可能远超预期。建议在服务端配置里限制 OCR 并发数并把队列的等待时间计入调用方超时。更进一步可以把超时策略改成“轮询任务状态”HTTP 接口先返回一个 task_id客户端再去查任务结果这样避免长连接占用资源。这个模式我们也放在 README 的高级用法里了。7.4 内存占用过高有些用户在内存只有 2GB 的小机器上跑遇到 OOM内存溢出是正常的。mdskill的默认配置是按 8GB 内存的机器调优的PyMuPDF 对大型 PDF 会缓存页面对象OCR 模型权重也占内存不小。建议按这个配置内存限制场景推荐内存worker 数说明纯文本类转换1GB4不加载 OCR 模型混合文档转换4GB2按需加载 OCROCR 重度使用8GB1-2预留模型权重和批量图像缓存另外可以开启环境变量MDSKILL_LOW_MEMORY1这会关闭 PyMuPDF 的页面缓存并降低 OCR 批处理大小。代价是速度下降 30% 左右但能保住服务稳定性。8. 后续路线图与我的几点心得这个项目已经稳定运行了几个月Star 数还在涨但我心里清楚真正要做的还有很多更好的公式识别、更稳定的原生化表格结构预测、更轻量的模型部署方案、以及跟更多 Agent 框架的官方适配。这些方向我会继续投入也会把每一阶段的落地结果同步到开源社区。回到开头那句话mdskill本质上解决的不是“格式转换”这个表层问题而是“让非结构化信息变得可用”这个底层问题。在 AI 应用越来越依赖高质量文档输入的今天文档解析的准确率和效率会成为 Agent 能力上限的一部分。我个人在实际开发中最大的体会是规则、启发式和模型能力之间不能一味追求用模型解决所有问题。能用规则解决的绝对不动用模型——不仅因为速度和成本更因为规则是可解释的出了问题你知道怎么改。模型是拿来兜底的不是拿来当主力的。这个取舍原则无论做开源项目还是做企业内部系统都值得反复掂量。最后再分享一个小技巧如果你要在一个新环境里快速部署mdskill不要把ocr和use_llm两个高级参数默认打开。先跑通主链路再按数据情况逐步启用增强选项。这个思路也适用于任何复杂系统——先把最小闭环跑通再谈优化。