这半年来我干得最多的一件事,就是帮团队里的人把AI生成的内容从对话框里搬运到正式文档。搬运本身倒不麻烦,真正让人头疼的是搬运完的那一刹那:列表层级丢了、代码块的高亮没了、公式变成一行纯文本乱码、表格在Word里摊成一片废墟。明明在AI窗口里看着整整齐齐的内容,一导出就"残废"。我被这种"AI内容导出乱、格式崩、公式变"的问题折磨了无数个晚上之后,干脆动手写了个内部小工具,代号就叫 DuckIt,图标是只橡皮鸭。
一开始纯粹是自嘲——老一辈程序员遇到解决不了的Bug会把橡皮鸭放在桌上,对着它一行行讲代码。我遇到的Bug虽然不是程序逻辑问题,但性质差不多:AI写出来的内容本身质量不错,只要别让我碰格式,它写得比很多初级编辑都好。可一旦涉及导出、转换、排版,它立刻从"高级助手"变成"格式粉碎机"。
这篇文章就围绕鸭子的开发过程,把"AI内容导出"这个问题掰开揉碎讲清楚。我会先分析格式崩、公式坏、内容乱这三类问题的根因,再说我为什么选择"解析-改写-渲染"三段式架构,然后把攻防记录和实测数据列出来,最后聊聊这只鸭子还能往哪儿走。如果你也经常被AI生成的Markdown、Word文档、公式转换折磨,这篇文章应该能给你一些可以直接用的思路。
1. 先搞清楚三个痛点到底痛在哪:格式崩、公式坏、内容乱的根因
很多人遇到AI内容导出问题,第一反应是"换个转换工具再试一次"。我一开始也是这样,pandoc换到Pandoc,Typora换到Obsidian,导出插件装了七八个,结果该崩的还是崩。后来我意识到一个问题:不是工具不够好,是我们把问题归因搞错了。AI生成内容的格式问题从来不是"转换器不够强",而是"AI平台输出的格式本身就带病"。
1.1 格式崩:不是AI不行,是Markdown从来就没有统一标准
Markdown最大的优点是简单,最大的缺点也是简单。它没有一个像HTML那样的W3C标准组织来维护统一规范,于是出现了CommonMark、GitHub Flavored Markdown、Pandoc Markdown等一系列方言。AI平台在训练时吸收了各种风格的语料,输出时又会根据用户的提示词自由发挥,这就导致同一个AI在不同对话里可能输出完全不同的Markdown结构。
我实测过同一个模型生成的同一份技术方案:第一次输出用圆点列表嵌套方括号,第二次输出用数字列表加四个空格缩进,第三次干脆混着来,外圈是数字列表,内圈是圆点列表。单独看任何一次输出,在对话窗口里渲染都正常,可是一旦丢给pandoc转Word,问题就全出来了。Terrible 的地方在于解析器会强制按标准规则解释,比如pandoc遇到列表缩进不一致时,不会自动"猜"你的意图,而是直接把列表打断成好几段。
表格是另一个重灾区。AI输出Markdown表格时经常出现列数不一致的情况:第一行表头写了6列,数据行里有的写了5格,有的写了7格。这在GitHub上渲染时会被宽容处理,看起来只是错位,但一旦导成Word或者HTML,表格就会彻底崩坏。还有更隐蔽的情况——AI喜欢在表格单元格里塞Markdown语法,比如加粗、行内代码、甚至一个完整的代码块。按规范这不算错,但大部分表格解析器根本不支持单元格内有块级元素,转换时直接丢弃内容。
我统计过自己经手的200多份AI生成文档,"格式崩"里最常见的三类是:表格列数不一致、列表嵌套缩进混乱、代码块内部出现未转义的反引号或三个反引号。这三类问题几乎每个星期都能遇到。
1.2 公式变:LaTeX、MathML、UnicodeMath,三套方言互相打架
公式问题比格式问题更让人头大。原因很简单:公式在AI对话窗口里显示得好,是因为前端做了渲染;而一旦导出,后端需要把公式翻译成目标格式能识别的语言,这里就有三套完全不同的体系在打架。
第一套是LaTeX。这是AI输出公式最常见的格式,ChatGPT、Claude这些模型默认就给LaTeX,用$...$或者$$...$$包裹。问题在于LaTeX本身是排版语言,不是数据格式,同一个数学表达式可以用完全不同的LaTeX命令表达。比如\frac{a}{b}和a\over b,甚至a/b,在LaTeX里都能表示分数,但不同转换器对它们的支持程度不一样。
第二套是MathML。它是为了在网页里表示数学而生的XML语言,结构规整,信息完整,但极度冗长。一个简单的分数在MathML里要写十几行标签。很多导出流程会把LaTeX先转换成MathML,再由MathML转换成其他格式,这个链路越长,出错概率越高。
第三套是Word原生公式使用的格式。Word在2010年之后主要支持UnicodeMath(也称为线性格式)以及OMML(Office Math Markup Language)。问题在于,从LaTeX到UnicodeMath的转换并不是一一对应的,很多结构在LaTeX里有表达方式,在UnicodeMath里根本没有等价物,或者表达方式完全不同。
举个例子,分段函数里常用的\begin{cases} ... \end{cases}结构,在UnicodeMath里对应的是⌈这样的专用括号加矩阵排列,绝大多数转换器直接放弃处理。还有多行对齐的\begin{aligned}、矩阵的\begin{matrix}、求和符号上下限的\sum_{i=0}^{n},这些在LaTeX里稀松平常的结构,一旦要转成Word公式,十有八九会变成一坨纯文本或者乱码。
更麻烦的是,同一平台在不同场景下的公式存储方式还不一样。Markdown导出时是LaTeX,通过浏览器复制粘贴时可能变成MathML,直接复制渲染结果时又变成UnicodeMath。格式不统一,转换工具就很难做兼容。
1.3 内容乱:AI爱用没有语义的Markdown完成"格式化表演"
格式崩和公式变是显性问题,内容乱是隐性问题,但它带来的后果往往更严重。
什么是"内容乱"?不是说文字排列歪了,而是指Markdown标记被AI当成"格式化表演"来用,失去了真正的语义。很多AI生成的文档,看起来层次分明,但它的"层次"只是视觉上的——该是标题的地方用了加粗,该是列表的地方用了缩进加横线,该是正文的地方用了引用块。这些内容在对话窗口里渲染,"看起来"像一份结构清晰的文档,但一旦丢给解析器提取大纲、生成目录、做书签,立刻露馅。
我遇到过几个典型场景。有一次让AI帮忙写一份季度汇报,它为了突出"三个重点",写了三个引用块,每个引用块里再套列表,这样从视觉上确实很清晰。可是我要把这份文档转成带目录的Word投屏,生成的目录只有"汇报"两个字,三个重点全是灰色正文。还有一次AI把整个流程说明写在了表格里,但过程有十二步,表格只有两列,结果就是内容全堆在一个单元格里,完全没法编辑。
我自己也反思过这个问题:AI为什么会这样?因为模型训练时接触了大量"格式优美"的网页内容,它学会了"怎么让文字看起来有条理",但没有学会"怎么让文档在语义上结构正确"。加粗、缩进、引用块这些视觉元素,对AI来说可能只是"看起来更专业"的手段。但对和文档处理工具打交道的我们来说,这些都是误导信号。
所以鸭子的核心目标从一开始就确定了:不只是把马克down变成Word,而是把"视觉上有条理的内容"变成"语义上结构正确的内容"。这个定位决定了后面的整个架构设计。
2. 鸭子工具的整体设计:我为什么选择"解析-改写-渲染"三段式架构
确定要自己动手之后,我第一件事不是写代码,而是花了两天时间把手头的AI内容和各种转换器的输出全部过了一遍。结果让我下了决心:不能用现成的转换器硬拼,必须做一个能"治病"的中间层。
2.1 为什么不能靠市面上现成的转换器
市面上的转换器分两类。一类是"从A格式到B格式"的直接转换,代表是pandoc;另一类是"渲染并导出"的富文本编辑器或笔记软件,代表是Typora、Notion、语雀。这两类工具在自己的生态内都做得不错,但用来处理AI内容都有一个通病:一条流水线从源头直接到目标,中间没有"语义检查"和"结构修复"的机会。
pandoc是我最开始用的,也是用得最多的。它的转换质量确实高,尤其在标准文档上。但pandoc默认把"输入内容"当作"已经在语义上正确的内容"处理,它不会去判断一个表格列数是否一致,不会去修复嵌套列表的缩进,更不会去纠正AI把"强调"写成"标题"的问题。换句话说,pandoc是个优秀的翻译官,但不是个负责任的编辑——原文有病句,翻译出来也是病句。
Typora这类编辑器又不一样。它们通常在渲染层做了一些宽容处理,比如表格列数不一致时自动补空栏,但宽容带来的问题是你根本不知道原始文本哪里有问题。等你导出成Word才暴露,这时候又要回头改原始Markdown,工作效率极低。
更重要的是,AI平台输出格式还在快速变化。上个季度ChatGPT的列表缩进还是标准四空格,这个季度可能就变成两空格;有些模型开始用HTML标签包裹列表,有些模型习惯在代码块外再加独立行内代码。任何依赖"固定输入格式"的转换工具,都注定要被时代甩在后面。
所以我当时的判断是:要处理AI内容导出问题,不能靠"转换器",要靠"解析器+修复器+渲染器"。也就是先理解AI到底输出了什么,再动手改,最后才做转换。
2.2 三段式架构到底怎么拆
鸭子的第一版架构很简单,三个模块,职责分明。
解析层负责把不同来源的内容统一成抽象语法树(AST)。无论是ChatGPT直接复制来的Markdown、Claude导出的纯文本、还是从Notion里带格式复制出来的富文本,解析层都先把它变成一棵结构化的树。这一步的关键是"宽容"——尽量保留原始信息,即使语法不规范也不要丢,把能识别的节点都标记出来,识别不了的放进"fuzzy"节点等后面处理。
改写层负责做结构修复。这是鸭子和普通转换工具最大的区别。改写层拿到解析层输出的AST之后,不是立刻渲染,而是先做一轮结构检查:表格列数不一致就补列;列表缩进混乱就按语义重排;标题层级跳跃就插入占位节点;公式内容无法映射就保留原文并标注警告。这一层相当于给AI输出做了一次"结构化体检"。
渲染层负责目标格式输出。同一棵修复过的AST,可以根据需要渲染成Word的docx、标准Markdown、带样式的HTML、PDF,甚至直接输出成纯文本。渲染层不关心内容的对错,它只负责把AST节点映射成目标格式的语法。因为AST已经修好了,渲染时基本不会遇到结构性问题。
为了方便理解,我贴一段鸭子早期版本的类型定义,不长,但能直观看到这个架构的轮廓:
// 鸭子工具早期版本的核心AST类型定义 interface DuckNode { type: string; // 节点类型:heading, paragraph, list, table, codeBlock, mathBlock... children?: DuckNode[]; props: Record<string, unknown>; // 结构属性:level, ordered, align... source?: string; // 原始文本片段,用于debug和回退 warnings?: string[]; // 改写层的修复记录 } interface DuckDocument { meta: { title: string; createdAt: string; model?: string }; nodes: DuckNode[]; }实际开发中,改写层的核心是一组按节点类型注册的"管道处理器"。表格问题有表格处理器,列表问题有列表处理器,公式问题有公式转换器。这样设计的好处是,每种问题都可以单独修、单独测。比如后来我发现Claude输出的表格单元格里偶尔会出现<br>标签,我只需要在表格处理器里加一条规则,不需要动其他代码。
2.3 为什么"鸭子"能扛住真实场景
说了这么多架构上的理由,可能有人觉得我在过度设计。我真实的心路历程是这样:第一版鸭子我偷懒了,直接用正则表达式做了个"修复脚本",目标是最快最省事。正则方案在最开始的十几次转换里表现还行,可到了第二十次就崩了——遇到一个AI在表格里嵌套代码块的极端案例,我的正则匹配直接错位,把整个文档的结构搞乱了。
那次翻车让我下了决心,必须用解析树而不是正则以字符串匹配。用AST之后,处理逻辑变成了"树的遍历和节点操作",每一步都可以打印、检查、单测。真实世界里的AI输出千奇百怪,但无论怎么变形,只要它还能被解析成树,就有机会被修复。就像做菜,不用管食材本身是什么形状,先切块,切完才开始烹饪。切块的过程就是把"原料"变成"可处理的状态"。
而且三段式架构还有一个隐形好处:可测试性。我可以给每个环节写独立的测试用例。比如解析层需要保证"无论输入多乱的Markdown,都不抛异常";改写层需要保证"表格列数不一致时自动补列,不丢失单元格内容";渲染层需要保证"同一棵AST生成Word和生成HTML的内容完全一致"。这些测试帮我挡住了很多回归问题,AI平台一升级,我先跑一遍测试就知道哪里受影响了。
3. 三场硬仗的攻防记录:乱、崩、公式怎么逐个击破
架构定下来之后,剩下的就是硬仗了。我按痛点的优先级排了三个攻坚方向:先解决格式崩,因为它是出现频率最高的;再解决公式变,因为它最影响专业文档的质量;最后处理内容乱,因为它需要更精细的语义判断。
3.1 第一仗:搞定"格式崩",从表格和代码块下手
表格的问题,我选择用独立解析器处理。所谓独立解析器,就是在解析层不去"猜"表格哪一列是哪一列,而是严格按Markdown表格语法解析,把每一行拆成单元格数组,然后把"列数是否一致"的判断留给改写层。改写层里有一条规则:当同一表格内不同行的列数不一致时,以表头为准执行数组对齐,缺失的格子补空字符串,多出来的格子内容拼接到最后一个单元格后面,并记录一条警告。
这个策略在理论上很简单,但实操中我踩了一个很重要的坑:AI生成表格时,单元格里可能出现竖线|字符,比如代码片段"Math.abs(x) | 0"。在标准Markdown里这需要用\|转义,但很多AI不转义。如果我按竖线硬切,单元格内容就散架了。后来我在解析表格时加了个状态机,只在"非代码块、非转义"的竖线处切分,这才把问题压下去。
代码块的问题,主要集中在嵌套反引号上。AI非常喜欢在代码块里贴另一段包含反引号的代码,合法的Markdown可以用双反引号包裹外层,但我见过AI直接输出三个反引号包一个包含三个反引号的代码片段,这在任何解析器里都是灾难。我的处理方式是:解析层先把识别到的最外层代码块包围标记记下来,然后在改写层做一次"代码块内容安全检查"——如果代码内容里出现了和包围标记相同的连续反引号,就自动提升包围层级。
这一步看起来简单,但背后的逻辑是:不要试图"告诉AI该怎么写",而是把AI写坏的内容修复成解析器能理解的形式。因为无论AI怎么升级,总会有边界情况,修复逻辑必须比AI输出更宽容。
3.2 第二仗:搞定"公式变",从LaTeX到Word原生公式的最短路程
公式转换是整只鸭子投入精力最多的地方,没有之一。
一开始我也想直接找现成的LaTeX转OMML库,试了两三个开源方案,发现效果都一般:简单公式没问题,但一遇到\begin{cases}、\begin{bmatrix}、\begin{aligned}这种环境块,要么报错,要么输出成满屏的OMML标签,Word里根本没法编辑。后来我放弃了一步到位的思路,改成三层处理。
第一层是LaTeX解析层。我不直接转OMML,而是先把LaTeX解析成一个数学表达式树。这里要重点处理的是"环境块"——\begin{cases}、\begin{matrix}等在标准LaTeX里属于"环境",它们不仅有符号含义,还有布局语义。我把它解析成带"布局类型"的数学节点,比如cases节点、矩阵节点、多行对齐节点。
第二层是符号映射层。这一层负责把数学表达式树里的LaTeX命令映射成UnicodeMath能理解的符号。比如\alpha映射成α,\sum_{i=0}^{n}映射成带下标和上标的∑结构,\frac{a}{b}映射成a/b的线性表示,同时给渲染器标注"这是一个分数节点,需要用分数布局渲染"。
第三层才是OMML拼接层。我基于内嵌的公式引擎直接构建OMML XML节点,核心是m:oMath元素内部的各种结构节点。分数用m:f,上下标用m:sSubSup,矩阵用m:m,分段函数用m:d加分组。这一层最笨,但效果最可控,因为每个节点都是我亲手映射的,不会像第三方库那样"尽力而为"。
这里我特别想强调一个细节:公式的双写机制。Word文档里,OMML是给Word渲染用的,但一旦用户用别的工具打开,或者复制粘贴到纯文本环境,OMML就变成了满屏乱码。为了让公式"可读可编辑",我在生成Word时会把原始LaTeX同时写入公式节点的alt文本。这样就算一个公式在某个环境下渲染失败,用户至少还能凭LaTeX原文找回内容,不会彻底丢失。
这个双写机制我强烈推荐给所有做文档转换的人。它不能提高转换成功率,但能大幅降低转换失败带来的损失。
3.3 第三仗:搞定"内容乱",用AST做语义级整理
搞定格式和公式之后,我花了好几周处理"内容乱"的问题,因为它的判断标准不像前两个那么明确。什么叫"结构正确"?不同场景有不同答案。
我采取的判断标准是:以文档目标格式的语义模型为准。如果目标是Word文档,那么标题必须是标题样式,列表必须是列表样式,强调必须用字符级属性,而不是用"看起来像标题的加粗段落"。这个标准说起来简单,但执行起来需要一系列规则。
以标题为例。AI经常把章节标题写成"实心加粗大号字体"这种视觉样式,在AST里它就是一个"加粗段落",但实际语义是标题。改写层里加了一个"标题嗅探"规则:当一个段落同时满足"全文独占一行""内容长度小于80个字符""带加粗属性""前面有数字序号或特定标志词"时,就自动把它提升为标题节点。这个规则有误判风险,我做了个折中设计:只升不降,并且在警告列表里记录"已按疑似标题处理",让用户可以在最终预览时手动退回正文。
列表的整理也有类似问题。AI经常输出"1. 第一步 - 打开设置 - 找到开关"这样的混排列表。解析层会把这些拆成不同层级的列表项,但缩进可能完全不对。改写层用了一个简单的栈式算法重建层级:遇到列表项时,根据它的上一级列表项的缩进和序号状态,决定是"同一层级"还是"降一级"还是"升一级"。这个算法不完美,但对90%的AI列表内容能给出正确结果。
还有一类"乱"我花了很多时间才意识到:空行和分隔线。AI为了让文档"透气",会在段落之间插入大量水平分割线---,在Word里会变成一条线。视觉上没问题,但一旦文档被脚本处理,这些分割线会干扰语义解析。我在改写层加了一个规则:连续出现2条以上的分割线时,只保留最后一条,并记录警告。这条规则看起来很小,但在实际导入知识库时帮了大忙。
4. 实测复盘:在真实工作流里鸭子能解决多少,还剩哪些边界
理论说得再漂亮,最终得看实测。鸭子开发到现在已经跑了五个月,我用它处理了400多份来自不同AI平台、不同场景的文档,这里把数据和个人感受都摊开说。
4.1 主流模型与常见平台的实测
需要说明一点:这不是严格控制的对比实验,而是我在真实工作流里的观察记录。所谓"成功率",指"转换完成后不需要人工手动清理整个段落"的比例,不包含个别标点、个别样式的小修小补。
| 来源 | 样本量 | 格式崩修复率 | 公式保留率 | 表格修复率 | 主要遗留问题 |
|---|---|---|---|---|---|
| ChatGPT(GFM输出) | 120 | 96% | 92% | 90% | 列表缩进偶尔重排不彻底 |
| Claude(Markdown输出) | 85 | 91% | 88% | 85% | 表格内嵌HTML标签残留 |
| 通义(富文本复制) | 70 | 84% | 79% | 80% | 复制时丢失空格和换行信息 |
| 文心(纯文本回复) | 55 | 89% | 无公式场景 | 84% | 转换前需自动补Markdown标记 |
| 本地开源模型 | 45 | 78% | 70% | 76% | 平台输出格式极不稳定,需调解析层级 |
几个明显发现。第一,ChatGPT的Markdown输出在结构上最规整,说明底层做了比较严格的规范约束。第二,Claude喜欢在列表和表格里塞HTML标签,增加了解析负担,但处理掉标签后内容质量依然很高。第三,通义这种"富文本复制"模式的难点在于信息丢失——你从对话框复制内容到剪贴板时,换行和空格已经被压缩了,解析层很难做到无损还原。
公式保留率是我比较担心的数字。92%的"公式保留率"不等于92%的"公式转OMML成功",有些公式是原样保留成LaTeX文本了。真正能做到"转成Word原生公式且可编辑"的比例,在ChatGPT场景里大约85%。剩下的15%基本都是极端环境块,比如\begin{rcases}或者自定义宏包命令,这些只能保留LaTeX原文并给出警告。
4.2 途中踩过的额外坑
除了三大核心问题,还有几个"看似不起眼,实际能坑人一整天"的细节,值得单独记一笔。
Unicode字符的编码坑。AI输出里经常有各种奇怪的Unicode数学字符,比如≠、∈、∫。这些字符在Word里显示没问题,但在转HTML或PDF时,如果目标编码不支持,就会变成问号方块。鸭子里的处理方式是:在所有渲染层的入口统一做一次字符规范化,把Unicode数学字母平面(Mathematical Alphanumeric Symbols)映射到普通字母加斜体标记。
图片和链接的引用问题。AI生成的Markdown里经常夹带,但URL可能是临时链接、过期链接,甚至base64数据。鸭子的渲染层默认开启"图片下载并本地化"选项,遇到base64数据直接解码写入docx内的embed,遇到临时链接尝试下载,下载失败就放占位图并在警告列表里提示。
超长文本的分段问题。有一次我让AI写了一份上万字的操作手册,导出时Word直接卡死。后来发现是渲染层构建docx时把所有内容塞进了一个大段落,没有任何分页和分节逻辑。我花了一个周末给渲染层加了"智能分段"——按标题层级自动生成分页,按列表和段落实现分节,这才把大文档的处理速度提上来。
4.3 提高输出质量的个人建议
测试做多了我发现一个规律:鸭子能修复的质量上限,取决于AI原始输出的信息完整度。如果AI输出的内容本身就缺了半个字,或者把一个语法完整的句子自言自语截断了,任何工具都没法完美复原。
所以我的实际工作流做了调整:在提示词端就把"导出友好"这件事交代给AI。具体做法是在提示词里加一句:"请使用标准Markdown格式输出,标题层级从H2开始,不要使用嵌套引用块,确保表格行内不用竖线符号。"这句提示词看起来轻描淡写,实际效果却非常明显,格式问题至少减少了六成。
双保险的做法也强烈推荐:永远保留AI的原始输出。鸭子只负责"再加工",不覆盖原始来源。这是因为AI对话窗口本身就带"重新生成"能力,如果鸭子转换失败,你可以让AI换个输出风格再试一次,而不是抱着同一份坏数据反复修补。这是处理"不可控内容"最基本的防御策略。
5. 这只鸭子接下来还能做什么:从文档修补到内容工程
鸭子目前解决的是"AI内容导出后乱七八糟"的问题,但它本质上做的是一件更通用的事:将不可控内容标准化。基于这一点,我能看到的扩展方向还挺多的。
第一是批量处理能力。单个文档转换跑通之后,批量转换是水到渠成的事。我已经给鸭子写了简单的事件队列模块,支持把一个目录下的所有Markdown文件批量转成Word,并生成一个汇总的修复报告。这个能力对于需要把几十份AI日报、周报统一归档的团队非常有用,每次转完还能自动发一份"哪些地方被自动修复过"的警告清单给负责人。
第二是与知识库系统的对接。很多团队现在把AI生成的技术文档直接丢进内部知识库,比如语雀、飞书云文档、Confluence。这些系统都有导入接口,但接口接收的格式不同,对格式的容忍度也不一样。鸭子输出的标准化Markdown和docx,天然适合作为导入前的"预处理器"。我下一步计划是给鸭子加上输出适配器,让它直接生成不同知识库平台期望的格式,省掉"先转Word再粘进系统"这一步。
第三是CI/CD集成。对于技术团队,AI生成的代码注释、API文档、变更记录经常要进Git仓库。鸭子可以设计成一个命令行工具或Git插件,在提交之前自动检查Markdown格式规范、修复表格和公式问题、统一标题层级。这相当于给AI生成的内容加一道"格式CI检查",在文档进入正式仓库之前就把病句和乱格式拦下来。
回到我自己最常用的场景。我每周至少用鸭子处理十几份AI辅助写成的技术文档,从初稿整理、格式修复到最终交付,整个链路顺畅了很多。以前一个下午都搞不定的"AI内容整理成Word文档",现在大约一两分钟。真正让我觉得钱和精力没白花的是,那些"AI生成得很好但格式乱成一团"的内容,现在丢给鸭子就能恢复原本该有的模样。
如果让我给同样被这个问题困扰的人提一条建议,我想说:别急着无脑修一个个具体案例,先把"AI输出→AST→修复→渲染"这条链路搭起来。格式崩、公式变、内容乱这三个问题,表面看着是三个独立问题,根上都是同一个病:缺少一个可靠的结构化中间层。把中间层做稳了,后面全是顺的。
还有一句个人体会。做这种工具最快乐的事,不是技术环节有多高级,而是每次亲眼看到AI输出被修得整整齐齐的那一刻,会有一种"终于把不可控的事情变成可控了"的踏实感。鸭子到现在也不完美,我也一样,但至少,我们都能在工作流里面站得更稳一点了。