84 lines
2.9 KiB
Python
84 lines
2.9 KiB
Python
"""阶段 4:汇总报告 + 结构化问题抽取。"""
|
||||
|
|
|
|||
|
|
import json
|
|||
|
|
from datetime import datetime
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
from .. import config
|
|||
|
|
|
|||
|
|
|
|||
|
|
def summarize(ctx, findings, log=print):
|
|||
|
|
"""用 LLM 把各图框结论汇总为总报告。"""
|
|||
|
|
if not findings:
|
|||
|
|
return "未识别到可审查的图框,或所有图框均无有效包围盒。"
|
|||
|
|
|
|||
|
|
report_input = "\n\n".join(
|
|||
|
|
f"【图框 {f['frame']}(图幅 {f.get('sheet')})】\n{f['finding']}" for f in findings
|
|||
|
|
)
|
|||
|
|
system = (
|
|||
|
|
"你是图纸审查负责人。请把下面各图框的审查意见汇总为一份结构化的审查报告:"
|
|||
|
|
"先给整体结论,再按图框列出问题,最后给出整改建议。语言简洁、条目化。"
|
|||
|
|
)
|
|||
|
|
try:
|
|||
|
|
return ctx.llm.chat_text(system, report_input)
|
|||
|
|
except Exception as exc: # noqa: BLE001
|
|||
|
|
log(f"[汇总失败] {exc}")
|
|||
|
|
return report_input
|
|||
|
|
|
|||
|
|
|
|||
|
|
_EXTRACT_SYSTEM = (
|
|||
|
|
"你是结构化信息抽取器。请把下面的图纸审查意见抽取为 JSON 数组,"
|
|||
|
|
"每个元素形如:"
|
|||
|
|
'{"frame": <图框序号>, "handle": "<实体句柄,没有则省略>", '
|
|||
|
|
'"category": "<图层/文字/标注/几何/图面整体>", '
|
|||
|
|
'"severity": "<high|medium|low>", "message": "<问题描述>"}。'
|
|||
|
|
"只输出 JSON 数组,不要输出任何解释、不要用 markdown 代码块包裹。"
|
|||
|
|
"若某条意见无法定位到具体实体,省略 handle 字段。"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def extract_issues(ctx, findings, log=print):
|
|||
|
|
"""把自由文本的审查意见抽取为结构化问题列表(用于批注回写)。"""
|
|||
|
|
if not findings:
|
|||
|
|
return []
|
|||
|
|
source = "\n\n".join(
|
|||
|
|
f"图框 {f['frame']}:\n{f['finding']}" for f in findings
|
|||
|
|
)
|
|||
|
|
try:
|
|||
|
|
text = ctx.llm.chat_text(_EXTRACT_SYSTEM, source)
|
|||
|
|
text = text.strip()
|
|||
|
|
if text.startswith("```"):
|
|||
|
|
text = text.strip("`")
|
|||
|
|
text = text.split("\n", 1)[1] if "\n" in text else text
|
|||
|
|
data = json.loads(text)
|
|||
|
|
if isinstance(data, list):
|
|||
|
|
return data
|
|||
|
|
except Exception as exc: # noqa: BLE001
|
|||
|
|
log(f"[结构化抽取失败] {exc}")
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
|
|||
|
|
def save_report(markdown, findings, plan, issues=None, log=print):
|
|||
|
|
"""落盘 Markdown + JSON 报告,返回两个路径。"""
|
|||
|
|
out_dir = Path(config.REPORT_DIR)
|
|||
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|||
|
|
stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|||
|
|
md_path = out_dir / f"review_{stamp}.md"
|
|||
|
|
json_path = out_dir / f"review_{stamp}.json"
|
|||
|
|
|
|||
|
|
md_path.write_text(markdown, encoding="utf-8")
|
|||
|
|
json_path.write_text(
|
|||
|
|
json.dumps(
|
|||
|
|
{
|
|||
|
|
"generated_at": stamp,
|
|||
|
|
"findings": findings,
|
|||
|
|
"issues": issues or [],
|
|||
|
|
"frames": plan.get("frames"),
|
|||
|
|
},
|
|||
|
|
ensure_ascii=False,
|
|||
|
|
indent=2,
|
|||
|
|
),
|
|||
|
|
encoding="utf-8",
|
|||
|
|
)
|
|||
|
|
log(f"[报告] 已写入 {md_path} 与 {json_path}")
|
|||
|
|
return md_path, json_path
|