6.3 KiB
6.3 KiB
name, description, source, extracted_at
| name | description | source | extracted_at |
|---|---|---|---|
| 代码替换请求的现状审计 | 处理用户"代码替换/新增"指令时,先审计磁盘真实状态再用 ask_user_question 确认——避免覆盖已落盘的高版本代码 | auto-skill | 2026-06-03T05:36:58.746Z |
代码替换请求的现状审计
适用场景
用户给出"代码替换"或"按某版本代码新增"指令,但没有提供与磁盘当前状态对比信息时。典型触发:
- 用户贴了一段代码说"请帮我写/替换这个"
- 用户引用某个文档/旧版本/旧 chat 说"按这个来"
- 之前的
state_snapshot/memory/git log描述可能与磁盘现状不一致
核心原则
永远不要盲信"用户给的代码是最新版本"——磁盘上的代码可能已经是更完善的版本(用户或其他 agent 已迭代过)。覆盖 = 丢功能。
直接覆盖的代价不一定是显式 bug,也可能是"丢失用户已批准的设计决策"(如 duck-type 探测 / ctx 抽象 / 信号协议 / 二次确认窗 / 错误定位)。
5 步标准操作
1. 确认文件存在
glob 或 list_directory 看目标文件是否已存在:
- 不存在 → 新建
- 存在 → 进入第 2 步审计
2. grep 关键符号 + 读关键段
- 找"用户贴的代码"里的 3-5 个关键符号(函数名 / 类名 / 关键常量 / import)
- 在磁盘文件里 grep 同样的符号
read_file关键段(行号从 grep 结果直接拿)
3. 构造差异对照表
列出:
| 目标文件 | 用户贴的版本 | 磁盘现有版本 | 直接覆盖会丢失 |
关键列:"直接覆盖会丢失什么"——让用户判断成本。具体粒度到"功能模块 / 设计决策 / 防御层 / 入口协议",不要写"代码差异"这种空话。
4. ask_user_question 让用户拍板
3 个标准选项(措辞可调,但必须给出现状 + 三选一):
- A. 保留现状(推荐,磁盘已是更新版)—— 直接进 Smoke Test
- B. 强制覆盖到旧版 —— 写明丢什么 + 备份建议(git stash / 复制到
_old.py) - C. 混合:只取某段增量 —— 见第 5 步
不要在第 1 次 ask 时就列具体的"哪段增量"——先让用户在 A/B/C 之间选。如果选 C,再做第 5 步。
5. 若用户选 C,识别"真正增量"
对比 1.0 vs 2.0,识别 1.0 真正独有的部分(2.0 没有的):
- ❌ 排除 1.0 比 2.0 简单的(2.0 是超集 / 工厂分层 / 多了 CLI)
- ❌ 排除 1.0 整体被 2.0 工厂分层超越的(_make_objective vs _build_model + _get_search_space)
- ✅ 关注 1.0 独有的功能层(即使 2.0 不"明显"需要)
对每个候选增量,再问一次"采纳哪段",让用户具体选(multiSelect=false,一次只选 1 段最稳)。
落地原则
执行"采纳 1.0 某段增量到 2.0"时:
- 最小化外科手术式编辑:只动需要动的文件,只改需要改的段
- 保留 2.0 的设计决策(duck-type 探测 / ctx 抽象 / 信号协议 / 二次确认窗 / 错误定位)
- 顶部 import 增量用
replace_all=False单点插入,避免破坏其他 import 顺序 - 同名变量全链路替换(如
self.config→clean_config)要贯穿 ctx 构造 / v2 调用 / v1 fallback,避免双源差异 - 单步模式不一定要清洗(不走 panel 完整 config,与清洗器无关)
- 清洗器这种"防患于未然"的代码要给日志(
self.log_message.emit(f"[清洗器] 已删除 N 个未知 key"))让运行时可见
验证三件套
落地后必跑:
- AST 语法检查:
ast.parse(open(p, encoding='utf-8-sig').read())对 5 个核心文件- 必加
utf-8-sig:WQ_GUI 的 water_quality_gui.py line 1 是 BOM,plainutf-8必挂
- 必加
- 关键符号 grep:确认新代码的关键符号(import / 关键函数调用)都命中,hit 数符合预期
- 顶层导入测试:用 mock PyQt5 +
sys.path.insert(0, 'src/gui/core'),验证模块整体可加载- PyQt5 mock 模板见下方"参考代码"
- Windows 环境调 Python:用 conda env 的
python.exe全路径,不要靠 PATH
反例(不要做)
- ❌ "按用户贴的代码原封不动写入"——1.0 简化版的覆盖陷阱
- ❌ "保留 state_snapshot 描述"——state snapshot 可能不准确(写的是意图,磁盘才是事实)
- ❌ "用 git log 反推当前状态"——git log 不能反映工作区未提交改动
- ❌ "靠 memory 推断当前状态"——memory 可能是 22 天前的(已确认过期)
- ❌ "磁盘和用户给的代码看起来一样就不审计"——一行之差可能就是"防弹层"丢失
参考代码
PyQt5 mock 模板(worker_thread.py 顶层导入测试)
import os, sys
os.environ['GDAL_FILENAME_IS_UTF8'] = 'YES'
os.environ['SHAPE_ENCODING'] = 'UTF-8'
sys.path.insert(0, 'src/gui/core')
import types
pyqt5 = types.ModuleType("PyQt5")
qtc = types.ModuleType("PyQt5.QtCore")
class _QThread:
def __init__(self, *a, **kw): pass
class _Signal:
def __init__(self, *a, **kw): pass
qtc.QThread = _QThread
qtc.pyqtSignal = _Signal
qtc.Qt = type("Qt", (), {"QueuedConnection": 1, "UserRole": 0})()
sys.modules["PyQt5"] = pyqt5
sys.modules["PyQt5.QtCore"] = qtc
import worker_thread
# 副作用: check_pipeline_dependencies() 会打印依赖检查日志(可忽略)
Windows 上跑 conda env python
cmd /c "D:\xxx\anconda\envs\XXX\python.exe D:\path\to\script.py"
PowerShell 单行 python -c "..." 在中文路径 / 双引号 / 单引号嵌套时易翻车,写临时 .py 文件再用 cmd /c 调最稳。
案例来源(2026-06-03 WQ_GUI 路线 B MVP)
- 用户贴 1.0 简化版:300 行 automl_trainer / 简化 worker_thread.run() / 简化 on_run_all_clicked
- 磁盘上 2.0 落盘版:545 行 automl_trainer(_build_model + _get_search_space 工厂 / argparse CLI)/ duck-type 探测 v2 + PipelineContext 抽象 / 完整二次确认窗 / 失败步骤 _focus_step 定位 / [DEPRECATED] stop 保留
- 1.0 唯一真增量 = "防弹级参数清洗器"(method_map 14 项 + inspect.signature 过滤未知 key + has_kwargs 豁免 + 未知 key 数量日志)
- 落地:worker_thread.py:run() 内 set_callback 之后插入 53 行清洗器,self.config 6 处替换为 clean_config
- 验证:5 文件 AST 全通过 + 关键符号 7 项命中 + PyQt5 mock 下 import 成功
- 净增行数:407 → 457(+50 行)