# Bid Template Fill

> 投标书模板填充引擎。当用户明确要求「填模板」「写标书」「出标书」「生成投标文件」「标书填充」「套模板」「做标书」，或提供了脱敏 DOCX 模板需要填充时使用。扫描模板中的占位符和虚拟文本，提取招标信息、收集投标人数据、构建替换映射、执行填充，输出可直接提交的成品标书。覆盖商务标+技术标+报价书全部章节。

- Skill: `cherryhq/bid-template-fill` (Agent Skill, multi-file: 10 files)
- Install (CLI): `npx skillmds@latest add cherryhq/bid-template-fill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cherryhq/bid-template-fill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: CherryHQ (https://skillmd.com/u/cherryhq)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cherryhq/bid-template-fill

---


# 标书模板填充 SKILL

## 概述

将脱敏 DOCX 模板填充为可提交的投标文件。覆盖设备采购类投标的商务标 + 技术标 + 报价书全部章节。

## 智能提取流程（新增 v2.3）

技能现在支持直接读取公司信息 DOCX 和招标 PDF/DOCX，自动提取并映射到模板：

`
公司信息.docx ──→ scripts/extract_company.py ──→ company.json（自动映射脱敏Key）
招标文件.pdf  ──→ scripts/extract_bid.py     ──→ 招标方结构化数据
                     ↓
              scripts/bid_pipeline.py ──→ 合并+填充 ──→ 成品标书.docx
`

**一键命令：**
```bash
# 从公司DOCX + 招标PDF 直接生成标书
python scripts/bid_pipeline.py 招标文件.pdf --company-docx 公司信息.docx --templates-dir ./模板/
```

> **输出文件夹：**生成的标书文件默认保存在招标文件同目录下的 成品标书/ 子文件夹中，文件命名格式：标书类型_项目名.docx。可通过 --output-dir 自定义。

**分步使用：**
```bash
# 步骤A：提取公司信息
python scripts/extract_company.py 公司信息.docx -o company.json

# 步骤B：提取招标信息
python scripts/extract_bid.py 招标文件.pdf --print

# 步骤C：管道生成
python scripts/bid_pipeline.py 招标文件.md --company-json company.json
```

> **首次使用**：编辑 `desensitize_map.json`，将 `template_desensitize_map` 中的虚拟文本改为你模板中实际出现的脱敏名。也可通过 `--map` 指定自定义映射文件：`python scripts/extract_company.py 公司信息.docx --map my_map.json`

## 环境要求

- **Python 方式（推荐）**：Python 3.7+，安装 `python-docx`：`pip install python-docx`
- **Node.js 方式（零依赖备选，仅 Windows）**：Node.js 16+
- **管道方式**：Python 3.7+，`python-docx`，模板需按约定命名

## 工作流

```
步骤1: 读取招标文件 → 提取招标方信息
步骤2: 扫描脱敏模板 → 发现占位符与脱敏文本
步骤3: 收集投标人信息 → 向用户询问必要数据
步骤4: 构建替换数据 → 映射数据到占位符
步骤5: 执行填充 → 调用 scripts/fill_py.py 或 scripts/fill_template.js
步骤6: 验证输出 → 检查遗漏占位符
```

---

## 步骤1: 读取招标文件，提取招标方信息

读取招标文件（PDF 或 Markdown/Word），提取并保存为结构化数据：

| 提取项 | 常见模板占位符 | 用途 |
|--------|--------------|------|
| 项目名称 | `[项目名称]`、`_[项目名称]_` | 保证金表、页眉 |
| 招标编号 | `_[招标编号]_`、`[招标编号]` | 投标函、授权书、保证金表 |
| 招标方名称 | `_[招标方名称]_`、`[招标方名称]` | 投标函抬头 |
| 招标代理名称 | `_[招标代理名称]_`、`[招标代理名称]` | 投标函抬头、保证金表抬头 |
| 投标有效期天数 | `【 30 】` | 投标函第4款 |
| 投标保证金金额 | 脱敏金额文本 | 保证金表 |
| 付款方式/质保期/交货期 | — | 差异表 |
| 是否接受联合体/分包 | — | 投标函 |
| 需何种认证 | `_[认证承诺]_` | 投标函第12款 |
| 标段名称 | `（标段1名称）` | 报价总表 |

> 如招标文件为 PDF，使用可用的 PDF 读取工具提取文本。如已有解析好的 Markdown，可直接用 `scripts/bid_pipeline.py` 自动提取。

---

## 步骤2: 扫描脱敏模板，发现占位符

脱敏模板中需要替换的内容分三类：

### 2.1 通用占位符（三套格式）

模板中可能同时存在以下格式的占位符，**全部需要替换**：

| 格式 | 示例 |
|------|------|
| `_[xxx]_` 双下划线 | `_[招标方名称]_`、`_[招标编号]_` |
| `[xxx]` 纯方括号 | `[招标方名称]`、`[项目名称]` |
| `（xxx）` 中文全角括号 | `（标段1名称）` |

**操作**：解包模板扫描所有占位符：

```bash
# PowerShell 解包并扫描
Add-Type -AssemblyName System.IO.Compression.FileSystem
$tmp = "$env:TEMP\scan_tmpl"
[System.IO.Compression.ZipFile]::ExtractToDirectory("模板.docx", $tmp)
Get-ChildItem -Recurse $tmp -Filter *.xml | ForEach-Object {
  $txt = Get-Content $_.FullName -Raw
  [regex]::Matches($txt, `'_[^_]+_`') | % { $_.Value }
  [regex]::Matches($txt, `'\[[^\]]+\]`') | % { $_.Value }
  [regex]::Matches($txt, `'（[^）]+）`') | % { $_.Value }
} | Sort-Object -Unique
```

### 2.2 脱敏文本（公司特定字符串）

脱敏模板中的敏感信息已被替换为虚拟文本。常见模式：
- 公司全称 → `XX重工股份有限公司`
- 公司简称 → `XX重工`
- 法人姓名 → `金红萍`
- 授权代表 → `沈小芳`
- 地址 → `[公司地址]`、`江苏省 苏州市`
- 银行信息 → `[开户银行名称]`、`[银行账号]`
- 联系方式 → `[电话已脱敏]`、`[手机已脱敏]` 等
- 金额占位 → `【 随机金额万美元 ... 】`

**操作**：阅读 `word/document.xml` 和 `word/header*.xml`，找出上述模式的文本。

### 2.3 日期处理（自动，无需手动配键）

**日期由引擎自动处理，不需要在 JSON 中配置。**

> **处理顺序**：阶段1先替换日期 → 阶段2再替换占位符。日期优先处理避免占位符值干扰日期匹配。

引擎通过三套正则覆盖所有日期变体，自动替换为当天实时日期：

| 正则 | 覆盖格式 | 示例 |
|------|---------|------|
| 完整日期（含下划线/空格分隔） | `2014年7月29日`、`__2014__年__7__月__29__日` |
| 有年份无月日数字 | `2014年    月    日`、`__2014__年_____月_____日` |
| 无年份的月日占位 | `年    月    日` |

日期**始终替换为当天**（`YYYY年MM月DD日`），当日日期自动跳过。如招标文件有特定截止日期要求，在 pipeline 解析时会保留。

> ⚠️ **不要**在 JSON 中添加日期碎片键（如 `"2014"`、`"7月"`、`"29日"` 等），这会导致与正则替换冲突产生乱码。

### 2.4 构建占位符清单

汇总为表格，标注每项的**数据来源**：

| 原文（模板中） | 类型 | 需填入 | 来源 |
|---------------|------|--------|------|
| `_[招标方名称]_` | 占位符 | 招标方全称 | 步骤1 |
| `[招标编号]` | 占位符 | 招标编号 | 步骤1 |
| `XX重工股份有限公司` | 脱敏文本 | 投标人全称 | 步骤3 |
| `金红萍` | 脱敏文本 | 法人姓名 | 步骤3 |
| `【 30 】` | 脱敏文本 | 投标有效期 | 步骤1 |
| `【 随机金额... 】` | 脱敏文本 | 实际报价金额 | 步骤3 |
| 日期 `2014年7月29日` | 自动替换 | 今天日期 | 引擎 |

---

## 步骤3: 收集投标人信息

向用户逐项询问。**严禁编造**：

```
1. 投标人全称（营业执照名称）：
2. 投标人简称：
3. 统一社会信用代码：
4. 法定代表人姓名：
5. 授权代表姓名：
6. 注册地址、邮编：
7. 开户银行全称、账号：
8. 联系人姓名、电话、手机、传真：
9. 报价总价（大写+小写）：
10. 近3年类似项目业绩：
11. 持有资质证书清单：
12. 设备主要技术参数：
```

**报价金额必须由用户确认，绝不自行决定。**

---

## 步骤4: 构建替换数据

将步骤1-3 的数据映射为 JSON。

### 规则

- 占位符原文作为 JSON key，实际数据作为 value
- **不要包含日期碎片键**（日期由引擎自动处理）
- 公司名全称 + 简称两个键都要配
- 不需要手动 XML 转义，引擎自动处理

### 快速开始：最小可用的 basic.json

将下方保存为 `basic.json`，替换【】内容为实际数据：

```json
{
  "_[招标方名称]_": "【招标方全称】",
  "[招标方名称]": "【招标方全称】",
  "_[招标编号]_": "【招标编号】",
  "[招标编号]": "【招标编号】",
  "_[招标代理名称]_": "【招标代理全称】",
  "[招标代理名称]": "【招标代理全称】",
  "[项目名称]": "【项目全称】",
  "（标段1名称）": "【标段1名称】",
  "（标段2名称）": "【标段2名称】",
  "（标段3名称）": "【标段3名称】",
  "【 30 】": "【 90 】",
  "XX重工股份有限公司": "【你的公司全称】",
  "XX重工": "【你的公司简称】",
  "金红萍": "【法定代表人】",
  "沈小芳": "【授权代表】",
  "[公司地址]": "【注册地址】",
  "[开户银行名称]": "【开户银行全称】",
  "[银行账号]": "【银行账号】",
  "[电话已脱敏]": "【电话】",
  "[手机已脱敏]": "【手机】",
  "[传真已脱敏]": "【传真】",
  "[邮编已脱敏]": "【邮编】",
  "江苏省 苏州市": "【省份 城市】",
  "174774": "【报价总价数字】",
  "184968": "【报价总价数字】",
  "壹拾叁万元整": "【报价总价大写】",
  "元玖拾玖万贰仟伍佰叁拾陆元整": "【报价总价大写】",
  "￥          元": "￥ 【金额】 元",
  "￥           元": "￥ 【金额】 元",
  "大写                       ": "大写    【大写金额】"
}

> **金额空白占位符**：模板中金额可能显示为仅有空格无数字（如 `￥          元`），需在 JSON 中精确匹配替换。扫描模板中所有 `￥` 或 `大写` 附近文本，确保 JSON 覆盖所有空白金额模式。
> **页眉页脚**：引擎会自动修补 python-docx 可能遗漏的非默认页眉类型（header1/2/3.xml 等），确保页眉中的占位符也被替换。
```

> **日期不需要配置**，引擎自动将所有旧日期替换为当天实时日期。

### JSON 模板示例

```json
{
  "_[招标方名称]_": "华能国际电力股份有限公司",
  "[招标方名称]": "华能国际电力股份有限公司",
  "_[招标编号]_": "HNZB2026-00123",
  "[招标编号]": "HNZB2026-00123",
  "_[招标代理名称]_": "华能招标有限公司",
  "[招标代理名称]": "华能招标有限公司",
  "[项目名称]": "华能XX电厂锅炉设备采购",

  "XX重工股份有限公司": "大连华锐重工集团股份有限公司",
  "XX重工": "大连重工",
  "金红萍": "张三",
  "沈小芳": "李四",

  "[公司地址]": "辽宁省大连市西岗区八一路169号",
  "[开户银行名称]": "中国工商银行大连分行",
  "[银行账号]": "3400200123456789012",
  "[电话已脱敏]": "0411-86881234",
  "[手机已脱敏]": "13901011234",
  "[传真已脱敏]": "0411-86881235",
  "[邮编已脱敏]": "116001",

  "【 30 】": "【 90 】",
  "【 】": "【 人民币捌仟伍佰陆拾万元整 ¥85,600,000.00 】",
  "【 随机金额万美元 美元玖拾玖万贰仟伍佰叁拾陆元整 】": "【 人民币捌仟伍佰陆拾万元整 ¥85,600,000.00 】",

  "江苏省 苏州市": "辽宁省 大连市",
  "（标段1名称）": "锅炉本体",
  "（标段2名称）": "辅机设备"
}
```

### 特殊值处理

| 类型 | 规则 | 示例 |
|------|------|------|
| 大写金额 | 用户提供，不自行转换 | "捌佰伍拾陆万元整" |
| 认证承诺 | 根据招标文件要求撰写 | "我单位承诺在收到中标通知书后6个月内..." |
| 公司名称 | 全称+简称都要配 | 页眉可能只有简称 |
| 随机金额 | 整串文本匹配替换 | 不是占位符，是脱敏示例文本 |
| **日期** | **不要在JSON中配置，引擎自动处理** | — |

---

## 步骤5: 执行填充

### > **大文件处理**：模板 >5MB 时自动使用 fill_large 纯 Python zipfile 引擎，零外部依赖，同样支持日期优先 + 占位符后处理。

方式 A：Python（推荐）

```bash
python scripts/fill_py.py <模板.docx> <输出.docx> <替换数据.json>
```

两阶段引擎：先占位符替换，再整日期替换。自动报告遗漏占位符。

> ⚠️ **格式注意**：当占位符跨多个 Word run 时，引擎会按比例将替换文本分配回各 run，保留原有加粗/斜体等格式属性。若替换文本长度与原占位符差异较大，可能导致文字在不同格式 run 间重新分配，但不会丢失格式定义。
>
> **页眉页脚处理**：`python-docx` 只能访问默认页眉。引擎保存后会额外解包 DOCX，直接修补 `header*.xml` 和 `footer*.xml` 中遗漏的占位符和日期。

### 方式 B：Node.js（零依赖，仅 Windows）

```bash
node scripts/fill_template.js <模板.docx> <输出.docx> <替换数据.json>
```

同两阶段策略，额外自动检查遗漏占位符。依赖 PowerShell 进行 ZIP 操作，不支持 macOS/Linux。

### 方式 C：管道一键生成

```bash
python scripts/bid_pipeline.py 招标文件.md [--company-json 公司数据.json] [--templates 模板1.docx 模板2.docx ...]
```

自动完成步骤1→4→5，从招标文件 Markdown 到成品标书。

成品文件默认输出到招标文件同目录下的 成品标书/ 子文件夹。文件命名格式： 标书类型_项目名称.docx。

- 默认查找模板目录下 `*脱敏*.docx` 文件
- 可通过 `--templates` 显式指定模板列表
- `--company-json` 指定公司脱敏数据（格式同步骤4中的公司部分）
- 大文件（>5MB）自动切换纯 Python zipfile 引擎，跨平台兼容

---

## 步骤6: 验证输出

填充完成后自动运行 scripts/verify_bid.py 进行二次核对：

```bash
# 自动（pipeline 内置）
python scripts/bid_pipeline.py 招标文件.pdf --company-docx 公司信息.docx
# → 填充完成后自动审核每个输出文件

# 手动
python scripts/verify_bid.py 成品标书.docx --company-docx 公司信息.docx

# 跳过审核
python scripts/bid_pipeline.py 招标文件.md --company-json company.json --no-verify
```

**检查等级：**

| 等级 | 检查项 | 示例 |
|------|--------|------|
| P0 致命 | 遗漏占位符、日期未更新、公司名不一致 | _[招标编号]_ 未替换 |
| P1 严重 | 关键字段为空、疑似乱码、金额不一致 | 投标报价字段空白 |
| P2 提示 | 空白方括号、签名区提醒、异常标点 | 【  】 未填充 |

手动补充检查：
- 投标函抬头、授权书姓名、保证金账户
- 报价总表金额、签名区
- 页眉页脚信息

## 故障排除

| 问题 | 可能原因 | 解决方案 |
|------|---------|---------|
| 占位符未被替换 | JSON 中的 key 与模板中的文本不完全一致 | 检查空格、全角/半角字符 |
| 日期没有被替换 | 日期格式不匹配正则 | 确保日期为"YYYY年MM月DD日"格式 |
| `scripts/fill_py.py` 报 ModuleNotFoundError | 未安装 python-docx | `pip install python-docx` |
| `scripts/fill_template.js` 报错 | 非 Windows 系统 | 改用 Python 方式 |
| 输出文件格式错乱 | 模板中的复杂格式 | 参见步骤5的格式警告 |
| 保证金表格金额为空 | 模板金额为空白占位符（`￥  元`） | 在 JSON 中精确匹配该空白模式 |
| 页眉未更新 | 模板使用了非默认页眉类型 | 引擎已自动处理，无需手动干预 |
| 模板未找到 | 命名不符合约定 | 使用 `--templates` 显式指定 |

---

## 严禁事项

- **绝不编造**业绩、资质、人员、报价等任何信息
- **绝不跳过**招标文件要求的任何附件
- **报价金额**必须经用户确认
- 模板中已有的标准条款结构**不要修改**，只替换变量
- **不要在 JSON 中配置日期碎片键**，日期由引擎自动整日期替换

