AI Agent学习日记 Day2

张开发
2026/4/3 15:14:41 15 分钟阅读
AI Agent学习日记 Day2
今天继续实现word翻译功能上次的代码翻译完后会丢失图片等元素让deepseek改了好几版代码都还是有问题我决定先搞懂根本原因再改代码。经调查Word 的文档结构通过 COM 对象模型如下Document└─ Paragraphs└─ Range├─ Runs (文本块)├─ InlineShapes (内联图片、图表等)└─ Fields (域代码如目录)其中Paragraphs段落的定义是一个段落是用户按下回车键结束的一段内容。一个Paragraph对象包含一个RangeRange覆盖该段落的所有内容包括文本、图片、表格、形状等。段落是文档流的基本块每个段落可以包含多种元素文本、内联图片、域代码等。Range范围是文档中连续的任意区域可以是一个字符、一个段落、整个文档。Range可以包含多个Paragraph和多个InlineShape内联对象并且可以通过Range.Runs访问该范围内的所有文本块Runs。通过Range可以操作文本、格式、以及插入或删除内容。Runs文本块是Range中具有相同格式的连续文本序列。Word 在内部将文本按格式变化自动分割为多个 Run。例如同一段落中字体加粗、颜色变化、或插入图片都会导致 Run 的拆分。每个 Run 是纯文本或包含内嵌对象如InlineShape、Field等。一个 Run 中如果有图片则这个 Run 的InlineShapes.Count 0且Run.Text通常为空或只包含占位符字符。图片本身不是一个 Run而是附着在某个 Run 上的对象。之前的代码中我们通过操作Range提取出Range中的所有文本然后翻译完再替换掉。这就会导致原本存在于Range中的图片等元素也被覆盖掉所以会丢失元素。deepseek给出的解决方案是逐 Run 处理不再整个替换掉Range。但是我想到既然Run会按照格式变化划分那”这是一句完整的话”这样的句子就会被划分为“这是一句”“完整”“的话”三个Run这样喂给AI翻译效果肯定不行于是我放弃了这个方案。既然这条路不通那就回过头去重新开始。实际上python有专门的库python-docx来操作word但是这个库处理不了复杂的元素如插入的图形文本框等所以我一开始才会采用win32com。在我实际的业务中word文件里面文本框等复杂元素有不少所以这个问题必须要解决。随着调查的深入我发现 Word 文档.docx实际上是一个 ZIP 压缩包包含多个 XML 文件。python-docx 库主要封装了document.xml中主流元素段落、表格、图片的操作但文本框TextBox在 XML 中属于绘图对象DrawingML结构复杂且多变官方库未提供直接 API。但是我可以通过直接解析document.xml查找w:txbxContent标签文本框内容的容器提取并修改其中的文本。root doc.part.element # 查找所有 w:txbxContent 元素忽略命名空间 txbx_contents root.xpath(.//*[local-name()txbxContent]) for txbx in txbx_contents: # 遍历文本框内的所有段落 for para_elem in txbx.xpath(.//*[local-name()p]): # 提取所有文本节点内容 original .join(para_elem.xpath(.//*[local-name()t]/text())).strip() if original and len(original) 1: translated translate_text(original, target_lang, delay, contextcontext) if translated: # 替换所有 w:t 节点的文本 for t_elem in para_elem.xpath(.//*[local-name()t]): t_elem.text translated完美解决下一步就是多线程并行翻译了Excel和PPT都有word也一定要有我的PPT是按页Excel是按sheet并行翻译但是word是流式文档不存在天然可拆分的单位即使word有页码但每一页也并不是独立的甚至一句话都能跨两页。所以只能先把要翻译的元素全部提取出来再多线程并行翻译最后单线程顺序写回。这既能大幅提升性能API 调用成为瓶颈又能避免并发修改文档导致的数据损坏。收集阶段单线程遍历文档收集所有需要翻译的文本单元段落、表格单元格内的段落、文本框内的段落。每个单元包含唯一标识如索引、原始文本、对象引用用于写回。并行翻译阶段多线程使用线程池将文本单元分配给多个线程每个线程调用translate_text进行翻译返回(单元索引, 译文)。写回阶段单线程按索引顺序将译文写回对应的文档对象。我本来想着PPT都能多线程写回去为什么word不行原因如下python-pptxPPT每个Slide对象在内部对应 XML 树的不同分支且python-pptx在加载整个文档后各个幻灯片之间的 XML 节点树是相对独立的。多线程同时修改不同幻灯片的形状shape.text ...实际上是在操作不同子树只要没有同时修改同一个对象例如同一张幻灯片通常不会引发冲突。python-pptx也没有在内部维护复杂的全局缓存如 xpath 缓存所以实践中并行是安全的。python-docxWord文档的段落、表格、文本框等元素都位于同一棵 XML 树中且python-docx内部会维护一些缓存和索引例如段落编号。多个线程同时修改不同位置的文本虽然理论上可能不会直接冲突但lxml底层在修改节点时可能会触发父节点重新序列化或缓存失效存在潜在风险。更稳妥的做法是只并行翻译不并行写回。但是我看了设计思路后觉得还有问题如果只是把所有的翻译文本直接分给多个线程翻译那么每个线程拿到的文本块都是零散的为了保证翻译效果我在把文档喂给AI翻译时是传递了上下文的所以只有上下文连贯地发给AI翻译才能取得最好的翻译效果。于是我将翻译文本全部提取出来后按线程数将翻译文本分组每一组拿到的文本都是连贯的最后再多线程分组翻译。嗯效果拔群完美下次继续实现自动写脚本的Agent吧。

更多文章