使用统一 diff(`@@` 块、`-`/`+`/空格行)对单个文件做精确编辑:追加、插入、替换、删除都可以在一次调用里完成。 硬性规则: 1) 补丁必须被 `*** Begin Patch` 与 `*** End Patch` 包裹。 2) 每个修改块必须以 `@@ [id:数字]` 开头。 3) 块内每一行只能是三类之一: - 上下文行:以空格开头(` ␠`),表示“文件里必须原样存在”的锚点; - 删除行:以 `-` 开头,表示要从文件中移除的原文; - 新增行:以 `+` 开头,表示要写入的新内容。 4) 任何“想新增/想删除/想替换”的内容都必须逐行写 `+` 或 `-`;如果你把多行新内容直接贴上去却不加 `+`,它会被当成上下文锚点去匹配原文件,极易导致“未找到匹配的原文”。 5) 重要语义:一个块里如果完全没有上下文行(空格开头)也没有删除行(`-`),那么它会被视为“仅追加(append-only)”,也就是把所有 `+` 行追加到文件末尾——这对“给空文件写正文”很合适,但对“插入到中间”是错误的。 正面案例(至少 5 个,且都包含多行原文/多处修改) 1) 给空文件写完整正文(追加到末尾;空文件=正确) 目标:新建 README.md 后一次性写入标题、安装、用法、FAQ(多段落、多行)。 要点:没有上下文/删除行 → 追加模式;空文件时最常用。 *** Begin Patch @@ [id:1] +# 项目名称 + +一个简短说明:这个项目用于…… + +## 安装 + +```bash +pip install -r requirements.txt +``` + +## 快速开始 + +```bash +python main.py +``` + +## 常见问题 + +- Q: 为什么会报 xxx? + A: 先检查 yyy,再确认 zzz。 + *** End Patch 2) “不删除,直接插入内容”到函数内部(必须用上下文锚定插入位置) 目标:在 def build_prompt(...): 里插入日志与参数归一化,但不改动其它行。 要点:插入发生在“两个上下文行之间”,上下文必须精确(包含缩进)。 *** Begin Patch @@ [id:1] def build_prompt(user_text: str, system_text: str, tags: list): prompt_parts = [] + # 参数归一化:去掉首尾空白,避免模型误判 + user_text = (user_text or "").strip() + system_text = (system_text or "").strip() + + logger.debug("build_prompt: tags=%s, user_len=%d", tags, len(user_text)) prompt_parts.append(system_text) prompt_parts.append(user_text) if tags: prompt_parts.append("TAGS: " + ",".join(tags)) *** End Patch 3) 复杂替换:整段函数重构(多行 old/new + 保留稳定上下文) 目标:把旧的 apply_patch()(弱校验)替换成新实现(多分支、异常信息更清晰)。 要点:替换不是“改一行”,而是“删一段、加一段”,并用函数签名/相邻代码作锚点。 *** Begin Patch @@ [id:1] class FilePatcher: def __init__(self, root: Path): self.root = root def apply_patch(self, path: str, patch_text: str) -> dict: - # old: naive replace - content = (self.root / path).read_text(encoding="utf-8") - content = content.replace("foo", "bar") - (self.root / path).write_text(content, encoding="utf-8") - return {"success": True} + full_path = (self.root / path).resolve() + if self.root not in full_path.parents and full_path != self.root: + return {"success": False, "error": "非法路径:越界访问"} + + if "*** Begin Patch" not in patch_text or "*** End Patch" not in patch_text: + return {"success": False, "error": "补丁格式错误:缺少 Begin/End 标记"} + + try: + original = full_path.read_text(encoding="utf-8") + except Exception as e: + return {"success": False, "error": f"读取失败: {e}"} + + # 这里省略:解析 blocks、逐块应用、失败回滚等 + updated = original + try: + full_path.write_text(updated, encoding="utf-8") + except Exception as e: + return {"success": False, "error": f"写入失败: {e}"} + + return {"success": True, "message": "已应用补丁"} *** End Patch 4) 复杂多块:同一文件里同时“加 import + 替换逻辑 + 插入新 helper + 删除旧函数” 目标:一次调用完成 4 种操作,且每块都有足够上下文,避免误匹配。 要点:不同区域用不同 @@ [id:n] 分块,互不干扰。 *** Begin Patch @@ [id:1] -import json +import json +import re from pathlib import Path @@ [id:2] def normalize_user_input(text: str) -> str: - return text + text = (text or "").strip() + # 压缩多余空白,减少提示词抖动 + text = re.sub(r"\\s+", " ", text) + return text @@ [id:3] def load_config(path: str) -> dict: cfg_path = Path(path) if not cfg_path.exists(): return {} data = cfg_path.read_text(encoding="utf-8") return json.loads(data) + +def safe_get(cfg: dict, key: str, default=None): + if not isinstance(cfg, dict): + return default + return cfg.get(key, default) @@ [id:4] -def legacy_parse_flags(argv): - # deprecated, kept for compatibility - flags = {} - for item in argv: - if item.startswith("--"): - k, _, v = item[2:].partition("=") - flags[k] = v or True - return flags - def main(): cfg = load_config("config.json") # ... *** End Patch 5) 删除示例:删除一整段“废弃配置块”,并顺手修正周围空行(多行删除 + 上下文) 目标:删掉 DEPRECATED_* 配置和旧注释,确保删除位置精确。 要点:删除行必须逐行 `-`;保留上下文行确保定位。 *** Begin Patch @@ [id:1] # ============================== # Runtime Config # ============================== -DEPRECATED_TIMEOUT = 5 -DEPRECATED_RETRIES = 1 -# 注意:这些字段将在下个版本移除 -# 请迁移到 NEW_TIMEOUT / NEW_RETRIES NEW_TIMEOUT = 30 NEW_RETRIES = 3 *** End Patch 如何写“带上下文”的正确姿势(要点) - 上下文要选“稳定锚点”:函数签名、类名、关键注释、紧邻的两三行缩进代码。 - 不要用“容易变的行”当唯一锚点:时间戳、日志序号、随机 id、生成内容片段。 - 上下文必须字节级一致(空格/Tab/大小写/标点都算),否则会匹配失败。 反面案例(至少 3 个,且都是“真实会踩坑”的类型) 反例 A(来自一次常见错误):空文件时只有第一行加了 `+`,后面直接贴正文 这会让后面的正文变成“上下文锚点”,工具会去空文件里找这些原文,必然失败(常见报错:未找到匹配的原文)。 *** Begin Patch @@ [id:1] + 仰望U9X·电驭苍穹 银箭破空电光闪 三千马力云中藏 *** End Patch 正确做法:正文每一行都要写 `+`(包括空行也写 `+`)。 (对应的正确 patch 示例:向空文件追加多行) *** Begin Patch @@ [id:1] + +仰望U9X·电驭苍穹 +银箭破空电光闪 +三千马力云中藏 *** End Patch 反例 B:想“插入到中间”,却只写 `+`(没有任何上下文/删除行) 这种块会被当成“追加到文件末尾”,结果内容跑到文件最后,不会插入到你以为的位置。 *** Begin Patch @@ [id:1] +# 我以为会插到某个函数上面 +print("hello") *** End Patch 正确做法:用上下文锚定插入点(见正面案例 2)。 (对应的正确 patch 示例:用上下文把内容插入到函数内部,而不是追加到文件末尾) *** Begin Patch @@ [id:1] def main(): config = load_config("config.json") + # 这里插入:启动提示(不会移动到文件末尾) + print("hello") run(config) *** End Patch 反例 C:补丁在第一个 `@@` 之前出现内容 / 或漏掉 Begin/End 标记 解析会直接报格式错误(例如:“在检测到第一个 @@ 块之前出现内容”、“缺少 Begin/End 标记”)。 (错误形态示意) 这里先写了一段说明文字(没有 @@) @@ [id:1] +... 正确做法:确保第一段非空内容必须从 `@@ [id:n]` 开始,并且整体有 Begin/End。 (对应的正确 patch 示例:完整结构、第一段内容从 @@ 块开始) *** Begin Patch @@ [id:1] # ============================== # Runtime Config # ============================== +# 说明:此处新增一行注释作为示例 NEW_TIMEOUT = 30 *** End Patch