news 2026/9/24 22:01:45

Ekko Studio docx Skill 源码级解析:Word 修订(Tracked Changes)与批注(Comments)的 WordprocessingML 处理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ekko Studio docx Skill 源码级解析:Word 修订(Tracked Changes)与批注(Comments)的 WordprocessingML 处理
  • AI 应用
  • 人工智能
  • AI Agent
  • 本地部署
  • 前端
  • 后端
  • 工作流自动化

【免费下载链接】ekko-studio

Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.

项目地址:https://gitcode.com/gh_mirrors/he/ekko-studio
点击查看免费下载

本篇技术指南聚焦 Ekko Studio 仓库中packages/ekko-agent/skills/docx这一文档处理 Skill 的核心难点——Word 修订追踪(w:ins/w:del)与批注(Comments)的底层 WordprocessingML 处理。文章以 revisions-and-comments.md 为骨架,结合 docx_revisions.py 与 docx_comments.py 的源码实现,帮助读者理解修订接受/拒绝的决议语义、批注的"三件套"XML 结构,以及如何在日常自动化流程中安全地使用这些命令。读完本文,你将能读懂任意 .docx 中的修订与批注 XML,并能用命令行完成列出、接受、拒绝、增删批注等全部操作。

一、docx Skill 中修订与批注的定位

Ekko Studio 的 docx Skill 是一套围绕 python-docx 与 lxml 构建的 Word 文档处理工具集,其入口与总览见 SKILL.md。其中与本文主题直接相关的两个脚本是:

  • docx_revisions.py:检查并决议修订追踪(w:ins/w:del);
  • docx_comments.py:列出、添加、删除批注。

这两个脚本被定位为"深层参考"(deep reference),日常使用只需按 SKILL.md 的操作流程走;只有需要推理原始 WordprocessingML、扩展脚本或调试异常文档时,才需要进入 revisions-and-comments.md 这一层。SKILL.md 还给出了明确的安全约定:除非用户明确要求,绝不丢弃批注或修订accept-all/reject-all与批量删除批注均属于破坏性变换,操作前必须确认范围。

脚本运行环境仅需两个 Python 依赖(见 SKILL.md 的 Core dependency 段):

python3 -m pip install python-docx lxml

所有脚本均以python3 <脚本路径> ...方式调用,例如:

python3 packages/ekko-agent/skills/docx/scripts/docx_read.py input.docx --json python3 packages/ekko-agent/skills/docx/scripts/docx_validate.py output.docx

二、修订追踪的 XML 结构:w:ins/w:del

Word 将"运行级"(run-level)修订记录为段落w:p内部的包装元素(wrapper element),命名空间为:

http://schemas.openxmlformats.org/wordprocessingml/2006/main

revisions-and-comments.md 给出的典型结构如下:

<w:p> <w:r><w:t>Base </w:t></w:r> <w:ins w:id="1" w:author="Editor" w:date="2026-01-02T03:04:05Z"> <w:r><w:t>inserted text</w:t></w:r> </w:ins> <w:del w:id="2" w:author="Editor" w:date="2026-01-02T03:04:05Z"> <w:r><w:delText>deleted text</w:delText></w:r> </w:del> </w:p>

两个关键事实决定了脚本的全部行为:

  1. 被删除的文本存放在w:delText而非w:t。正因为如此,普通的纯文本提取(如 python-docx 的paragraph.text)天然呈现"接受修订"后的视图——插入可见、删除隐藏。这可以从 docx_read.py 的 docstring 中得到印证:"Body text is the accepted/as-is text (python-docx ignores deleted-in-revision text and shows inserted text)"。
  2. 决议(resolve)语义是确定性的,见下表:
修订类型动作处理方式
w:ins接受(accept)解包(unwrap):把子 runs 上移到父级,移除包装元素
w:ins拒绝(reject)移除包装元素及其全部内容
w:del接受(accept)移除包装元素及其全部内容
w:del拒绝(reject)把每个w:delText重命名为w:t,然后解包

上述"解包"逻辑在 docx_revisions.py 中有直接实现:_unwrap找到父元素中当前元素的位置,把其子节点逐个插入到原位置并移除包装器;_apply则按上表分支执行——拒绝删除时对delText改名再解包,从而实现"恢复被删文本"。

三、docx_revisions.py:五个子命令与调用链

docx_revisions.py 提供五个子命令(对应argparsesubparsers):

子命令说明额外参数
list以 JSON 列出全部修订:id、author、date、type、text
accept-all接受全部插入与删除-o/--output
reject-all拒绝全部插入与删除-o/--output
acceptw:id接受单个修订--id(必填)
rejectw:id拒绝单个修订--id(必填)

所有命令的通用参数是输入path-o/--output省略时原地覆盖输入文件(源码中out = args.output or args.path),因此生产环境建议总是显式传-o

典型用法(摘自脚本 docstring):

python3 docx_revisions.py list report.docx python3 docx_revisions.py accept-all report.docx -o accepted.docx python3 docx_revisions.py reject report.docx --id 3 -o out.docx

输出为结构化 JSON,例如list返回{"ok": true, "revisions": [...]}accept/reject返回{"ok": true, "output": ..., "resolved": n, "action": "accept"|"reject"}。当按--id决议但找不到该 id 时,返回{"ok": false, "error": "no revision with id ..."}并以退出码 1 结束(见 docx_revisions.py)。

覆盖范围:正文、表格、页眉页脚

脚本遍历的是"body 根 + 每个页眉/页脚部件根",核心是root.iter(W+"ins", W+"del"),它按文档顺序递归查找任意深度的元素。提供这套遍历的是公共模块 docx_common.py 中的iter_part_roots:它依次产出 body 根,以及每个 section 的 header / footer / first_page_header / first_page_footer / even_page_header / even_page_footer 的 XML 根(用id(part._element)去重,避免同源部件重复处理)。因此:

  • 正文段落、表格单元格(含嵌套表格)、页眉、页脚、文本框中的修订都能被发现和决议;
  • 修订可以出现在任何允许块级内容(block content)的位置。

关于w:id的注意事项

w:id的值在每个修订元素上是唯一的,但一次逻辑上的编辑会话可能产生多个元素。因此accept/reject --id精确作用于携带该 id 的一个或多个元素——源码resolve()rev_id is None or el.get(q("id")) == rev_id正是这种"按 id 精确匹配"的语义。

四、脚本不处理的修订类型与检测手段

revisions-and-comments.md 明确列出了脚本不做决议、仅检测的修订类型:

  • 段落标记修订(paragraph-mark revisions,即w:pPr上的w:rPr/w:ins);
  • 表格行插入/删除(w:trPr/w:ins);
  • 格式变更记录(w:rPrChangew:pPrChange);
  • 移动修订(w:moveFrom/w:moveTo)。

其中移动修订在常见编辑器中较为罕见,文档建议:如果文档中存在移动修订,直接用 Word 本身处理,不要猜测

这些类型的"检测"由 docx_read.py 的--revisions参数完成,其detect_revisions()实现方式非常轻量:直接以 zipfile 打开 .docx,扫描word/下所有 XML 部件的原始字节,匹配<w:ins<w:del<w:rPrChange以及word/comments部件是否存在(见 docx_read.py),返回has_tracked_changescomments等布尔标记。建议任何编辑操作前先运行它做"是否有修订/批注"的摸底。

五、批注的"三件套"XML 结构

批注由三个相互协作的部分组成(见 revisions-and-comments.md 的 Comments 一节):

  1. word/comments.xml部件——每个批注一个w:comment元素,携带w:idw:authorw:initialsw:date及正文段落。它通过关系类型.../comments与 document.xml 关联,内容类型为application/vnd...wordprocessingml.comments+xml,同时需要在[Content_Types].xml中登记 override——python-docx 的 part 机制在部件注册时会自动补上。
  2. 故事(story)中的范围标记——锚定文本之前放置w:commentRangeStart w:id="N",之后放置w:commentRangeEnd w:id="N"
  3. 引用 run——一个包含w:commentReference w:id="N"w:r,紧跟范围结束标记之后,它把批注气泡与位置绑定。

三者的位置关系可示意为:

<w:p> <w:r><w:t>Q3 </w:t></w:r> <w:commentRangeStart w:id="0"/> <w:r><w:t>revenue</w:t></w:r> <w:commentRangeEnd w:id="0"/> <w:r><w:commentReference w:id="0"/></w:r> </w:p>

六、docx_comments.py:list / add / delete 的实现细节

docx_comments.py 提供三个子命令:

子命令说明关键参数
list按批注输出 JSON:id、author、initials、date、text、anchored_textpath
add--target文本首次出现处锚定新批注--target--text必填;--author(默认Hermes)、--initials--xml
delete--id删除批注及其全部范围标记--id必填

list:XML 层的锚定文本重建

list/delete始终工作在 XML 层,因此能处理任何生产者生成的文档。anchored_text的重建算法(见 docx_comments.py)是:遍历每个部件根(同样复用iter_part_roots,保证按文档顺序),维护一个"活跃 id 集合"——遇到commentRangeStart加入 id,遇到commentRangeEnd移除 id;期间遇到的所有w:t文本都追加到该 id 的文本缓冲中。这保证跨 run、甚至跨段落锚定的文本都能被正确拼接。

add:先切分 run 再锚定

add的第一步是把目标文本隔离成完整的 runfind_anchor_runs在全文(正文+表格+页眉页脚,经 docx_common.py 的iter_all_paragraphs)中查找--target的首次出现;若匹配起点或终点落在某个 run 中间,_split_run会在边界处把 run 一分为二——切分时会深拷贝w:rPrright = deepcopy(run_el)),因此格式(粗体、斜体、颜色等)得以保留,并且新w:t会设置xml:space="preserve"防止前后空格丢失。

随后按环境二选一:

  • python-docx >= 1.2:使用原生document.add_comment(runs, ...)API,由 python-docx 自己创建 comments 部件、范围标记和引用 run(见add_comment_native);
  • 旧版本或显式--xml:脚本自行构建word/comments.xml——通过 OPC 层创建Part(pack URI 为/word/comments.xml,内容类型为 comments+xml),用part.relate_to(part, RT.COMMENTS)注册关系,再手工插入范围标记与引用 run(见add_comment_xml)。为让编辑结果能写回保存,代码还给 part 动态换上了自定义 blob 属性,每次保存时重新序列化 live 的 XML 树。

新批注的 id 由_next_id计算:取 comments 部件中现存全部数字 id 的max + 1,避免冲突。

delete:同时清理四类痕迹

删除批注会移除w:comment元素以及该 id 的全部三种标记commentRangeStartcommentRangeEndcommentReference),其中引用 run 标记还会连带删除其外层w:r(见 docx_comments.py)。被锚定的文档正文文本不受影响——这一点在测试中也被明确断言:删除后docx_read.py --text仍能读到完整句子。

commentsExtended.xml 的边界

现代 Word 还会写出commentsExtended.xml,用于记录回复(threading)与"已解决"(resolved)状态。脚本既不读取也不产出该部件:回复和 resolved 标记在此不可见,由本 Skill 添加的批注都是顶层(top-level)批注。这是使用前必须知晓的能力边界。

七、实战组合:完整操作流程

结合 SKILL.md 的工作流,一个典型的"修订+批注"自动化场景如下:

# 1. 摸底:是否有修订/批注 python3 docx_read.py report.docx --revisions # 2. 查看全部修订 python3 docx_revisions.py list report.docx # 3. 拒绝某条错误的插入(按 id) python3 docx_revisions.py reject report.docx --id 3 -o step1.docx # 4. 接受其余全部修订 python3 docx_revisions.py accept-all step1.docx -o step2.docx # 5. 在关键段落添加批注 python3 docx_comments.py add step2.docx --target "Q3 revenue" \ --text "Needs a source" --author "Reviewer" --initials "R" -o step3.docx # 6. 查看批注(含锚定文本) python3 docx_comments.py list step3.docx # 7. 结构校验后交付 python3 docx_validate.py step3.docx

其中第 7 步 docx_validate.py 做的是"健康检查"而非完整 XSD 校验:验证 zip 可读、必需部件存在、所有关系可解析(悬空引用报错)、r:id/r:embed引用有效、嵌入图片非空且魔数正确、文档引用的样式 id 在 styles.xml 中存在,并尝试用 python-docx 打开。任何 error 级问题都会使退出码为 1。

八、测试套件如何背书这些语义

这些行为并非仅靠文档描述,端到端测试 test_docx_skill.py 直接以子进程方式运行脚本并断言结果:

  • TestRevisions:构造同时含正文与表格单元格修订的文档(_add_ins/_add_del直接向w:p注入w:ins/w:del),验证list输出 4 条记录、accept-all后正文变为Base ADDED且表格变为Cell CELLADDreject-all后为Base REMOVED/Cell CELLGONE、按--id单条决议只影响目标 id、未知 id 返回退出码 1。
  • TestComments:验证addlistdelete全链路——anchored_text精确等于目标文本、文档正文不受影响、--xml强制走回退路径后文件仍可被 python-docx 正常打开、目标文本不存在时报错退出。
  • 测试还固定了LC_ALL=CPYTHONIOENCODING=utf-8,证明脚本在无本地化环境下的非 ASCII 文本处理是稳定的。

这些测试文件位于 packages/ekko-agent/skills/docx/tests,是阅读本文后继续深挖底层行为的最佳入口。

九、总结与安全边界

  • 修订决议是纯 XML 层的确定性操作:接受插入=解包,拒绝插入=移除;接受删除=移除,拒绝删除=delText改名w:t后解包。
  • 批注由 comments 部件、范围标记、引用 run 三件套构成;list/delete通用兼容任何生产者,add会先切分 run 保留格式,再选择原生 API 或 XML 回退路径。
  • 边界:段落标记修订、表格行修订、格式变更、移动修订只检测不决议;commentsExtended.xml的回复与 resolved 状态不可见。
  • 安全:删除批注或批量决议修订属于破坏性操作,应遵循 SKILL.md 的约定——先docx_read.py --revisions摸底、保留可恢复的原始副本、默认输出到新文件、操作后运行docx_validate.py校验,并在布局敏感的文档上用 LibreOffice 渲染核对。

对于需要对接 Word 协作工作流的 Agent 与自动化管线,理解本文的 WordprocessingML 细节,是避免"修订丢失""批注错位""文件损坏"等问题的前提。

  • AI 应用
  • 人工智能
  • AI Agent
  • 本地部署
  • 前端
  • 后端
  • 工作流自动化

【免费下载链接】ekko-studio

Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.

项目地址:https://gitcode.com/gh_mirrors/he/ekko-studio
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 22:01:09

孪生神经网络实战:PyTorch实现点选验证码识别

简介&#xff1a;本资源是一套基于孪生神经网络实现点选识别验证码的完整项目源码&#xff0c;面向计算机、人工智能、通信工程等专业的在校学生与教师&#xff0c;也适合具备一定Python基础、希望进阶深度学习实战的开发者&#xff0c;可用于毕业设计、课程设计、作业或项目初…

作者头像 李华
网站建设 2026/9/24 22:00:51

系统日志分析与错误代码定位实战:从单机排查到Graylog集中化管理

1. 系统日志分析到底在解决什么问题很多人第一次接触系统日志&#xff0c;都是被一个具体的报错逼到墙角&#xff1a;软件装不上、服务起不来、系统蓝屏、共享文件夹打不开&#xff0c;屏幕上弹出一串十六进制代码&#xff0c;搜索引擎搜出来的答案五花八门&#xff0c;照着做还…

作者头像 李华
网站建设 2026/9/24 22:00:22

MyBatis-Plus实体类字段忽略:@TableField(exist=false)用法与避坑指南

作为一个整天和 MyBatis-Plus 打交道的后端开发&#xff0c;我第一次遇到实体类加字段导致 SQL 报错&#xff0c;是在一个周四下午。当时订单列表接口突然全部 500&#xff0c;日志里冒出一句SQLSyntaxErrorException: Unknown column role_names in field list。我第一反应是数…

作者头像 李华
网站建设 2026/9/24 22:00:21

为什么哺乳动物没有绿色毛发?色素、结构色与进化的答案

开头去年冬天带着侄子去逛自然博物馆&#xff0c;他在两爬展柜前蹲了老半天&#xff0c;突然回头问了我一句&#xff1a;“叔叔&#xff0c;为什么有绿色的蜥蜴、绿色的鸟&#xff0c;就是从来没有绿色的猫和狗&#xff1f;”我当时被问得愣了一下。仔细想想&#xff0c;好像真…

作者头像 李华
网站建设 2026/9/24 21:59:55

Codex生成可编辑PSD:提示词工程与自动化实践

1. 为什么“让 Codex 生成 PSD”这件事值得单独聊先把结论摆在前面&#xff1a;让 Codex 直接吐出一个能用的 PSD 文件&#xff0c;本身并不难&#xff0c;难的是很多人把提示词写成了“许愿池”&#xff0c;指望一句话就让模型理解图层结构、命名规范、画布尺寸、色彩模式、字…

作者头像 李华
网站建设 2026/9/24 21:59:54

TLD+GOTURN双模态多摄像头目标跟踪实战

简介&#xff1a;本资源是一套面向计算机视觉开发者与高校研究者的多摄像头目标跟踪实战项目&#xff0c;聚焦安防监控等实际场景中跨视角目标持续追踪的难点问题&#xff0c;融合TLD&#xff08;跟踪-学习-检测&#xff09;与GOTURN&#xff08;基于CNN回归的目标跟踪网络&…

作者头像 李华