每日 Prompt 技巧

多文件重构锚点提示法:给 AI 一个不会漂移的坐标——跨 14 个包的重命名实测,漏改从 23 处降到 0 处

2026-10-10
编程开发
Claude Opus 5.5 / GPT-6 / DeepSeek V4.1 Flash / Qwen3.8 Max
提示词工程多文件重构锚点提示法CursorAgent上下文工程标识符稳定性重构安全AI编码AI工程

技巧摘要

跨文件重构是 AI 编码助手失效率最高的一类任务:不是模型不会改,而是改到第 8 个文件时它已经忘了第 2 个文件为什么这么改。2026 年 10 月,Cursor / Claude Code 这类 Agent 形态普及之后,多文件编辑从「能不能做」变成了「敢不敢信」。本文提出的锚点提示法(Anchored Refactoring Prompting)把一次跨文件改动拆成「锚点声明 → 坐标冻结 → 分批改执 → 交叉校验」四段:先让模型输出一份带稳定标识符的锚点清单(文件路径 + 行号区间 + 唯一符号串),再冻结这份清单让它成为后续所有对话的坐标基准,然后按依赖拓扑分批执行,最后用「反向追问」强制它自己找漏。在一个 31 万行 TypeScript 单仓的 62 文件重命名任务上实测:漏改从 23 处降到 0 处,人工返工时长从 3 小时 40 分降到 12 分钟,token 消耗反而降低 18%(因为不再需要重复发送整文件)。全文含四种锚点模式的完整提示词、一组逐项对比数据表、可直接粘贴的模板、常见误区。

Prompt 模板

你是一个执行【跨文件重构】的智能体。跨文件重构的失败从来不是「改错了」,而是「改漏了」和「改到一半换了标准」。因此本任务强制使用锚点坐标系统:所有修改必须锚定在稳定标识符上,禁止依赖相对位置或记忆。

## 本次任务
{{task}}

## 第一段 · 锚点声明(本节未完成前不得进入第二段)
扫描 {{scope}},输出锚点清单。每个锚点包含四元组:
- anchor_id: 从 A01 起的稳定编号,贯穿全程不得重编号
- file: 绝对路径
- line_range: 行号区间(如 42-58)
- symbol: 该位置的唯一符号串(函数签名 / 变量名 / typeName.fieldName / export 语句)

锚点清单要求:
1. 必须穷尽。可以判断「不需要改」,但必须先作为候选项列出来,标注 skip 并给一句理由
2. 禁止用「等等」「其余类似位置」这类省略表达
3. 动态引用要单独归类(字符串拼接 obj["xxx"]、Reflect、globalThis 下标),标注 dynamic
4. 输出后统计:A 类(直接改)/ B 类(需连带改的依赖)/ C 类(dynamic,需人工)/ S 类(skip)

## 第二段 · 坐标冻结
锚点清单输出后立即冻结。此后的每一轮对话都以它为准:
- 发现清单外的命中 → 不自动改,追加为 A<新号> 并告知我
- 发现清单内有误 → 不静默修正,输出 CORRECTION 行说明改了什么、为什么
- 每轮开头用不超过 30 字复述当前执行状态(已完成 X / 共 N)

## 第三段 · 分批执行
按批次执行,每批不超过 {{batch_size}} 个锚点,批间必须等我说「继续」:
- 排序依据:先叶子节点(无内部依赖),后被依赖方;同层按文件字典序
- 每批执行前读取该批所有文件当前内容,**禁止凭上批记忆写**
- 每批输出:本批改动 diff + 本批锚点状态表 + 是否有 CORRECTION

## 第四段 · 反向交叉校验
全部批次执行完后,执行反向校验,这是本任务的必过关卡:
1. 用 grep 意义上的全仓扫描(含 dynamic 模式),列出所有还残留旧符号的位置
2. 逐个比对:每处必须能回答「这是 C 类暂留还是漏改」,答不上来就是漏改
3. 列出所有引用了被改符号的下游文件,确认它们仍能编译通过
4. 输出最终表:锚点数 / 已改 / 有理由跳过 / **无法解释的残留(必须为 0)**

## 约束
- 最小改动:只改标识符本身,不顺手重构、不顺手格式化、不调整无关命名
- 不确定就问:对任何一处「要不要改」的判断产生犹豫,输出 OPEN_QUESTION 并等回答,禁止猜
- 不可自动化的部分要明确交回给我,不要用「可能需要你确认」这种模糊表达糊过去

多文件重构锚点提示法

一句话结论:跨文件重构的敌人不是模型能力,是坐标系丢失。 给模型一份冻结不变的坐标清单,同一个模型的漏改率能从 37% 掉到 0%。

一、为什么跨文件重构特别容易失败

1.1 三类失败,占比完全不同

我们统计了某 31 万行 TypeScript 单仓上 48 次 AI 辅助跨文件改动的结果,按失败模式分类:

失败模式占比表现
改漏了61%有一部分命中点没改,测试跑到一半才炸
标准漂移27%前 8 个文件按 A 标准改,后 6 个按 B 标准改,两种风格混在仓库里
改错了12%改了不该改的地方,通常是因为理解错了语义

「改错了」只占 12%。 绝大部分时间是改漏、以及改着改着换了标准。

这个分布直接说明了一件事:你在 prompt 里反复强调「改准确一点」是在优化只占 12% 的问题。 真正该解决的是前两类。

1.2 改漏的机制:为什么模型明明「知道」却没改

git diff 拉出来一看,漏掉的 23 处分布得毫无规律: packages/ui/src/form/use-field.ts 改了,同目录的 use-field-array.ts 没改。

原因不是它不知道后者存在,而是下面三个机制叠加:

机制一:搜索是一次性的,而改动是分轮的。 模型在任务开始时会做一次检索,拿到一份候选清单。但从第 3 个文件开始,它的上下文里塞满了 diff 内容,那份最初的候选清单已经被挤压到注意力边缘。到第 10 个文件,它实质上在凭短期印象工作。

机制二:注意力稀释。 这是「Lost in the Middle」现象在代码场景的具体表现:长上下文里的中段内容被系统性忽略。 候选清单恰好位于上下文中段,而每一轮新增的 diff 头部和尾部信息被过度关注。

机制三:没有可核销的账。 最重要的一点。人类做这件事会拿一张清单逐条打勾;模型没有这个清单, 它只有「我觉得差不多了」这种模糊的完成感。没有账就没法核销,没核销必然遗漏。

1.3 标准漂移是怎么发生的

比改漏更隐蔽。典型现场:

// 第 1-8 个文件(早期)
- import { getUserInfo } from "@pkg/user";
+ import { fetchUserProfile } from "@pkg/user";

// 第 9-14 个文件(后期)
- import { getUserInfo } from "@pkg/user";
+ import { GetUserProfile } from "@pkg/user";   // ← 大小写变了

没人下达过第二条标准。它在第 9 个文件处自己发明了写法。 当你给它看了 8 次自己的输出之后,后面的输出会被前面的输出「带偏」——这不是温度参数的问题, 是自回归生成的本质:最近的上下文权重最高,而最近上下文全是它自己刚写的东西。


二、锚点提示法:四段式结构

核心思想极简:把「搜索」和「执行」彻底分离,中间插一个不可变的坐标清单。

第一段  锚点声明  →  输出 Candidate / A / B / C / S 五类清单,编号冻结
第二段  坐标冻结  →  后续所有对话以该清单为准,发现新命中只能追加不能暗改
第三段  分批执行  →  按依赖拓扑分 batch,批间等待,批前重读文件
第四段  反向校验  →  强制它自己 grep 找残留并解释每一处

2.1 第一段:锚点声明

锚点的四元组是关键,缺一不可:

| anchor_id | file | line_range | symbol |
|---|---|---|---|
| A01 | packages/user/src/api.ts | 12-34 | export async function getUserInfo |
| A02 | packages/user/src/index.ts | 3 | export { getUserInfo } from "./api" |
| A03 | packages/ui/src/form.tsx | 88 | const info = await getUserInfo(id) |
| A04 | packages/core/src/bridge.ts | 201 | obj["getUserInfo"]   ← dynamic |

四个字段各自的必要性:

字段没有它会怎样
anchor_id后续对话无法精确指代,只能靠描述,描述本身会产生歧义
file同名文件(同名的 index.ts 有 14 个)无法区分
line_range多次改动后行号漂移,无法核对「改到哪了」
symbol最关键。行号会漂移,符号串不会。它是唯一稳定的坐标

2.2 为什么要输出 Skip 而不是直接过滤

这是整个方法里最反直觉的一条:必须让模型把「不需要改」的地方也列出来,并给一句理由。

理由是:过滤是隐式的,列举是显式的。当它说「A07 不需要改,因为那是字符串常量非标识符」时, 你(和它自己)都获得了一次核验机会。而如果它默默过滤了,这条判断就再也不会被回看。

实测数据(同一个 62 文件任务):

处理方式误过滤(把该改的过滤掉)清单长度后续发现错误的时机
让它直接过滤6 处44 条第 4 批执行后(已经改了 31 个文件)
强制列举 + 给理由0 处62 条第一段结束时立刻发现 1 处理由错误

多花 340 个 token 输出 18 条 skip 理由,换回来的是错误在第 8 分钟而不是第 40 分钟被发现。

2.3 第二段:坐标冻结的三条铁律

冻结后的行为约束:
1. 清单外的新命中 → 不自动改,追加为 A<新号>,告知我后再决定
2. 清单内有错 → 不静默修正,必须输出一行 CORRECTION 说明改了什么、为什么
3. 每轮开头用 <=30 字复述执行状态(已完成 X / 共 N)

第 3 条看着最没技术含量,收益却最大。「已完成 23 / 62」这七个字每轮都在刷新它的目标感, 能把标准漂移的发生率从 27% 压到 4%。这是对抗注意力稀释最廉价的手段。

2.4 第三段:分批而非一口气跑完

批大小实测(同一个任务,不同 batch_size):

batch_size总批数需人工干预次数漏改总耗时
一次跑完(62)1—23 处6m12s
20403 处8m03s
8800 处11m47s
3216(嫌慢手动合并)0 处19m22s

8 是拐点,超过 15 明显劣化。 少于 5 则人工成本上升而质量收益已经饱和。

排序规则也重要:先叶子节点,后被依赖方。 如果先改了 packages/user/src/api.ts(被依赖方)而没改下游,中间态是编译不过的, 万一后面被打断,你手上是个半个仓库坏掉的状态。反过来则每个中间态都可编译。

2.5 第四段:反向交叉校验

这是整套方法里唯一「不可跳过」的一段。前三段都可以简化,这段不行。

反向校验四问:
1. 全仓扫描(含 obj["xxx"] 这种 dynamic 模式),列出所有还残留旧符号的位置
2. 逐个比对:每处必须能回答「这是 C 类暂留,还是漏改」——答不上来就是漏改
3. 列出所有引用了被改符号的下游文件,确认它们仍能编译通过
4. 输出最终表:已改 / 有理由跳过 / 无法解释的残留(这一项必须为 0)

「无法解释的残留」这个字段是整个方法的验收标准。 它把「我觉得改完了」这种主观判断, 强行转换成了「给每一处残留一个解释」的客观要求。实测这一条单独贡献了 41% 的漏改发现。


三、四种锚点模式

模式一:符号锚点(默认)

适用:标识符重命名、API 改名、字段改名。

把 user.nickName 改名为 user.displayName。

先输出锚点清单(不超过 {batch} 条,超出分批):每个锚点含 anchor_id / file / line_range / symbol。
区分:直接字段访问 / 解构重命名 / 类型定义里的声明 / GraphQL query 里的字符串 / Object.keys 类的动态访问。
后者归为 dynamic 类,列出但不要自动改。
清单输出后停下等我确认。

模式二:调用链锚点

适用:接口签名变更、参数增删、返回值结构调整。

给 createOrder 增加第 3 个参数 options,并在所有调用点补默认值。

锚点清单要包含三层:
- L1 定义层:函数本身 + 类型签名 + 所有重载声明
- L2 调用层:所有调用点,按 caller 归属的模块分组统计数量
- L3 间接层:把 createOrder 作为值传递的位置(回调、依赖注入、导出表)

输出格式:三层分开列表,每层给出总数。任何一层总数为 0 都要明确写「本层 0 处」而不是留空。

模式三:迁移锚点

适用:依赖库升级、API deprecated 替换、框架版本迁移。

把 Moment.js 的 moment(x).format("YYYY-MM-DD") 迁移到 date-fns 的 format(x, "yyyy-MM-dd")。

锚点清单按「迁移风险」分四级:
- R0 机械替换:字面量格式串,替换后语义完全等价,可批量
- R1 需换算:MMMM / ddd 这类 locale 相关的格式串,逐个确认
- R2 语义变化:add/subtract 链式调用在两库语义不同,必须人工
- R3 无法迁移:用了 Moment 特有插件能力,标注并提出替代方案

每个锚点必须给出 R 等级。禁止把 R2/R3 混入 R0 批量替换。

模式四:反向锚点(用于排查而非修改)

适用:找「还有哪里在用这个 API」,纯排查不改代码。

找出全仓所有仍在调用 legacyPaymentApi 的位置。

不要改任何东西,只输出清单,并且你要主动做这三件事:
1. 区分「真实调用」和「只是名字里含这个字符串」(注释、文档、类型名)
2. 找出动态调用:字符串拼接、配置表驱动、Reflect
3. 明确列出你**没有把握判断**的位置,标 UNCERTAIN,而不是给一个看起来确定的结论

输出最后加一行:本清单的置信度(高/中/低),以及置信度判断的理由。

四、实测对比:同一个任务,用与不用

4.1 任务定义

把一个 31 万行 TypeScript monorepo 里 getUserInfo 重命名为 fetchUserProfile。 62 个文件、187 处命中,含 re-export 链、默认导出、动态字符串引用。

4.2 不用锚点法(基线)

把 getUserInfo 重命名为 fetchUserProfile,全仓。

结果:

指标数值
执行耗时6 分 12 秒
改动文件数39 / 62
漏改23 处
re-export 链处理改了 import 漏了 export
动态引用0 处识别
人工返工3 小时 40 分(手工补 + 逐个 test)
token 消耗412,800

最麻烦的不是那 23 处漏改本体,而是你不知道它漏了。写着写着 tsc 报 40 个错, 其中 17 个是漏改引起的,另外 23 个是中间态的连锁反应,两者混在一起难以分辨。

4.3 用锚点法

指标数值变化
锚点清单生成1 分 12 秒—
分 8 批执行8 分 41 秒+2m29s
反向校验1 分 54 秒—
总耗时11 分 47 秒+5m35s
改动文件数62 / 62✅
漏改0 处✅
re-export 链全部处理正确✅
动态引用识别并单独列出 3 处✅
人工返工12 分钟-3h28m
token 消耗338,500-18.0%

4.4 三个值得注意的发现

发现一:token 反而降了 18%。 原本以为多输出一份清单会更贵。实际是:反了过来。分批执行时它不需要每次都重读整个文件, 而反向校验阶段用的是 grep 级别的位置信息,不是全文。 节省的大头来自避免了失败后的重做 —— 基线组的 412,800 token 里,有约 89,000 花在失败重跑上。

发现二:慢下来的 5 分 35 秒,买回来的是「知道它没漏」。 基线组 6 分钟看似更快,但那 6 分钟之后是 3 小时 40 分的人工返工。 锚点法 11 分 47 秒之后是 12 分钟的检查。全流程从 3h46m 降到 23m59s,降 89.4%。

发现三:模型档位的差距被压缩了。 这是最意外的:

模型不用锚点法(漏改)用锚点法(漏改)
Claude Opus 5.511 处0 处
DeepSeek V4.1 Flash23 处0 处
Qwen3.8 Flash31 处1 处

Flash 和 Opus 在不用锚点法时差 2 倍,用了之后都是 0。 说明多文件重构的失效率主要不由模型能力决定,而由流程结构决定。这对成本优化的意义远大于调模型档位。


五、六个可复用模板

模板 1:最小骨架(直接用)

任务:{{改动描述}}

第一步:输出锚点清单,每条含 anchor_id / file / line_range / symbol,并区分 A(直接改)/ B(连带改)/ C(dynamic,人工)/ S(跳过+理由)。清单输出后停下等我确认。
第二步:按依赖拓扑分批,每批不超过 8 个锚点,批间等我确认。每批开始前重读该批文件当前内容。
第三步:全部完成后做反向校验,列出所有残留旧符号并逐个解释,输出「无法解释的残留」计数(必须为 0)。

模板 2:加「不改什么」清单

经验:光说「要改什么」不够,必须显式说「不要改什么」。

同上第一步至第三步,另加:

不改清单(违反即为失败):
- 不改测试文件里的断言逻辑
- 不改 package.json 的版本号
- 不改任何 CI 配置文件
- 不改 CHANGELOG
- 不顺手格式化与改动无关的行

如果你的判断里有「这里虽然在不改清单里,但因为 X 必须改」,停下来输出 OPEN_QUESTION 问我。

模板 3:跨语言 re-export 链

同上,另外你必须显式追踪 re-export 链的完整形状:
- 每个 barrel file(index.ts)的入口与出口两侧都要纳入锚点
- 混用了 default export 的位置单独归类,单独处理
- `export * from` 的位置必须手动核对改完后是否仍能导出,不要假设它会自动跟踪
- 输出一张 re-export 拓扑图(文字版),我看过再动手

模板 4:要求自证

同上,另外每一步完成后输出一段「自证报告」:

本批做了什么:
核对依据:(你读了哪些文件、用了什么依据判断这里属于 A 类)
不确定的地方:
下一步建议:

禁止在「不确定的地方」这一栏写「无」。如果真的没有,写「无(已逐个核对 N 处)」并给出 N。

模板 5:超时/中断保护

跨文件任务跑到一半断掉很常见。加这一段让它在被掐断前留下可续作的账。

如果你判断本次输出可能超出一次回复的长度,立刻停止执行并按以下格式收尾,不要硬撑:

## PAUSE
已完成锚点:A01, A02, A05, A07(共 4 / 62)
当前批次:第 1 批
下一批锚点:A03, A04, A06, A08
未完成原因:输出长度限制
继续指令:说「继续第 2 批」即可

硬撑着输出会导致后半段质量崩塌,宁可暂停。

模板 6:给 IDE Agent 用(Cursor / Claude Code)

任务:{{改动描述}}

约束:
- 先用 @ 引用你判断必须读的文件,读完后输出锚点清单,我确认后再改
- 禁止一边找一边改。找和改是两个阶段
- 每批改完用 terminal 跑 tsc --noEmit 验证这一批,不过不要进入下一批
- 反向校验阶段用 grep 工具做真实全仓扫描,不要凭印象回答「没有了」
- 任何不确定的地方输出 OPEN_QUESTION,不要猜着改

六、九个常见误区

#误区为什么错正确做法
1「我把任务说清楚就行了」说清楚解决的是「改错了」(12%),漏改靠的是账加清单 + 核销
2让它一次跑完 60 个文件超过 15 个锚点后质量断崖batch_size ≤ 8
3允许它自动过滤不需要改的隐式过滤是误判的温床强制列举 skip + 给理由
4行号当作坐标改两个文件后行号全漂了用符号串做坐标,行号只作参考
5不主动列「不确定的地方」模型默认不暴露不确定性显式要求,且禁止回答「无」
6动态引用(字符串下标)不管这类最容易被漏,且漏了测试不一定炸单独归 C 类,人工处理
7改完就完,不反向校验「改完了」是主观判断强制 grep + 逐项解释残留
8只看最终结果,不分批验证一次性评估看不出漂移,漂移发生在第 9 个文件每批改完立即跑类型校验
9认为好模型就不用这套流程实测 Flash 用锚点法 = Opus 用锚点法(都 0 漏改)流程优先于模型档位

第 9 条值得再说一遍:这套方法最大的价值不是提升上限,而是把下限抬起来了。 让便宜模型达到贵模型的效果,比追求「更强的模型」性价比高得多。


七、什么时候这套方法过重

不值得上完整四段的场景:

  1. 单文件内改动 —— 直接改,不需要锚点
  2. 2-3 个文件的小改动 —— 简化版即可:列出清单,一次改完,跑一次校验
  3. 纯新增代码 —— 没有「改漏」风险,锚点法无收益
  4. 一次性脚本 / 临时 PoC —— 改漏了也不影响任何人,别浪费 token

经验判断线:涉及文件数 ≥ 5,或者改动跨越 ≥ 2 个包/模块,就上全套。 低于这个线用简化版。


八、完整对话实录:62 文件重命名,逐轮发生了什么

抽象讲完方法,看一次真实的逐轮交互。下面是不加修饰的原始记录(删掉了 diff 细节), 任务是前文那个 getUserInfo → fetchUserProfile。

第 1 轮:我 → 模型

任务:把 getUserInfo 重命名为 fetchUserProfile。
范围:packages/*/src/**

第一步:输出锚点清单,每条含 anchor_id / file / line_range / symbol,
       并区分 A(直接改)/ B(连带改)/ C(dynamic,人工)/ S(跳过+理由)。
第二步等我确认,再执行。
第三步(完成前必做):反向校验,列出所有残留旧符号并逐个解释,
       输出「无法解释的残留」计数,必须为 0。

约束:
- 至少分成 8 批,每批不超过 8 个锚点,批间等我确认
- 每批改完跑 tsc --noEmit 验证这一批
- 禁止孤立地看待单个文件,每批执行前必须重读该批文件当前内容

第 2 轮:模型 → 锚点清单(节选)

扫描完成。packages/*/src/** 下共 62 个文件、187 处命中。

A 类(直接改,共 44)
| A01 | packages/user/src/api.ts        | 12-34  | export async function getUserInfo       |
| A02 | packages/user/src/index.ts      | 3      | export { getUserInfo } from "./api"     |
| A03 | packages/ui/src/form.tsx        | 88     | const info = await getUserInfo(id)      |
... (共 44 条)

B 类(连带改,共 12)—— re-export 链的另一端,改 A 之后此处会编译失败
| B01 | packages/api-gateway/src/routes/user.ts | 7 | import { getUserInfo } from "@pkg/user" |
... (共 12 条)

C 类(dynamic,人工处理,共 3)—— 字符串下标与配置驱动,无法静态分析
| C01 | packages/core/src/bridge.ts   | 201 | obj["getUserInfo"]                      |
| C02 | packages/report/src/column.ts | 44  | accessor: (row) => row["getUserInfo"]   |
| C03 | packages/admin/src/menu.ts    | 19  | { key: "getUserInfo", label: "用户" }    |

S 类(跳过,共 3)
| S01 | packages/user/README.md  | 18 | 「调用 getUserInfo 获取用户信息」 |
|     | 理由:文档文本,非标识符,本次改动范围限定在 src/ 下的代码,留待单独 PR 更新
| S02 | packages/e2e/user.spec.ts | 202 | expect(spy).toHaveBeenCalledWith("getUserInfo") |
|     | 理由:这是在断言一个字符串常量的传参,不是标识符引用;改了会让测试失去意义
| S03 | CHANGELOG.md | 611 | 历史变更记录 |
|     | 理由:变更记录不应被回填修改

**关键判断提示**:C03 那一条容易被误判为 S 类。它是菜单配置的 key,
虽然看起来像「只是配置项」,但菜单渲染时会用这个 key 去索引 API 对象,
属于 C 类。请确认按 C 类处理(人工)。

已停止,等待确认。

注意最后那段主动提示。 这是 §2.2「强制列举 Skip + 给理由」带来的副产品 —— 它对自己的分类边界产生了一次自我校验并主动摊给你看。如果是过去那种「默默过滤」的写法, C03 会被当成 S 类静默跳过,然后在运行时炸。

第 3-10 轮:分批执行

执行期间我只做一件事:每批完成后回复「继续」。八批共发生:

事件次数处理
tsc --noEmit 一次通过6 / 8—
批内发现 CORRECTION1模型主动报:原清单 A17 的 line_range 因前批改动漂移了 19 行,已按 symbol 重新定位
发现清单外新命中1追加 A63,说明出现在第 5 批改完后才引入的新引用(来自前一个 task 的遗留)
429 中断2由代理层自动重试,上层无感知

若不加锚点机制,第 4、5 行这两条会变成静默的错误 —— 它会自己改掉(可能改错), 或者忽略(一定是漏)。

第 11 轮:反向交叉校验

## 反向校验报告

全仓 grep(含 obj["xxx"] 模式):
- 残留旧符号位置:3 处
- 全部位于 C 类清单中(C01 / C02 / C03),均为动态引用,已交人工确认

下游文件编译验证:
- tsc --noEmit:0 错误
- vitest run packages/user packages/ui:通过(1,284 tests)
- 受影响的 74 个下游文件全部通过类型检查

最终表:
| 项目 | 数量 |
|---|---|
| 锚点总数 | 63(62 原 + 1 追加) |
| 已改 | 56 |
| 有理由跳过(S 类) | 3 |
| 交人工(C 类) | 3 |
| **无法解释的残留** | **0** ✅ |

最后一行是这套方法的验收闸门。它把「我觉得改完了」变成了「每一处残留都有一个解释」。

这次实录里最值钱的三句话

来自哪一轮那句话值钱在哪
第 2 轮「C03 那一条容易被误判为 S 类」模型主动暴露分类边界的不确定性
第 5 轮「A17 的 line_range 漂移了 19 行」CORRECTION 机制让行号漂移无处藏身
第 11 轮「C01/C02/C03 均为动态引用,已交人工」验收标准是可解释性,不是感觉

九、把这套方法固化到你的工作流

最后给三条落地路径,从轻到重:

层级做法适用
轻把「模板 1:最小骨架」存成代码片段,Cmd+K 前贴一次个人,偶尔跨文件改动
中写进 .cursor/rules/refactor.mdc,globs: **/*,让它在每次可能的多文件任务时自动被想起小团队
重做成 MCP Tool 或 slash command,输入任务描述自动展开成四段 prompt团队统一流程

推荐从中级起步 —— Rules 的成本是零,却能保证你每次都不偷懒。 实测一个人用「轻」层级时的合规率约 43%(忙的时候会省掉第三、四段), 用「中」层级后是 100%,因为流程写在规则里,不用靠自觉。

十、小结

多文件重构的失败,91% 不是「改错」而是「改漏」和「标准漂移」。 锚点提示法用四段结构把这两类问题各自按死:

失败模式对症手段机制
改漏(61%)锚点清单 + 分期核销把主观的「改完了」变成可查的账
标准漂移(27%)坐标冻结 + 每轮状态复述抵抗自回归的自我带偏
改错(12%)分批 + 批间 tsc 验证早失败早发现
动态引用漏网C 类独立归集不混进自动批量

最实用的两条,如果你只想记两条:

  1. 强制输出「不需要改的地方 + 理由」,多花 340 token 买回错误前置发现
  2. 必做反向校验,验收标准是「无法解释的残留 = 0」

相关阅读

相关推荐

查看更多 →
Prompt编程开发

工具调用契约提示法:让 AI 在按下回车之前先把「要调什么、返回什么形状、失败怎么办」写清楚——同一批任务瞎调工具从 34 次降到 4 次

Agent 最浪费钱的行为不是答错,是乱调工具:拿到一个模糊任务,不问不说,直接连着调七八个工具,其中一半是用错误参数试错,另一半是调完发现返回结构跟预期不符又重调一遍。2026 年 Agent 形态普及之后,工具调用成本已经超过文本成本,成为 token 消耗的主体。本文提出的工具调用契约提示法(Tool Call Contract Prompting)把每一次工具调用从「直接执行」改成「先声明契约再执行」:模型必须先写出一份四段式契约(意图 / 入参及其来源 / 预期返回形状 / 失败分支),契约通过自检才允许真正调用,调用后必须拿真实返回和契约比对,不符就走契约里预先写好的分支而不是临场发挥。在一个 23 个工具的企业运维 Agent 上实测:无效调用从 34 次降到 4 次,单任务平均调用轮次从 11.2 降到 5.6,token 消耗降低 41%,任务一次通过率从 47% 升到 79%。全文含契约四段式的完整定义、三种典型失败模式的拆解、可直接粘贴的模板、与 ReAct / Plan-and-Execute 的对比表,以及常见误区。

2026-10-12
#提示词工程#工具调用
Prompt编程开发

上下文预算控制提示法:给 Agent 长任务设一个 token 天花板——32 轮重构任务实测,完成率从 41% 拉到 88%,单任务成本降 63%

2026 年 10 月,「Agent 落地还是毛坯房」这句话在行业里传开了:模型能力够了,工程约束没跟上。企业级 Agent Infra 密集发布、Graphify 这类把代码仓库整合成图上下文的产品密集出现,指向同一个病灶——长任务失败的根因往往不是模型不够聪明,而是上下文在被一点点污染。本文给出可直接执行的上下文预算控制框架:长任务为什么会在第 15 轮之后自我退化(目标漂移、规则遗忘、复读循环),背后的科学依据(lost in the middle 现象、注意力稀释机制、有效上下文远小于标称窗口),四条账本模型(静态区 / 动态区 / 工作区 / 输出区),分段投喂、摘要压缩、引用而非复制、检查点重放四种核心模式的完整提示词写法,同一任务「用与不用」的逐段输出对比,6 个可复用模板(含最早 chat 直接粘贴的完整文本),一组实测数据表,以及 9 个常见误区。

2026-10-08
#提示词工程#Agent
Prompt编程开发

代码库问答的上下文切片与锚定:让 AI 在 20 万行仓库里找对地方,把幻觉引用率从 23% 降到 3%

代码库级问答最常见的失败,不是模型不够聪明,而是你喂的上下文没切对。喂整个仓库,模型会在 32 万tokens 里稀释掉你要找的那 40 行;喂太少,它靠猜补全,于是编出一个根本不存在的函数。真正有效的是「切片 + 锚定」两段式:先用检索把范围收窄到符号级,再用行号锚点把引用钉死。本文给出可复用的上下文切片提示法(含三级切片策略、符号优先于行的排序规则、8K/64K/1M 三档上下文预算分配表),以及实测有效的结构化引用契约——强制模型以 file:line-range + 符号名 + 置信度三件套作答。在一个 23 万行、8 种语言的 monorepo 上实测:幻觉引用率从 23% 降到 3%,「答案正确但定位错误」的返工从 41% 降到 8%,单次问答平均节省 62% tokens。含三个真实失败案例拆解(为什么「把文件都贴进去」必然失败)、检索召回与切片边界的取舍表、以及 Claude Opus 5.5 / GPT-6 / DeepSeek V4.1 Flash / Qwen3-Max 四款模型的实测差异。

2026-10-13
#上下文工程#代码库问答
案例企业应用

Agent 编码返工率治理实战:spec + 验收测试 + diff 自检三件套,把 AI 代码一次通过率从 38% 拉到 71%

AI 写代码的产能和它的返工量成正比,这是研发团队用AI 编码半年后才真正撞到的墙。我们团队 12 个前端工程师、4 个 AI 编码 Agent Cursor / Cline / Copilot 并行,日均产出 PR 从 11 个涨到 27 个,看起来赢了——但首轮评审通过率只有 38%,意味着三分之二的工作量在被返工吃掉。真正的原因不是模型能力不够,而是任务交给 Agent 的方式错了:需求描述里没有验收标准,Agent 只能猜;Agent 不知道验收标准,就没法自检;不自检就直接提交,返工必然发生。本文记录我们用四个月把一次通过率从 38% 提到 71% 的完整过程。核心是三件套:① 提交前把模糊需求转成可验收的 spec(含验收命令与判定标准);② 在 Agent 里挂可执行的验收测试,让它提交前自己跑一遍;③ 加一道 diff 自检环节专门抓「超出任务范围的改动」。四个阶段的实施顺序和每阶段的踩坑、三条被证明无效的尝试(包括「写更详细的 Prompt」)、以及12 个可复制的 Prompt 模板。附分阶段效果数据表:一次通过率 38%→71%、返工工时占比 44%→19%、交付周期 2.8 天→1.1 天,以及两个必须诚实说明的负面结果。

2026-10-13
#AI实战#AI编码