Tabby tabby-agent 智能应用定位提示词解析:provide-smart-apply-line-range 与 Smart Apply 实现原理

发布时间:2026/9/10 11:06:53
Tabby tabby-agent 智能应用定位提示词解析:provide-smart-apply-line-range 与 Smart Apply 实现原理 Tabby tabby-agent 智能应用定位提示词解析provide-smart-apply-line-range 与 Smart Apply 实现原理【免费下载链接】tabbySelf-hosted AI coding assistant项目地址: https://gitcode.com/GitHub_Trending/tab/tabby导读provide-smart-apply-line-range.md是 tabby-agentTabby 的 LSP 智能体中负责代码落点定位的核心提示词模板当用户要求把一段新代码应用apply到当前文件时模型需要先判断这段代码应该落在文件的哪个位置并返回一个与待插入代码长度最接近的连续代码段行号区间。本文以该提示词为骨架完整拆解其输入格式、输出协议与约束规则并结合smartApply.ts、smartRange.ts、protocol.ts等源码还原它在 Smart Apply 全流程中的真实调用链、解析逻辑与配置方式帮助你理解如何定制这套提示词或在其基础上构建类似的代码落点定位能力。一、提示词在 Smart Apply 流程中的定位在 tabby-agent 中智能应用Smart Apply是一条完整的 LSP 请求链路客户端如 IDE 插件发起tabby/chat/smartApply请求服务端经过范围定位 → 生成编辑 → 流式应用三个阶段完成对文档的修改。provide-smart-apply-line-range.md正是第一阶段范围定位的提示词模板。从 protocol.ts 可以看到该请求的完整定义方法名tabby/chat/smartApply参数SmartApplyParams { location: Location; text: string }返回boolean可能错误ChatFeatureNotAvailableError、ChatEditDocumentTooLongError、ChatEditMutexError请求携带了目标文档的location和待应用的新代码文本text。服务端拿到后在 smartApply.ts 的provideSmartApplyEdit中执行两步定位let applyRange getSmartApplyRange(document, params.text); //if cannot find range, lets use backend LLMs if (!applyRange) { applyRange await provideSmartApplyLineRange(document, params.text, ...); }即先尝试本地模糊匹配快速路径匹配失败才调用 LLM慢速兜底路径。provide-smart-apply-line-range.md就是兜底路径所使用的提示词因此它在整个流程中承担着最后一道范围判定的角色直接决定后续编辑作用于文档的哪一段。二、提示词的角色设定与任务目标提示词开篇即明确模型身份You are an AI assistant specialized in determining the most appropriate location to insert new code into an existing file.它要求模型扮演一个代码插入位置判定专家输入是现有文件内容 待插入代码输出是文件中一个与待插入代码长度最相似的连续代码段的行号区间。任务拆解为三个步骤分析现有代码结构与待插入的新代码找出与待插入代码长度最相似的一段连续现有代码仅返回该相似长度段的行号区间。注意关键词length长度该提示词采用的定位策略并非语义相似度而是长度行数相似度——这与后面源码中的模糊匹配策略在思路上是一致的详见第五节。这种策略的合理性在于Smart Apply 常见的场景是在相同结构处追加并列代码块例如在if (method debug) {...}之后追加一个if (method add) {...}此时与目标代码结构最相似的既有代码段往往就是正确的插入锚点。三、输入格式约定带行号的逐行文档提示词规定了输入侧的严格格式The file content is provided line by line, with each line in the format:line number | code即文档内容必须逐行呈现每行前缀为该行的行号用竖线|分隔代码本身。例如13 | target.trace(tagMessage(message), ...args);。在源码实现中这个格式由 smartApply.ts 的provideSmartApplyLineRange负责构造const documentText document .getText() .split(\n) .map((line, idx) ${idx 1} | ${line}) .join(\n);可以看到它用 1-based 行号idx 1逐行拼装与提示词要求的 one-based (starting from 1) 完全一致。待插入的新代码则通过 XML 标签包裹传入The new code to be inserted is provided inAPPLYCODE/APPLYCODEXML tags.对应的模板占位符为{{applyCode}}文档内容占位符为{{document}}。填充逻辑同样位于provideSmartApplyLineRange中content: promptTemplate.replace(/{{document}}|{{applyCode}}/g, (pattern: string) { switch (pattern) { case {{document}}: return documentText; case {{applyCode}}: return applyCodeBlock; ... } }),四、输出协议GENERATEDCODE 标签与闭区间行号提示词对输出格式的要求极为严格这是本模板最值得注意的工程化设计——结构化输出禁止任何多余解释You must reply with ONLY the suggested range in the formatstartLine-endLine, enclosed inGENERATEDCODE/GENERATEDCODEXML tags. Do not include any explanation, existing code, or the code to be inserted in your response.格式要点输出唯一内容GENERATEDCODEstartLine-endLine/GENERATEDCODE行号为 1-based从 1 开始startLine与endLine均为闭区间inclusive closed interval区间必须覆盖一段与待插入代码长度相似的连续代码段。提示词中给出的输出示例为GENERATEDCODE10-12/GENERATEDCODE对应在 10-12 行发现了一段与 3 行新代码长度相似的代码段。这一输出协议在源码端有精确的解析逻辑与之配套。smartApply.ts 中const regex /GENERATEDCODE(.*?)\/GENERATEDCODE/s; const match response.match(regex); if (match match[1]) { response match[1].trim(); } const range response.split(-); if (range.length ! 2) { return undefined; } const startLine parseInt(range[0] ?? 0, 10) - 1; const endLine parseInt(range[1] ?? 0, 10) - 1; return { range: { start: { line: startLine 0 ? 0 : startLine, character: 0 }, end: { line: endLine 0 ? 0 : endLine, character: Number.MAX_SAFE_INTEGER }, }, action: startLine endLine ? insert : replace, };解析逻辑的几个关键点用正则/s标志匹配GENERATEDCODE标签内的任意内容并提取按-拆分为两段若段数不为 2 则判定失败返回undefined整个 Smart Apply 静默放弃1-based 行号转 0-based- 1并做负数保护 0 ? 0行为判定规则startLine endLine时动作为insert插入否则为replace替换——即单个行号区间代表纯插入跨行区间代表替换该段代码。这个判定与 smartRange.ts 中快速路径的判定逻辑保持了一致的设计哲学。五、示例学习机制EXAMPLE_DOCUMENT 与 EXAMPLE_APPLYCODE提示词中段通过两个 XML 标签引入了少样本示例few-shot examplesEXAMPLE_DOCUMENT/EXAMPLE_DOCUMENTXML tags indicate the example code document.EXAMPLE_APPLYCODEXML tags indicate the example code to be applied.示例内容为一个 TypeScript 的日志方法分发代码段EXAMPLE_DOCUMENT包含 13-25 行带行号的方法分发代码trace/debug/info分支EXAMPLE_APPLYCODE一个 4 行的新分支代码if (method add) { return (message: string, ...args: unknown[]) { target.error(tagMessage(message), ...args); }; }随后提示词给出两条标注答案If a 4-line segment similar to the apply code is found at lines 16-19, return:GENERATEDCODE16-19/GENERATEDCODEIf a 4-line segment similar to the apply code is found at lines 21-24, return:GENERATEDCODE21-24/GENERATEDCODE这两条答案的价值在于同一份新代码可能对应多个合法落点只要返回的区间长度与目标代码匹配且落在现有代码的连续段上都属于可接受答案。它向模型传递了定位结果是候选而非唯一解的松弛语义有效避免模型在存在多个相似段落时陷入选错即错的过度纠结。六、快速路径基于 Levenshtein 距离的本地模糊匹配理解了 LLM 兜底路径后再看快速路径getSmartApplyRangesmartRange.ts你会发现两者在设计上互为表里function fuzzyApplyRange(document: TextDocument, snippet: string): { range: Range; score: number } | null { const lines document.getText().split(\n); const snippetLines snippet.split(\n); let [minDistance, index] [Number.MAX_SAFE_INTEGER, 0]; for (let i 0; i lines.length - snippetLines.length; i) { const window lines.slice(i, i snippetLines.length).join(\n); const distance levenshtein(window, snippet); if (minDistance distance) { minDistance distance; index i; } } ... }实现要点用滑动窗口遍历文档的每一行起点取与待插入代码等长的窗口与目标代码计算Levenshtein 编辑距离距离最小者即长度相同且内容最接近的锚点段返回 0-based 闭区间startLine indexendLine index snippetLines.length - 1若minDistance未更新窗口根本不存在即文档行数少于目标代码行数返回null触发 LLM 兜底。有趣的是快速路径用等长窗口 编辑距离寻找内容相似段而 LLM 提示词寻找长度相似段——两者都是从锚定一段既有代码的角度出发只是相似度度量不同。这也解释了为什么provide-smart-apply-line-range特别强调 similar in length它要复刻快速路径的行为模式只是放开了内容相似的约束允许模型基于对代码结构的语义理解做出更灵活的落点判断。七、两级定位后的编辑生成与流式应用范围定位成功后流程进入第二阶段provideSmartApplyEditLLMsmartApply.ts会使用另一个提示词模板 generate-smart-apply.md占位符{{document}}/{{code}}生成实际编辑内容。这一阶段的关键工程细节文档长度裁剪若文档总长超过config.chat.edit.documentMaxChars会以选中区间为中心对前后缀做对称截断documentPrefix/documentSuffix各保留剩余额度的一半确保送入 LLM 的上下文在预算内smartApply.ts编辑互斥通过mutexAbortController保证同一时刻只有一个 Smart Edit 在途重复触发会抛出ChatEditMutexErrorsmartApply.ts流式应用生成结果经readResponseStream流式回传依据config.chat.edit.responseDocumentTag默认[GENERATEDCODE, /GENERATEDCODE]识别最终代码段config/default.ts并通过workspace/applyEdit写入编辑器区间校正调用编辑阶段时将结束位置从end.line修正为end.line 1行首以容纳整段代码的替换/插入smartApply.ts。八、配置项与自定义方式provide-smart-apply-line-range.md作为提示词模板已内置在 tabby-agent 的默认配置中。相关配置链如下类型定义type.d.ts 声明chat.smartApplyLineRange.promptTemplate: string与chat.smartApply.promptTemplate: string默认值config/default.ts 将chat.smartApplyLineRange.promptTemplate绑定为provideSmartApplyLineRangePrompt即本文剖析的模板chat.smartApply.promptTemplate绑定为generateSmartApplyPrompt模板加载prompts 目录下的.md文件通过 index.d.ts 的模块声明declare module *.md被 TypeScript 作为字符串模块导入。因此若想调整落点定位的行为可以在配置文件中覆盖chat.smartApplyLineRange.promptTemplate替换为自定义提示词。自定义时需要注意维持源码所依赖的接口契约否则会破坏解析输出必须包含GENERATEDCODEstartLine-endLine/GENERATEDCODE形式的唯一响应行号必须 1-based、闭区间必须使用{{document}}与{{applyCode}}占位符接收输入见provideSmartApplyLineRange中的正则替换逻辑smartApply.ts。九、失败模式与容错设计整条链路对模型输出的异常有完善的兜底理解这些边界有助于你在自研类似功能时规避同类坑点失败场景处理位置行为快速路径找不到等长窗口smartRange.ts返回null转入 LLM 兜底LLM 响应中无GENERATEDCODE标签smartApply.ts正则匹配失败match[1]为空最终返回undefined响应格式非法非a-bsmartApply.tssplit(-)长度不为 2返回undefined文档不存在 / LSP 连接断开smartApply.ts直接返回false静默失败Chat 功能不可用无 chat_modelsmartApply.ts抛出ChatFeatureNotAvailableError编辑期间并发冲突smartApply.ts抛出ChatEditMutexError编辑目标超长smartApply.ts抛出ChatEditDocumentTooLongError可见该提示词的设计始终围绕机器可解析、失败可降级展开约束输出格式、提供少样本示例、允许候选多解、配合多层容错这正是生产级 LLM 功能中提示词工程的关键范式。结语provide-smart-apply-line-range.md表面是一份只有 60 余行的提示词实则完整定义了 tabby-agent Smart Apply 的落点定位协议输入侧约定带行号的逐行文档与{{applyCode}}占位符输出侧约定GENERATEDCODE闭区间格式与 insert/replace 语义中间以长度相似段为定位目标并辅以少样本示例松弛候选约束。配合 smartRange.ts 的 Levenshtein 快速路径与 smartApply.ts 的解析、互斥、流式应用逻辑构成了本地优先、LLM 兜底、失败静默的完整工程闭环。若你正在为代码编辑类 AI 功能设计定位锚点提示词本文剖析的这套输入/输出协议与容错设计可直接复用。【免费下载链接】tabbySelf-hosted AI coding assistant项目地址: https://gitcode.com/GitHub_Trending/tab/tabby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考