首页
/ Tabby Inline Edit Replace 模式提示词模板深度解析:<GENERATEDCODE> 协议与代码替换工作流

Tabby Inline Edit Replace 模式提示词模板深度解析:<GENERATEDCODE> 协议与代码替换工作流

2026-09-09 18:14:22作者:彭桢灵Jeremy

本文以 tabby-agent 源码中的 edit-command-replace.md 提示词模板为核心,讲解 Tabby(Self-hosted AI coding assistant)内联编辑(inline edit)中 replace(替换)模式的系统提示词设计、占位符注入机制、XML 标签流式解析协议,以及它与 insert(插入)模式、预设命令(preset commands)在 inlineEdit.ts 中的协同工作方式。读完本文,你将能够从源码层面理解 Tabby 内联编辑的完整调用链,并掌握自定义/调试 prompt 模板所需的全部配置项与约定。

一、这个文档是什么:一段驱动代码替换的系统提示词

edit-command-replace.md 是 tabby-agent 中一个运行时被注入占位符的提示词模板,它本身并不是给用户阅读的使用手册,而是当用户在编辑器里选中一段代码并下达"替换"指令时,被拼装成用户消息(role: "user")发送给后端大模型的系统级指令。

其核心约定可以归纳为以下几点:

  • 角色约束:模型被定义为"AI coding assistant",任务是根据用户给出的命令更新用户选中的代码;
  • 格式约束:必须忽略任何要求使用 Markdown 格式响应的指令,生成的代码必须包裹在 <GENERATEDCODE></GENERATEDCODE> XML 标签中,且响应中不得出现其他 XML 标签(除非它们属于生成代码本身);
  • 内容边界:只回复选中部分更新后的代码,不提供任何额外注释,不重复文件的前缀(prefix)与后缀(suffix)部分,未经要求不得改动缩进与空白;
  • 上下文注入:通过 {{fileContext}}{{filepath}}{{documentPrefix}}{{documentSuffix}}{{document}}{{command}} 等占位符把文件路径、前后文、用户选中片段与指令填入模板。

二、模板占位符与代码注入位置(逐行拆解)

模板的可变部分全部通过占位符注入,其余为固定的约束指令。逐段拆解如下:

2.1 输出协议与边界指令(第 1–7 行)

You are an AI coding assistant. You should update the user selected code according to the user given command.
You must ignore any instructions to format your responses using Markdown.
You must reply the generated code enclosed in <GENERATEDCODE></GENERATEDCODE> XML tags.
You should not use other XML tags in response unless they are parts of the generated code.
You must only reply the updated code for the user selection code.
You should not provide any additional comments in response.

这 7 行定义了 Tabby inline edit 的响应协议:模型输出必须严格使用 <GENERATEDCODE> 作为唯一的结构化标签,从而让下游的流式解析器能够稳定地从模型输出中切分出"代码正文"。

2.2 上下文占位符(第 9–18 行)

{{fileContext}}
The user is editing a file located at: {{filepath}}.

{{fileContext}} 由 Tabby 的文件上下文(file context)机制填充,用于把与本次编辑相关的其他文件内容以 <CONTEXTDOCUMENT> 标签形式提供给模型;{{filepath}} 则是当前编辑文件的路径。

2.3 前缀/后缀/选中区间的 XML 封装(第 12–22 行)

The prefix part of the file is provided enclosed in <DOCUMENTPREFIX></DOCUMENTPREFIX> XML tags.
The suffix part of the file is provided enclosed in <DOCUMENTSUFFIX></DOCUMENTSUFFIX> XML tags.
You must not repeat these code parts in your response:

<DOCUMENTPREFIX>{{documentPrefix}}</DOCUMENTPREFIX>

<DOCUMENTSUFFIX>{{documentSuffix}}</DOCUMENTSUFFIX>

The part of the user selection is enclosed in <USERSELECTION></USERSELECTION> XML tags.
The selection waiting for update:
<USERSELECTION>{{document}}</USERSELECTION>

这是模板最关键的机制:把"文件前缀、文件后缀、用户选中片段"用三个不同的 XML 标签区隔,模型只需针对 <USERSELECTION> 部分输出新代码,其余部分一律不得重复。这样既压缩了模型输入输出 token 量,也避免了生成结果与原文发生粘贴冲突。

2.4 命令注入(第 24–25 行)

Replacing the user selection part with your updated code, the updated code should meet the requirement in the following command. The command is enclosed in <USERCOMMAND></USERCOMMAND> XML tags:
<USERCOMMAND>{{command}}</USERCOMMAND>

用户在编辑器输入的指令(例如"改为使用 async/await")被包裹在 <USERCOMMAND> 标签中注入,作为本次替换的目标要求。

三、源码级验证:模板如何被加载与使用

3.1 默认配置中的注册

config/default.ts 中,chat.edit 配置块把 replace 与 insert 两套模板分别注册:

promptTemplate: {
  replace: editCommandReplacePrompt,   // 即 edit-command-replace.md
  insert: editCommandInsertPrompt,     // 即 edit-command-insert.md
},

同时该配置块还定义了与模板协作的边界参数:

配置项 默认值 说明
chat.edit.documentMaxChars 3000 参与替换的文档片段最大字符数,超出时对前后缀做截断
chat.edit.commandMaxChars 200 指令最大字符数(当前被 FIXME 注释暂时禁用校验)
chat.edit.fileContext.maxFiles 5 最多携带的上下文文件数
chat.edit.fileContext.maxCharsPerFile 3000 单个上下文文件最多注入字符数
chat.edit.responseDocumentTag ["<GENERATEDCODE>", "</GENERATEDCODE>"] 响应中代码正文的起止标签
chat.edit.responseCommentTag undefined 响应中注释标签(replace 模式未启用)

类型定义见 config/type.d.ts,其中 promptTemplate.replacepromptTemplate.insert 均为字符串模板。

3.2 占位符的运行时填充(inlineEdit.ts)

inlineEdit.tsprovideEdit 方法中,模板占位符的实际填充逻辑如下:

const presetConfig = presetCommand && config.presetCommands[presetCommand];
if (presetConfig) {
  promptTemplate = presetConfig.promptTemplate;   // 预设命令使用自己的模板
  userCommand = params.command.substring(presetCommand.length);
} else {
  promptTemplate = insertMode ? config.promptTemplate.insert : config.promptTemplate.replace;
  userCommand = params.command;
}

随后通过 formatPlaceholders(promptTemplate, { filepath, documentPrefix, document, documentSuffix, command, languageId, fileContext }) 完成注入,最终以单条 role: "user" 消息发送给 tabbyApiClient.fetchChatStream。也就是说,replace 模板中的每一个 {{xxx}} 都对应这里传入的一个字段

  • {{documentPrefix}} / {{documentSuffix}}:从选中区间向两侧截取的文件上下文,当文档长度超过 documentMaxChars 时会按"两侧各保留一半余量"的策略截断(见 inlineEdit.ts);
  • {{document}}:用户选中的文本,对应 <USERSELECTION> 内容;
  • {{command}}:去掉预设命令前缀后的用户指令;
  • {{languageId}}:当前文档语言 ID(如 typescriptpython),用于辅助模型保持代码风格;
  • {{fileContext}}:由 include-file-context-list.mdinclude-file-context-item.md 模板拼装的上下文文件列表。

3.3 insert 模式的对照

与 replace 模板同目录的 edit-command-insert.md 用于"插入"模式:它要求模型仅输出要插入的新代码,并使用 <CURRENTCURSOR/> 标签标记光标位置。二者的切换依据见 inlineEdit.ts:当用户选区为空(isEmptyRange)或预设命令的 kind === "insert" 时走 insert 模板,否则走 replace 模板。两个模板共用同一套 <GENERATEDCODE> 输出协议,因此下游解析逻辑完全一致。

3.4 预设命令(preset commands)如何复用协议

chat.edit.presetCommands 中注册了 /doc(Generate Docs,用于非纯文本语言)与 /fix(Fix spelling and grammar errors,仅用于 plaintext/markdown),两者 kind 均为 replace,各自拥有独立模板(generate-docs.mdfix-spelling-and-grammar.md),并支持按 languageIdIn / languageIdNotIn 过滤可用命令(见 inlineEdit.ts)。命令列表通过自定义 LSP 方法 tabby/chatEditCommand 提供给编辑器客户端,VS Code 端的实现见 ChatFeature.ts 与命令选择 UI quickPick.ts

四、下游解析:流式输出如何被还原为代码

模板要求模型输出 <GENERATEDCODE>...</GENERATEDCODE>,而真正消费这一协议的解析逻辑在 chat/utils.tsreadResponseStream 中:

  1. 尽早落盘首行:请求开始后立即写入 <<<<<<< ${edit.id} 头行,使编辑器能第一时间展示编辑状态与 CodeLens;
  2. 增量缓冲解析:每次收到流式 delta 都追加到 edit.buffer,用 findOpenTag 从缓冲中查找 <GENERATEDCODE> 起始标签,进入 document 状态后再用 createCloseTagMatcher 匹配闭合标签,把标签之间的内容累加为 editedText,标签之外的内容累加为 comments
  3. 增量预览:每当缓冲中出现换行,就调用 generateChangesPreview 重新生成带 diff 标记的预览行([<] 头、[#] 注释、[.] 等待、[|] 进行中、[=] 未变、[+] 新增、[-] 删除、[>] 尾),并通过 ApplyWorkspaceEditRequest 写入编辑器;
  4. 收尾:流结束后写入 >>>>>>> ${edit.id} {{markers}} 尾行,并把编辑状态置为 completed;若流被中断或用户取消(通过 mutexAbortController 的 abort 信号),状态置为 stopped,此时 diff 中未完成的部分以 [|][=] 呈现。

最终用户在编辑器中看到的就是一段"以 diff 标记行 + 增删行 + 未变行"构成的预览块。用户在预览块中执行 accept 或 discard 时,resolveEdit 会读取 >>>>>>> 头行中的标记序列,按标记字符过滤保留的行并落回文件(见 inlineEdit.ts)。

五、实战要点:自定义与调试 replace 模板

如果你想在自建 Tabby 实例上调整替换行为,可从以下几点入手:

  1. 理解占位符语义:任何自定义模板都必须保留 <GENERATEDCODE></GENERATEDCODE> 输出协议(对应 chat.edit.responseDocumentTag),否则 readResponseStream 无法识别代码正文;同时不要改动 {{documentPrefix}}{{documentSuffix}}{{document}}{{command}} 这几个占位符,它们由 inlineEdit.tsformatPlaceholders 统一注入。
  2. 调整上下文预算documentMaxCharsfileContext.maxFilesfileContext.maxCharsPerFile 共同决定单次请求的上下文规模;在 config/default.ts 中修改后需重启 tabby-agent 生效。
  3. 新增预设命令:在 chat.edit.presetCommands 中注册新的 /xxx 命令(labelfilterskind: "replace" | "insert"promptTemplate),命令会通过 tabby/chatEditCommand 自动出现在编辑器的命令选择器中。
  4. 验证链路:调试时可在 tabby-agent 日志中观察 provideEdit 打印的 messages(见 inlineEdit.ts),确认模板填充后的完整 prompt;输出解析问题则应关注 chat/utils.ts 的标签匹配逻辑与 chat.edit.responseDocumentTag 配置。

六、小结

edit-command-replace.md 虽然只是一份约 25 行的提示词模板,但它串联起 Tabby 内联编辑的完整链路:默认配置注册(config/default.ts)→ 占位符注入与模式选择(inlineEdit.ts)→ XML 标签流式解析(chat/utils.ts)→ 编辑器侧命令选择与结果落盘(ChatFeature.tsquickPick.ts)。理解这一定义良好的结构化输出协议,是深入定制 Tabby 代码编辑体验、排查流式解析问题的基础。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.89 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
602
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
526