2026-10-16 18:55:31 +08:00
|
|
|
|
"""验收:审查阶段是否也在「绘图区最大化」状态下进行,且结束后精确还原。
|
|
|
|
|
|
|
|
|
|
|
|
做法:在取证 `collect` 与审查 `review_frame` 入口各采一次窗口矩形,
|
2026-10-16 19:15:32 +08:00
|
|
|
|
并在 `McpClient.call_tool` 上挂计数器统计子循环的 zoom/截图次数,
|
|
|
|
|
|
同时记录 LLM 实际调用的**取证工具**(含 agent 侧虚拟工具 `inspect_region`)。
|
2026-10-16 18:55:31 +08:00
|
|
|
|
用法:先启动已加载插件的 CAD 并打开图纸,然后 `python verify_review_maximized.py`。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import ctypes
|
2026-10-16 19:15:32 +08:00
|
|
|
|
import json
|
2026-10-16 18:55:31 +08:00
|
|
|
|
import sys
|
|
|
|
|
|
from ctypes import wintypes
|
|
|
|
|
|
|
|
|
|
|
|
from dwg_review import __main__ as cli
|
|
|
|
|
|
from dwg_review.mcp_client import McpClient
|
|
|
|
|
|
from dwg_review.phases import evidence as ev
|
|
|
|
|
|
from dwg_review.phases import review as rv
|
|
|
|
|
|
|
|
|
|
|
|
_u = ctypes.windll.user32
|
|
|
|
|
|
_u.GetWindowTextW.argtypes = [wintypes.HWND, wintypes.LPWSTR, ctypes.c_int]
|
|
|
|
|
|
_u.IsWindowVisible.argtypes = [wintypes.HWND]
|
|
|
|
|
|
_u.GetWindowRect.argtypes = [wintypes.HWND, ctypes.POINTER(wintypes.RECT)]
|
|
|
|
|
|
_WNDENUMPROC = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _find_main_window():
|
|
|
|
|
|
hit = [None]
|
|
|
|
|
|
|
|
|
|
|
|
def cb(hwnd, _lparam):
|
|
|
|
|
|
if not _u.IsWindowVisible(hwnd):
|
|
|
|
|
|
return True
|
|
|
|
|
|
n = _u.GetWindowTextLengthW(hwnd)
|
|
|
|
|
|
if n == 0:
|
|
|
|
|
|
return True
|
|
|
|
|
|
buf = ctypes.create_unicode_buffer(n + 1)
|
|
|
|
|
|
_u.GetWindowTextW(hwnd, buf, n + 1)
|
|
|
|
|
|
if "AutoCAD" in buf.value and "[" in buf.value:
|
|
|
|
|
|
hit[0] = hwnd
|
|
|
|
|
|
return False
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
_u.EnumWindows(_WNDENUMPROC(cb), 0)
|
|
|
|
|
|
return hit[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HWND = _find_main_window()
|
|
|
|
|
|
if not HWND:
|
|
|
|
|
|
print("[X] 未找到 AutoCAD 主窗口")
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def rect():
|
|
|
|
|
|
r = wintypes.RECT()
|
|
|
|
|
|
_u.GetWindowRect(HWND, ctypes.byref(r))
|
|
|
|
|
|
return [r.left, r.top, r.right, r.bottom]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def area(r):
|
|
|
|
|
|
return (r[2] - r[0]) * (r[3] - r[1])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- 采样点 ---------------------------------------------------------------
|
|
|
|
|
|
phase_rects = {"evidence": [], "review": []}
|
|
|
|
|
|
zoom_calls = []
|
2026-10-16 19:15:32 +08:00
|
|
|
|
llm_tools = [] # LLM 在审查子循环里实际调用的取证工具(名称 + 参数)
|
2026-10-16 18:55:31 +08:00
|
|
|
|
|
|
|
|
|
|
_orig_collect = ev.collect
|
|
|
|
|
|
_orig_review = rv.review_frame
|
|
|
|
|
|
_orig_call = McpClient.call_tool
|
2026-10-16 19:15:32 +08:00
|
|
|
|
_orig_run_tool = rv._run_readonly_tool
|
2026-10-16 18:55:31 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def collect(ctx, index, frame):
|
|
|
|
|
|
phase_rects["evidence"].append(rect())
|
|
|
|
|
|
return _orig_collect(ctx, index, frame)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def review(ctx, evidence, system_prompt, log=print):
|
|
|
|
|
|
phase_rects["review"].append(rect())
|
|
|
|
|
|
return _orig_review(ctx, evidence, system_prompt, log=log)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def call_tool(self, name, arguments=None, **kw):
|
|
|
|
|
|
if name in ("zoom_to_window", "zoom_to_entity", "get_viewport_screenshot"):
|
|
|
|
|
|
zoom_calls.append(name)
|
|
|
|
|
|
return _orig_call(self, name, arguments, **kw)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-16 19:15:32 +08:00
|
|
|
|
def run_tool(ctx, name, args, budget, **kw):
|
|
|
|
|
|
llm_tools.append((name, args))
|
|
|
|
|
|
return _orig_run_tool(ctx, name, args, budget, **kw)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-10-16 18:55:31 +08:00
|
|
|
|
ev.collect = collect
|
|
|
|
|
|
rv.review_frame = review
|
|
|
|
|
|
McpClient.call_tool = call_tool
|
2026-10-16 19:15:32 +08:00
|
|
|
|
rv._run_readonly_tool = run_tool
|
2026-10-16 18:55:31 +08:00
|
|
|
|
|
|
|
|
|
|
before = rect()
|
|
|
|
|
|
print(f"起始窗口矩形 = {before} 面积={area(before)}")
|
|
|
|
|
|
|
|
|
|
|
|
rc = cli.main(["--frames", "1"])
|
|
|
|
|
|
|
|
|
|
|
|
after = rect()
|
|
|
|
|
|
print()
|
|
|
|
|
|
print("=== 结果 ===")
|
|
|
|
|
|
print(f"取证阶段采样 = {phase_rects['evidence']}")
|
|
|
|
|
|
print(f"审查阶段采样 = {phase_rects['review']}")
|
|
|
|
|
|
print(f"结束窗口矩形 = {after} 面积={area(after)}")
|
2026-10-16 19:15:32 +08:00
|
|
|
|
print(f"子循环 zoom/截图调用 = {len(zoom_calls)} 次")
|
|
|
|
|
|
print(f"LLM 取证工具调用 = {len(llm_tools)} 次")
|
|
|
|
|
|
for name, args in llm_tools:
|
|
|
|
|
|
brief = json.dumps(args, ensure_ascii=False)
|
|
|
|
|
|
print(f" - {name} {brief[:160]}")
|
2026-10-16 18:55:31 +08:00
|
|
|
|
|
2026-10-16 19:15:32 +08:00
|
|
|
|
inspect_n = sum(1 for name, _ in llm_tools if name == "inspect_region")
|
2026-10-16 18:55:31 +08:00
|
|
|
|
ok_ev = all(area(r) > area(before) for r in phase_rects["evidence"]) if phase_rects["evidence"] else False
|
|
|
|
|
|
ok_rv = all(area(r) > area(before) for r in phase_rects["review"]) if phase_rects["review"] else False
|
|
|
|
|
|
print()
|
|
|
|
|
|
print(f"[{'PASS' if ok_ev else 'FAIL'}] 取证阶段处于最大化(面积大于基线)")
|
|
|
|
|
|
print(f"[{'PASS' if ok_rv else 'FAIL'}] 审查阶段处于最大化(面积大于基线)")
|
|
|
|
|
|
print(f"[{'PASS' if after == before else 'FAIL'}] 结束精确还原窗口矩形")
|
2026-10-16 19:15:32 +08:00
|
|
|
|
print(f"[{'PASS' if inspect_n else 'INFO'}] LLM 使用了 inspect_region({inspect_n} 次)")
|
2026-10-16 18:55:31 +08:00
|
|
|
|
sys.exit(0 if (ok_ev and ok_rv and after == before and rc == 0) else 1)
|