用国标GB/T 9704-2012+AI自动排版公文:开源Skill工作流解析
说实话,把公文格式调明白这件事,我劝你死了"手动党"的心。字体字号、行距段距、页边距、页码奇偶位置,每一条都有国家标准盯着,你肉眼看着没问题,打印出来可能就歪了。GitHub 上那个 700 多 star 的开源 Skill 项目我盯了一阵子,最近终于完整跑通了一套工作流:把内容丢给 AI,它按国标 GB/T 9704-2012 的参数生成 Word,标题用几号字、正文用什么字体、行距固定多少磅、页码该挂哪儿,全部自动处理,不需要你动一下 Word 的格式工具栏。
这个项目对你的实际价值,取决于你的日常身份。如果你是行政、文秘、办公室岗,每天要碰通知、函件、会议纪要,它是实打实的效率工具;如果你是律所助理、咨询顾问、国企办事员,偶尔要出正式文书但背不下格式规范,它相当于一个"国标排版老师傅";哪怕你只是大学生要交一份格式规范的报告,这路子也能救急。我把这个项目的技术逻辑、国标细节、接入步骤和踩坑实录都过了一遍,下面拆开讲。
1. 公文格式为什么是"最没技术含量又最要命"的事
1.1 国标 GB/T 9704-2012 到底卡了多少参数
国标 GB/T 9704-2012《党政机关公文格式》,本质上是给公文排版定的一套刚性技术参数。它不是审美要求,而是强制规范,覆盖了页面、字体、字号、行距、页码、成文日期等几乎每个维度。核心参数我用表格梳理一下,你要是手动调过格式,看到这张表应该会头皮发麻:
| 项目 | 国标要求 | 换算成 Word 参数 |
|---|---|---|
| 用纸 | A4 | 210mm × 297mm |
| 版心 | 156mm × 225mm | 上白边 37mm,下白边 35mm,左白边 28mm,右白边 26mm |
| 标题 | 2号小标宋体字 | 22pt,居中,可分行排布 |
| 正文 | 3号仿宋体字 | 16pt,一般每面排 22 行、每行 28 个字 |
| 一级标题 | 3号黑体 | 16pt,黑体 |
| 二级标题 | 3号楷体 | 16pt,楷体 |
| 三级、四级标题 | 3号仿宋体 | 16pt,仿宋,可加粗 |
| 首页行距 | 每面 22 行 | 固定行距一般取 28~29 磅 |
| 页码 | 4号半角宋体阿拉伯数字 | 14pt,单页码居右空一字,双页码居左空一字 |
| 首行缩进 | 每段首行缩进 2 字 | 32pt(以 3 号字 16pt 为基准) |
单独看每一项,好像都不难。组合起来就麻烦了:版心尺寸算错一个毫米,行数和字数就对不上;正文用了仿宋但没锁中文字体,Windows 上显示正常、Mac 上打开就变宋体;页码忘了插域代码,文档分节后全乱跳。这些都是"看着会,做起来崩溃"的细节。
1.2 手动排版的崩溃循环
我见过太多人排版一套正式文书的时间,比写内容的时间还长。核心原因有三个。
第一,Word 的默认样式跟国标没有关系。你打开空白文档,默认正文是五号宋体、单倍行距,和公文需要的 3 号仿宋、固定行距完全是两套体系。从头到尾刷格式,光是标题和正文之间来回切换字体,就够你点几百次鼠标。
第二,不同版本的 Word、WPS 对样式的解释有细微差异。同一个文档,在 Win 版 Office 里行距是对的,发到 Mac 版里可能就变了;对方用 WPS 打开,页码位置又偏移一点。手动排版的东西,格式稳定性全看运气。
第三,复制粘贴是格式灾难的放大器。从网页、PDF、聊天记录里复制内容进文档,会带入各种隐藏的 CSS 样式、字体属性、段落标记。清理这些残留格式,有时候比重排一遍还麻烦。
这个痛点到什么程度?市面上专门有人卖"公文格式排版服务",单篇收费几十到上百元。这说明需求足够刚性,而手动操作的效率实在太低。所以当有人把这一整套规范做成一个开源 Skill,让 AI 来执行,核心价值不在于"省了几分钟",而在于把一批人从重复劳动里彻底解放出来。
2. Skill 机制是怎么把这套活包给 AI 的
2.1 用"岗位工具包"来理解 Skill
先说 Skill 是什么。在 Claude、Cursor 这类支持 Agent 能力的工具里,Skill 是一种功能扩展包,本质是一个结构化的文件夹,里面放了三样东西:一份说明文档(通常是 SKILL.md)、若干可执行的脚本(scripts/)、一堆参考资料(resources/)。
你可以把它理解成给 AI 配了一个"岗位工具包"。AI 本身会写文字、懂逻辑,但它不天然知道公文国标的每一项参数,也不会直接操作 Word。有了 Skill 之后,它接到任务时会先读 SKILL.md——里面写清楚了适用范围、触发条件、执行步骤——然后按说明调用 Python 脚本,用 python-docx 库生成 Word 文件。整个过程对用户来说就像跟一个懂行的人对话:你说要什么,它把工具掏出来干活。
Skill 的关键在"依赖注入"。脚本里不需要写死文字内容,AI 负责生成和整理文字,脚本负责把文字按国标参数排好版。一个 Skill 里甚至可以定义多个子命令,比如生成通知、生成函件、生成会议纪要,每种文书有差异化的版式规则,由 AI 判断该走哪条分支。
2.2 为什么是"Skill"而不是"命令行脚本"
有人可能会问:直接用 Python 脚本套模板不就行了吗?为什么绕一圈让 AI 来做?
区别在于"内容生产"和"格式生产"的耦合程度。纯模板脚本解决的是"文字已经就绪,只差排版"的问题。但实际场景里,用户的需求往往是一句话:"写一份关于召开年终总结会议的通知,时间是下周五下午两点,地点在 301 会议室,要求各部门负责人参加。"
这句话里有三个层次的工作:生成符合公文语境的文字内容、判断哪个句子是标题、哪个段落是正文、按国标参数把这些内容排版成 Word。第一层工作纯脚本做不了,第三层工作需要专业知识,而 Skill 正好把 AI 的内容生成能力和脚本的排版能力绑在一起。
更妙的是,Skill 方案对用户是"零学习成本"的。命令行脚本你得会配环境、会传参数、会填模板字段;Skill 只需要你告诉 AI"按公文格式写个通知",剩下的事全部自动完成。这类项目能上 700 星,恰恰说明处理的是真实痛点,而不是技术自嗨。
3. 国标细节全拆解:一个参数都不能错的底层逻辑
3.1 页面设置:版心 156mm × 225mm 是怎么来的
A4 纸的尺寸是 210mm × 297mm,但公文排版不是把文字从纸张边缘开始排,而是规定了一个"版心"——也就是文字实际占据的区域。版心宽 156mm,高 225mm。
计算过程很直观:版心宽度 = 210 - 28(左边距) - 26(右边距)= 156mm;版心高度 = 297 - 37(上边距) - 35(下边距)= 225mm。为什么左右边距不对称?因为公文左侧要留出装订线或装订位置,右白边自然可以窄一些。
在 python-docx 里设置页边距的代码非常直接:
from docx.shared import Mm section = doc.sections[0] section.page_width = Mm(210) section.page_height = Mm(297) section.top_margin = Mm(37) section.bottom_margin = Mm(35) section.left_margin = Mm(28) section.right_margin = Mm(26)实际操作里最容易翻车的点是单位。Word 默认单位可能是厘米,python-docx 用毫米或磅,容易混。我的建议是统一用 Mm() 构造器,别自己去换算小数,避免精度漂移。
3.2 字体与字号体系:中文字体必须单独设置
公文用字有严格的层级体系,这是整个国标里最容易出错的环节。标题用 2 号小标宋体,22pt;正文用 3 号仿宋体,16pt;一级标题用 3 号黑体,二级标题用 3 号楷体,三级四级标题用 3 号仿宋体。字号相对好记,难的是字体在程序里怎么正确设置。
python-docx 设置英文或西文字体用 run.font.name = "Times New Roman" 就能生效,但中文字体不同。Word 的文档结构里,中文字体存放在 XML 的 w:eastAsia 属性中,直接指定 run.font.name 只改了西文字体,中文仍然可能是宋体。必须用下面的方式同时设置中文字体:
from docx.oxml.ns import qn def set_run_font(run, cn_font, en_font, size_pt): run.font.size = Pt(size_pt) run.font.name = en_font run._element.rPr.rFonts.set(qn('w:eastAsia'), cn_font)这个细节我相信很多用 python-docx 的人踩过坑:代码里设了"仿宋",生成的文档在别人电脑上打开却显示成宋体。原因就是 eastAsia 属性没设。Skill 项目里如果封装不好这个函数,输出的文件就是"外行看热闹,内行一打开就露馅"。
还有一个容易忽略的点:小标宋体不是 Word 自带的字体。多数普通电脑只有"方正小标宋简体"或"华文中宋"等替代字体,生成文档的机器上没有这个字体,Word 会静默替换,排版效果立刻偏离国标。关于这个坑,我在后面避坑章节详细说。
3.3 行距、字距与首行缩进的精确算法
国标规定公文每面排 22 行、每行排 28 个字。22 行对应 225mm 版心高度,折算下来每行占用约 10.23mm。3 号字是 16pt,一行文字的高度大约是 5.64mm,留白部分就是行距的补偿。所以行业共识是行距设为固定值 28 磅或 29 磅(1 磅约 0.353mm,28 磅约 9.88mm),能比较精确地保证一页 22 行。
在 python-docx 里设置固定行距需要用 WD_LINE_SPACING.EXACTLY,同时指定磅值:
from docx.enum.text import WD_LINE_SPACING pf = paragraph.paragraph_format pf.line_spacing_rule = WD_LINE_SPACING.EXACTLY pf.line_spacing = Pt(28)这里有个常见的误解:直接在 pf.line_spacing 里传数字,可能被解释为倍数,比如传 28 就变成 28 倍行距,文档直接炸开。必须结合 line_spacing_rule 明确的枚举值来用,才能保证是固定磅值。
首行缩进的要求是每段缩进 2 字。注意,这是按字号动态计算的:正文是 16pt,缩进就是 32pt;如果某处分级标题用 16pt 但缩进规则不同,要区分处理。用 Pt(size * 2) 能确保跟着字号走,不会出现改了字号缩进比例就乱的情况。
字距方面,国标没有强制要求,Word 默认的字距即可,不需要额外调整。过度调整字距反而会破坏"每行 28 个字"的版面约束。
3.4 页码体系:域代码才是正确姿势
页码是公文书里最不起眼、但最容易做错的地方。国标要求页码用 4 号半角宋体阿拉伯数字,置于版心下边缘之下,数字左右各放一条一字线,比如"— 1 —"。单页码居右空一字,双页码居左空一字。
"空一字"的意思是在页码外侧留出一个字的宽度。实际实现时,单页码在页脚段落右侧缩进约 14pt,双页码在左侧缩进约 14pt。但更麻烦的是页码必须是自动编号,不能手动输入数字,否则增删内容后页码全乱。
python-docx 没有直接提供插入页码域的高级 API,需要操作底层的 XML,插入 PAGE 域代码:
from docx.oxml import OxmlElement def add_page_number(section): footer = section.footer footer.is_linked_to_previous = False p = footer.paragraphs[0] p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT fld_begin = OxmlElement('w:fldChar') fld_begin.set(qn('w:fldCharType'), 'begin') instr = OxmlElement('w:instrText') instr.set(qn('xml:space'), 'preserve') instr.text = 'PAGE' fld_end = OxmlElement('w:fldChar') fld_end.set(qn('w:fldCharType'), 'end') run = p.add_run() run._element.append(fld_begin) run._element.append(instr) run._element.append(fld_end)这里面还有一个大坑:如果文档有分节,新节的页脚默认会"链接到前一节",导致所有节的页码样式互相污染。必须在每个 section 的 footer 上设置 is_linked_to_previous = False,才能让各节独立控制页码。很多从模板改出来的文档,页码乱跳都是这个问题导致的。
4. 从零到一:把 Skill 接到你的工作流里
4.1 准备环境:Python 和 python-docx
虽然 Skill 的核心是让 AI 干活,但脚本运行依赖本地 Python 环境。你需要满足几个前提:
- Python 3.9 以上版本
- 安装 python-docx 库:
pip install python-docx - 一台能运行 Claude 桌面版或类似支持 Skill 机制的 Agent 工具
- 字体环境至少要有仿宋、黑体、楷体、小标宋(或可替代的中文字体)
如果你之前完全没碰过 Python,也不用慌,这套东西不需要你写代码,只需要会跑两条命令。Skill 下载好之后,AI 会自己处理环境问题,你更需要的其实是把目录放对位置。
4.2 Skill 文件的典型结构与安装方法
一个标准的公文排版 Skill,文件夹结构大致长这样:
official-doc-skill/ ├── SKILL.md ├── scripts/ │ ├── generate_docx.py │ └── format_checker.py └── resources/ └── gbt9704_params.jsonSKILL.md 是 AI 阅读的"操作手册",里面写明:本 Skill 用于按 GB/T 9704-2012 生成公文格式 Word 文档;触发词包括"公文""通知""函件""红头文件"等;执行步骤是先解析用户输入,再调用 generate_docx.py,最后校验格式。resources 里的 JSON 文件把国标参数集中管理,方便修改和维护。scripts 里则是真正干活儿的 Python 脚本。
安装方法因工具而异,通常是把整个文件夹放到你的 Agent 客户端指定的 skills 目录下,或者在客户端设置里导入 zip 包。装好之后,你可以直接在对话里问 AI:"你现在具备按国标生成公文的能力吗?"它如果读到了 SKILL.md,会明确告诉你可用。
4.3 核心代码逐段拆解
项目里真正负责生成文档的脚本,核心逻辑大概可以拆成几步。第一步是初始化文档对象和页面设置,前面已经展示过;第二步是定义添加段落的公共函数,把所有格式参数封装进去:
def add_para(doc, text, cn_font='仿宋_GB2312', size=16, align=None, indent=True, line_spacing=28): p = doc.add_paragraph() run = p.add_run(text) set_run_font(run, cn_font, 'Times New Roman', size) pf = p.paragraph_format pf.line_spacing_rule = WD_LINE_SPACING.EXACTLY pf.line_spacing = Pt(line_spacing) if align is not None: pf.alignment = align if indent: pf.first_line_indent = Pt(size * 2) return p这个函数的聪明之处在于把"字体、字号、对齐、缩进、行距"全部收敛到一个入口。AI 调用它生成段落时,不需要每次重复设置 5 个参数,也避免了不同段落之间的格式漂移。
第三步是"按需组装",这部分通常由 AI 根据内容结构动态决定:
doc = Document() # 页面设置 setup_page(doc) # 标题:2号小标宋,居中 add_para(doc, '××××××关于印发××工作方案的通知', cn_font='方正小标宋简体', size=22, align=WD_ALIGN_PARAGRAPH.CENTER, indent=False) # 一级标题:3号黑体 add_para(doc, '一、总体要求', cn_font='黑体', size=16, indent=False) # 正文:3号仿宋,首行缩进2字 add_para(doc, '为深入贯彻落实……', cn_font='仿宋_GB2312', size=16) doc.save('output.docx')这段逻辑本身不难,难在 AI 要学会判断"标题和正文",它不是无脑套参数,而是根据输入的语义结构来选择用哪个分支。这正是 Skill 里的 SKILL.md 要反复训练的内容。
4.4 实际调用:一句话触发全流程
环境准备好后,使用体验是这样的。你在对话框里输入:
"请按公文格式生成一份通知,标题是《关于召开第一季度工作调度会的通知》,正文三段,第一段说明会议时间地点,第二段明确参会人员,第三段提工作要求。"
AI 收到指令后,会先确认你已经安装了公文 Skill,然后开始工作:生成符合公文语体的文字,调用脚本创建 Word 文档,再自动打开或保存文件。整个过程大概几十秒,比你手动排版快得多,而且格式稳定性有保障。
有一点提醒:输出目录要提前约定好。比如让 AI 保存到~/Documents/公文输出/下,免得它每次把文件写在乱七八糟的临时目录里,找半天找不着。
5. 实际使用中的常见问题和避坑实录
5.1 字体缺失导致排版静默错乱
这是我最想提醒的问题。国标规定的"小标宋""仿宋_GB2312"等字体,在很多系统默认环境里压根儿不存在。AI 在脚本里设置了 22pt 方正小标宋,但目标机器上没有这个字体,Word 会自动用宋体替代,标题的庄重感立刻打折。
解决思路有两条。一是提前在系统里安装字体,把仿宋_GB2312、楷体_GB2312、方正小标宋简体都装上,一劳永逸。二是改脚本里的字体映射,如果没有小标宋,就用"华文中宋"或"宋体加粗"顶替,但这属于牺牲规范性的妥协,兼容性好了、规范性弱了。
检查字体是否生效的办法很简单:生成文档后,选中标题看字体栏显示的名称,再右键查字体嵌入情况。如果显示的是"宋体"而不是"小标宋",说明 eastAsia 没设置成功或者字体缺失,回到第 3 章的 set_run_font 函数排查。
5.2 行距"看起来对,打印出来不对"
行距这个参数在屏幕上最容易骗人。固定值 28 磅的设置在 Word 里显示正常,但用 WPS 打开可能自动放大或缩小;导出成 PDF 时,如果文档里残留着"单倍行距"样式的段落,字体大一点行距就被撑开,整页行数对不上 22 行。
我排查这类问题的经验是:不要靠眼睛判断,直接把行距规则打印出来看。在 python-docx 里读取段落的 line_spacing_rule 和 line_spacing 值,确认每个段落都是 EXACTLY 且固定值一致。还有一招是导出 PDF 后数一下每页行数,超过 22 或不足 22 都说明有段落没吃到样式。
5.3 页码不显示或奇偶位置错乱
页码域代码插入后,最常见的故障是页脚显示不出"— 1 —"。原因多半是 XML 结构拼接顺序出了问题,w:fldChar 的 begin、instrText、end 必须按顺序放在同一个 run 下。如果代码是在不同 run 里拼的,Word 可能识别不了。
奇偶页位置错乱则是另一个问题。国标要求单页码居右、双页码居左,这依赖文档"奇偶页不同"的页面设置。在 python-docx 里要设置section.even_page_footer和section.odd_page_footer,分别配置两套页脚。很多实现图省事,只在默认页脚里放了居右的页码,双页码就拿不到准确位置。
5.4 模型"幻觉":AI 自作主张改格式
最后说一个与代码无关、但与 AI 使用强相关的问题。Skill 机制依赖模型的理解力,有时候用户一句话没说清楚,AI 会脑补出格式要求。比如你想生成"函"这种文种,AI 可能按"通知"格式生成了;你要求"正文加粗重点句",AI 可能把整个正文都刷成黑体。
解决办法是在 SKILL.md 里把"什么文种走什么模板"写清楚,并用"如果用户未指定文种,默认使用通知格式"这类约束来限制模型自由度。这是 Skill 调优最有价值的部分,比反复改代码管用。
5.5 我的另外两个小技巧
在跑通项目之后,我自己的习惯是维护了一个"格式回归测试"目录,里面放着通知、函件、纪要三类样例文档。每次改完 Skill 的参数或代码,就重新生成这几个样例,肉眼核对一遍关键格式点。这个习惯帮我提前发现了很多次"改了一处、坏了一片"的问题。
还有一个小技巧:让脚本在保存前自动跑一遍格式自检,通过 python-docx 读取所有段落的字体、字号、行距,和 resources 里的国标参数比对,不一致就报错。AI 可以把这个校验结果直接反馈给你,"第 3 段行距应为 28 磅,实际为 1.5 倍行距",定位问题精确到段落,非常实用。
这类开源 Skill 的价值,不在于写代码的人有多厉害,而在于它把国标知识沉淀成了可被 AI 调用的工具。对使用者来说,你不需要成为 Word 排版高手,也不需要背诵 9704 的参数表,只需要会说话,AI 就能把一份格式规范的 Word 按需生产出来。我自己的体会是,第一次看到它生成的文档时还挺惊讶的——页边距、字体、行距、页码全部对得上,这种"把标准变成工具"的思路,比单纯学技巧更值得推广。后续如果你想扩展,可以往红头文件、印章位置模拟、批量生成多个公文的方向继续折腾,底子打好了,剩下的就是想象力的事。