基于无头浏览器与沙箱拦截的代码重构闭环 (Closed-Loop Code Refactoring via Headless Browser Verification and Sandbox Interception)
在利用大语言模型(Large Language Models, LLMs)和编码代理(Coding Agents)进行批量前端与交互式页面重构时,最常见的问题是模型的幻觉通过(Hallucinated Completion)——即模型修改完几行 CSS 后即声称“已完美适配手机端并解决所有冲突”,而实际在真实浏览器渲染时仍然存在横向滚动条、元素打架或 JavaScript 控制台报错。
为了彻底解决这一问题,必须构建一套由 轻量工具沙箱(Sandboxed Toolset)+ 动态提示词(Minimalist Prompting)+ 无头浏览器真实验收(Headless Browser Ground-Truth Verification)+ 有限轮次熔断博弈(Circuit Breaker Loop) 构成的强闭环重构引擎。
一、 核心架构设计
flowchart TD
TaskQueue["待重构目标队列 (Target Queue)"] --> Dispatcher["并发调度器 (Worker Dispatcher)"]
Dispatcher --> Worker["独立 Worker (Agent Context)"]
subgraph AgenticLoop["Agent 交互迭代博弈 (Max 60 Rounds)"]
Worker --> LLM["模型生成 (LLM Tool Calls)"]
LLM --> ToolExec{"工具类型分发"}
ToolExec -->|read_file / edit_file / write_file| FileOps["安全文件操作"]
ToolExec -->|bash| Sandbox["沙箱拦截 (禁止外网访问/危险命令)"]
FileOps --> Worker
Sandbox --> Worker
ToolExec -->|mark_complete| Verifier["Runtime 真实真机验收关卡"]
end
subgraph Verification["Ground-Truth 验收与反馈"]
Verifier --> GitCheck{"1. Git Diff 真实性校验"}
GitCheck -->|无代码改动| Reject1["驳回: EMPTY_DIFF"]
GitCheck -->|有效修改| BrowserCheck{"2. Playwright 真实渲染 (Chromium)"}
BrowserCheck --> ConsoleCheck{"控制台 JS 报错?"}
ConsoleCheck -->|存在 Uncaught Error| Reject2["驳回: JS_ERROR + 堆栈"]
ConsoleCheck --> OverflowCheck{"375px 窄屏横向溢出检测"}
OverflowCheck -->|rect.right > innerWidth| Reject3["驳回: 提取具体超界 DOM 节点"]
OverflowCheck -->|0 报错 & 0 溢出| Pass["✅ 验收通过 (PASS)"]
end
Reject1 --> Worker
Reject2 --> Worker
Reject3 --> Worker
Pass --> Archive["双视口截图归档与标记完成"]二、 核心组件与实现细节
1. 最小化提示词与四工具约束 (Minimalist Toolset)
给重构模型的上下文不应堆砌冗余的角色设定,而应聚焦于严格的操作约束与反馈:
read_file: 指定行号读取目标源码。edit_file/write_file: 精准替换与覆写,禁止篡改核心业务逻辑与算法。bash(沙箱限制): 允许运行本地无害命令(如grep,python快速 AST 检查),但在代码层用正则与前置规则拦截外网请求(curl,wget,git,npm,pip)及系统高危破坏指令(rm -rf,format等)。mark_complete: 由模型显式发起验收申请,而非模型单方面决定结束。
2. Playwright 真机双视口验收 (Headless Browser Verification)
模型提交 mark_complete 后,Runtime 并不信任模型的自述,而是自动唤起 Playwright 无头 Chromium 实例:
双视口测试:
- 桌面端:
1440x900 - 移动端:
375x812(真实模拟窄屏手机视口)
- 桌面端:
DOM 级精准溢出诊断:
通过在页面上下文注入 JavaScript,遍历所有 DOM 元素的getBoundingClientRect():const docWidth = window.innerWidth; const elements = Array.from(document.querySelectorAll('*')); const overflowing = elements.filter(el => { const rect = el.getBoundingClientRect(); return rect.right > docWidth + 1 && rect.width > 0; }).slice(0, 8).map(el => ({ tag: el.tagName.toLowerCase(), id: el.id, className: el.className, overflow_px: (el.getBoundingClientRect().right - docWidth).toFixed(1), width: el.getBoundingClientRect().width.toFixed(1) }));- 结构化错误反馈:
如果发现超界,Runtime 绝不仅返回“测试失败”,而是将具体的超界节点(如<table class="preview-table"> width=480px, overflow=105px)作为工具执行结果原样打回给模型。模型据此能在 1~2 轮内精准定位并加上容器横向滚动或换行断词规则。
3. 并发调度与熔断保护 (Concurrency & Circuit Breaking)
- 多 Worker 异步并发:利用 Python
asyncio同时拉起多个 Worker,各自独立维护上下文会话与文件读写。 - 轮次熔断(60 轮保护):单款页面设定 60 轮最大交互上限,防止模型在极深死循环中消耗无谓 Token。未在限额内通过验收的页面自动标记为
MANUAL_REVIEW_REQUIRED,并在最终汇总表中高亮。
三、 移动端与全缩放布局高频踩坑与重构经验
| 故障模式 (Pitfall) | 典型现象 | 根因分析 | 标准治理方案 |
|---|---|---|---|
| 表格撑破视口 | 375px 出现全页横向滚动条 | table 默认根据内容展开,未限制宽度 | 外层包裹 overflow-x: auto; -webkit-overflow-scrolling: touch;,或设置 table-layout: fixed; width: 100%; 并给单元格加 word-break: break-all; |
| 标签/切换按钮超界 | 格式栏撑到视口外 | flex-wrap: nowrap 或子项设置了固定 min-width | 改为 flex-wrap: wrap,设置 flex: 1 1 auto; min-width: 0;,内边距自适应缩小 |
| 复杂周期表/画布越界 | 18 列化学周期表无法看清或挤压变形 | 强行缩放会导致字号过小无法辨认 | 将 18 族主体严格封锁在横向滚动卡片容器 .pt-table-scroll 内,同时确保全页 body/main 保持 overflow-x: hidden; |
| 绝对定位伪元素超界 | 页面视觉无溢出,但 DOM 评测报溢出 | 装饰性气泡或导引伪元素使用了 left: 100% | 移动端媒体查询下针对超出的绝对定位装饰节点设置 display: none; 或 max-width: 100%; right: 0; |
四、 总结与最佳实践 (Takeaway)
- 不给模型留“假通过”的自评空间:评测判定逻辑必须掌握在外部 Runtime 手中,代码改动、控制台健康度、真机像素边界是唯一客观准绳。
- 精细到 DOM 节点的反向输入是收敛的关键:只有将具体的超宽像素和标签属性反馈给 Agent,多轮重构循环才能在极低轮次(平均 10~15 轮)内快速收敛。
- 安全沙箱做在代码门禁层:拦截一切外网访问与破坏性 Shell 调用,确保重构过程纯本地、防投毒、防密钥泄露。
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/AI apps