技术路线图绘制(线框草图 → 全元素可编辑 PPT / DOC / 文档插图)
定位与核心承诺
两阶段工作流。核心承诺:成品中每一个框、每条线、每段文字都是原生可编辑对象(框=原生矩形、文字=原生文本框/形状内文本、箭头=原生连接符、弧线=原生曲线)。绝不整图贴图片——那会毁掉可编辑性。
- 阶段一:生成纯线框 SVG 设计草图(白底描边,浏览器即开即看),交用户确认布局、箭头语义、文字压缩。
- 阶段二:用户确认后,由同一份坐标参数生成原生成品。SVG 不进入成品(不走转换路线,避免样式降级);SVG 与成品共享同一套坐标参数,天然一致。成品形态三选一(见「横版 vs 竖版 vs doc」表):PPTX(
scripts/roadmap_gen.py)、DOCX(参考scripts/docx_native_reference.py)、SVG。
为什么线框用 SVG 先行:迭代快(浏览器即开)、可作独立矢量源复用、确认前置避免返工;而成品质量取决于"原生生成 vs 转换",与是否经过 SVG 无关。
信息图/文字密集图的硬边界:当图中中文技术标注 ≥ 30 条(如 RAG、NER、圈复杂度、遗漏变量偏误等术语密集出现),禁止用 AI 生图(banana-drawing 等)——AI 对密集小号中文零错字无保证,正式文档插图出错字即不可用。此类图一律代码绘制(SVG→原生 PPTX/DOCX),设计流程(学情分析→设计方案→审阅)不变,只替换生成步骤。
双入口:用户给没给描述性文本
入口 A:用户给了文本(项目书 / 方案段落)
- 结构抽取:识别
- 阶段/模块(3~5 个最佳;每个 3~4 个关键步骤);
- 箭头语义(谁流向谁,有无反馈闭环 →loop);
- 是否有"承载型"底座(平台/基础设施/部署 → 放底部通栏band,用虚线向上支撑各阶段;原文若说"承载上述所有功能"即属此类);
- 输入源(语料/资料/数据 → 顶部source节点)。 - 文字压缩:每框 10~14 字。示例:"利用命名实体识别和关系抽取…提取关键概念" → 「NER·关系抽取:概念/实体/关系」。压缩对照表要随草图一并给用户核对。
- 写 spec JSON(格式见
references/spec-format.md,样例在assets/)→ 跑脚本出 SVG 草图。 - 确认:草图 + 布局决策说明一起交用户;用 AskUserQuestion 给具体选项(确认生成PPT / 内容要调整 / 换一种布局 / 风格微调)。未确认不得生成 PPT。
入口 B:用户没给文本
用 AskUserQuestion 问清:① 主题与用途;② 阶段数量与名称(给出具体候选,如"按研发流程分 4 阶段:需求→设计→开发→测试",不要只给抽象标签);③ 横版还是竖版(见下表);④ 有无承载底座、反馈闭环。AI 据此起草 spec,然后走入口 A 的第 3~4 步。禁止在未确认结构的情况下直接出成品。
横版 vs 竖版 vs doc
| 用途 | layout | 画布 | 成品 |
|---|---|---|---|
| PPT 放映 / 评审 / 投影 | landscape |
1520 × H | 16:9 PPTX(13.33×7.5 in) |
| 插入 Word / A4 文档插图 | portrait |
900 × H(有 loop 时 1070 × H,右侧 170px 为回路车道) |
竖版 PPTX(宽 7.5 in,高按画布比例自适应,无白边);或直接出 A4 纵向 DOCX |
| 用户要求"适配 doc 比例" | — | A4 比例画布 | 直接交付 DOCX(原生可编辑形状),不交付 PNG。通用生成器:scripts/docx_spec_gen.py --spec spec.json --out-dir ./out(读同一份 spec,复用 layout 引擎坐标;横版→A4 横向、竖版→A4 纵向,自动等比铺满并居中 + 通用自检);手写坐标场景的范例:scripts/docx_native_reference.py(输出默认写当前目录,--out 可覆盖,勿写回脚本目录) |
DOCX 原生可编辑路线(2026-09 新增,王珏老师明确规则):用户说"适配 doc/文档比例"时,最终交付物就是 .docx——页面设 A4、四边距 0,画布 px 以 7560 EMU/px 映射(1000px→21cm 宽,1414px→29.7cm 高,恰好铺满 A4)。每个图元 = wps:wsp 浮动形状(wp:anchor 定位到 page、wrapNone、allowOverlap):
- 框 =
roundRect/round2SameRect(顶圆角标题条;adj = 圆角px ÷ min(w,h) × 100000),文字直接放形状内wps:txbx(点击形状即改字,编辑体验最好); - mc 门控(必做,2026-09 排查"文件损坏"后确立):每个手写图元必须整体包一层
<mc:AlternateContent><mc:Choice Requires="wps"><w:drawing>…</w:drawing></mc:Choice><mc:Fallback><w:pict/></mc:Fallback></mc:AlternateContent>——Word 原生输出对 2010 扩展命名空间(wps)一律如此门控,缺失时 Word 报"文件已损坏/内容有问题"。两个附带坑:①xmlns:w必须声明在包装层(mc:Fallback里的<w:pict/>在w:drawing的作用域之外);②Requires="wps"引用的xmlns:wps也要在包装层声明。 - 线/箭头 =
straightConnector1(竖线 cx=0),箭头写<a:tailEnd>。回路弧有两种等价实现,按生成器选用:① 通用生成器docx_spec_gen.py用custGeom折线(13 点采样,与 PPTX 版的 freeform 一致,改动风险最低);② 参考范例docx_native_reference.py用prst="arc"+ adj1/adj2(角度×60000,弧更圆滑)。注意 arc 恒顺时针扫——若箭头要指向"起点"端,把三角箭头放headEnd而非tailEnd。 - 竖排中文标注 = 文本框
bodyPr vert="eaVert"; - 字号 px→半磅:画布尺寸可变时用
px × S/12700 × 2(S = 页面 EMU ÷ 画布 px,随缩放;通用生成器即此式,13px→7pt 左右);画布固定 7560 EMU/px 时可用固定 ×1.5(参考范例即此式)。线宽:pt → EMU = pt × 12700;若按 px 给线宽则px × 9525。 - 零散文本 = 无填充无边框透明文本框;同段多色用多个
<w:r>。
python-docx 注入坑位:① 每段 runs 结构要归一化(tuple=单 run 段,list=多 run 段),直接混传会 too many values to unpack;② 空 Document 没有段落,doc.paragraphs[0] 会 IndexError,用 doc.paragraphs[0] if doc.paragraphs else doc.add_paragraph();③ 自检解析用全 URI 命名空间(lxml findall 不认未声明的 wp: 前缀)。自检指标:anchors 数、prstGeom 分布、tailEnd/headEnd/prstDash 计数应与 SVG 图元一一对应。
DOCX 渲染验证策略(本机无 Word/LibreOffice 时):DOCX 本身无法本机渲染成图,验证靠两条:① XML 结构自检(图元计数与 SVG 一一对应,见上);② 坐标零偏差复用——DOCX 图元坐标直接取自已确认的 SVG 版(同一份布局常量),版式已在 SVG 阶段经用户目检,DOCX 只是把 px 换算为 EMU。若需要给用户可视预览,可用 Edge/Chrome headless 截图 SVG(msedge --headless=new --screenshot=out.png --window-size=W,H --force-device-scale-factor=3 file:///...svg,HTML 包一层去 body margin)——注意此 PNG 仅作预览,不是交付物(用户明确规则:适配 doc 时不交付 PNG)。另:Word 打开边距为 0 的满版文档会提示"超出可打印区域",属正常,需在交付说明中告知用户点"忽略"。
DOCX 打开报错诊断(Word 报"文件已损坏/发现无法读取的内容")
2026-09-25 实战教训:手工注入 wps 形状的 DOCX,静态检查全过但 Word 拒开。排查流程按性价比排序:
- 先查 mc 门控(本技能 BUG 的真凶,最高嫌疑):
document.xml中每个含wps:wsp的w:drawing是否包在mc:AlternateContent/mc:Choice(Requires="wps")内。核验:AlternateContent/Choice/Fallback计数 = anchors 数。修复方法见上文「mc 门控」。 - 包级检查(lxml,5 分钟):zip 完整性(
testzip)→ 全部 XML 部件 well-formed →[Content_Types].xml覆盖所有部件 → rels 无断链、r:id无悬空引用。 - 结构检查:anchor 必需属性、子元素顺序(simplePos→positionH/V→extent→effectExtent→wrap*→docPr→cNvGraphicFramePr→graphic)、
docPr id唯一非零、wsp 含cNvSpPr/cNvCnPr+bodyPr(末位)、a:ln内 prstDash 在 headEnd/tailEnd 前、custGeom 六件套、pPr/rPr首子元素、控制字符扫描。 - XSD 规范级校验(前 3 步全过仍报错时,可选 / 外置):本技能的轻量质检(mc 门控计数、几何 lint、结构检查,见
scripts/与tests/)已内置且自包含,不依赖 XSD,可独立保证交付物可编辑、可打开。若需 ISO-IEC29500-4 schema 级别的严格校验,可单独获取ooxml_validator工具(约 1.2 MB / 39 个.xsd+validate.py,来自raw.githubusercontent.com/OpenCoworkAI/open-cowork/main/.claude/skills/docx/scripts/ooxml/)。因 skillhub 不接受.xsd文件类型,该工具不随技能分发包提供,需自行部署到本地后再用。用法——必须先造一个空白基线文件当--original:
# 假设校验器已单独部署到本地 ./ooxml_validator/
python ooxml_validator/validate.py 待检.docx --original 空白基线.docx -v
两个大坑:
- --original 传同一个文件会把所有报错当作"原有错误"忽略——校验目标与 original 绝不能是同一文件,等于没校验(首次实测就踩了这个坑);基线用 python-docx/python-pptx 生成的空文档即可(见脚本头注)。
- ISO schema 对 wps:/w14: 等微软扩展命名空间一律报 "no declaration found"——这是噪声可过滤,真错误是非扩展命名空间的报错;wps 全量校验需微软扩展 schema。
5. 本机限制:ECMA 官网 Schema 下载链接全是软 404(curl rc=35,只能用 GitHub 镜像);Word/WPS COM 自动化被安全策略拦截,无法程序化验证打开行为——最终判定只能靠用户实开 Word 验证,交付说明里要写明"请打开验证"。
经验:静态检查(1~3)全过≠Word 能开。手写 OOXML 与 Word 原生输出的任何结构差异都可能触发严格校验,因此生成器必须逐字模仿 Word 原生输出结构(mc 门控是最显著差异点),而不是只满足 schema。
2026-09-25 审计补充(自检必须覆盖几何,不能只测文字宽度):roadmap_gen.py --check 已加几何 lint(框/标注/弧越界 + loop-label 与 source 重叠告警),起因是竖版回路标注越出画布右缘(x=908 > W=900)却仍报"全部通过"——SVG 里被裁掉、PPT 里挂到幻灯片外,文字宽度检查完全抓不到。任何新增布局改动后,必须确认自检输出含"几何检查: 无越界/无重叠"。
竖版布局规则:阶段卡纵向堆叠、箭头竖直向下、回路弧外凸于卡片右侧(有 loop 时画布加宽到 1070px,右侧 170px 为回路车道;卡片列中心 = MX + card_w/2,不是画布中心)、底栏纵向列表、末卡与底栏之间用实线箭头表承载。
横版布局规则:画布 1520 × H,top=130(有 source 时 174),MX=80;卡宽 = (1520−160−(n−1)×gap)/n(gap:n≤3 取 110,n≤5 取 70);回路弧在卡片上方(ry=36),其标注放弧内侧 y=top-ry+8;band 为底部通栏(高 150、条目高 86),虚线向上支撑;箭头标注受 70px 间隙挤压,必须用缩写。
A4 比例速查:画布 1000×1414 px(1:1.414)× 7560 EMU/px = 21×29.7 cm 整页铺满;若需留页边距,等比缩小画布或改 wp:posOffset 整体偏移,EMU 换算恒用 7560/px。
脚本用法
# 基本用法(同时产出 SVG 草图 + PPTX)
python roadmap_gen.py --spec spec.json --out-dir ./out --name my_roadmap
# 覆盖 spec 中的版式
python roadmap_gen.py --spec spec.json --out-dir ./out --layout portrait
# 生成并自检(必做)
python roadmap_gen.py --spec spec.json --out-dir ./out --check
--check 输出:文字溢出预警(框内文字 + 非通栏标注,逐条估宽 vs 可用宽)、几何检查(越界/重叠)、图元统计、PPTX 形状类型分布、箭头 tailEnd 与虚线计数。交付前必须跑 --check 并逐条核验——文字宽度与几何两项都要是"通过/无"。
DOCX 生成(适配 doc 时)
# 通用生成器:任意 spec → 原生可编辑 DOCX(横版→A4 横向,竖版→A4 纵向;自动铺满居中 + 自检)
python docx_spec_gen.py --spec spec.json --out-dir ./out --name my_roadmap
python docx_spec_gen.py --spec spec.json --out-dir ./out --layout portrait # 覆盖 spec 版式
自检项:anchors / prstGeom / custGeom / tailEnd / headEnd / prstDash 计数、mc 门控三件套计数 = anchors 数、docPr id 唯一、图元总数 = anchors。任一项不 OK 不得交付。
docx_native_reference.py 是「无 spec、手写坐标」场景的可运行范例(含 prst="arc" 圆弧与竖排文本写法),输出默认写当前目录。
回归测试(改动引擎后必跑)
python tests/audit_regression.py # T1~T8 应全部 PASS;任一 FAIL 说明发生回归
覆盖:横/竖版实跑自检、通用 DOCX 生成器(双版式)、几何 lint、PPTX 线属性顺序、4 类边缘用例(2 卡无 band / 4 卡 / 6 卡 / band 5 项)、mc 门控计数。用 T9 复核具体成品 docx 时设 AUDIT_DOCX=<路径>。
本机(王珏老师 Windows 环境)运行注意
- 用隔离 venv:
C:\Users\jowa\.workbuddy\binaries\python\envs\default\Scripts\python.exe(Windows venv 是Scripts/不是bin/;需pip install python-pptx)。 - 本机 Git Bash 的 PATH 已损坏(
ls/tail/head/dirname不可用),且会把/c/...参数拼成e:\c\...。正确姿势:用 venv 全路径 + runpy 在 python 内部传 Windows 路径:
VPY=/c/Users/jowa/.workbuddy/binaries/python/envs/default/Scripts/python.exe
"$VPY" -c "
import runpy, sys
base = r'C:\Users\jowa\.workbuddy\skills\tech-roadmap-ppt'
sys.argv = ['roadmap_gen.py', '--spec', base + r'\assets\example_spec_landscape.json',
'--out-dir', 'output', '--name', 'demo', '--check']
runpy.run_path(base + r'\scripts\roadmap_gen.py', run_name='__main__')
"
硬性坑位(代码已封装,改动前必读)
- 箭头:python-pptx 1.0.2 的
LineFormat没有end_arrowhead属性(也没有MSO_ARROWHEAD枚举)。必须直接写 OOXML:在<a:ln>下追加<a:tailEnd type="triangle" w="med" len="med"/>(脚本add_arrowhead())。 - 曲线:
build_freeform(x, y)只接受标量 EMU(不是元组),且没有add_curve_to。曲线一律add_line_segments(顶点列表, close=False)折线近似,圆弧取 13 个采样点(脚本 loop 部分即是模板)。 - 中文字体:run 需同时设置 latin 与 eastAsia(
<a:ea typeface="Microsoft YaHei"/>),否则中文回退主题字体(脚本set_run_font())。 - 独立图元:每个 rect/connector/freeform/textbox 单独创建并带 id 语义命名,禁止 group 合并、禁止贴图。
- 箭头标注与回路弧的避让(2026-09-25 实战补两坑):
- 横版回路弧标注放「弧内侧」,不放弧顶上方:弧高仅ry=36,弧顶上方紧邻副标题带(y 62~84),标注放上方(原y=top-ry-26=68)必与副标题叠成一团。正确位置y=top-ry+8(弧内侧、卡片上方,仍与卡片顶留 ≥10px)。
- 箭头标注(flow-label)宽度必须 ≤ 卡片间隙:横版 5 卡时间隙仅 70px,原标签固定w=170px会横跨并压住相邻卡片的条目框。标签一律用术语缩写(如QKV 投影/多头并行/Add & Norm/上下文表征,均 ≤62px);生成器现已把 flow-label 的w设为卡间隙,超宽即由自检预警。
- loop 的起止卡不得包含 source 所在卡:否则弧线撞 source 节点(自检已告警)。 - 线属性子元素顺序:OOXML
CT_LineProperties要求prstDash在headEnd/tailEnd之前。PPTX 侧表现为set_dash()必须在add_arrowhead()之前调用(2026-09 审计发现:freeform 回路弧曾生成<a:ln><solidFill/><tailEnd/><prstDash/></a:ln>的违规顺序);DOCX 侧构造a:ln时同理{dash}{tailEnd}顺序不可颠倒。 - 输出路径纪律:生成器一律把成品写到
--out-dir/当前目录,绝不写回脚本所在目录——否则成品会被打进技能分发包(2026-09 审计发现参考脚本曾把 docx 写进scripts/)。打包前顺手确认scripts/下只有.py。提交 skillhub 前确认技能根目录不含.xsd等被拒文件类型——重型 XSD 校验器(ooxml_validator,含 39 个.xsd)已外置到工作区_skill_backup/,切勿放回技能目录(放回会导致 skillhub 报"不允许的文件类型")。本技能的轻量质检(mc 门控计数、几何 lint、结构检查)均自包含,不需要 XSD。
交付前质量门
- [ ]
--check通过:文字宽度"全部通过" + 几何检查"无越界/无重叠",tailEnd/虚线数量符合预期(箭头数 = 阶段间箭头 + source 箭头 + 回路弧 + band 箭头[竖版]);并且肉眼核验一遍 SVG 预览——自检判不了「文字叠字」的观感(2026-09-25 横版弧标注与副标题叠字,自检放行、看图才发现;同类还有箭头标注压卡) - [ ] PPTX 打开后每个框/线/文字可单独选中编辑(抽验 3 处)
- [ ] 箭头方向与语义标注经用户确认
- [ ] 文字压缩对照表已交用户核对,无编造原文没有的内容
- [ ] DOCX 交付追加:anchors/prstGeom/tailEnd/headEnd/prstDash 计数与 SVG 图元一一对应;mc 门控核验:AlternateContent/Choice(Requires="wps")/Fallback 计数 = anchors 数(缺门控 Word 报"文件损坏");docPr id 唯一非零;文字全部在形状内 txbx 或独立文本框(无贴图);已告知用户"边距 0 → 打开时点忽略";交付说明要求用户实开 Word 验证;未交付 PNG(除非用户另行索要预览图)
- [ ] 打包前:
scripts/下无成品文件、无__pycache__