Install
openclaw skills install make-to-markdown工业级RAG Markdown物料生成技能,使用 markitdown 将各类文档和文件转换为 Markdown 格式。支持 .doc/.ppt 老格式自动预处理(Word/PowerPoint COM / LibreOffice)。启动时自动检测 OS/版本/能力,按平台选择最佳执行路径。触发词:转 Markdown / 转换文档 / markitdown / 文档转 md / 批量转换。当需要将 PDF、Word (.docx/.doc)、PowerPoint (.pptx/.ppt)、Excel (.xlsx, .xls)、HTML、CSV、JSON、XML、图片(含 EXIF/OCR)、音频(含语音转写)、ZIP 压缩包、YouTube 链接或 EPub 电子书转换为 Markdown 格式,为知识库提供统一的"通用语言"时触发此技能。
openclaw skills install make-to-markdown文档结构:SKILL.md = 核心运行时指令(Agent 执行必读),REFERENCE.md = 扩展参考与运维细节。非核心内容不得写入本文件。
-o 指定的输出文件若已存在,convert.py 启动前必须暂停并告知用户路径及已有内容来源,获得明确授权后方可覆盖。禁止自动跳过或静默覆写。File is encrypted / 密码保护提示)时,首次失败即终止该文件转换,提示用户解密后重试。禁止反复尝试、猜测密码或绕过保护。.doc / .ppt 旧格式且在 COM 和 LibreOffice 均不可用的环境下,必须暂停并告知用户安装 Office 或 LibreOffice。禁止静默跳过该文件或输出空结果。markitdown / uvx markitdown / 原生库进行转换。所有转换必须经由 scripts/convert.py 统一入口,以确保依赖补全、降级兜底和后置清洗管线完整执行。python_executor,避免 shell_executor(PowerShell/cmd 可能因 WinError 2 找不到网络驱动器路径)shell_executor 通常可用,但路径中含空格或特殊字符时回退到 python_executorpathlib.Path 处理路径,无需硬编码平台判断rm -rf / del /f /s / Remove-Item -Recurse -Force 等递归强制删除 | 格式化(format / mkfs) | 对工作目录外任意路径的批量写操作git reset --hard / git clean -fdx | 数据库 DROP TABLE / TRUNCATE / DROP DATABASE | 清空回收站 | shred / wipe 等安全擦除diskpart clean | 注册表 reg delete 批量删除 | sc delete 删除系统服务 | bcdedit 修改引导配置push --force / reset --hard
违反本禁令的脚本或流水线必须被拒绝执行并告知用户原因post_clean.py 的输出不得再次作为 convert.py 的输入;convert.py 的输出不得再次调 post_clean.py。二次清洗会导致摘要叠加、噪声模式重复匹配、标题层级二次修复误触发。核心入口:scripts/convert.py。依赖检测 → uv tool install markitdown --with=... 安装完整 extras → 转换 → 失败降级 → 后置清洗。零人工干预。
🟢 最小可用示例:
python scripts/convert.py input.docx。全选项见 §3,异常处理见 §5。
当用户要求转换文件时,只需调用一次 scripts/convert.py,脚本内部自动完成以下全部步骤:
(.doc/.ppt?)→预处理为 .docx/.pptx → 依赖检测 → 自动安装 → markitdown extras → markitdown 转换 → (失败则) 原生降级 → 后置清洗 → 输出
.docx/.pptx,详见 §2.5 旧格式预处理。pip install 安装,120 秒超时。
3.5. markitdown extras(v2.4):自动检测 pip 和 uv 两侧的 markitdown 可选依赖完整性;缺失时自动安装,避免 MissingDependencyException。pypdf 已就绪则跳过全部安装。markitdown 对非纯文本格式采用可选依赖策略:pip install markitdown 仅装核心,以下格式需额外包。convert.py 会在转换前自动检测并安装缺失依赖。
| 扩展名 | pip 包 | import 模块 | markitdown extra 名 |
|---|---|---|---|
| .docx | python-docx | docx | docx |
| .doc | python-docx | docx | docx |
| .xlsx | openpyxl | openpyxl | xlsx |
| .xls | xlrd | xlrd | xls |
| .pptx | python-pptx | pptx | pptx |
| .ppt | python-pptx | pptx | pptx |
| pypdf | pypdf | ||
| .epub | ebooklib | ebooklib | epub |
以下格式无需额外依赖,标记为完整安装时可省略 --with:
.html .htm .csv .json .xml .png .jpg .jpeg .gif .bmp .tiff .webp .mp3 .wav .m4a .ogg .flac .zip
uv 环境等效命令(手动安装参考,脚本自动处理):
# 安装 markitdown 及全部可选依赖(一次性)
uv tool install markitdown --with python-docx --with openpyxl --with python-pptx --with pypdf --with xlrd --with ebooklib
# 或仅安装当前文件所需依赖
uvx --with python-docx markitdown input.docx -o output.md
.doc 和 .ppt(Office 97-2003 二进制格式)不被 markitdown 直接支持。convert.py 在转换前自动尝试两种方式转为新版格式,其中 LibreOffice 按四级优先级链定位 soffice 可执行文件:
| 旧格式 | 目标格式 | 方式 1 | 方式 2 |
|---|---|---|---|
.doc | .docx | Word COM (仅 Windows, Documents.Open → SaveAs2) | soffice --headless --convert-to docx |
.ppt | .pptx | PowerPoint COM (仅 Windows, Presentations.Open → SaveAs) | soffice --headless --convert-to pptx |
LibreOffice 定位优先级链(v2.8):
shutil.which("soffice") — PATH 中的 soffice$SOFFICE_PATH 环境变量os.path.exists 校验)"soffice" 兜底🔴 CHECKPOINT:若 .doc/.ppt 旧格式且 COM 和 LibreOffice 均不可用,必须立即暂停,明确告知用户需安装 Microsoft Office(含 Word/PowerPoint)或 LibreOffice,等待用户确认后再继续。禁止静默跳过旧格式文件。
.xls 则由 markitdown 原生支持([xls] 可选依赖组,底层 xlrd),无需预处理。
# 单文件转换(自动检测依赖 + 清洗)
python scripts/convert.py input.docx -o output.md
# 使用默认输出名(同目录下 input.md)
python scripts/convert.py input.pdf
# 跳过文档摘要
python scripts/convert.py data.xlsx --no-summary
# 静默模式
python scripts/convert.py report.pptx -q
🔴 CHECKPOINT:若
-o指定的输出文件已存在,convert.py 会直接覆盖。覆盖前必须暂停并告知用户输出路径已存在已有内容(如上次转换结果、手动修正后的版本),等待用户确认是否覆盖。若用户拒绝,改用-o <新文件名>或先备份。
当 markitdown 无法处理(依赖缺失且自动安装失败、超时、加密文档等),convert.py 自动启用原生降级:
| 格式 | 降级方案 | 能力 |
|---|---|---|
| .docx / .doc | python-docx | 提取段落样式→标题层级、表格→Markdown 表格、粗体短文本→内嵌标题 |
| .xlsx / .xls | openpyxl | 每个 Sheet → H2 章节,数据行→ Markdown 表格 |
| .pptx | python-pptx | 每页幻灯片→ H2 章节,文本层级保留,表格自动转换 |
降级转换器的输出同样经过后置清洗流程,质量标准与 markitdown 一致。
🔴 CHECKPOINT:批量转换前必须向用户确认:源目录路径、目标输出路径、待转换文件数量(预估)、限定格式(如有
--ext)。等待用户确认后再启动。禁止不经确认即递归遍历整个目录。
# 递归转换整个目录
python scripts/batch_convert.py <源目录> <输出目录>
# 仅转换指定格式
python scripts/batch_convert.py docs/ output/ --ext .pdf .docx --clean
# 清洗单个文件
python scripts/post_clean.py output.md
# 注入摘要后输出到新文件
python scripts/post_clean.py output.md --summary-file summary.txt -o cleaned.md
# 仅检查结构不修改
python scripts/post_clean.py output.md --check-only
convert.py 内联了后置清洗逻辑,无需单独调用 post_clean.py。清洗流程:
噪声模式去除(6 类 15+ 正则):
| 类别 | 匹配模式 |
|---|---|
| 工具水印 | [Generated by ...] / Converted by markitdown / [Created with ...] / Powered by ... markitdown |
| 页码标记 | Page X of Y / [PAGE X] / --- Page X --- |
| 机密标记 | Confidential / DRAFT / Internal Use Only / For Review Only |
| 版权声明 | © YYYY / All Rights Reserved |
| 空白压缩 | 连续 3+ 空行 → 2 行 |
| 行尾空格 | 移除空行末无用空格 |
标题层级修复:
表格分隔符补全:检测 |---| 缺失行并补全。
文档摘要注入:提取首段或首个 H1 后段落作为摘要。
完整清洗项及正则模式详见 REFERENCE.md §1。
| 错误类型 | 典型症状 | 原因 | 处理方式 |
|---|---|---|---|
| 依赖安装失败 | pip install 超时或报错,转换终止 | Python 包索引不可达 / 权限不足 / 包版本冲突 | 终止转换,输出具体缺失的包名,提示用户手动 pip install <pkg> |
| markitdown 失败 | markitdown CLI 返回非零退出码 | 文件格式损坏 / markitdown extras 缺失 / 未知格式 | 自动切换到原生降级转换器(python-docx/openpyxl/python-pptx),降级后仍经后置清洗 |
| 原生降级也失败 | 降级转换器报错或输出空内容 | 加密文档 / 文档严重损坏 / 格式不兼容 | 终止转换,输出"文档可能已加密或格式不受支持" |
| 加密文档 | markitdown + 原生库均提示 File is encrypted 或类似信息 | 文档受密码保护 | 终止转换,提示"文档已加密,请解密后重试"。禁止反复重试 |
| 旧格式无预处理环境 | .doc/.ppt 文件但 COM 和 LibreOffice 均不可用 | Windows 未装 Office pywin32;Linux/macOS 未装 LibreOffice | 🔴 CHECKPOINT:暂停,告知用户安装 Office 或 LibreOffice |
| 批量场景单文件失败 | 个别文件转换报错,其余正常 | 该文件格式异常 / 权限不足 / 正在被其他程序占用 | 跳过该文件,在 _conversion_errors.log 记录文件名+错误原因,其余文件继续 |
| 输出路径不可写 | convert.py 写文件时报 PermissionError | 目标目录不存在且无创建权限 / 磁盘满 | 终止,提示检查输出路径权限和磁盘空间 |
| uv 不在 PATH | ensure_markitdown_extras() 检测不到 uv | uv 未安装或未加入 PATH | 跳过 markitdown extras 安装,转换仍可进行(仅限内置支持格式) |
| 网络驱动器不可达 | [WinError 2] 或 FileNotFoundError | 网络驱动器断连(E:\Marvis_Data 等) | 提示用户检查网络驱动器连接状态 |
🔴 CHECKPOINT(参见 §2.5 预处理流程):以下两类情况必须立即暂停,等待用户介入,禁止自动跳过或反复重试:
- 加密文档(
File is encrypted):首次检测到即终止该文件转换,提示用户解密后重试- 旧格式无预处理环境(.doc/.ppt 且 COM 和 LibreOffice 均不可用):告知用户安装 Office 或 LibreOffice
| 静默失败形态 | 检测机制 |
|---|---|
| 清洗跳过某些噪声模式 | §4 噪声模式库覆盖 15+ 正则 |
| 降级转换器输出格式不一致 | 降级输出同样经过后置清洗 |
| 批量转换某文件失败未记录 | _conversion_errors.log 自动汇总 |
| 标题层级修复误触发(二次修复) | v2.6+ 标题修复仅执行一次,有状态标记 |
| markitdown extras 缺失但转换仍"成功" | convert.py 启动时检测全部可选依赖 |
详见 REFERENCE.md §7。
转换成功后反馈示例:
成功将
report.pdf转换为 Markdown [markitdown] | H1=1 H2=5 H3=12 | 表格=3 | 48,256 bytes 输出: E:\output\report.md
反馈格式固定模板(所有转换结果必须统一使用):
{状态}将 `{源文件名}` 转换为 Markdown [{转换方式}] | H1={n} H2={n} H3={n} | 表格={n} | {文件大小}
输出: {绝对路径}
| 字段 | 说明 | 取值 |
|---|---|---|
| 状态 | 转换结果 | 成功 / 失败 / 部分成功(批量场景) |
| 转换方式 | 所用引擎 | markitdown / 原生降级(python-docx) / 原生降级(openpyxl) / 原生降级(python-pptx) |
| H1/H2/H3 | 标题层级统计 | 整数,无标题时写 0 |
| 表格 | Markdown 表格数量 | 整数 |
批量转换反馈:
批量转换完成 | 总计=12 | 成功=10 | 失败=2
失败详情: _conversion_errors.log
输出: E:\output\markdown\
输出质量自检清单(7 项)已移至 REFERENCE.md §2。
前置校验:执行转换前,Agent 应通过 python -c "from pathlib import Path; import sys; sys.path.insert(0,'scripts'); assert Path('scripts/convert.py').exists(), 'convert.py missing'" 确认核心脚本存在。
| 脚本 | 用途 |
|---|---|
scripts/convert.py | 智能转换引擎(推荐):依赖自动补全 + markitdown + 降级兜底 + 内联清洗 |
scripts/batch_convert.py | 批量转换:递归遍历目录、保持结构、异常收集 |
scripts/post_clean.py | 后置清洗:去水印、注入摘要(含标题修复、面包屑注入、质量评分) |
scripts/platform_detect.py | 平台检测模块(v2.8):启动时检测 OS/版本/能力,best_office_tool() 返回最优办公软件路由 |
Windows / Linux / macOS 三平台完全支持。关键约束:convert.py 启动时自动检测平台能力;网络驱动器路径使用 python_executor 执行;全部脚本使用 pathlib.Path 适配路径分隔符。详见 REFERENCE.md §3。更新记录见 REFERENCE.md §6。
核心禁令(完整版见 REFERENCE.md §4):
python scripts/convert.pypython_executor--ext .pdf .docx .pptx禁止操作:post_clean.py 输出→ convert.py 输入(循环清洗);.xls 用 COM 预处理;空 --ext 参数;二次 convert 已成功输出。
每次转换完成后,Agent 必须对输出文件执行以下逐项检查。任一未通过 = 转换失败,需进入 §5 异常处理流程。
| # | 检查项 | 通过标准 | 验证命令 |
|---|---|---|---|
| V1 | 文件存在且非空 | 输出文件大小 > 0 字节 | python -c "import os; s=os.stat('<输出.md>'); assert s.st_size>0, '空文件'" |
| V2 | 编码正确 | 文件可被 UTF-8 解码且无乱码 | python -c "open('<输出.md>',encoding='utf-8').read()" |
| V3 | 无残留水印 | 不含 Generated by markitdown / Confidential / Page X of Y | `python -c "import re; t=open('<输出.md>').read(); assert not re.search(r'Generated by |
| V4 | 标题层级正常 | 至少存在 1 个 H1,且 H 层级无跳跃(H1→H3 无 H2 报错) | python -c "import re; t=open('<输出.md>',encoding='utf-8').read(); hs=re.findall(r'^(#{1,6})\s',t,re.M); levels=[len(h) for h in hs]; jumps=[i for i in range(1,len(levels)) if levels[i]-levels[i-1]>1]; assert len(hs)>0 and levels[0]==1, f'无H1' if not hs else f'H{levels[0]}打头'; assert not jumps, f'层级跳跃: {jumps}'; print('PASS')" |
| V5 | 表格完整性 | 无 ` | --- |
| V6 | 源文件对应 | 每 1 个源文件产生 1 个输出文件(批量模式无漏转) | python -c "print(len(os.listdir('<输出目录>')))" 对比源文件数 |
Agent 在完成转换并写盘后,必须在最终回复中逐项确认以下内容(直接输出结果,不展开过程):
回归测试用例(RT1-RT3)和 Python 一键验证 oneliner 已移至 REFERENCE.md §5。