## 为什么做这一步 权威文档 74 份此前**只在本机**,评审者 clone 分支后看不到任何设计文档;而仓库里那两份同名目录 是 **2026-09-16 之前的过期副本,连文件名都是旧的**(无体系编号)。本次按「**权威覆盖过期**」入库。 ## 入库内容 | 目录 | 文件数 | 体积 | 说明 | |---|---|---|---| | `客服agent/` | 24 | 0.77 MB | `D2.1`~`D2.6` 对外交付四件套 + 演示脚本/答辩报告 + `_build` 构建工具 | | `开发文档/` | 50 | 2.16 MB | `D1.x` 索引与决策、`D3.x` 方案、`D4.x` 清除与重构留痕、`D5.x` 业务流程、`D6.x` 业务事实基座、`D7.x` 交付物、`D8.x` 规范 | **旧的过期副本整体移除**(`客服Agent执行Todolist.md` → `D2.1-客服Agent执行Todolist.md` 之类 的改名 + 新增 `D2.5`/`D2.6`),入库后目录内容与权威副本**逐文件一致(零差异,已复核)**。 ## 入库前的安全扫描(必须留痕) - 扫描规则:`sk-` 类密钥 / `Bearer` 长串 / `password=`、`api_key=` 赋值 / 会话中出现过的两把明文 key 片段。 - 结论:**真实密钥只出现在 `.env`**(已被 `.gitignore` 命中,未入库);`.env.example` 与 `config/risk.env.example` 只有**空占位**。 - 文档内唯一命中是 `D3.1` 里一处**截断的示例 JWT**(`Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`), 末尾带省略号,是接口文档的示意值,**不是可用凭据**。
100 lines
3.6 KiB
Python
100 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""四份交付文档 · 跨文档一致性自检。仅读取,不改文件。"""
|
||
import re
|
||
from pathlib import Path
|
||
|
||
D = Path(r"D:\桌面\金融\客服agent")
|
||
OUT = Path(r"D:\桌面\金融\_consistency.txt")
|
||
|
||
FILES = {
|
||
"需求": D / "D2.2-客服Agent需求文档.html",
|
||
"计划": D / "D2.3-客服Agent开发计划.html",
|
||
"Todo": D / "D2.1-客服Agent执行Todolist.md",
|
||
"知识库": D / "D2.4-客服Agent知识库设计方案.html",
|
||
}
|
||
|
||
texts = {}
|
||
lines = []
|
||
|
||
lines.append("== 一、文件基本情况 ==")
|
||
for name, p in FILES.items():
|
||
if p.exists():
|
||
t = p.read_text(encoding="utf-8")
|
||
texts[name] = t
|
||
lines.append(f" {name:4s} {p.name} {t.count(chr(10)) + 1} 行 / {len(t)} 字符")
|
||
else:
|
||
lines.append(f" [MISSING] {p}")
|
||
|
||
lines.append("")
|
||
lines.append("== 二、关键事实在各文档中的出现次数(0 = 遗漏)==")
|
||
FACTS = [
|
||
("功能需求 48 条", r"48 条"),
|
||
("FR-CS-048", r"FR-CS-048"),
|
||
("非功能需求 21 条", r"21 条"),
|
||
("NFR-CS-021", r"NFR-CS-021"),
|
||
("51 项", r"51 项"),
|
||
("7 批次 / 7 个批次", r"7 个?批次"),
|
||
("12 步", r"12 步"),
|
||
("6 文件 / 六文件", r"(六文件|6 个?文件)"),
|
||
("组 2 四文件", r"组 2|组2"),
|
||
("三集合", r"三集合"),
|
||
("阈值 0.75", r"0\.75"),
|
||
("阈值 0.70", r"0\.70"),
|
||
("阈值 0.65", r"0\.65"),
|
||
("over-fetch", r"over-fetch"),
|
||
("不变量", r"不变量"),
|
||
("actor.py", r"actor\.py"),
|
||
("knowledge_tier.py", r"knowledge_tier\.py"),
|
||
("三条红线", r"三条红线"),
|
||
("subject_type", r"subject_type"),
|
||
("零 DDL", r"零 ?DDL"),
|
||
]
|
||
hdr = " " + "事实".ljust(26) + "".join(n.center(8) for n in FILES)
|
||
lines.append(hdr)
|
||
for label, pat in FACTS:
|
||
row = " " + label.ljust(26)
|
||
for name in FILES:
|
||
t = texts.get(name, "")
|
||
row += str(len(re.findall(pat, t))).center(8)
|
||
lines.append(row)
|
||
|
||
lines.append("")
|
||
lines.append("== 三、投顾残留检查(应全部出现在「已清除/作废」语境)==")
|
||
for name, t in texts.items():
|
||
if name == "知识库":
|
||
continue
|
||
hits = [m.start() for m in re.finditer("投顾", t)]
|
||
bad = []
|
||
for h in hits:
|
||
ctx = t[max(0, h - 60): h + 60].replace("\n", " ")
|
||
if not re.search(r"已(整体)?清除|作废|不再|已删除|清除后|已移除|已下架|原|投顾模块已", ctx):
|
||
bad.append(ctx)
|
||
lines.append(f" {name}: 命中 {len(hits)} 处,其中疑似「仍在依赖」{len(bad)} 处")
|
||
for b in bad[:5]:
|
||
lines.append(f" ⚠ …{b}…")
|
||
|
||
lines.append("")
|
||
lines.append("== 四、HTML 锚点自检(TOC href vs 正文 id)==")
|
||
for name in ("需求", "计划", "知识库"):
|
||
t = texts.get(name, "")
|
||
hrefs = set(re.findall(r'class="toc-item" href="#([^"]+)"', t))
|
||
ids = set(re.findall(r'<h[1-4] id="([^"]+)"', t))
|
||
missing = sorted(hrefs - ids)
|
||
lines.append(f" {name}: TOC {len(hrefs)} 条 / 正文 id {len(ids)} 个 / 失效 {len(missing)}")
|
||
for m in missing:
|
||
lines.append(f" ⚠ 失效锚点: {m}")
|
||
|
||
lines.append("")
|
||
lines.append("== 五、四文档交叉引用检查 ==")
|
||
EXPECT = {
|
||
"需求": ["客服Agent开发计划", "客服Agent执行Todolist", "客服Agent知识库设计方案"],
|
||
"计划": ["客服Agent需求文档", "客服Agent执行Todolist", "客服Agent知识库设计方案"],
|
||
"知识库": ["客服Agent需求文档"],
|
||
}
|
||
for name, refs in EXPECT.items():
|
||
t = texts.get(name, "")
|
||
for r in refs:
|
||
lines.append(f" {name} → {r}: {'✅' if r in t else '❌ 缺'}")
|
||
|
||
OUT.write_text("\n".join(lines), encoding="utf-8")
|