proposal+ppt: 路径用 <skill_dir>, 补 spec_lock 模板与 --spec 覆盖度检查

按代码评审建议改的 5 项:

1. 所有脚本/资源路径改成相对 <skill_dir> (load_skill 头里的绝对路径),
   不再假设 cwd 是 zcbot 仓库根。proposal+ppt 的 SKILL.md / icons.md /
   INDEX.md 都改了。

2. quality_check.py REQUIRED_SECTIONS 给 key_rd 补上 11_team /
   12_budget / 13_appendix —— 之前模板有但检查没到, 缺团队/预算/附件
   也会显示结构完整。

3. 新增 templates/spec_lock.md, 把"八条对齐"固化成可复制字段
   (含考核指标矩阵表 + TODO 列表 + 引文清单), 阶段二/三都从这里读。

4. quality_check.py 加 --spec <spec_lock.md> 选项, 解析 spec 中的
   考核指标矩阵, 关键词模糊匹配 sections, 提示哪些指南指标在正文
   未充分覆盖。SKILL.md 阶段三命令同步。

5. SKILL.md 新增"阶段零: 摄取素材", 用 run_python + pypdf/python-docx/
   openpyxl 把 PDF/DOCX/XLSX 转成 source/*.md, 不再要新脚本。同时
   把 spec_lock 字段引用改写到模板。

顺带:
- proposal SKILL.md 明确 7 类基金里只有 3 类 (key_rd/major_project/
  nsfc_joint_fund) 有完整章节模板, 其它 4 类复用骨架, 差异查 fund_types.md
- ppt SKILL.md 阶段三命令路径错误 (python scripts/quality_check.py)
  顺带修了
This commit is contained in:
caoqianming 2026-05-07 16:05:44 +08:00
parent d1f39f05f5
commit 647d92f532
6 changed files with 235 additions and 46 deletions

View File

@ -57,8 +57,8 @@ description: 生成 PowerPoint 演示文稿 (.pptx)。当用户要求做汇报 P
每页前 **必须 read 一次 `spec_lock.md`**,只用里面定的颜色/字体/图标 —— **不允许凭记忆或临时发挥**。这条规则是为了对抗长 deck 中的上下文漂移。
每页流程:
1. 读 `spec_lock.md` (即使刚读过)
2. **图标先于版式**: 这一页要用什么概念图标? 先 `glob skills/ppt/assets/icons/` 看本地有没有,没有就 `python skills/ppt/scripts/fetch_icon.py <name> --set tabler --color C00000 --size 128 -o skills/ppt/assets/icons/...` 拉一个;`add_picture` 嵌入。**几何形状(圆点/徽章/装饰线)不算图标,走 layouts.md helper 即可**
1. 读 `<task_dir>/spec_lock.md` (即使刚读过)
2. **图标先于版式**: 这一页要用什么概念图标? 先 `glob <skill_dir>/assets/icons/` 看本地有没有 (`<skill_dir>` 是 `load_skill` 头里的绝对路径),没有就 `python <skill_dir>/scripts/fetch_icon.py <name> --set tabler --color C00000 --size 128 -o <skill_dir>/assets/icons/...` 拉一个;`add_picture` 嵌入。**几何形状(圆点/徽章/装饰线)不算图标,走 layouts.md helper 即可**
3. 写一个 `run_python` block,用 python-pptx 添加这一页 (载入已有 .pptx → append slide → save)
4. 报这一页:版式、标题、要点条数、用了哪些图标
5. 用户确认 / 微调后再下一页
@ -70,7 +70,7 @@ description: 生成 PowerPoint 演示文稿 (.pptx)。当用户要求做汇报 P
### 阶段三: 验收
```bash
python scripts/quality_check.py <output.pptx> --spec spec_lock.md
python <skill_dir>/scripts/quality_check.py <task_dir>/<output.pptx> --spec <task_dir>/spec_lock.md
```
不通过的项,回头 edit 对应页。

View File

@ -16,23 +16,23 @@
按下面 3 行命令拉取首批最常用 18 个,够覆盖 80% 商务汇报场景:
```bash
ICONS_DIR=skills/ppt/assets/icons
ICONS_DIR=<skill_dir>/assets/icons # <skill_dir> 来自 load_skill 头
# 战略 / 目标 / 启动
for n in target rocket flag bulb; do
python skills/ppt/scripts/fetch_icon.py $n --set tabler --color C00000 --size 128 \
python <skill_dir>/scripts/fetch_icon.py $n --set tabler --color C00000 --size 128 \
-o "$ICONS_DIR/tabler_${n}_C00000_128.png"
done
# 数据 / 趋势 / 报表
for n in chart-bar chart-line trending-up calculator; do
python skills/ppt/scripts/fetch_icon.py $n --set tabler --color C00000 --size 128 \
python <skill_dir>/scripts/fetch_icon.py $n --set tabler --color C00000 --size 128 \
-o "$ICONS_DIR/tabler_${n}_C00000_128.png"
done
# 团队 / 流程 / 时间
for n in users settings calendar clock check shield-check arrow-right alert-triangle currency-yuan circle-check; do
python skills/ppt/scripts/fetch_icon.py $n --set tabler --color C00000 --size 128 \
python <skill_dir>/scripts/fetch_icon.py $n --set tabler --color C00000 --size 128 \
-o "$ICONS_DIR/tabler_${n}_C00000_128.png"
done
```

View File

@ -14,7 +14,7 @@
## §A. Iconify 个性化图标 (本地缓存 + 网络拉取)
### A1. 本地库
路径: `skills/ppt/assets/icons/`,详见 [INDEX.md](../assets/icons/INDEX.md)。
路径: `<skill_dir>/assets/icons/`,详见 [INDEX.md](../assets/icons/INDEX.md)。
命名规约: `<set>_<name>_<colorhex>_<sizepx>.png`(如 `tabler_rocket_C00000_128.png`)
**用之前先 `glob` 检查本地有没有**,有就直接 `add_picture`,免去网络往返。
@ -22,12 +22,12 @@
### A2. fetch_icon.py 拉新图标
```bash
# 主红色 128px PNG (推荐)
python skills/ppt/scripts/fetch_icon.py rocket --set tabler --color C00000 \
--size 128 -o skills/ppt/assets/icons/tabler_rocket_C00000_128.png
python <skill_dir>/scripts/fetch_icon.py rocket --set tabler --color C00000 \
--size 128 -o <skill_dir>/assets/icons/tabler_rocket_C00000_128.png
# 强调色金黄
python skills/ppt/scripts/fetch_icon.py target --set tabler --color FFC107 \
--size 128 -o skills/ppt/assets/icons/tabler_target_FFC107_128.png
python <skill_dir>/scripts/fetch_icon.py target --set tabler --color FFC107 \
--size 128 -o <skill_dir>/assets/icons/tabler_target_FFC107_128.png
```
`--set` 默认 `tabler`(4500+ 商务图标,MIT)。其它选 `lucide / heroicons / material-symbols / carbon / fluent / mdi`。**整 deck 只用一个 set**。
@ -37,7 +37,7 @@ PNG 转换需 `pip install cairosvg`(推荐)或 `pip install svglib`。没装也
### A3. 嵌入幻灯片
```python
slide.shapes.add_picture(
"skills/ppt/assets/icons/tabler_rocket_C00000_128.png",
"<skill_dir>/assets/icons/tabler_rocket_C00000_128.png",
Inches(1.0), Inches(2.5),
width=Inches(0.8), # 装饰图标 0.5-1.5 in;别超 2 in
)
@ -53,7 +53,7 @@ slide.shapes.add_picture(
```python
from pptx.util import Inches, Pt
stages = ["调研","设计","开发","测试","上线"]
icon_path = "skills/ppt/assets/icons/tabler_chevron-right_C00000_64.png"
icon_path = "<skill_dir>/assets/icons/tabler_chevron-right_C00000_64.png"
for i, label in enumerate(stages):
x = 0.7 + i * 2.4
add_textbox(slide, x, 3.7, 1.8, 0.5, label, 16, bold=True,
@ -79,7 +79,7 @@ Iconify 都没合适的时候用。避 emoji,用单色符号:
```bash
# 强调色对号 96px → PNG
python scripts/render_icon.py "✓" --color "#C00000" --size 96 -o slides/check.png
python <skill_dir>/scripts/render_icon.py "✓" --color "#C00000" --size 96 -o <task_dir>/slides/check.png
```
## §C. 硬规则

View File

@ -8,37 +8,62 @@ description: 撰写中国科研项目申报书 / 课题任务书 (国家重点
把课题信息变成可提交的申报书 .docx。**先定基金类型 → 八条对齐 → 逐章起草 → 验收渲染** —— 不要一口气出全文。
## 资源
- `references/fund_types.md` —— 6 类基金的章节骨架 + 字数预算 + 必填表格 (always read)
- `references/review_redlines.md` —— 评审雷区与不可考核词清单
- `references/citation_gbt7714.md` —— GB/T 7714 顺序编码制 + 文献真实性铁律
- `references/budget_rules.md` —— 间接费用台阶 + B1-B4 表
- `templates/{key_rd,major_project,nsfc_joint_fund}.md` —— 章节骨架 (复制到 sections/ 填空)
- `scripts/render_docx.py` —— md→docx,自动加目录、解析 `**bold**`/`*italic*`/`` `code` ``、列表分行
- `scripts/word_count.py` —— 章节字数 vs 预算
- `scripts/quality_check.py` —— 结构完整性 / 假大空话术 / 占位符未替换扫描
下面所有路径都相对 **`<skill_dir>`** —— `load_skill` 返回头里的 `[skill=proposal, dir=<绝对路径>]`,用这个绝对路径拼脚本/资源,不要假设 cwd。
- `<skill_dir>/references/fund_types.md` —— 6 类基金的章节骨架 + 字数预算 + 必填表格 (always read)
- `<skill_dir>/references/review_redlines.md` —— 评审雷区与不可考核词清单
- `<skill_dir>/references/citation_gbt7714.md` —— GB/T 7714 顺序编码制 + 文献真实性铁律
- `<skill_dir>/references/budget_rules.md` —— 间接费用台阶 + B1-B4 表
- `<skill_dir>/templates/spec_lock.md` —— 阶段一八条对齐的固定字段模板 (复制到 `<task_dir>/spec_lock.md`)
- `<skill_dir>/templates/{key_rd,major_project,nsfc_joint_fund}.md` —— **有完整章节模板**的 3 类基金;其它 4 类 (`nsfc_general` / `nsfc_youth` / `provincial` / `enterprise`) 复用 `nsfc_joint_fund``key_rd` 骨架,差异看 `fund_types.md` § 4-6
- `<skill_dir>/scripts/render_docx.py` —— md→docx,自动加目录、解析 `**bold**`/`*italic*`/`` `code` ``、列表分行
- `<skill_dir>/scripts/word_count.py` —— 章节字数 vs 预算
- `<skill_dir>/scripts/quality_check.py` —— 结构完整性 / 假大空话术 / 占位符未替换 / 指南覆盖度 (--spec 选项)
## 阶段零: 摄取素材 (有 PDF/DOCX 时才走)
用户给指南 PDF / 团队介绍 DOCX / 预算 XLSX → 先转成 `<task_dir>/source/<name>.md`,后续阶段一才能读。用 `run_python` 即可,不需要新工具:
```python
# PDF (指南文件)
from pypdf import PdfReader
text = "\n\n".join(p.extract_text() or "" for p in PdfReader(pdf_path).pages)
Path("<task_dir>/source/guide.md").write_text(text, encoding="utf-8")
# DOCX (团队/前期成果)
from docx import Document
doc = Document(docx_path)
md = "\n".join(p.text for p in doc.paragraphs if p.text.strip())
# 表格
for t in doc.tables:
for row in t.rows: md += "\n| " + " | ".join(c.text.strip() for c in row.cells) + " |"
# XLSX (预算)
from openpyxl import load_workbook
wb = load_workbook(xlsx_path)
for ws in wb.worksheets:
for row in ws.iter_rows(values_only=True): print(row)
```
转完后 spec_lock 阶段直接 `read <task_dir>/source/*.md` 拿事实,不要凭印象写。
## 阶段一: 八条对齐
产物 `spec_lock.md` —— 申报书"宪法",阶段二每章前都要重读。
产物 `<task_dir>/spec_lock.md` —— 申报书"宪法",阶段二每章前都要重读。
**先读 `references/fund_types.md` 选基金类型**(章节、字数、表格各不相同),按下表给用户预览,⛔ **BLOCKING:用户确认后才进阶段二**
1. **复制模板**: `read <skill_dir>/templates/spec_lock.md``write <task_dir>/spec_lock.md`
2. **先读 `<skill_dir>/references/fund_types.md` 选基金类型**(章节、字数、表格各不相同)
3. 按 spec_lock.md 字段填,给用户预览
4. ⛔ **BLOCKING:用户确认后才进阶段二**
| # | 项 | 默认/示例 |
|---|----|---------|
| 1 | 基金类型 | `key_rd` 重点研发 / `major_project` 重大专项 / `nsfc_joint_fund` 联合基金 / `nsfc_general` 面上 / `nsfc_youth` 青年 / `provincial` 省基金 / `enterprise` 横向 |
| 2 | 指南方向 | **一字不改**抄指南。无指南的(青年)填研究方向 |
| 3 | 关键科学/技术问题 | 1-2 个,与指南对齐;**问题 ≠ 任务** |
| 4 | 创新点 | ≤3 条,每条 ≤500 字。**小而尖** |
| 5 | 研究内容骨架 | 5-8 个研究模块 (重点研发要分课题) |
| 6 | 申报/牵头单位 + 团队 | 含主要参与单位与负责人 (空缺填 `<TODO>`) |
| 7 | 考核指标 | 量化、可考核、覆盖指南。形式: 软件 N 个 / 平台 N 个 / 专利 N 项 / 标准 N 项 |
| 8 | 经费预算 | 总额 + 中央财政 + 自筹 + 其他渠道 (查 `budget_rules.md`) |
字段清单见 `<skill_dir>/templates/spec_lock.md` (基金类型 / 指南方向 / 关键科学技术问题 / 创新点 / 研究内容骨架 / 团队 / 考核指标矩阵 / 经费预算 / TODO 列表)。
## 阶段二: 逐章起草
每章流程:
1. 读 `spec_lock.md``references/fund_types.md` 拿本章字数预算与必填要素
2. 复制 `templates/<fund_type>.md` 对应小节到 `sections/NN_xxx.md`,填空
1. 读 `<task_dir>/spec_lock.md``<skill_dir>/references/fund_types.md` 拿本章字数预算与必填要素
2. 复制 `<skill_dir>/templates/<fund_type>.md` 对应小节到 `<task_dir>/sections/NN_xxx.md`,填空
3. 报告: 章节名 / 实际字数 / 字数预算 / 与指南对齐情况
4. ⛔ **BLOCKING:停下来等用户明确反馈** ("OK"、"下一章"、"继续") 后才进下一章。"看起来不错"、沉默、追问都不算确认 —— 主动问"这一章可以了吗?要改哪里?"
@ -49,12 +74,12 @@ description: 撰写中国科研项目申报书 / 课题任务书 (国家重点
## 阶段三: 验收 + 渲染
```bash
python skills/proposal/scripts/word_count.py <task_dir>/sections/ --fund-type key_rd
python skills/proposal/scripts/quality_check.py <task_dir>/sections/ --fund-type key_rd
python skills/proposal/scripts/render_docx.py <task_dir>/sections/ --fund-type key_rd -o <task_dir>/<topic>.docx
python <skill_dir>/scripts/word_count.py <task_dir>/sections/ --fund-type key_rd
python <skill_dir>/scripts/quality_check.py <task_dir>/sections/ --fund-type key_rd --spec <task_dir>/spec_lock.md
python <skill_dir>/scripts/render_docx.py <task_dir>/sections/ --fund-type key_rd -o <task_dir>/<topic>.docx
```
不通过的项,回头 edit 对应章节。
`--spec` 让质量检查交叉对照 spec_lock,提示哪些指南考核指标在 sections 里没出现。不通过的项,回头 edit 对应章节。
## 工作目录

View File

@ -22,6 +22,7 @@ REQUIRED_SECTIONS: dict[str, list[str]] = {
"00_basic_info", "01_summary", "02_background", "03_objectives",
"04_content", "05_decomposition", "06_innovation", "07_benefit",
"08_basis", "09_schedule", "10_organization",
"11_team", "12_budget", "13_appendix",
],
"major_project": [
"00_basic_info", "01_objectives", "02_content", "03_innovation",
@ -87,10 +88,62 @@ def check_placeholders(text: str, file_label: str) -> list[str]:
return issues
def parse_spec_metrics(spec_path: Path) -> list[str]:
"""从 spec_lock.md 的"7. 考核指标矩阵"段抽出"指南考核指标"那列。
寻找形如 `| 1 | 指南指标 | ... |` 的表行(序号 = 数字),取第 2
返回每条指南指标的关键短语列表 (用于在 sections 中模糊匹配)
"""
if not spec_path.exists():
return []
txt = spec_path.read_text(encoding="utf-8")
# 截取 "考核指标矩阵" 段到下一节标题
m = re.search(r"考核指标矩阵.*?(?=\n##\s|\Z)", txt, re.DOTALL)
if not m:
return []
block = m.group(0)
out: list[str] = []
for line in block.splitlines():
if not line.strip().startswith("|"):
continue
cells = [c.strip() for c in line.strip().strip("|").split("|")]
if len(cells) < 3:
continue
# 表头行 / 分隔行 跳过
if not cells[0].isdigit():
continue
guide_metric = cells[1]
if guide_metric and not guide_metric.startswith("<TODO") and not guide_metric.startswith("`"):
out.append(guide_metric)
return out
def check_spec_coverage(sections_dir: Path, spec_path: Path) -> list[str]:
"""每条指南考核指标必须在某个章节里以**关键词**形式出现 (>=2 个核心词命中)。"""
metrics = parse_spec_metrics(spec_path)
if not metrics:
return []
# 把 sections 全文拼起来
full = "\n".join(f.read_text(encoding="utf-8") for f in sections_dir.glob("*.md"))
issues = []
for metric in metrics:
# 提关键词: 取长度 >=2 的中文片段 / 数字 / 字母组合
tokens = re.findall(r"[一-鿿]{2,}|[A-Za-z][\w]*|\d+\.?\d*", metric)
if not tokens:
continue
hits = sum(1 for t in tokens if t in full)
# 至少命中 2 个 token, 且至少 30% 的 token 出现
if hits < 2 or hits / len(tokens) < 0.3:
issues.append(f"指南指标可能未在正文覆盖: '{metric[:50]}...' (命中 {hits}/{len(tokens)} 关键词)")
return issues
def main() -> None:
ap = argparse.ArgumentParser(description="申报书质量检查")
ap.add_argument("sections_dir", type=Path)
ap.add_argument("--fund-type", required=True, choices=list(REQUIRED_SECTIONS.keys()))
ap.add_argument("--spec", type=Path, default=None,
help="spec_lock.md 路径; 提供后会做指南考核指标覆盖度检查")
ap.add_argument("--strict", action="store_true",
help="严格模式: 任何检查项失败均退出 1")
args = ap.parse_args()
@ -99,7 +152,7 @@ def main() -> None:
print(f"[ERR] {args.sections_dir} not a directory", file=sys.stderr)
sys.exit(2)
print(f"\n[质量检查]申报书质量检查 ({args.fund_type})\n")
print(f"\n[质量检查] fund_type={args.fund_type}\n")
all_issues: list[str] = []
@ -113,7 +166,7 @@ def main() -> None:
else:
print("[OK] 结构完整")
# 2-4. 内容
# 2-4. 内容 (假大空 / 不可考核词 / 占位符)
files = sorted(args.sections_dir.glob("*.md"))
print(f"\n{len(files)} 个章节, 逐章扫描...\n")
for f in files:
@ -128,13 +181,30 @@ def main() -> None:
print(f" -{s.split('] ', 1)[1]}")
all_issues.extend(sub_issues)
# 5. 指南覆盖度 (--spec 提供时)
if args.spec:
if not args.spec.exists():
print(f"\n[ERR] spec 文件不存在: {args.spec}")
all_issues.append("spec 文件不存在")
else:
print(f"\n[指南覆盖度] 对照 {args.spec.name}")
cov_issues = check_spec_coverage(args.sections_dir, args.spec)
if cov_issues:
print("[WARN] 部分指南指标可能在正文未充分覆盖:")
for s in cov_issues:
print(f" -{s}")
all_issues.extend(cov_issues)
else:
print("[OK] 指南考核指标在正文均有体现")
print("\n" + "=" * 60)
if all_issues:
print(f"[WARN] 共发现 {len(all_issues)} 个问题。")
print("\n建议:")
print(" - 假大空词组 → 换成具体数字 / 对比")
print(" - 不可考核词 → 量化指标 (TPS / 准确率 / 万元 / N 篇)")
print(" - 占位符未替换 → 找用户提供真实数据 / 替换 <TODO>")
print(" - 假大空词组 -> 换成具体数字 / 对比")
print(" - 不可考核词 -> 量化指标 (TPS / 准确率 / 万元 / N 篇)")
print(" - 占位符未替换 -> 找用户提供真实数据 / 替换 <TODO>")
print(" - 未覆盖指南指标 -> 在对应章节明确写出该指标的实现方式")
if args.strict:
sys.exit(1)
else:

View File

@ -0,0 +1,94 @@
# 申报书 spec_lock
> 阶段一产物。**写定后不再改**,阶段二每章前都要 read。`<TODO>` 是占位符,需要用户明确填值;不要硬编。
## 1. 基金类型
`<TODO 基金类型代码>` (key_rd / major_project / nsfc_joint_fund / nsfc_general / nsfc_youth / provincial / enterprise)
对应资助强度: `<TODO>` 万元 / `<TODO>`
## 2. 指南方向
> **一字不改**抄指南文本。无指南 (青年/横向) 填研究方向。
```
<TODO 指南原文 项目名称 / 所属专项 / 指南方向 (榜单任务) / 创新分类>
```
## 3. 关键科学问题 / 关键技术问题
(1-2 个,与指南对齐;**问题 ≠ 任务**)
- 关键问题 1: `<TODO 一句话提炼,不是"研究 XXX 技术">`
- 关键问题 2: `<TODO ...>`
## 4. 创新点
(≤3 条,每条 ≤500 字。三特征公式: 基本形态 + 前沿性 + 知识产权特征)
- 创新点 1: `<TODO 标题 + 50 字摘要>`
- 创新点 2: `<TODO ...>`
- 创新点 3: `<TODO ...>`
## 5. 研究内容骨架 / 课题分解
| # | 名称 | 牵头单位 | 关键技术 | 经费占比 |
|---|---|---|---|---|
| 1 | `<TODO>` | `<TODO>` | `<TODO>` | XX% |
| 2 | `<TODO>` | `<TODO>` | `<TODO>` | XX% |
| ... |
(重点研发/重大专项必须分课题,通常 4-6 个;NSFC 面上/青年填 3-5 项研究内容即可)
## 6. 团队
### 牵头单位
`<TODO 单位名称>` —— `<TODO 一句话定位 / 关键资质>`
### 项目/课题负责人
`<TODO 姓名 + 职称 + 学位 + 研究方向>` —— 在本项目中承担 `<TODO 角色>`
### 主要参与单位 (按重要性)
- `<TODO 单位 1>` — 定位 + 在本项目中承担 X
- `<TODO 单位 2>` — ...
## 7. 考核指标矩阵 ← 阶段三 quality_check 会读这一节
> 一行一条指南指标。**指南那栏字面抄,不能改写**;本项目那栏可超出,不能低于。
| # | 指南考核指标 (字面抄) | 本项目考核指标 (量化) | 评测方式 | 验收交付物 |
|---|---|---|---|---|
| 1 | `<TODO 抄指南>` | `<TODO 量化, 等于或超出>` | CNAS/CMA / 测试大纲 / 应用示范报告 | `<TODO>` |
| 2 | `<TODO>` | `<TODO>` | `<TODO>` | `<TODO>` |
| ... |
## 8. 经费预算
| 科目 | 金额 (万元) | 备注 |
|---|---|---|
| 中央财政 | `<TODO>` | |
| 直接费用 - 设备费 | `<TODO>` | 50 万以上需 B3 表 |
| 直接费用 - 业务费 | `<TODO>` | |
| 直接费用 - 劳务费 | `<TODO>` | 仅给临时聘用 |
| 间接费用 | `<TODO>` | 台阶: ≤500 万≤30% / 500-1000≤25% / >1000≤20% |
| 自筹 / 其他 | `<TODO>` | 应用示范类常需 ≥1:1 配套 |
| **合计** | `<TODO>` | 与指南资助量级匹配 |
## 9. TODO 列表
> 阶段一过程中冒出来的"等用户提供"事项。阶段二每章开头扫一眼;阶段三 quality_check 会扫余留的 `<TODO>`
- [ ] `<TODO 比如:索取团队代表性论文清单>`
- [ ] `<TODO 比如:确认设备购置型号>`
- [ ] `<TODO ...>`
## 10. 引文清单 (用户提供)
> 编造文献是学术不端。引文必须用户给清单 — 这里粘 BibTeX/EndNote/纯文本均可,阶段三按 GB/T 7714 排版。
```
[1] <TODO 用户提供>
[2] <TODO>
...
```