# -*- coding: utf-8 -*- """客服 Agent 四份交付文档 · 构建器(JSON 配置驱动) 从既有文档抽取「项目 HTML 版式外壳」,再灌入新正文,保证版式与既有体系逐字一致。 用法:python _docbuilder.py """ import json import re import sys from pathlib import Path SRC = Path(r"D:\桌面\金融\开发文档\D3.1-客服Agent需求开发文档与设计方案.html") OUT_DIR = Path(r"D:\桌面\金融\客服agent") text = SRC.read_text(encoding="utf-8") HEAD_END = '
' SHELL_HEAD = text[: text.index(HEAD_END) + len(HEAD_END)] # ⚠️ 结束锚点必须取「正文第一段注释」的位置:若取 `
`, # 那在文件里是**最后一行**,会把原文档整段正文一并带进来(曾因此产出 4920 行的错件)。 BODY_START = '' SHELL_MID = text[text.index(' \n'): text.index(BODY_START)] SHELL_TAIL = text[text.index(''):] assert 'doc-header' in SHELL_MID and len(SHELL_MID) < 4000, "SHELL_MID 抽取异常" assert SHELL_TAIL.startswith('') # 备份一份外壳,便于后续单独构建 Path(r"D:\桌面\金融\_shell_head.html").write_text(SHELL_HEAD, encoding="utf-8") Path(r"D:\桌面\金融\_shell_mid.html").write_text(SHELL_MID, encoding="utf-8") Path(r"D:\桌面\金融\_shell_tail.html").write_text(SHELL_TAIL, encoding="utf-8") def build(spec: dict) -> str: head = SHELL_HEAD head = head.replace( "XX科技·智能财富管家系统 — 智能客服 Agent 需求开发文档与设计方案", f"{spec['title']}") head = head.replace( '', f'') parts = [] for idx, grp in enumerate(spec["toc"], start=1): active = " active" if idx == 1 else "" arr = " open" if idx == 1 else "" kids = " open" if idx == 1 else "" parts.append(f'
\n' f'\n' f'
') for href, label in grp["items"]: parts.append(f'{label}') parts.append("
\n") toc_html = "\n".join(parts) mid = SHELL_MID mid = mid.replace( '
智能财富管家系统 — 智能客服 Agent 需求开发文档与设计方案
', f'
{spec["topbar"]}
') mid = mid.replace('v2.3 · 访客 ≠ 角色 · 重构基线', f'{spec["badge"]}') mid = mid.replace('
专项需求开发文档 · 详细设计方案
', f'
{spec["eyebrow"]}
') mid = mid.replace( '

XX科技·智能财富管家系统
智能客服 Agent 需求开发文档与设计方案

', f'

{spec["doctitle"]}

') meta_html = "".join( f'
\n' f' {k}\n' f' {v}\n' f'
\n' for k, v in spec["meta"]) mid = re.sub(r'
.*?\n
\n', f'
\n{meta_html}
\n', mid, flags=re.S) tail = SHELL_TAIL tail = tail.replace( "(v2.2 双角色 · 重构基线版)", f"({spec['closing']})") # ⚠️ 版式外壳是从旧版式文档抽取的,会把旧品牌字面一并带进来(曾因此漏改 4 份文档的 # /<h1>/sidebar 品牌)。这里对「外壳」做一次品牌兜底归一,保证任何一次重新生成 # 都不会回退到旧口径。只作用于 head/mid/tail;body 内可能有意保留历史字面(如 P-10 行),不动。 BRAND = ( ("XX科技·智能财富管家系统", "南方基金·智能服务系统"), ("XX科技 · 智能金融事业部", "南方基金 · 智能服务事业部"), ("XX科技", "南方基金"), ("智能财富管家系统", "智能服务系统"), ("南方财富管理有限公司", "南方基金管理股份有限公司"), ("南方财富", "南方基金"), ("400-XXX-XXXX", "400-889-8899"), ("400-826-9518", "400-889-8899"), ("nanfangwm.com", "nffund.com"), ) head, mid, tail = ( __import__("functools").reduce( lambda s, kv: s.replace(kv[0], kv[1]), BRAND, part) for part in (head, mid, tail) ) body = Path(spec["body_file"]).read_text(encoding="utf-8") return head + "\n" + toc_html + mid + body + tail def main() -> None: spec = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) OUT_DIR.mkdir(parents=True, exist_ok=True) out = OUT_DIR / spec["out_file"] html = build(spec) out.write_text(html, encoding="utf-8") print(f"OK {out} ({html.count(chr(10)) + 1} lines)") if __name__ == "__main__": main()