你的 Agent 写不出能打开的 pptx,GenOffice 把整套 Office 引擎接给了它
- GenOffice 把 docx、xlsx、pptx、PDF 等 Office 引擎接入 Agent,提供 CLI、skill、MCP 三条通道共 29 个工具
- 核心设计是数据库式脏页写回:仅重写改动的 XML 块,未触碰内容逐字节保留,补丁后还做术后复查并支持失败回退
- 生成管线带强制校验闭环:check 验大纲、audit 查溢出重叠、render 渲染 PNG 供模型回看自修,相当于给文档生成补上编译器和测试
- 项目 54 天获 7590 Star,背后是融资约 3.6 亿美元的 Mainfunc 公司,GitHub 为镜像仓库且预留企业版目录,存在 open-core 与默认 GA4 遥测
- CLI 的 render 和 PDF 转换需拉起隐藏应用加 xvfb-run,纯容器部署需注意镜像体积;引擎大量复用 Univer、PDFium、Tiptap 等开源组件
上周有个朋友跟我吐槽,他让 Claude Code 给客户做一份 20 页的投资 deck。Agent 很努力,写了一晚上 Markdown,附了一堆 Mermaid 图,最后他自己花了整个周末在 PowerPoint 里人肉重排。这事在 Agent 开发者里太常见了,模型明明能想清楚内容,落不了地。Python 那边有 python-docx、openpyxl、python-pptx 能凑合,但那是给程序员用的 API,模型拿它们排出来的幻灯片,字号溢出、元素重叠是家常便饭,而且它自己看不见。
GenOffice 干的就是把「落地」这一段补齐。它把一整套 Office 引擎,docx、xlsx、pptx、PDF、Markdown、HTML 的解析、编辑、渲染,全部接给了 Agent,还配了一圈校验工具让模型自己检查自己。
54 天攒出一个 Office 套件
先交代背景。这个项目 2026 年 7 月 31 日才建仓,到我写这篇时 54 天,7590 Star、987 Fork、398 个提交,平均每天 7 个多的提交节奏,10 天发了 5 个 release,最新版本号是 v0.10.1038。这种巨型补丁号一看就是按内部构建自动递增的,也从侧面说明了背后团队的生产强度。
团队不是草根。背后是 Mainfunc 公司,就是做 Genspark 超级 Agent 的那家,前百度高管 Eric Jing 创办,公开融资累计约 3.6 亿美元。所以这个项目从第一天起就不是玩具,是一次有资源的大厂开源。
体量摆在这。7 个 Electron 应用(Docs、Sheets、Slides、PDF、Markdown、HTML 加一个多标签壳),底下 18 个纯 TypeScript 引擎包,光 packages 目录就 885 个 TS 文件,还有一个 Rust 写的 xlsx 边车进程。README 第一句自我介绍是「世界首个全功能开源 AI Office 套件」,这话营销味很重,LibreOffice 和 OnlyOffice 都活得好好的,只是它们没把「Agent 优先」当第一公民。这个差异等下展开,先把最硬核的部分拆了。
把 Word 文件当数据库,只写脏页
GenOffice 最值钱的设计决策,是它对「保存」的态度。
一般的开源方案处理 docx 就两条路。要么整个文件重建成新的 OOXML(python-docx 系),格式细节必丢;要么只在上面盖注释层(很多在线 PDF 编辑器的做法),看起来改了,实际文件没动。GenOffice 选了第三条,也是最难的一条,像数据库写脏页一样,只重写你动过的块,其余字节原样保留。
它的流程分两段。打开 docx 时,先把原始文件按哈希归档,此后永不触碰,再解析 word/document.xml 顶层元素,建一棵块树,每个块锚定到自己的原始 XML 切片。保存时只有标脏的块会生成新的 OOXML 片段,拼回原 document.xml,没动过的块保持原始字节,最后重打包 zip,其余所有条目逐字节复制。
这里面有两个文件值得单独点名。packages/docx-engine/src/zip-splice.ts 实现了裸 zip 手术,条目在压缩包之间按存储字节直接搬运,从不解压再压缩。文件头注释写得很直白,一个 1 GB 的文档,代价只是读它那几个 XML 部分的字节。packages/docx-engine/src/text-patch.ts 里的 patchParagraphTexts 函数更细,改动的段落只替换公共前缀和后缀之间的 w:t 文本节点,段落内的加粗、超链接、图片 run 全部原样保留。
我特别想强调 text-patch 里的一个细节。补丁打完之后,函数会把结果重新提取一遍文本,跟目标逐字比对,对不上就整个放弃手术,返回 null 让上层回退到重建整条。这种「手术自带术后复查」的写法,比大多数同类库都谨慎。
一条链路图大概是这样的。
翻译成人话,原文件是唯一事实源,编辑器只是寄生在它上面的一个视图,保存等于一次最小 diff 的落盘。样式也遵循同样哲学,generate.ts 里 GenerateContext.headingStyleIds 这个结构,映射的是「标题级别到原文档 styles.xml 里已有的 styleId」,新生成的内容只引用原文档里存在的样式,绝不凭空发明新样式。所以你拿 GenOffice 改过的文件回 Word 里打开,母版、编号、修订记录都还在。CONTRIBUTING 里甚至把这一条写成了硬规矩,凡是碰打开保存路径的 PR,必须附一个「未触碰内容逐字节存活」的往返测试。
给 Agent 装上眼睛的生成管线
第二个硬核部分,是它给编码 Agent 修的那条生产管线。传统 CLI 工具给你一个 create 命令就完事了,GenOffice 不一样,它把「生成」拆成了带强制质检的流水线。
拿做 deck 来说。skill 教 Agent 先写风格表和大纲,跑 genoffice slides check 验大纲,然后一页一页写页面 spec,每页都要过 check(构建单页、审计溢出和重叠),全部干净了才允许 create 组装 pptx,最后 slides render 把每一页渲染成 PNG 交回给模型看。注意最后一步,这是整套设计的灵魂。模型本来没有视觉,渲染回看等于给流水线装了眼睛,它能看到自己排的版挤没挤、歪没歪,然后自己修。README 里那个可再生能源 deck 的案例,一次任务 38 次工具调用、约 13 分钟,其中 3 页就是模型看了渲染图之后自己重做的。
通道有三条,CLI、随仓库分发的 skill(skills/genoffice/SKILL.md,版本号已经迭代到 2.51.0),以及 MCP 服务器。我特意去数了 packages/cli/src/mcp/tools.ts,25 个工具,加上 deck.ts 里 deck_start、deck_page、deck_build、deck_replace 四个分阶段工具,正好 29 个,和 README 宣称的数字对上了。deck 这组工具的 description 本身就是工作流说明书,deck_page 的描述里明确写着「一页没过检查就不落盘,修到三类 finding 全空再写下一页」,服务端自带纪律,不装 skill 也不会跑偏。
那个 skill 文件也值得一看。215 行里有两节标题很少见,一节叫 Pitfalls(坑),一节叫 Before you say done(在你说完成之前),后者列了一堆自查项,比如「render 过了吗」「audit 干净吗」。这不是普通的工具文档,这是在给模型写防偷懒条款。
顺带说一句 Agent 运行时本身。packages/agent-core/src/loop.ts 有 832 行,是全部 7 个应用共享的 Agent 循环。它的上下文压缩是按 UTF-8 字节数而不是消息条数计费的,默认 256 KB 触发压缩、保留最近 96 KB、在用户消息边界切齐,另外历史超过 40 条也会修剪。每次运行的第一个变更型工具执行前会捕获一个快照,这就是 UI 里「一键回滚」的钩子。这些数字和边界处理,能看出是真实生产环境磨出来的。
引擎层的底牌
坦白讲,这套东西不是从零造的,底牌页写得很诚实。表格 UI 核心是 Univer(Apache 2.0),PDF 编辑靠 PDFium 内容流引擎,docx 块编辑器是 Tiptap,Rust xlsx 边车的读层用 calamine、算层用 IronCalc,复杂文本整形上 HarfBuzz 的 wasm 版。GenOffice 自己造的是最上面那层,解析成块树、窄补丁写回、几何审计、格式转换。
四层摞起来是这么个全景。
顺着图从上往下看,三条 Agent 入口最终都落在同一层纯 TypeScript 引擎上,这层不依赖 Electron,可以单测,CLI 无头跑的也是它。Rust 边车多说两句。它跑在独立进程里,和渲染层之间是一条自定义行协议,apps/sheets/native/xlsx-engine/src/main.rs 开头的 PROTOCOL_VERSION 常量管版本握手,命令走 serde 标签分发,open、read_range、read_formula_cells 这些。电子表格重算这种 CPU 密集活放 Rust 里跑,UI 线程一点不沾。
转换器是另一个亮点,全部本地。pdf2docx 走 PDFium 字符级提取加纯几何排版分析,不依赖云服务。html2docx 更有意思,页面在应用自带的 Chromium 里渲染,在浏览器内归约成一棵「文档意图树」,再写成原生 OOXML,只有图表这类 Word 没有对应物的东西才截图嵌入。
| 维度 | GenOffice | LibreOffice / OnlyOffice | python-docx / openpyxl 系 |
|---|---|---|---|
| Agent 入口 | CLI + skill + MCP 三通道 | 无原生设计 | 程序员 API,模型可用但盲 |
| 生成后自检 | check/audit/render 强制闭环 | 无 | 无 |
| 字节保留 | 是,窄补丁 | 部分(自有序化格式) | 否,整档重建 |
| 运行形态 | 桌面应用 + 无头 CLI | 桌面/服务 | 库 |
开源的姿势有点微妙
说完成绩说边界,这部分我读源码和文档时记了几笔,有几处确实出乎意料。
第一处,这个 GitHub 仓库是个镜像。CONTRIBUTING 写得明白,真实开发在私有树里进行,main 靠定期的 squashed 快照推进,外部 PR 被接受后会带着你的 Co-authored-by 标记进入下一个快照,然后在 GitHub 上显示为 closed 而不是 merged。对贡献者来说署名保住了,但你没法在这个仓库上做考古,提交历史是断代的。这种模式我在一些国产大厂项目上见过,个人观察,不评价好坏,但选型时要知道,你看到的不是开发现场。
第二处,遥测。README 的 FAQ 承认打包版默认发送有限的使用统计,但 PRIVACY.md 里有个更细的说法,统计从应用首次启动就开始收集,包括 onboarding 提示弹出来之前的那一次。走的是 Google Analytics 4 Measurement Protocol,带一个随机安装 UUID。声明里也写清了不含文档内容、文件名、路径和账号,源码自建版本是空操作 tracker,什么都不发。口径是诚实的,但「本地优先」的应用默认开 GA4,这个组合你接不接受,自己掂量。
第三处,open-core 的伏笔。仓库根目录有个 ee/,目前只有一份 README 和一份企业协议,内容写明这个目录留给未来的企业模块,生产使用需要和 Mainfunc 签企业协议,外部 PR 不允许碰这个目录。现在还是空城,但地基已经打了。另外 Apache 2.0 之外商标条款单独 carve out,分叉得换牌子。
第四处是实打实的功能边界,也是我认为最该写进选型清单的一条。CLI 并不是完全独立的,render、转 PDF、create_pdf 这三个操作会拉起一个隐藏的 GenOffice 进程,无头服务器上得装好整套应用再加 xvfb-run 虚拟显示。想在纯容器里跑文档渲染管线,先掂量一下镜像体积。
还有一个我很欣赏的诚实细节。packages/cli/src/commands/slides.ts 的几何审计,检查出界、文字溢出、元素重叠,但输出里自带一行说明,字形宽度是启发式的,溢出数字是近似值。工具没有假装自己是精确的排版引擎。这个项目的安全意识也在线,近期提交里有连续几条硬加固,比如拒绝声明数据超出档案边界的 zip 条目(防 zip 炸弹和畸形压缩包,PR #761)、给 EMZ 解压输出设上限,这类「被攻击过才会补」的提交,说明 CLI 解析的是不可信输入,他们想清楚了。
带校验回路的生成器
拆完这个项目,我一直琢磨它到底教了我什么。
它教的东西可以起个名字,带眼睛的生成器。这波 Agent 产品大多在卷「生成」,GenOffice 把力气花在生成之后的环上,check 拒绝、audit 挑错、render 回看,三个环节全是给模型自己用的。过去我们说代码生成之所以能用,是因为有编译器和测试兜底,文档生成一直没有等价物,模型写完就是写完了,烂尾你买单。GenOffice 给 Office 文档补上了编译器和测试。
这个思路完全可以迁移。你在做 Agent 产出任何结构化产物的工作流吗,不管是前端页面、数据报表还是 SQL,问自己一句,产物有自动校验器吗,校验结果能变成模型可感知的输入吗?没有的话,你的 Agent 就是那个写了一晚上 Markdown 的 Agent,内容全对,交付为零。
至于要不要用它,我的判断很简单。重度文档工作流、想让编码 Agent 直接产出可交付 Office 文件的团队,这套管线目前没有同量级的替代品,值得一试,MCP 一行命令就能挂上。轻度用户先别急着换掉 LibreOffice,这东西是给 Agent 造的引擎,人能编辑只是顺带。项目毕竟才 54 天,Windows 打印质量、RTL 文字支持这些 issue 区的抱怨都还开着,让它再跑跑。
感兴趣的自己去数那 29 个工具,顺便看一眼那个自带术后复查的 text-patch。

评论互动