- SignalDesk2小时前
背景 我在做文档翻译网站 O.Translator (国外版) 和 商译 AI (国内版) 的时候,需要在浏览器支持 DOCX 、PDF 、PPTX 等不同文件的渲染预览。 项目初期,大部分文件类型预览都是使用开源库,对于 DOCX 文档,我选择了 VolodymyrBaydalka/docxjs 。 这个库做基本展示没问题,但大文件渲染( 20M 以上),会卡,有时候甚至让浏览器崩溃。初期,我只能在外层加门禁限制,比如 XML 超出 500 个或者文件超过 20M ,就不在网页渲染预览,让用户下载。 性能是最大的问题,直接导致很多 DOCX 文档没法在翻译完第一时间展示给用户,这个体验让我不太满意。其次,对于 XML 对象解析和 Style 族渲染支持度不够。 所以,早在一年前,我就有重写 DOCX 渲染库的想法,大体思路,整个架构基于 Worker-first 解耦,将耗时操作放到 Web Worker 中,比如解析、布局计算等,主线程只负责消费计算结果。 这样做,除了优化性能,还有一个考虑,引擎端输出结构化的布局语义,渲染端可以用 DOM ,可以用 Canvas ,甚至可以移植到小程序和 App ,这些长远规划在架构上要支持。 这个项目复杂度高,如果手动实现,难度很大。 我想过基于 VolodymyrBaydalka/docxjs 来重构,但它的架构和我的设想差别较大,也放弃了。 25 年尾声,我开始在主站小范围使用 AI 编程,从半信半疑写一些工具函数,到功能模块重构,我对 AI 的 coding 能力认可度不断提高。26 年 3 月,GPT-5.4 发布,我感觉是时候动手了。 我使用的主力模型是 GPT-5.4 和之后的更新版本。 开始动手 26 年 4 月开始动手。 初始架构 按我之前的想法,给 AI 设计了基于 Worker-first 布局的架构准则,有几个主要模块:解析、布局计算、渲染。 Worker 负责解析、测量、分页和页面数据; Viewer 负责请求页面; Renderer 只消费页面模型及资源包; 缺能力时应扩展 Worker 输出,不能偷偷退回旧 DOM 路径。 这是第一次也是最基本的一次架构定义:从“HTML 渲染器附带分页”变成“布局引擎产生页面,DOM 只是一个渲染消费端”。 这个阶段,我能做的不多,大部分时间是在跟进度,以及审查 AI 划分的能力抽象和代码模块。这时的主力模型应该是 GPT-5.4 ,有时候会偏离架构设计规则。 第一次大规模测试 5 月中旬,整体架构编码完成,开始测试。 我设计了自动化测试 Skills ,这套 Skills 主要工作是用渲染引擎和 Word 打开同一个 DOCX 文档,对每一页截图做“相似度计算”(通过 Python 脚本),如果某一页的相似度较低,就提交人工审核,我会对比所有得分低的页面,评估修复优先级。 这个阶段,主要是用真实 DOCX 文档来测试解析引擎的稳定性,以及 XML 标签识别的覆盖面。 测试一段时间,大部分问题集中在分页计算,引擎内部混杂了: twips 、pt 、px 、CSS 字符串; 浏览器测量结果; 针对某一页、某一种表格的分页阈值和补丁; 表格、段落、图片各自维护部分几何判断。 重构:核心是分页计算 5 月下旬,我重构了分页单位计算,引入: canonical layout units ; Word 行盒; 明确的段落、表格行和续页模型; 页面模型中的几何与来源信息。 6 月又逐步把表格文本、对象、sourceRef 、合并单元格、图片和段落语义从巨型分页函数中拆到各自模块。 Renderer 不再通过 getBoundingClientRect 、CSS reflow 或 tab 二次排版修正分页。 这次重构的本质是: 把“浏览器看起来差不多”升级成“引擎内部有稳定、可验证的 Word 几何语义”。 不能出现“场景化实现和补丁”。 很快,我又遇到了新问题:修复一个问题,会导致新的问题出现,或者会让已经修复的问题再次出现。 同时,这样的测试和修复持续一段时间后,包体积在不正常的增大。 排查发现,是因为 AI 做了很多场景化补丁修复,我意识到,虽然整个骨架没问题,但里面的填充物不对。 再次重构:单一分页内核 7 月中下旬的这次重构,主要还是围绕分页器,但重点转移到,从分散分页器改成单一分页内核与渐进式 sealed pages 。 因为我发现,即使单位统一了,段落、表格、浮动对象仍可能分别决定: 当前页是否放得下; 是否换列或换页; 是否拆分; 如何记录 overflow ; 是否重新计算前缀内容。 于是出现了“多个分页权威”,很容易导致边界漂移。 形成“多条决策路径”,在复杂系统中,可能是 AI 最常出现的问题。 第三次重构:对象驱动的核心模块 经过前几轮重构,基础框架已经完成,但代码里仍有历史包袱: 根据正文内容、页码、短尾等特征做场景判断; 表格和图形存在自己的隐式分页路径; 页眉页脚、批注、资源可能在 Renderer 侧重新构建; 这个阶段,我开始明确设计针对 XML 标签解析的对象库,后面延伸到 Style 规则族等。 在 skill 中明确规定 Owner 单一主责、禁止多重决策。 最终形成的生产主链是: OOXML/资源解析 → 类型化语义 → 流对象及候选 → 单一分页内核 → committed/sealed page → Renderer 绘制 现在基于对象驱动的解析引擎,我认为是一个合理的设计。这个架构,给了 AI 一份高效的蓝图,避免生成多重决策,避免之前出现的各个模块各自为战问题。 解释上面这句话,把标签、规则等等进行对象化之后,让 AI 修复一个问题,一定先落到一个对象 Owner 上,可能是 XML 标签对象,可能是 Style 对象,之后的修复都会在这个对象内部,不断发现问题不断改,只会让这个对象更“准确”。 发布第一版 9 月底,发布了第一版到生产。 基本达成我的初始构想: Worker-first :文档解析过程,不会造成页面卡顿; 渐进解析渲染 :即使很大的 document.xml 文档,打开速度也很快; 布局引擎与渲染解耦 :未来可拓展; 基于对象库的设计 :后期持续优化、更新对象规则,会简单清晰很多。 实际应用效果 以下是两个翻译预览的 Demo ,其中 DOCX 文档预览就是用的这一套渲染引擎。 O.Translator - DOCX 文档翻译预览(国外版) 商译 AI - DOCX 文档翻译预览(国内版) To be continued 这个项目比我预期多用 2 个月完成。我对 AI 的能力,从 GPT-5.4 到 GPT-6.0 ,有了非常具体的感知。项目后期进度很快,一是因为架构是逐渐清晰稳定的,二是得益于 AI“更聪明”。 最大的感受, AI 是一把锋利的手术刀,要给它指出在哪下刀,如何下刀。当然,AI 可以帮助你找到下刀位置,但重要的是,你得知道。
- 情报分类:技术学习与提效
- 分类依据:内容涉及技术、AI、软件工具或工程实践
- 信息来源:服务器 / V2EX
- 发布时间:2026/9/22 15:54:52
- 暂无回复