@cliven/mddocx 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,493 @@
1
+ ---
2
+ name: mddoc
3
+ description: 将 Markdown 内容转换为特定学术格式的 Word 文档 (.docx)。当用户想要将 Markdown 文件、粘贴的 Markdown 文本转换为格式化的 docx 文档时使用,特别是学术论文、技术报告、毕业论文等需要严格格式要求的场景。触发词包括:/mddoc、markdown转docx、md转word、生成格式化文档、学术格式转换。即使用户只说"把这个转成word"而内容是Markdown,也应使用此技能。
4
+ ---
5
+
6
+ # mddoc — Markdown 转学术格式 DOCX
7
+
8
+ ## 快速开始
9
+
10
+ ```bash
11
+ # 安装依赖(仅首次)
12
+ pip install python-docx Pillow requests mistune
13
+
14
+ # 转换 Markdown 文件 → 输出到同目录
15
+ python <skill-path>/scripts/md2docx.py paper.md
16
+
17
+ # 指定输出路径
18
+ python <skill-path>/scripts/md2docx.py paper.md -o /path/to/output.docx
19
+
20
+ # 直接转换粘贴的文本
21
+ python <skill-path>/scripts/md2docx.py --text "# 标题\n\n正文内容" -o out.docx
22
+ ```
23
+
24
+ 其中 `<skill-path>` = `/home/kkk/.claude/skills/mddoc`
25
+
26
+ ## 工作流程
27
+
28
+ 1. **读取输入** — 若用户粘贴 Markdown 文本则直接读取;若用户提供文件路径(含 `@` 引用)则读取该文件
29
+ 2. **安装依赖**(如未安装)— `pip install python-docx Pillow requests mistune`
30
+ 3. **执行转换** — 优先使用内置脚本 `scripts/md2docx.py`;若 Markdown 结构特殊则参照下方格式规范编写自定义脚本
31
+ 4. **确定输出** — 文件路径输入→同目录;粘贴内容→当前目录;文件名=「题目.docx」,题目从第一个 `# 标题` 提取
32
+ 5. **验证** — 检查 outline level、图片嵌入、页眉、分页符
33
+
34
+ ---
35
+
36
+ ## 格式参考
37
+
38
+ > 每个元素配可直接使用的 python-docx 代码。公共导入和工具函数:
39
+
40
+ ```python
41
+ from docx import Document
42
+ from docx.shared import Pt, Cm
43
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
44
+ from docx.oxml import OxmlElement
45
+ from docx.oxml.ns import qn
46
+
47
+ def set_cn_font(run, cn_name, en_name='Times New Roman', size_pt=10.5, bold=False):
48
+ """设置 run 的中英文字体、字号、加粗、颜色(黑)"""
49
+ run.font.size = Pt(size_pt)
50
+ run.font.bold = bold
51
+ run.font.name = en_name
52
+ run.font.color.rgb = None
53
+ rPr = run._element.get_or_add_rPr()
54
+ rFonts = rPr.find(qn('w:rFonts'))
55
+ if rFonts is None:
56
+ rFonts = OxmlElement('w:rFonts'); rPr.insert(0, rFonts)
57
+ rFonts.set(qn('w:eastAsia'), cn_name)
58
+ rFonts.set(qn('w:ascii'), en_name)
59
+ rFonts.set(qn('w:hAnsi'), en_name)
60
+
61
+ def add_empty(doc):
62
+ """五号空行"""
63
+ p = doc.add_paragraph()
64
+ run = p.add_run('')
65
+ set_cn_font(run, '宋体', size_pt=10.5)
66
+ return p
67
+
68
+ def set_outline(para, level):
69
+ """设置 outline level(XML方式,兼容所有python-docx版本)"""
70
+ pPr = para._element.get_or_add_pPr()
71
+ ol = OxmlElement('w:outlineLvl')
72
+ ol.set(qn('w:val'), str(level))
73
+ pPr.append(ol)
74
+ ```
75
+
76
+ ### 基础设置
77
+
78
+ 五号=10.5pt,1.3倍行距,段前段后0磅,全黑,A4页边距左3cm右2cm上2cm下2cm,页脚距底1cm。
79
+
80
+ ```python
81
+ doc = Document()
82
+ section = doc.sections[0]
83
+ # 页边距:左3cm 右2cm 上2cm 下2cm
84
+ section.left_margin = Cm(3)
85
+ section.right_margin = Cm(2)
86
+ section.top_margin = Cm(2)
87
+ section.bottom_margin = Cm(2)
88
+
89
+ sty = doc.styles['Normal']
90
+ sty.font.size = Pt(10.5)
91
+ sty.font.name = 'Times New Roman'
92
+ sty.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
93
+ sty.paragraph_format.line_spacing = 1.3
94
+ sty.paragraph_format.space_before = Pt(0)
95
+ sty.paragraph_format.space_after = Pt(0)
96
+ ```
97
+
98
+ ### 空行规则
99
+
100
+ - **段落之间**:不空行
101
+ - **题目**:上下各空一行
102
+ - **一级标题**:上下各空一行
103
+ - **二级/三级标题**:上下不空行
104
+ - **图片**:上方空一行,图题下方空一行
105
+ - **表格**:表题上方空一行,表格下方空一行
106
+
107
+ ### 题目(第一个 `#`)
108
+
109
+ 三号黑体(16pt)、居中、上下各空一行、**不设 outline level**。
110
+
111
+ ```python
112
+ add_empty(doc)
113
+ p = doc.add_paragraph()
114
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
115
+ # 题目不设 outline_level
116
+ run = p.add_run(title_text)
117
+ set_cn_font(run, '黑体', size_pt=16)
118
+ add_empty(doc)
119
+ ```
120
+
121
+ ### 一级标题(后续 `#`)
122
+
123
+ 三号黑体(16pt)、居中、上下各空一行、outline_level=1、**前加分页符**。
124
+
125
+
126
+ ```python
127
+ add_empty(doc)
128
+ p = doc.add_paragraph()
129
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
130
+ set_outline(p, 1)
131
+ # 分页符
132
+ pPr = p._element.get_or_add_pPr()
133
+ pPr.append(OxmlElement('w:pageBreakBefore'))
134
+ run = p.add_run(text)
135
+ set_cn_font(run, '黑体', size_pt=16)
136
+ add_empty(doc)
137
+ ```
138
+
139
+ ### 二级标题(`##`)
140
+
141
+ 四号黑体(14pt)、**不加粗**、顶格(无缩进)、outline_level=2。上下不空行。
142
+
143
+ ```python
144
+ p = doc.add_paragraph()
145
+ set_outline(p, 2)
146
+ p.paragraph_format.first_line_indent = Pt(0)
147
+ run = p.add_run(text)
148
+ set_cn_font(run, '黑体', size_pt=14, bold=False)
149
+ ```
150
+
151
+ ### 三级标题(`###`)
152
+
153
+ 小四宋体(12pt)、**不加粗**、首行缩进Pt(21)(=2个中文字)、outline_level=3。
154
+
155
+ ```python
156
+ p = doc.add_paragraph()
157
+ set_outline(p, 3)
158
+ p.paragraph_format.first_line_indent = Pt(21)
159
+ run = p.add_run(text)
160
+ set_cn_font(run, '宋体', size_pt=12, bold=False)
161
+ ```
162
+
163
+ ### 正文段落
164
+
165
+ 五号(10.5pt)宋体/TNR、首行缩进Pt(21)、1.3倍行距、段前段后0磅。
166
+
167
+ ```python
168
+ p = doc.add_paragraph()
169
+ p.paragraph_format.first_line_indent = Pt(21)
170
+ p.paragraph_format.line_spacing = 1.3
171
+ p.paragraph_format.space_before = Pt(0)
172
+ p.paragraph_format.space_after = Pt(0)
173
+ run = p.add_run(text)
174
+ set_cn_font(run, '宋体', size_pt=10.5)
175
+ ```
176
+
177
+ ### 图片
178
+
179
+ 下载嵌入(不压缩)、等比缩放(8-12cm规则)、居中、上方空一行。
180
+
181
+ ```python
182
+ import requests
183
+ from PIL import Image
184
+
185
+ def dl_image(url):
186
+ h = {'User-Agent': 'Mozilla/5.0 (compatible; mddoc/1.0)'}
187
+ r = requests.get(url, headers=h, timeout=30); r.raise_for_status()
188
+ return r.content # bytes, 可保存为临时文件
189
+
190
+ def img_width(path):
191
+ img = Image.open(path)
192
+ dpi = img.info.get('dpi', (96, 96))
193
+ dx = dpi[0] if dpi and dpi[0] > 0 else 96
194
+ w_cm = img.width / dx * 2.54
195
+ if w_cm > 12: tw = 12.0
196
+ elif w_cm < 8: tw = 8.0
197
+ else: tw = w_cm
198
+ return Cm(tw)
199
+
200
+ add_empty(doc)
201
+ p = doc.add_paragraph()
202
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
203
+ p.add_run().add_picture(img_path, width=img_width(img_path))
204
+ ```
205
+
206
+ ### 图题
207
+
208
+ 图片下方(不空行)、小五(9pt)宋体加粗居中、格式 `图<章>-<序号> <alt>`、逐章编号。
209
+
210
+ ```python
211
+ p = doc.add_paragraph()
212
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
213
+ run = p.add_run(f'图{ch}-{idx} {alt_text}')
214
+ set_cn_font(run, '宋体', size_pt=9, bold=True)
215
+ add_empty(doc) # 图题下方空一行
216
+ ```
217
+
218
+ ### 表格 + 表题
219
+
220
+ **三线表**(顶线1.5pt/表头底线0.75pt/底线1.5pt粗,无竖线)。表题在上方(不空行)、五号(10.5pt)宋体加粗居中、格式`表<章>-<序号> <描述>`。
221
+
222
+ ```python
223
+ # 表题
224
+ add_empty(doc)
225
+ p = doc.add_paragraph(); p.alignment = WD_ALIGN_PARAGRAPH.CENTER
226
+ run = p.add_run(f'表{ch}-{idx} {caption}')
227
+ set_cn_font(run, '宋体', size_pt=10.5, bold=True)
228
+
229
+ # 表格
230
+ tb = doc.add_table(rows=nrows, cols=ncols)
231
+ tb.alignment = WD_ALIGN_PARAGRAPH.CENTER
232
+
233
+ # 三线表边框
234
+ Pr = tb._tbl.tblPr or OxmlElement('w:tblPr')
235
+ B = OxmlElement('w:tblBorders')
236
+ for tag, sz in [('top','12'),('bottom','12')]:
237
+ e = OxmlElement(f'w:{tag}'); e.set(qn('w:val'),'single')
238
+ e.set(qn('w:sz'),sz); e.set(qn('w:space'),'0'); e.set(qn('w:color'),'000000')
239
+ B.append(e)
240
+ for tag in ('left','right','insideH','insideV'):
241
+ e = OxmlElement(f'w:{tag}'); e.set(qn('w:val'),'none')
242
+ e.set(qn('w:sz'),'0'); e.set(qn('w:space'),'0'); e.set(qn('w:color'),'auto')
243
+ B.append(e)
244
+ Pr.append(B)
245
+ # 表头行每格底部加细线 0.75pt(cell级别,不影响数据行)
246
+ for cell in tb.rows[0].cells:
247
+ tcPr = cell._tc.get_or_add_tcPr()
248
+ tcB = OxmlElement('w:tcBorders')
249
+ btm = OxmlElement('w:bottom')
250
+ btm.set(qn('w:val'),'single'); btm.set(qn('w:sz'),'6')
251
+ btm.set(qn('w:space'),'0'); btm.set(qn('w:color'),'000000')
252
+ tcB.append(btm); tcPr.append(tcB)
253
+
254
+ # 表头行 — 居中、9pt、无缩进
255
+ for j, txt in enumerate(header):
256
+ c = tb.rows[0].cells[j]; c.paragraphs[0].clear()
257
+ c.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
258
+ run = c.paragraphs[0].add_run(txt)
259
+ set_cn_font(run, '宋体', size_pt=9)
260
+ c.paragraphs[0].paragraph_format.first_line_indent = Pt(0)
261
+
262
+ # 数据行 — 左对齐、9pt、无缩进
263
+ for i, row in enumerate(data):
264
+ for j, txt in enumerate(row):
265
+ c = tb.rows[i+1].cells[j]; c.paragraphs[0].clear()
266
+ c.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.LEFT
267
+ run = c.paragraphs[0].add_run(txt)
268
+ set_cn_font(run, '宋体', size_pt=9)
269
+ c.paragraphs[0].paragraph_format.first_line_indent = Pt(0)
270
+
271
+ add_empty(doc) # 表格下方空一行
272
+ ```
273
+
274
+ ### 页眉
275
+
276
+ 左顶格"xxxxx"、右顶格文档题目、小五(9pt)黑体。
277
+
278
+ ```python
279
+ sec = doc.sections[0]; hdr = sec.header
280
+ hdr.is_linked_to_previous = False
281
+ r = hdr.paragraphs[0].add_run('xxxxx')
282
+ set_cn_font(r, '黑体', size_pt=9)
283
+ p2 = hdr.add_paragraph(); p2.alignment = WD_ALIGN_PARAGRAPH.RIGHT
284
+ r2 = p2.add_run(title_text)
285
+ set_cn_font(r2, '黑体', size_pt=9)
286
+ ```
287
+
288
+ ### 列表
289
+
290
+ 有序列表用(1)、(2)、(3)序号;无序列表用 • 符号。按正文格式(首行缩进Pt(21))。
291
+
292
+ ```python
293
+ # 有序列表 — (1)、(2)、(3) 格式
294
+ for idx, item in enumerate(items, 1):
295
+ p = doc.add_paragraph()
296
+ p.paragraph_format.first_line_indent = Pt(21)
297
+ run = p.add_run(f'({idx}){item}')
298
+ set_cn_font(run, '宋体', size_pt=10.5)
299
+
300
+ # 无序列表
301
+ for item in items:
302
+ p = doc.add_paragraph()
303
+ p.paragraph_format.first_line_indent = Pt(21)
304
+ run = p.add_run(f'• {item}')
305
+ set_cn_font(run, '宋体', size_pt=10.5)
306
+ ```
307
+
308
+ ### 代码块
309
+
310
+ Courier New 等宽字体、五号、左缩进。
311
+
312
+ ```python
313
+ p = doc.add_paragraph()
314
+ p.paragraph_format.left_indent = Cm(1)
315
+ run = p.add_run(code)
316
+ set_cn_font(run, '宋体', en_name='Courier New', size_pt=10.5)
317
+ ```
318
+
319
+ ### 行内公式(`$...$`)
320
+
321
+ 嵌于段落中,转换为 OMML(Office Math Markup Language)格式,WPS/Word 可直接渲染。
322
+ 公式在段落中作为 `<m:oMath>` 元素插入,与文字 run 同级。
323
+
324
+ ```python
325
+ # 行内公式通过 split_inline_math() 拆分为 segments
326
+ # text segment → 普通 run,math segment → latex_to_omml() → m:oMath 元素
327
+ p = doc.add_paragraph()
328
+ p.paragraph_format.first_line_indent = Pt(21)
329
+ p.paragraph_format.line_spacing = 1.3
330
+ for seg in segments:
331
+ if seg['type'] == 'text':
332
+ if seg['content']:
333
+ run = p.add_run(seg['content'])
334
+ set_cn_font(run, '宋体', size_pt=10.5)
335
+ else: # math — 转换为 OMML 并插入段落 XML
336
+ omml = latex_to_omml(seg['content'], display=False)
337
+ p._element.append(omml)
338
+ ```
339
+
340
+ ### 行间公式(`$$...$$`)
341
+
342
+ 独立成行,上下各空一行。公式通过 OMML 居中渲染,编号右对齐。编号格式同图/表:`(章-序号)`,逐章编号。编号括号用五号宋体,数字用五号 Times New Roman。
343
+
344
+ 布局方式:段落设置居中和右对齐 tab stop,OMML `<m:oMath>` 元素位于两个 tab 之间,实现公式居中、编号右对齐。
345
+
346
+ ```python
347
+ add_empty(doc) # 上方空一行
348
+ p = doc.add_paragraph()
349
+
350
+ # 设置 tab stops:居中和右对齐
351
+ # 左边距3cm右边距2cm → 可用宽度16cm → 中心8cm(4536twips), 右边16cm(9072twips)
352
+ pPr = p._element.get_or_add_pPr()
353
+ tabs = OxmlElement('w:tabs')
354
+ ct = OxmlElement('w:tab'); ct.set(qn('w:val'), 'center'); ct.set(qn('w:pos'), '4536')
355
+ rt = OxmlElement('w:tab'); rt.set(qn('w:val'), 'right'); rt.set(qn('w:pos'), '9072')
356
+ tabs.append(ct); tabs.append(rt)
357
+ pPr.append(tabs)
358
+
359
+ # tab → 公式 OMML 居中
360
+ run_t1 = p.add_run()
361
+ run_t1._r.append(OxmlElement('w:tab'))
362
+ omml = latex_to_omml(formula_text, display=False)
363
+ p._element.append(omml)
364
+
365
+ # tab → 编号右对齐:(章-序号)
366
+ run_t2 = p.add_run()
367
+ run_t2._r.append(OxmlElement('w:tab'))
368
+ run_lp = p.add_run('(')
369
+ set_cn_font(run_lp, '宋体', en_name='宋体', size_pt=10.5)
370
+ run_num = p.add_run(f'{ch}-{eq_num}')
371
+ set_cn_font(run_num, '宋体', en_name='Times New Roman', size_pt=10.5)
372
+ run_rp = p.add_run(')')
373
+ set_cn_font(run_rp, '宋体', en_name='宋体', size_pt=10.5)
374
+
375
+ add_empty(doc) # 下方空一行
376
+ ```
377
+
378
+ ### LaTeX → OMML 转换
379
+
380
+ 内置 `latex_to_omml()` 函数将 LaTeX 数学公式转换为 OMML XML,支持以下构造:
381
+
382
+ - 上下标:`a^b`、`a_b`、`a_b^c`
383
+ - 分数:`\frac{a}{b}`
384
+ - 根式:`\sqrt{x}`、`\sqrt[n]{x}`
385
+ - 希腊字母:`\alpha`、`\beta`、`\Gamma` 等
386
+ - 大运算符:`\sum`、`\prod`、`\int`(含上下限)
387
+ - 函数名:`\sin`、`\cos`、`\log`、`\lim` 等
388
+ - 重音:`\hat`、`\bar`、`\vec`、`\tilde` 等
389
+ - 符号:`\infty`、`\cdot`、`\times`、`\partial`、`\nabla` 等
390
+ - 定界符:`\left(...\right)`、`\left[...\right]`
391
+ - 文本:`\text{...}`
392
+
393
+ ### 页码
394
+
395
+ 从第一章(第一个一级标题所在页)开始编页码,格式 `第×页 共×页`,页脚边距 1cm。
396
+
397
+ ```python
398
+ from docx.oxml import OxmlElement
399
+ from docx.oxml.ns import qn
400
+ from docx.shared import Cm
401
+
402
+ section = doc.sections[0]
403
+ section.footer_distance = Cm(1.1) # 页脚边距
404
+
405
+ footer = section.footer
406
+ footer.is_linked_to_previous = False
407
+ fp = footer.paragraphs[0]
408
+ fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
409
+
410
+ # "第" 字
411
+ run1 = fp.add_run('第')
412
+ set_cn_font(run1, '宋体', size_pt=10.5)
413
+ # 页码字段
414
+ run_page = fp.add_run()
415
+ fldChar1 = OxmlElement('w:fldChar'); fldChar1.set(qn('w:fldCharType'), 'begin')
416
+ run_page._r.append(fldChar1)
417
+ instrText = OxmlElement('w:instrText'); instrText.set(qn('xml:space'), 'preserve')
418
+ instrText.text = ' PAGE '
419
+ run_page._r.append(instrText)
420
+ fldChar2 = OxmlElement('w:fldChar'); fldChar2.set(qn('w:fldCharType'), 'end')
421
+ run_page._r.append(fldChar2)
422
+ # "页 共"
423
+ run2 = fp.add_run('页 共')
424
+ set_cn_font(run2, '宋体', size_pt=10.5)
425
+ # 总页数字段
426
+ run_total = fp.add_run()
427
+ fldChar3 = OxmlElement('w:fldChar'); fldChar3.set(qn('w:fldCharType'), 'begin')
428
+ run_total._r.append(fldChar3)
429
+ instrText2 = OxmlElement('w:instrText'); instrText2.set(qn('xml:space'), 'preserve')
430
+ instrText2.text = ' NUMPAGES '
431
+ run_total._r.append(instrText2)
432
+ fldChar4 = OxmlElement('w:fldChar'); fldChar4.set(qn('w:fldCharType'), 'end')
433
+ run_total._r.append(fldChar4)
434
+ # "页"
435
+ run3 = fp.add_run('页')
436
+ set_cn_font(run3, '宋体', size_pt=10.5)
437
+ ```
438
+
439
+ > 注意:页码从第一章开始计数需在第一个一级标题处插入分节符并重置页码。简单场景可统一从第1页开始。
440
+
441
+ ### 跨页表格(续表)
442
+
443
+ 表头行设为重复标题行,跨页自动重复。续表在右上方标"续表<编号>"。
444
+
445
+ ```python
446
+ # 表头行设为重复标题行(跨页自动出现)
447
+ tblHeader = OxmlElement('w:tblHeader')
448
+ trPr = table.rows[0]._tr.get_or_add_trPr()
449
+ trPr.append(tblHeader)
450
+
451
+ # 对于确实跨页的大表格,在分页处手动加"续表"标记:
452
+ # (python-docx无法检测分页位置,建议生成后人工检查)
453
+ # 续表右对齐段落示例:
454
+ p_cont = doc.add_paragraph()
455
+ p_cont.alignment = WD_ALIGN_PARAGRAPH.RIGHT
456
+ run_cont = p_cont.add_run(f'续表{tab_label}')
457
+ set_cn_font(run_cont, '宋体', size_pt=10.5)
458
+ ```
459
+
460
+ ---
461
+
462
+ ## 生成后自检清单
463
+
464
+ - [ ] 题目:16pt黑体、居中、无outline、上下各空一行
465
+ - [ ] 一级标题:16pt黑体、outline_level=1、前有分页符、上下各空一行
466
+ - [ ] 二级标题:14pt黑体、顶格、outline_level=2、不加粗、**上下不空行**
467
+ - [ ] 三级标题:12pt宋体、首行缩进Pt(21)、outline_level=3、不加粗、**上下不空行**
468
+ - [ ] 正文:10.5pt、首行缩进Pt(21)、1.3倍行距、**段落间不空行**
469
+ - [ ] 图片:嵌入、等比缩放8-12cm、居中、上下各空一行
470
+ - [ ] 图题:9pt宋体加粗居中、图片下方
471
+ - [ ] 表格:三线表(顶/底粗、表头格底线细、数据行无线)、居中
472
+ - [ ] 表头行:居中、9pt、无缩进、tblHeader重复
473
+ - [ ] 表题:10.5pt宋体加粗居中、表格上方
474
+ - [ ] 列表:有序用(1)(2)(3)、无序用•、首行缩进
475
+ - [ ] 页眉:左"xxxxx"右题目、9pt黑体
476
+ - [ ] 行内公式:$...$ 嵌于段落、OMML格式、WPS/Word可渲染
477
+ - [ ] 行间公式:$$...$$ OMML居中、上下各空一行、编号(章-序号)右对齐、括号宋体数字TNR
478
+ - [ ] 页码:"第×页 共×页"、页脚边距1cm
479
+ - [ ] 续表:跨页表头重复、"续表xx"右上标注
480
+ - [ ] 页边距:左3cm 右2cm 上2cm 下2cm
481
+ - [ ] 全部黑色、无额外参数
482
+
483
+ ## 注意事项
484
+
485
+ - **第一个 `#` 是题目**(不设 outline),后续 `#` 是一级标题(outline_level=1)
486
+ - **图片尺寸用 `Cm()`,不手算 EMU** — `add_picture(width=Cm(x))` 自动转换
487
+ - **图片下载必设 User-Agent** — 否则 CDN/Wikipedia 返回 400
488
+ - **图题编号自动生成** — alt text 作为描述文字,图/表编号独立逐章编序
489
+ - **表题识别** — 表格前含"表"字的段落作为表题
490
+ - **outline_level 用 `set_outline()` 设置** — `paragraph_format.outline_level` 在部分python-docx版本不生效,统一用XML方式写入;读取时也从XML读取
491
+ - **公式编号自动生成** — 图/表/公式独立逐章编序,编号格式统一为 `章-序号`
492
+ - **不添加**:目录页、背景色
493
+ - **单位**:字体用 pt、尺寸用 cm
@@ -0,0 +1,23 @@
1
+ {
2
+ "skill_name": "mddoc",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "prompt": "帮我把这份 Markdown 文件转成 docx 格式的 Word 文档",
7
+ "expected_output": "生成 .docx 文件,包含正确的标题格式、正文格式、图片和表格,页眉正确设置",
8
+ "files": ["evals/test-sample.md"]
9
+ },
10
+ {
11
+ "id": 2,
12
+ "prompt": "将下面这段 markdown 转成格式化的 Word 文档,保存到当前目录:\n\n# 实验报告\n\n## 实验目的\n\n本实验旨在验证深度学习模型在小样本数据集上的泛化性能。通过对比实验分析不同正则化策略的效果。\n\n## 实验方法\n\n### 数据集准备\n\n实验采用 CIFAR-10 数据集,随机选取每类 100 张图片作为训练集。测试集保持原始大小。\n\n![实验流程图](https://upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Tensor_visualization.svg/400px-Tensor_visualization.svg.png)\n\n表 超参数配置\n\n| 参数 | 取值 |\n|------|------|\n| 学习率 | 0.001 |\n| 批量大小 | 32 |\n| 训练轮次 | 100 |\n\n### 评估指标\n\n采用 Top-1 准确率和 F1 分数作为评估指标。",
13
+ "expected_output": "在当前目录生成 实验报告.docx,包含题目、各级标题、正文、图片、表格的正确格式",
14
+ "files": []
15
+ },
16
+ {
17
+ "id": 3,
18
+ "prompt": "/mddoc 把这个转成word @evals/test-sample.md",
19
+ "expected_output": "在 evals/ 目录生成第一章 基于深度学习的图像识别研究.docx,格式完全符合学术规范",
20
+ "files": ["evals/test-sample.md"]
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,67 @@
1
+ # 第一章 基于深度学习的图像识别研究
2
+
3
+ ## 1.1 研究背景
4
+
5
+ 近年来,深度学习技术在计算机视觉领域取得了突破性进展。卷积神经网络(Convolutional Neural Network, CNN)作为深度学习的核心架构之一,在图像分类、目标检测、语义分割等任务中表现出色。
6
+
7
+ 图像识别技术已广泛应用于安防监控、自动驾驶、医疗诊断等领域。传统的图像识别方法依赖手工设计的特征提取器,如 SIFT、HOG 等,这些方法在复杂场景下的泛化能力有限。
8
+
9
+ ### 1.2.1 深度学习发展历程
10
+
11
+ 深度学习的概念可追溯至20世纪40年代的神经网络研究。经历了多次起伏后,2012年 AlexNet 在 ImageNet 竞赛中的突破性表现标志着深度学习时代的正式到来。
12
+
13
+ 此后,VGGNet、GoogLeNet、ResNet 等架构相继提出,不断刷新图像识别的准确率记录。其中 ResNet 引入的残差连接解决了深层网络的梯度消失问题。
14
+
15
+ ## 1.2 研究现状
16
+
17
+ 目前主流的图像识别方法可归纳为三类:基于 CNN 的方法、基于 Transformer 的方法以及混合架构方法。
18
+
19
+ ![ResNet 网络架构示意图](https://www.python.org/static/img/python-logo.png)
20
+
21
+ 表 主流图像识别模型性能对比
22
+
23
+ | 模型名称 | Top-1 准确率 | 参数量 | 发布时间 |
24
+ |----------|-------------|--------|---------|
25
+ | ResNet-50 | 76.2% | 25.6M | 2015 |
26
+ | EfficientNet-B4 | 83.0% | 19.3M | 2019 |
27
+ | ViT-B/16 | 84.1% | 86.0M | 2020 |
28
+ | ConvNeXt-T | 84.9% | 28.6M | 2022 |
29
+
30
+ 从上表可以看出,模型性能持续提升的同时,参数效率也在不断优化。EfficientNet 系列通过神经架构搜索实现了参数效率与精度的良好平衡。
31
+
32
+ ### 1.2.1 数据增强技术
33
+
34
+ 数据增强是提升模型泛化能力的重要手段。常用方法包括:
35
+
36
+ - 随机裁剪与缩放
37
+ - 水平翻转与旋转
38
+ - 色彩抖动与光照变换
39
+ - CutOut 与 MixUp 等高级增强策略
40
+
41
+ 研究表明,合理的数据增强策略可以有效缓解过拟合问题,尤其在训练数据有限的场景下。
42
+
43
+ ## 1.3 理论基础
44
+
45
+ 卷积操作的数学表达为 $y = W * x + b$,其中 $W$ 为卷积核权重,$b$ 为偏置项。
46
+
47
+ 损失函数采用交叉熵损失:
48
+
49
+ $$
50
+ L(y, \hat{y}) = -\sum_{i=1}^{C} y_i \log(\hat{y}_i)
51
+ $$
52
+
53
+ 梯度下降的参数更新规则为 $w_{t+1} = w_t - \eta \nabla L(w_t)$,其中 $\eta$ 表示学习率。
54
+
55
+ ## 1.4 本文主要工作
56
+
57
+ 本文旨在构建一个高效准确的图像识别系统,主要贡献包括:
58
+
59
+ 本文的代码实现基于 PyTorch 框架,训练过程如下:
60
+
61
+ ```
62
+ import torch
63
+ model = torch.hub.load('pytorch/vision', 'resnet50', pretrained=True)
64
+ model.fc = torch.nn.Linear(2048, num_classes)
65
+ ```
66
+
67
+ 后续章节将详细介绍模型设计、训练策略与实验结果。