feat: export core Hermes skills

This commit is contained in:
2026-07-15 02:45:56 +00:00
parent a028b63eda
commit 54711fee2a
308 changed files with 41310 additions and 1 deletions
@@ -0,0 +1,80 @@
# 用同案件已有 docx 作母版生成"全新文书"——页眉/边框继承陷阱
## 场景
同一案件已有一份成稿文书(如检察监督申请书 v9),需要再做一份**不同类型**的全新文书(如给法院的《情况反映》)。为保证字体、字号、行距、页边距、样式与申请书**同源**,最稳的做法是:把申请书 docx 当母版,克隆它的段落模板(标题段/正文段/落款段/附件段的 pPr+rPr),清空 body 后用模板段重建全文。
> ⚠️ 这套手法适用于**任何"用 A 文书作母版生成 B 文书"**,不限于《情况反映》。2026-06-25 郭同学案再次踩中:用万禹案**再审申请书**作母版生成**答辩状**,header2.xml 原样继承了"申 请 人:上海万禹实业… / 再审申请书 / 生效判决案号:(2025)沪01民终15828号"——OnlyOffice 渲染第 1 页页眉赫然是别案的当事人和案号。清空页眉法(见下)验证有效:删 header2.xml 所有 run、重新打包后页眉纯净。**判别哪个 header 文件有文字**:遍历 `word/header*.xml`,读各自 `w:t` 拼出文字,非空的那个才是要清的(母版常有 header1/2/3 三个,只有一个带内容)。
这套"克隆段落模板 + 替换 body"的手法对**正文**是对的、省心,但有一个隐蔽的坑:
## 坑:header/footer 和 pBdr 横线会原样继承,且常常张冠李戴
- 替换 `word/document.xml` 的 body 只动了正文。**`word/header1.xml``word/footer1.xml` 原封不动**被继承。
- 实测(2026-06-23 邹家案):申请书 v9 的 header1.xml 写的是"**申请人(被告)… / 申请监督民事诉讼案号:… / 受理法院:…**"——这是**检察监督申请书的页眉**。套到《情况反映》(受文机关是法院本身)上,称谓完全错配("申请监督""受理法院"对法院内部监督渠道不成立)。
- 更隐蔽的是**页眉横线**:清掉页眉文字后,OnlyOffice 渲染里页面顶端仍残留一条贯穿左右的黑线。它不是文字,是页眉段落 `pPr` 里的**段落下边框 `<w:pBdr>`**(页眉样式自带)。只删 run 文字删不掉它。
## 交付前必查(母版法专用三查)
凡是"用 A 文书作母版生成 B 文书",OnlyOffice 渲染后**必须逐页看页眉页脚**,确认:
1. 页眉文字是否属于 B 文书的类型(不是 A 残留的称谓/案号格式);
2. 页眉/页脚有无 A 残留的横线、单位名、修订标识;
3. 落款受文机关、抬头与 B 的收件对象一致。
vision 看第 1 页时,把"页眉区域内容"作为独立一项问出来——它最容易被正文的整洁掩盖过去。
## 修法(清空页眉 + 去横线)
默认推荐**清空页眉**(内部反映材料正文已含案号当事人,页眉非必需,留空最干净不会错配)。纯 zipfile+lxml:
```python
import zipfile, io
from lxml import etree
W='http://schemas.openxmlformats.org/wordprocessingml/2006/main'
def w(t): return f'{{{W}}}{t}'
zin = zipfile.ZipFile(src)
h = etree.fromstring(zin.read('word/header1.xml'))
# 1) 清空页眉所有 run 文字(保留空段结构,别删整段——破坏 schema)
for p in list(h.iter(w('p'))):
for r in list(p.findall(w('r'))):
p.remove(r)
# 2) 删 pBdr(横线的真正来源)——只删文字删不掉这条线
for ppr in h.iter(w('pPr')):
pBdr = ppr.find(w('pBdr'))
if pBdr is not None:
ppr.remove(pBdr)
# 3) 去掉 pStyle 引用(页眉样式常自带下边框,连根拔)
for ppr in h.iter(w('pPr')):
ps = ppr.find(w('pStyle'))
if ps is not None:
ppr.remove(ps)
new_h = etree.tostring(h, xml_declaration=True, encoding='UTF-8', standalone=True)
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zo:
for n in zin.namelist():
d = zin.read(n)
if n == 'word/header1.xml': d = new_h
zo.writestr(n, d)
open(src, 'wb').write(buf.getvalue())
```
若要**保留页眉但改成中性表述**(方案 B),把 run 文字替换成"案号:(…)… / 反映人:…"这类不带"申请监督""受理法院"的中性字段即可——但默认先问用户走 A 还是 B,别擅自保留错配页眉。
## 关掉修订模式(全新文书非修订态)
母版若是修订态(settings.xml 带 `<w:trackRevisions/>`),生成全新文书时要去掉它——新文书是全新成稿不是改稿,不该带修订痕迹:
```python
if n == 'word/settings.xml':
s = etree.fromstring(d)
tr = s.find(w('trackRevisions'))
if tr is not None: s.remove(tr)
d = etree.tostring(s, xml_declaration=True, encoding='UTF-8', standalone=True)
```
## 段落模板克隆要点(正文部分,已验证可用)
- 从母版抽各类型段落各一个作模板:标题(居中 bold sz30)、正文(首行缩进 fl480 sz24)、受文机关(顶格 bold sz24)、落款(右对齐 sz24)、附件标题(顶格 bold sz24)、附件项(fl480 sz24)。
- `mk()` 构造段落时:深拷贝模板段 → 删掉模板里的旧 run/ins/del → 删 numPr(避免继承自动编号)→ 强制字体(eastAsia=仿宋、ascii/hAnsi=Times New Roman)→ 按需 set/unset bold → 塞入新文字 run(`t.set('{http://www.w3.org/XML/1998/namespace}space','preserve')`)。
- 顶格段(受文机关、"丽水市…法院:")用 `no_indent=True` 删掉 ind,否则继承首行缩进会缩进两格。
- body 清空后把新段 `insert``sectPr` 之前(sectPr 必须保留在 body 末尾,决定页面尺寸/页边距)。
- 字体属性交付前用 zipfile 逐 run 统计核验:CJK 全仿宋、LATIN 全 Times New Roman,0 回退。