proposal: 阶段二两段式 + render_docx 透传 fenced 代码块
- SKILL.md 阶段二改两段式: 先列 3-6 条要点 → 用户确认 → 再起草 → 用户确认。关键章节 (立项依据/研究方案/技术路线/考核指标) 一段一卡。一次性出全文容易把错方向推到底,要点阶段拦得早 - render_docx.py 支持 ```...``` 围栏: 中文新宋体 + 西文 Consolas + 行距 1.0 + 不缩进 + xml:space=preserve。原先 ASCII 流程图被当散文段落合并,框完全错位 - PROGRESS.md backlog 加 mermaid 预渲染 (mmdc → PNG → add_picture),等 ASCII 透传不够用再做 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f4432cca20
commit
56e414e046
|
|
@ -86,3 +86,4 @@ Python 合计 ~1669 行
|
||||||
3. **小修打磨**(~半小时)—— `Session.save()` 改原子写(tmp + rename),防 surrogate 等异常 truncate
|
3. **小修打磨**(~半小时)—— `Session.save()` 改原子写(tmp + rename),防 surrogate 等异常 truncate
|
||||||
4. **Phase 7 Docker 沙盒**(~1 天)—— 替换 subprocess,run_python 安全升级
|
4. **Phase 7 Docker 沙盒**(~1 天)—— 替换 subprocess,run_python 安全升级
|
||||||
5. **Phase 7 更多 skill / 模型档案**(持续)
|
5. **Phase 7 更多 skill / 模型档案**(持续)
|
||||||
|
6. **Proposal mermaid 流程图预渲染**(~半天,看到第二张图再做)—— 现状是 ASCII 框图走 fenced code 透传 (新宋体 + Consolas + xml:space=preserve),中文与 box drawing 字符宽度对不齐时还是有错位。增强方案: ` ```mermaid ` 块在 `render_docx.py` 里调 `mmdc` (mermaid-cli) → PNG → `add_picture` 嵌入。依赖 Node.js + `npm i -g @mermaid-js/mermaid-cli`,首次配置略麻烦,所以等 ASCII 透传明显不够用再做
|
||||||
|
|
|
||||||
|
|
@ -47,15 +47,23 @@ markitdown https://example.com/x -o <task_dir>/source/policy.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"、"下一章"、"继续") 后才进下一章。"看起来不错"、沉默、追问都不算确认 —— 主动问"这一章可以了吗?要改哪里?"
|
|
||||||
|
|
||||||
**为什么逐章 + 强等?** 申报书 1.5-3 万字,模型连续生成时容易自我加速、把错的方向推到底。每章一卡可以在第 2 章被用户拦下。
|
**A. 起草前列要点** (改要点比改正文便宜):
|
||||||
|
1. 读 `<task_dir>/spec_lock.md` 与 `<skill_dir>/references/fund_types.md`,拿本章字数预算与必填要素
|
||||||
|
2. 列出 3-6 条要点骨架: 本章打算覆盖的论点 / 数据 / 表格,每条贴上对齐的指南要素与预估字数
|
||||||
|
3. ⛔ **BLOCKING:用户确认要点 (改 / 加 / 删) 后才动正文**
|
||||||
|
|
||||||
**例外**: 用户**主动且明确**说"别问,直接全做"或"一气呵成" —— 才能一次跑完,跑完必须 `quality_check.py`。"逐章太慢"之类的抱怨**不算**例外指令,继续问。
|
**B. 正文起草**:
|
||||||
|
4. 复制 `<skill_dir>/templates/<fund_type>.md` 对应小节到 `<task_dir>/sections/NN_xxx.md`,按要点填
|
||||||
|
5. **关键章节一段一卡** —— 立项依据 / 研究方案 / 技术路线 / 考核指标矩阵: 写一段 → 报字数 → 等用户确认 → 写下一段
|
||||||
|
普通章节一节一卡: 整节写完再报
|
||||||
|
6. 报告: 章节名 / 实际字数 / 字数预算 / 与指南对齐情况
|
||||||
|
7. ⛔ **BLOCKING:停下来等用户明确反馈** ("OK"、"下一章"、"继续") 后才进下一章。"看起来不错"、沉默、追问都不算确认 —— 主动问"这一章/这一段可以了吗?要改哪里?"
|
||||||
|
|
||||||
|
**为什么两段式 + 强等?** 申报书 1.5-3 万字,模型连续生成容易自我加速、把错方向推到底。要点阶段拦得早,关键章节段段卡可以在第 2 段被用户拦下。
|
||||||
|
|
||||||
|
**例外**: 用户**主动且明确**说"别问,直接全做"或"一气呵成" —— 才能一次跑完,跑完必须 `quality_check.py`。"逐章太慢"/"段段太碎"之类的抱怨**不算**例外指令,继续问。
|
||||||
|
|
||||||
## 阶段三: 验收 + 渲染
|
## 阶段三: 验收 + 渲染
|
||||||
|
|
||||||
|
|
@ -106,6 +114,8 @@ python <skill_dir>/scripts/render_docx.py <task_dir>/sections/ --fund-type key
|
||||||
|
|
||||||
- 未 spec_lock 就开始硬编正文
|
- 未 spec_lock 就开始硬编正文
|
||||||
- 一次性出全文 (中途改向就全推翻)
|
- 一次性出全文 (中途改向就全推翻)
|
||||||
|
- 跳过"列要点"直接写正文 —— 即使只有一章也要先列要点过一遍
|
||||||
|
- 关键章节 (立项依据 / 研究方案 / 技术路线 / 考核指标) 整章一次出 —— 必须段段卡
|
||||||
- **基于"通用模板"自行套基金类型** —— 重大专项任务书与国自然申请书结构完全不同,**先查 `fund_types.md`**
|
- **基于"通用模板"自行套基金类型** —— 重大专项任务书与国自然申请书结构完全不同,**先查 `fund_types.md`**
|
||||||
- **自己造数据/指标/单位/经费** —— 不知道就 `<TODO 待用户提供>`,不要硬编
|
- **自己造数据/指标/单位/经费** —— 不知道就 `<TODO 待用户提供>`,不要硬编
|
||||||
- 引文写"[Smith et al., 2023]" 但其实没这篇文献
|
- 引文写"[Smith et al., 2023]" 但其实没这篇文献
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,7 @@ _HEADING_RE = re.compile(r"^(#{1,6})\s+(.+)$")
|
||||||
_TABLE_LINE_RE = re.compile(r"^\s*\|.*\|\s*$")
|
_TABLE_LINE_RE = re.compile(r"^\s*\|.*\|\s*$")
|
||||||
_BLOCKQUOTE_RE = re.compile(r"^\s*>\s?")
|
_BLOCKQUOTE_RE = re.compile(r"^\s*>\s?")
|
||||||
_HR_RE = re.compile(r"^\s*-{3,}\s*$|^\s*={3,}\s*$|^\s*_{3,}\s*$")
|
_HR_RE = re.compile(r"^\s*-{3,}\s*$|^\s*={3,}\s*$|^\s*_{3,}\s*$")
|
||||||
|
_FENCE_RE = re.compile(r"^\s*(`{3,}|~{3,})\s*(\S*)\s*$")
|
||||||
|
|
||||||
# 列表项 (各自独立成段, 不跟相邻行合并, 不缩进首行)
|
# 列表项 (各自独立成段, 不跟相邻行合并, 不缩进首行)
|
||||||
_LIST_PATTERNS = [
|
_LIST_PATTERNS = [
|
||||||
|
|
@ -259,6 +260,29 @@ def is_hr(line: str) -> bool:
|
||||||
return bool(_HR_RE.match(line))
|
return bool(_HR_RE.match(line))
|
||||||
|
|
||||||
|
|
||||||
|
# ───────────────────────── 代码块 / ASCII 图 ─────────────────────────
|
||||||
|
|
||||||
|
def add_code_block(doc: Document, lines: list[str], lang: str = "") -> None:
|
||||||
|
"""fenced ``` 块 / ASCII 流程图: 等宽字体 + 行距 1.0 + 不缩进 + 不解析内联 + 保留空格。
|
||||||
|
|
||||||
|
中文用"新宋体"(NSimSun, Windows 自带等宽宋体), 西文用 Consolas, 这样
|
||||||
|
`─ │ ┌ ┐` 这类 box drawing 字符与中文字符的视觉宽度更接近, ASCII 流程图不至于错位。
|
||||||
|
"""
|
||||||
|
for ln in lines:
|
||||||
|
p = doc.add_paragraph()
|
||||||
|
pf = p.paragraph_format
|
||||||
|
pf.first_line_indent = None
|
||||||
|
pf.line_spacing = 1.0
|
||||||
|
pf.space_before = Pt(0)
|
||||||
|
pf.space_after = Pt(0)
|
||||||
|
run = p.add_run(ln if ln else " ") # 空行也占一行高
|
||||||
|
run.font.size = Pt(10.5) # 五号
|
||||||
|
_set_run_fonts(run, cn_font="新宋体", en_font="Consolas")
|
||||||
|
# docx 默认会压缩连续空格 -> 显式 xml:space=preserve, 否则 ASCII 对齐会被破坏
|
||||||
|
for t in run._element.iter(qn("w:t")):
|
||||||
|
t.set(qn("xml:space"), "preserve")
|
||||||
|
|
||||||
|
|
||||||
# ───────────────────────── 表格 ─────────────────────────
|
# ───────────────────────── 表格 ─────────────────────────
|
||||||
|
|
||||||
def _split_md_row(line: str) -> list[str]:
|
def _split_md_row(line: str) -> list[str]:
|
||||||
|
|
@ -322,6 +346,24 @@ def render_md_block(doc: Document, md_text: str) -> None:
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# fenced 代码块 / ASCII 流程图 (```...``` 或 ~~~...~~~)
|
||||||
|
m_fence = _FENCE_RE.match(line)
|
||||||
|
if m_fence:
|
||||||
|
fence = m_fence.group(1)
|
||||||
|
lang = m_fence.group(2) or ""
|
||||||
|
code: list[str] = []
|
||||||
|
i += 1
|
||||||
|
while i < n:
|
||||||
|
m_close = _FENCE_RE.match(lines[i])
|
||||||
|
# 闭围栏: 同种符号 (` vs ~) 且长度 ≥ 开围栏
|
||||||
|
if m_close and m_close.group(1)[0] == fence[0] and len(m_close.group(1)) >= len(fence):
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
code.append(lines[i]) # 不 rstrip, 保留原始空格
|
||||||
|
i += 1
|
||||||
|
add_code_block(doc, code, lang)
|
||||||
|
continue
|
||||||
|
|
||||||
# 表格 (连续若干行 | ... | 视为一张表)
|
# 表格 (连续若干行 | ... | 视为一张表)
|
||||||
if is_table_line(line):
|
if is_table_line(line):
|
||||||
block: list[str] = []
|
block: list[str] = []
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue