Skills Plugins MCP Prompt Model 博客 我的中心

fill-docx-template

当用户需要基于模板填充 Word 文档(.docx)、从模板生成报告、创建包含动态数据的合同,或自动化文档生成时使用此技能。包括替换普通占位符 {name} 替换文本、使用 {name|r:x,c:y} 格式标记的智能表格填充(支持从标记行开始向下填充,保留上方内容)、插入图片、批量生成文档等。如果用户提及 .docx 模板、邮件合并功能或以编程方式填写 Word 表单,请使用此技能。

DeepseekModel 官方收录技能 质量 优秀 · 90 v1.0.0

获取

https://deepseekmodel.com/api/download.php?id=leoyeai-openclaw-master-skills-skills-fill-docx-template-skill-md&format=skill
下载 .skill 标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用
.skill 文件中 system_prompt 字段的实际内容。
name fill-docx-template description 当用户需要基于模板填充 Word 文档(.docx)、从模板生成报告、创建包含动态数据的合同,或自动化文档生成时使用此技能。包括替换普通占位符 {name} 替换文本、使用 {name|r:x,c:y} 格式标记的智能表格填充(支持从标记行开始向下填充,保留上方内容)、插入图片、批量生成文档等。如果用户提及 .docx 模板、邮件合并功能或以编程方式填写 Word 表单,请使用此技能。 Word 文档模板填充指南 概述 本指南介绍如何使用 Python 向 Word(.docx)模板填充动态数据。支持: 普通占位符 : {variable_name} 替换文本 智能表格填充 :使用 {name|r:x,c:y} 标记在表格左侧任意位置,自动从标记行向下填充,保留上方内容,并自动调整表格行列数 ⚠️ 1. 表格自动调整的时机与条件 表格并非总是"提前自动扩展" - 这取决于占位符是否被正确识别: ✅ 会被自动调整的情况 :当且仅当 {name|r:x,c:y} 格式 完全正确 且位于**该行的最左侧单元格(第一列)**时,表格会在 DocxTemplateFiller 初始化时立即调整为声明的行列数 ❌ 不会被调整的情况 :如果占位符格式错误、包含空格(如 {name | r:5, c:4} )、或不在第一列,表格将 保持原样 ,不会自动扩展或收缩 ⚠️ 2. 普通占位符一般情况下需要被覆盖! 除非未提供值时保留原样 ❌3. 任何情况下不能修改、覆盖模板文件! ⚠️4. 尽量使用DocxTemplateFiller类的方法实现所有功能! 快速开始 from docx import Document from docx.shared import Inches from docx.oxml import OxmlElement from docx.oxml.ns import qn import re import os class DocxTemplateFiller : def __init__ ( self, template_path ): if not os.path.exists(template_path): raise FileNotFoundError( f"模板文件不存在: {template_path} " ) self .doc = Document(template_path) self .template_path = template_path self .named_tables = {} # 存储表格信息 # 初始化时扫描表格占位符 self ._process_table_placeholders() def _process_table_placeholders ( self ): """ 扫描所有表格的每一行第一个单元格,查找 {name|r:x,c:y} 格式 例如:{products|r:5,c:4} 表示从当前行开始,共5行,4列 """ pattern = re. compile ( r'\{(\w+)\|r:(\d+),c:(\d+)\}' ) for table_idx, table in enumerate ( self .doc.tables): for row_idx, row in enumerate (table.rows): if len (row.cells) == 0 : continue # 检查每行的第一个单元格(最左侧) first_cell = row.cells[ 0 ] text = first_cell.text.strip() match = pattern.search(text) if match : name = match .group( 1 ) target_rows = int ( match .group( 2 )) target_cols = int ( match .group( 3 )) # 保存表格配置信息 self .named_tables[name] = { 'table' : table, 'start_row' : row_idx, # 占位符所在行(从此行开始填充) 'target_rows' : target_rows, # 需要填充的总行数(包括占位符行) 'target_cols' : target_cols # 目标列数 } # 调整表格大小:上方保留 row_idx 行,从 row_idx 开始有 target_rows 行 self ._resize_table(table, row_idx, target_rows, target_cols) # 清除占位符文本(可选,因为填充时会覆盖,但清除更干净) # 保留单元格其他可能的文本(占位符前后文本) new_text = pattern.sub( '' , first_cell.text).strip() if new_text: first_cell.text = new_text else : first_cell.text = "" # 清空等待填充 def _resize_table ( self, table, start_row, target_rows, target_cols ): """调整表格大小:确保从 start_row 开始有 target_rows 行,总列数为 target_cols""" total_needed_rows = start_row + target_rows current_rows = len (table.rows) current_cols = len (table.columns) if table.columns else 0 # 调整行数 if total_needed_rows > current_rows: # 添加行 for _ in range (total_needed_rows - current_rows): table.add_row() elif total_needed_rows < current_rows: # 删除多余行(从末尾删除,保留前面的) self ._delete_rows_from_end(table, current_rows - total_needed_rows) # 调整列数 if target_cols != current_cols: self ._resize_columns(table, target_cols) def _delete_rows_from_end ( self, table, num_rows ): """从表格末尾删除指定行数""" tbl = table._tbl for _ in range (num_rows): if len (table.rows) > 0 : tr = table.rows[- 1 ]._tr tbl.remove(tr) def _resize_columns ( self, table, target_cols ): """调整表格列数""" current_cols = len (table.columns) tbl = table._tbl tblGrid = tbl.find(qn( 'w:tblGrid' )) if target_cols > current_cols: # 添加列定义 for _ in range (target_cols - current_cols): gridCol = OxmlElement( 'w:gridCol' ) tblGrid.append(gridCol) # 为每一行添加单元格 for row in table.rows: for _ in range (target_cols - current_cols): tc = OxmlElement( 'w:tc' ) tcPr = OxmlElement( 'w:tcPr' ) tc.append(tcPr) p = OxmlElement( 'w:p' ) tc.append(p) row._tr.append(tc) elif target_cols < current_cols: # 删除多余列定义 for _ in range (current_cols - target_cols): if len (tblGrid) > 0 : tblGrid.remove(tblGrid[- 1 ]) # 从每行删除多余单元格 for row in table.rows: for _ in range (current_cols - target_cols): tcs = row._tr.findall(qn( 'w:tc' )) if len (tcs) > target_cols: row._tr.remove(tcs[- 1 ]) def fill_placeholders ( self, data_dict ): """替换普通占位符 {key}(不包括表格定义格式)""" # 匹配普通占位符,排除表格定义格式 pattern = re. compile ( r'\{(\w+)\}(?!\|r:\d+,c:\d+)' ) # 处理段落 for para in self .doc.paragraphs: self ._replace_in_paragraph(para, pattern, data_dict) # 处理表格内的普通占位符(排除已识别的表格占位符单元格) for table in self .doc.tables: for row in table.rows: for cell in row.cells: for para in cell.paragraphs: self ._replace_in_paragraph(para, pattern, data_dict) def _replace_in_paragraph ( self, paragraph, pattern, data_dict ): """在段落中执行替换""" text = paragraph.text matches = pattern.findall(text) if not matches: return new_text = text for key in matches: if key in data_dict: placeholder = f'{{ {key} }}' value = str (data_dict[key]) new_text = new_text.replace(placeholder, value) if new_text != text and paragraph.runs: paragraph.runs[ 0 ].text = new_text for run in paragraph.runs[ 1 :]: run.text = "" def fill_named_table ( self, table_name, data ): """ 填充指定名称的表格 从占位符所在行开始(包括该行)向下填充,保留上方内容 :param table_name: 表格名称(来自占位符 {name|r:x,c:y}) :param data: 二维列表,如 [['产品A', '规格1', '10'], [...]] """ if table_name not in self .named_tables: raise KeyError( f"未找到名为 ' {table_name} ' 的表格,请确保模板中存在 '{{ {table_name} |r:x,c:y}}' 格式的占位符在最左侧单元格" ) table_info = self .named_tables[table_name] table = table_info[ 'table' ] start_row = table_info[ 'start_row' ] target_rows = table_info[ 'target_rows' ] target_cols = table_info[ 'target_cols' ] # 确保数据不超过声明的行数 if len (data) > target_rows: data = data[:target_rows] # 从 start_row 开始填充(包括该行) for row_offset, row_data in enumerate (data): actual_row_idx = start_row + row_offset if actual_row_idx >= len (table.rows): break # 确保不超出列数 if len (row_data) > target_cols: row_data = row_data[:target_cols] # 填充该行的每一列 for col_idx, value in enumerate (row_data): if col_idx >= len (table.columns): break table.cell(actual_row_idx, col_idx).text = str (value) def fill_all ( self, text_data= None , table_data= None ): """ 一键填充所有内容 :param text_data: 普通占位符字典,如 {'company': 'ABC公司'} :param table_data: 表格数据字典,如 {'products': [[...], [...]]} """ if text_data: self .fill_placeholders(text_data) if table_data: for name, data in table_data.items(): self .fill_named_table(name, data) def insert_paragraph_at ( self, index, text, style= None ): """在指定位置插入段落""" if index == - 1 or index >= len ( self .doc.paragraphs): p = self .doc.add_paragraph(text) else : p = self .doc.paragraphs[index].insert_paragraph_before(text) if style: p.style = style return p def insert_image ( self, paragraph_index, image_path, width= None ): """在指定段落后插入图片""" if not os.path.exists(image_path): raise FileNotFoundError( f"图片不存在: {image_path} " ) para = self .doc.paragraphs[paragraph_index] run = para.add_run() if width: run.add_picture(image_path, width=Inches(width)) else : run.add_picture(image_path) def save ( self, output_path ): self .doc.save(output_path) print ( f"✅ 文档已生成: {os.path.abspath(output_path)} " ) 模板创建规范 1. 普通占位符 使用 {variable_name} 格式: 甲方(购方):{company} 签署日期:{date} 合同编号:{contract_no} 2. 表格占位符(新逻辑) 格式 : {name|r:x,c:y} 位置 :表格最左侧的任意单元格(通常是某一行的第一列) 行为 : 从占位符所在行开始,向下填充 x 行 占位符所在行会被第一个数据行覆盖 占位符上方的行内容完全保留 表格会被调整为 y 列 示例 : | 序号 | 产品名称 | 规格 | 数量 | 单价 | 金额 | | ------ | -------- | --- | --- | --- | ---- | | 1 | 产品A | 规格1 | 10 | 100 | 1000 | | {items|r:3,c:6} | | | | | | | | | | | | | 说明 : {items|r:3,c:6} 放在第3行第1列(索引从0开始则为第2行) 程序会保留第0-2行(表头+第一行数据) 从第3行开始填充3行数据,覆盖占位符 表格自动调整为6列 3. 复杂模板示例 场景 :合同中有两个表格,第一个表格上方有静态说明行 采购合同 甲方:{company} 乙方:{seller} 产品列表(常规采购): | 产品名称 | 型号 | 数量 | 单价 | |----------|------|------|------| | {regular|r:4,c:4} | | | | | | | | | | | | | | | | | | | 紧急采购项(如有): | 产品名称 | 型号 | 数量 | 要求 | |----------|------|------|------| | 说明:紧急采购需24小时内到货 | | | | | {urgent|r:2,c:4} | | | | | | | | | 总计金额:{total_amount} 填充代码 : filler = DocxTemplateFiller( "template.docx" ) filler.fill_all( text_data={ 'company' : '北京科技' , 'seller' : '上海贸易' , 'total_amount' : '¥50,000' }, table_data={ 'regular' : [ [ '办公椅' , '人体工学' , '10' , '¥800' ], [ '办公桌' , '1.2米' , '5' , '¥1500' ], [ '文件柜' , '铁皮' , '3' , '¥600' ] # 第4行不会填充,因为只声明了r:3,但提供了3行数据 ], 'urgent' : [ [ '投影仪' , '4K激光' , '1' , '急需' ], [ '幕布' , '100寸' , '1' , '配套' ] ] } ) filler.save( "contract.docx" ) 结果 : 第一个表格:保留表头,从第2行(占位符行)开始填充4行,共6行(表头+1行静态+4行数据) 第二个表格:保留表头和说明行,从第3行(占位符行)开始填充2行 关键特性说明 1. 上方内容保护 占位符所在行上方的所有行(包括表头、说明文字、静态数据) 完全不会 被修改。 # 模板: # 第0行:表头 | 名称 | 价格 | # 第1行:说明 | 这是说明文字 | # 第2行:占位符 {data|r:2,c:2} | | # 第3行:空行 | | # 填充 data = [['A', '100'], ['B', '200']] # 结果: # 第0行:表头 | 名称 | 价格 | (不变) # 第1行:说明 | 这是说明文字 | (不变) # 第2行:A | 100 | (覆盖占位符)
Agent 识别该技能的关键词,点击任意一个即可复制。

该技能未提供触发词。

下载的 .skill 包内含以下字段。
字段 说明
format格式标识(skill/v1)
skill_id技能唯一 ID
name技能名称
version版本号
description技能描述
category所属分类(数组)
trigger_words触发词列表
tags标签列表
source来源标识
source_url来源链接(本页地址)
exported_at导出时间(每次下载生成)
system_prompt系统提示词正文
model_config模型参数:provider / model / temperature / max_tokens / top_p
examples示例
install_guide各平台导入说明(Coze / Dify / Claude / 自定义框架)
同一份技能可按不同平台格式导出。
.skill 标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用 下载
.skillpro 增强格式,额外含脚本 / 工具 / 依赖 / 钩子占位 下载
.json 纯 JSON 导出,只含 system_prompt 与模型参数 下载
Coze 带 frontmatter 的 Markdown,Coze 平台导入用 下载
Dify Dify DSL,创建应用后直接导入 下载

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。