Appearance
功能规格说明:下一步编辑建议
创建日期:2026-10-26
概述
下一步编辑建议(下称「下一步建议」,英文 Next Edit Suggestion)与行内补全同属「编辑器内预测」族,但预测的东西不同:补全预测光标处接着写什么(追加型建议),下一步建议预测下一次该改哪里、改成什么——产物是一个可能删行、可能换位置、也可能整段重排的差异,建议位置可以离光标很远。
决定它的输入因此也不同:不是「光标前缀 + 后缀」,而是近期编辑轨迹——用户对本文件最近若干次编辑的差异记录,加上当前文件上下文。用户在写一个函数时通常有几处连锁改动,下一步建议的价值就在于接住第二步。
它与补全共用同一条 CLI 通道与同一套模型来源,但各自开关、各自请求:同样不经过 agent 的对话轮次(无工具、无 system prompt、无 hooks),不共享 aiService 的全局 1 次/秒间隔;同一会话同一时刻只有一个在途的下一步建议请求。补全与本能力在同一时刻互斥(见边界情况)。
首期不做本地模型推理,也不训练专用模型。
用户场景与测试 (必填)
用户故事:编辑停顿后给出下一步的修改建议(优先级:P1)
作为编码中的用户,我希望在完成一次编辑后停顿一下,编辑器就把「接下来该改的那一处」以差异形式指出来,以便不必自己回想这次改动牵连到的下一个位置。
为什么是这个优先级:这是本能力的本体——没有「编辑后停顿 → 出现差异建议」这条链路,其余能力(接受、跳转、开关)都无处依附。
独立测试:在 VS Code 中打开一个 TypeScript 文件,把某函数里的参数 id 改名为 userId 但只改了这一处,然后停顿,验证编辑器在另一处引用处以差异形式给出建议;在 JetBrains 中对同一文件重复同样操作,验证两端行为一致。
验收场景:
- 假设 下一步建议开关为开启、模型可用,当 用户完成一次编辑并停顿(停顿时长沿用补全的自适应防抖口径),则 插件向 CLI 发起一次下一步建议请求,并把返回的差异渲染在建议所在区域(可以与光标所在位置不同)。
- 假设 建议区域与当前区域内容只差空白或与原文完全相同,当 响应到达时,则 不渲染任何建议,用户看不到任何变化。
- 假设 模型返回的建议为空,当 响应到达时,则 不渲染任何建议。
- 假设 建议中既包含新增行也包含删除行,当 建议被渲染时,则 用户能区分哪些行将被加入、哪些行将被移除。
- 假设 建议已渲染,当 用户继续输入、移动光标、切换编辑器或切换标签页时,则 建议消失,其在途请求被取消;迟到的响应不得再渲染。
- 假设 建议请求失败(网络错误、模型不可用、鉴权失效、超时),当 请求返回错误时,则 静默跳过本次建议,不弹窗、不写状态栏、不影响用户编辑。
- 假设 建议内容超出用户实际可见的范围(如整段重排),当 用户接受它时,则 只有建议区域内被声明改动的部分发生变化,区域外内容一字不动。
- 假设 建议要替换或删除编辑器里已有的若干行(而不是在光标处纯插入),当 建议渲染时,则 那几行在代码上带有可见标记,用户不必依靠行号、也不必逐字比对浮层文本来判断建议落在哪几行。
- 假设 建议已渲染,当 用户接受它、拒绝它,或它因任何原因失效时,则 代码上的标记随之清除,编辑器里不残留任何标记或样式。
用户故事:接受或拒绝建议(优先级:P1)
作为用户,我希望按一个键就接受建议、按另一个键就拒绝,并且接受之后能像撤销自己的一次编辑那样撤销它,以便下一步建议的操作手感和自己打字一致。
为什么是这个优先级:建议的产物是差异而不是追加文本,接受动作会改动用户已有代码;「怎么接受、怎么撤销」不用好会让这个能力变成破坏性操作,因此与生成同属 P1。
独立测试:在建议已渲染的情况下按接受键,验证差异被写入文档、光标落在最后一处变更处、按一次撤销即可回到接受前状态;再在另一个位置按拒绝键,验证文档无任何变化。
验收场景:
- 假设 建议已渲染,当 用户按接受键时,则 差异被写入文档,建议消失,光标移动到本次建议中最后一处变更的位置。
- 假设 用户刚刚接受了建议,当 用户按一次撤销时,则 文档回到接受建议之前的状态(这次接受不与用户的上一次输入合并成同一步)。
- 假设 建议已渲染,当 用户按拒绝键时,则 建议消失、文档不变、光标不动。
- 假设 用户拒绝了某处的建议,当 用户没有新的编辑、也没有移动光标时,则 该处不再重复出现同一条建议;用户下一次编辑或移动光标后可再次请求。
- 假设 建议已渲染而用户在该区域直接开始打字,当 文档发生变化时,则 建议立即失效,不接受、不部分应用。
- 假设 建议以宿主原生的行内建议形式呈现,当 用户按接受键时,则 只有当前可见的那一条被接受,不会一次接受两种建议;假设 该宿主的行内建议 API 无法呈现这类改动(例如只能插入、不能替换已有行),当 建议呈现时,则 该宿主提供自己的接受动作(一次用户动作即完成整条建议),并在提示里写明这个动作是什么。
用户故事:编辑轨迹的采集与失效(优先级:P1)
作为用户,我希望下一步建议依据的是我最近真实做过的改动,而不是文件从打开以来的全部历史或别人的改动,以便建议对得上我手上的活。
为什么是这个优先级:轨迹是这能力的输入,采错就全错(把外部改动当成用户意图会产生误导性建议);它同时是隐私边界,必须一开始就定清楚。
独立测试:连续做三次不相关的小编辑后停顿,验证建议与最近一次改动的意图相关;用外部命令改写该文件(如切换分支、运行格式化)后再停顿,验证这些改动不被当成用户编辑计入轨迹。
验收场景:
- 假设 用户在当前文件里做过若干次编辑,当 下一步建议请求发出时,则 轨迹只包含最近若干次用户输入产生的编辑,每条记录是一次编辑前后的差异。
- 假设 用户的编辑次数或编辑发生时间超过轨迹保留上限(条数与时间窗,均为默认值),当 新编辑产生时,则 最早的那些记录被丢弃,轨迹不会无限增长。
- 假设 文件内容由于保存者之外的原因整体变化(切换分支、外部格式化、被其他程序改写、编辑器重新打开),当 采集轨迹时,则 这类变化不被记为一条用户编辑。
- 假设 用户切换文件或切换到另一个编辑器,当 采集轨迹时,则 轨迹按文件隔离,不同文件的记录不混用。
- 假设 轨迹为空(例如刚打开文件还没编辑过一次),当 用户停顿触发时,则 不发起下一步建议请求(没有可用的编辑意图输入)。
- 假设 轨迹中最近一次编辑的差异采集失败,当 触发时,则 本次不发起请求,而不是退化成「无轨迹」再请求——没有轨迹的下一步建议退化为猜测,宁可不给。
用户故事:跳到建议所在位置(优先级:P2)
作为用户,我希望当建议不在我当前视线内时,能一键跳到它所在的位置再决定接受与否,以便我不用自己找。
为什么是这个优先级:这是「定位」那一半能力。没有它,建议仍能在光标附近工作(只是远处的位置用户看不见、等于没给),因此排在生成与接受之后。
独立测试:在一个长文件中把引用的符号改名但不滚动到该引用处,停顿后在可见范围内看不出变化;此时点击/触发跳转提示,验证编辑器滚动并把光标放到建议所在区域。
验收场景:
- 假设 建议所在区域既不在当前视口内、也无法在当前光标处呈现,当 建议渲染时,则 编辑器给出可跳转的提示,用户能看出「建议在别处」。
- 假设 存在可跳转的建议,当 用户触发跳转时,则 编辑器滚动到建议区域,光标落到该区域,建议仍处于待接受状态。
- 假设 存在可跳转的建议,当 用户没有触发跳转时,则 编辑器不自行移动光标或滚动(跳转必须是用户动作)。
- 假设 建议能在当前光标处以行内建议呈现,当 建议渲染时,则 不出现跳转提示(避免噪音)。
- 假设 用户触发了跳转但在此前文档已变化,当 跳转执行时,则 放弃这次跳转而不是跳到错误位置。
用户故事:触发时机、节流与与补全的互斥(优先级:P2)
作为用户,我希望下一步建议只在真正有可能的时候出现,不要和行内补全抢同一时刻,以便两者都不打断我的打字节奏。
为什么是这个优先级:这是体感层,缺失时能力仍可用(只是显得聒噪);但它直接决定用户会不会把功能关掉。
独立测试:在一次接受的编辑后连续停顿,观察不会每次停顿都发请求;在光标位置让补全与下一步建议都可能出现,验证同一时刻只出现一种、且不会出现两个请求并发。
验收场景:
- 假设 用户在同一个位置重复停顿且没有新的编辑,当 停顿发生时,则 不重复发起下一步建议请求。
- 假设 一次补全请求在途,当 停顿达到触发条件时,则 下一步建议不发出请求(两者互斥,且不得由同一时刻的两次请求同时占用通道)。
- 假设 用户的输入来自输入法组合过程(拼音未上屏),当 组合进行中时,则 不触发下一步建议。
- 假设 用户在只读文档、大文件、多光标或空选区以外的选区中(与补全同一套跳过条件),当 停顿发生时,则 不触发下一步建议。
- 假设 单个请求超过时限(与补全同一口径的超时上限),当 超时发生时,则 按「无建议」处理并取消在途请求。
用户故事:开关、模型与设置项(优先级:P2)
作为用户,我希望下一步建议有自己的开关、默认不影响我,并且用我配的补全模型即可工作,以便我不想用的时候它绝不出现、想用的时候不必再配一套模型。
为什么是这个优先级:默认关闭 + 独立开关是这能力上线的安全前提(它会改用户已有代码,比补全风险高),但对已开启的用户不是额外功能。
独立测试:在设置页把开关关掉,验证任何停顿都不产生请求、不显示建议;开启后恢复;不做任何模型设置时验证建议仍能返回。
验收场景:
- 假设 下一步建议开关为关闭,当 用户在编辑器中任意输入与停顿,则 不发起任何下一步建议请求、不渲染建议(开关在 SDK 侧判定,关闭时连一次模型请求都不发出)。
- 假设 用户从未设置过开关,当 首次使用时,则 该能力处于关闭状态(与补全默认开启不同:本能力会改动用户已有代码,先关闭灰度)。
- 假设 用户未单独指定下一步建议所用模型,当 请求发出时,则 使用补全模型的那条解析链(补全模型 → 快速模型);本规格不要求专用微调模型。
- 假设 用户拨动开关,当 拨动当下(不依赖底部「保存」,见 agent-config.md「设置页控件的保存时机」故事),则 该值立即写入 CLI 侧用户级设置文件的顶层键
nextEditEnabled,经 SDK 实时重载在下一轮生效,插件端不持有第二份配置。 - 假设 当前宿主没有代码编辑器(桌面端、远端网页等),当 渲染设置页时,则 不展示下一步建议的开关(与补全的宿主能力位判定同一套),CLI 侧键与语义照旧。
用户故事:建议的采纳度量(优先级:P3)
作为产品与研发,我希望下一步建议的展示与接受次数能像补全那样被记下来,以便判断这个能力是否值得保留。
为什么是这个优先级:没有指标不影响用户使用,但会影响我们是否继续投入,故为 P3。
独立测试:触发若干次建议并接受其中一部分,导出指标,验证展示与接受的计数与手工计数一致。
验收场景:
- 假设 指标导出已就绪,当 宿主渲染了一条下一步建议时,则 记一次「已展示」;当 用户接受它时,则 记一次「已接受」,两者与补全的采纳度量分开计(不得混入同一条比率)。
边界情况
- 通道独立于对话,也与补全分开排队。 下一步建议走新增的会话级 RPC
getNextEdit(请求含:编辑轨迹、建议候选区域、当前文件上下文、语言与文件路径),与补全的getCompletion平行;同一会话同一时刻只保留一个在途请求,新请求到达时旧请求被中止,因此迟到的响应不会渲染。补全与下一步建议互斥:两者不得在同一时刻各占一个在途请求。 - 建议的落点限定在「可编辑窗口」内。 首期只预测离光标有限距离内的改动(窗口为默认值),更远的位置不主动预测——远处建议的接受率低且无法验证。跳转只在「已生成且不在视口内」时出现,不代表可以预测任意远处。
- 轨迹的隐私边界。 发送的是编辑差异与当前文件上下文(与补全同源的精简片段),不上传整份文件历史、不上传仓库;轨迹只保留最近若干条,随会话结束即丢弃。
- 接受是唯一写入文档的动作。 渲染、拒绝、跳转、失效都不写文档、不入撤销栈、不移动光标;只有接受会写入,且是一次独立的撤销步骤。
- 宿主行内建议 API 的能力边界。 建议的产物是差异,而宿主的行内建议 API 未必能表达差异:VS Code 的行内补全只在光标处的纯插入时渲染,带非空替换区间的项会被 API 收下却不显示(2026-10-26 实测);JetBrains 平台的行内补全同样只能往光标处插灰字。因此「怎么显示差异」由各宿主自绘(装饰层/浮层),接受动作也随之由宿主提供——这属于「渲染方式由宿主决定」的下限,本规格只要求可观察行为一致:用户能看到增删、能用一次动作接受、能一次撤销。
- 「建议在哪」由代码本身指示,浮层不承担这个职责。 建议位置可以与光标不同,而浮层里的旧文本与编辑器里的文本往往只差几个字符,靠内容比对认位置不可靠;「只预测离光标有限距离内」也不保证目标行一定在视口内。因此宿主自绘差异时,被替换/删除的代码要在编辑器里就地标记,浮层只负责「改完长什么样」。这条是 2026-10-26 截图评审暴露的:浮层盖住了目标行、又没有行号,连我们自己都看不出建议落在第几行。Continue 两端是同一做法——IntelliJ 侧对被删字符加
EffectType.STRIKEOUT高亮器、并把浮层定位到光标所在行行尾(NextEditWindowManager.kt的renderDeletionDecorations/showCodePopup),VS Code 侧是 SVG 窗口 + deletion decorations。 - 触发条件与补全共用一套宿主侧判断(只读/未命名/虚拟文档、体积阈值、多光标与非空选区、输入法组合、文档版本漂移时丢弃响应、聚焦编辑器持有在途请求),本规格不重复定义,见 代码补全 的同名边界情况。
- 不重复同行踩过的两个坑:① 触发时刻与补全完全同一刻(Continue 的自陈问题)——本规格要求两者互斥而是不并列触发;② 最近一次编辑的差异采集不可靠时仍发起请求——本规格要求采集失败即放弃本次。
- 模型能力风险登记。 首期不训练、不微调专用模型,用通用补全模型加结构化提示词实现;若实测接受率不可接受,退路是关闭该能力或引入专用模型,而不是降低触发门槛去凑数据。
- 不做本地推理,与补全一致。
假设
- 用户在 IDE 中已登录 wave,且 CLI 侧已配置至少一个可用模型(或已配置快速模型作为回退)。
- 插件与 CLI 的 stdio 连接已建立(见 Stdio 传输层);下一步建议复用该连接与既有会话,不额外 spawn 进程。
- 两端(VS Code 扩展与 JetBrains 插件)共享请求链路与参数默认值,差异仅在宿主的建议渲染、快捷键接入、跳转提示与轨迹采集接口——具体渲染方式由各宿主平台能力决定,本规格只约束可观察行为。
- 规格中的数值(可编辑窗口、轨迹条数与时间窗、超时、防抖口径)均为默认值,可在实现与调优阶段调整,调整后需同步更新本规格。
- 采纳度量沿 OpenTelemetry 集成 的指标族扩展,事件名与属性在该规格登记。