refactor(pipeline): 路径直接传输 — 统一 ctx 字段名/panel key/step 形参名

This commit is contained in:
DXC
2026-06-03 17:29:41 +08:00
parent 517bb28611
commit 343e316799
99 changed files with 9127 additions and 91 deletions

View File

@ -0,0 +1,141 @@
---
name: 代码替换请求的现状审计
description: 处理用户"代码替换/新增"指令时,先审计磁盘真实状态再用 ask_user_question 确认——避免覆盖已落盘的高版本代码
source: auto-skill
extracted_at: '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")`)让运行时可见
## 验证三件套
落地后必跑:
1. **AST 语法检查**`ast.parse(open(p, encoding='utf-8-sig').read())` 对 5 个核心文件
- 必加 `utf-8-sig`WQ_GUI 的 water_quality_gui.py line 1 是 BOMplain `utf-8` 必挂
2. **关键符号 grep**确认新代码的关键符号import / 关键函数调用都命中hit 数符合预期
3. **顶层导入测试**:用 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 顶层导入测试)
```python
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
```bat
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 行)