Files
cad-agent/dwg_review/checklist.py
T
gjm e8140f2bc5 feat(review): 审查阶段对齐绘图区分辨率 + 放大取证闭环
发现的缺口:maximized_drawing_area 原先只包住取证循环,审查阶段没被包住 →
LLM 在受控子循环里 zoom + 截图拿到的是未最大化的 2110x462(正是上一轮刚解决的
低分辨率问题,在最需要放大看细节的审查阶段又出现了);且审查中的 zoom 不复位视口,
跑完把用户的 CAD 留在最后一次放大处。

改动:
1. __main__.run_review:把 maximized_drawing_area 从「只包取证」扩到「包取证 + 审查」
   (两者都要截图);report/annotate 留在 with 之外。
2. phases/review.py:新增 _VIEW_TOOLS;review_frame 拆成外壳 + _review_frame,
   外壳 try/finally 里仅当确实动过视口才复位到该图框整幅视图
   (zoom_to_window(win, margin=0.02))。纯查结构化数据的路径不受影响。
3. checklist.build_system_prompt:把「可调用只读工具」改写成明确的放大取证协议——
   截图看不清时用 zoom_to_window(坐标由图框/实体 bbox 算)或 zoom_to_entity,
   再 get_viewport_screenshot 复核;并约束不要为看图而反复放大。

次生问题(同轮修复):_MAX_OBS_IMAGES 原为硬编码 3,而模型一轮内会发 8 次截图 →
拍了 8 张只回灌 3 张。改为配置项 REVIEW_OBS_IMAGES(默认 6),并把预算耗尽的占位
文案改成明确告知模型「不要再请求截图,请给出结论」。

实测(真实图纸 1 图框,新增 verify_review_maximized.py):取证与审查两阶段窗口均为
[-4,-2,3444,1442](最大化态),结束精确还原 [68,0,2316,960],三项全 PASS;
子循环里 LLM 连发 8 组 zoom_to_window + get_viewport_screenshot,提示词生效。
pytest 304 passed。
2026-10-16 18:55:31 +08:00

99 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""审查清单加载 + 系统提示构建(清单驱动)。"""
import json
from . import config
DEFAULT_CHECKLIST = {
"name": "内置通用清单",
"categories": [
{"title": "图层规范性", "severity": "medium",
"checks": ["图元是否在正确图层", "是否存在空图层/命名异常图层"]},
{"title": "文字", "severity": "medium",
"checks": ["空文字", "错别字", "字高异常", "文字重叠"]},
{"title": "标注", "severity": "high",
"checks": ["关键尺寸缺失", "标注值异常"]},
{"title": "几何", "severity": "medium",
"checks": ["零长度/退化实体", "重复重叠实体", "未闭合轮廓"]},
{"title": "图面整体", "severity": "low",
"checks": ["图面完整性", "内容是否超出图框"]},
],
}
def load_checklist(path=None):
"""加载审查清单 JSON;失败时回退内置默认清单。"""
path = path or config.CHECKLIST_PATH
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, dict) and data.get("categories"):
return data
print(f"[Warn] 审查清单 {path} 结构异常,使用内置默认清单。")
except Exception as exc: # noqa: BLE001
print(f"[Warn] 无法加载审查清单 {path}: {exc},使用内置默认清单。")
return DEFAULT_CHECKLIST
def build_system_prompt(checklist, *, allow_tools=True):
"""根据审查清单动态生成 LLM 系统提示词。"""
lines = [
"你是一名资深建筑与工业厂房图纸审查专家。",
"你将逐张收到某个图框(一张图纸)的:结构化图元数据、文字内容,以及该图框的视口截图。",
"",
f"请依据审查清单《{checklist.get('name', '审查清单')}》逐项审查,"
"指出**具体问题**(尽量引用实体 handle 或文字内容),并标注严重程度:",
"",
]
for cat in checklist.get("categories", []):
lines.append(f"【{cat.get('title', '')}】(严重度:{cat.get('severity', 'medium')})")
for chk in cat.get("checks", []):
lines.append(f" - {chk}")
lines.append("")
if allow_tools:
lines += [
"你可以调用**只读**取证工具进一步确认细节;**不要凭猜测下结论**:",
" - **截图里的文字/标注/细节看不清时**:用 `zoom_to_window` 放大该区域"
"(窗口坐标用图框 bbox 或实体 bbox 计算,可适当留边距),"
"再 `get_viewport_screenshot` 复核;已知 handle 时也可直接 `zoom_to_entity` 放大该实体。",
" - 结构化数据不足以判断时:用 `get_entity_detail` / `find_duplicate_entities` /"
" `find_zero_length` / `get_layers` 等查证后再下结论。",
" - 放大截图是**复核手段**:确认清楚后即写入结论,不要为看图而反复放大。",
"",
]
lines += [
"要求:",
"- 只报告你**有依据**的问题,不要臆测;数据与截图冲突时以数据为准。",
"- 按清单条目组织结论,未命中的条目可省略。",
"- 不要复述输入数据。",
"",
"输出格式(**必须严格遵守**):",
"在你完成全部取证、准备给出该图框的最终结论时,**只输出一个 JSON 对象**,"
"不要输出任何解释文字,也不要用 markdown 代码块包裹。结构如下:",
"{",
' "summary": "<该图框的一句话整体结论;若未发现问题,写“未发现明显问题”>",',
' "issues": [',
" {",
' "category": "<图层/文字/标注/几何/图面整体 之一>",',
' "severity": "<high|medium|low>",',
' "message": "<问题描述,简洁具体>",',
' "handle": "<该问题对应的实体句柄;无法定位到具体实体时省略此字段>"',
" }",
" ]",
"}",
"其中 `handle` 必须是你**在取证数据或工具返回里真实见过**的句柄,不要编造;"
"无法确定具体实体时省略 `handle`,该问题仍会被记录。",
]
if allow_tools:
lines.append("(需要进一步确认时先调用工具;拿到结果后,最终再输出上述 JSON。)")
return "\n".join(lines)
def checklist_summary(checklist):
"""用于规划阶段的一句话摘要。"""
cats = checklist.get("categories", [])
return f"{checklist.get('name', '清单')}({len(cats)} 类:" + \
"、".join(c.get("title", "") for c in cats) + ")"