1. 概述
本文档旨在为代码编辑智能体(AI Agent)设计一套健壮、可靠的文件修改工作流程。该流程的核心是一种专为AI设计的、基于块的diff
格式,它将作为智能体与开发环境之间进行精确代码操作的基础通信协议。
本文将详细规定该diff
格式的规范、驱动其运行的提示词结构、核心工具集定义以及端到端的操作流程。
2. 自定义Diff格式规范
为了确保AI生成的文件修改指令能被准确无误地执行,我们设计了一套基于SEARCH
/REPLACE
的自定义diff
格式。
2.1 语法结构
一个完整的diff
修改单元由三个部分组成,并由明确的分隔符进行标记:
------- SEARCH
[要查找的原始代码块]
=======
[用于替换的新代码块]
+++++++ REPLACE
-
起始符 ( ------- SEARCH
): -
必须独占一行。 -
标记一个搜索块的开始。 -
解析器应使用正则表达式 ^[-]{7,} SEARCH$
进行匹配,以容忍7个以上(含7个)的连字符。 -
分隔符 ( =======
): -
必须独占一行。 -
用于分隔 SEARCH
块和REPLACE
块。 -
解析器应使用正则表达式 ^[=]{7,}$
进行匹配。 -
结束符 ( +++++++ REPLACE
): -
必须独占一行。 -
标记整个修改单元的结束。 -
解析器应使用正则表达式 ^[+]{7,} REPLACE$
进行匹配。
2.2 块内容规则
-
SEARCH
块: -
精确性: 此块中的内容必须是目标文件中待修改代码的逐字逐行精确拷贝,包括所有缩进、空格和换行符。这是保证匹配唯一性的关键。 -
上下文: 必须包含足够的上下文代码,以确保其在目标文件中的匹配是唯一的。例如,仅搜索一行 return true;
是不可靠的,应该包含其所在的整个函数或代码块。 -
空 SEARCH
块: 如果SEARCH
块为空,表示对整个文件进行操作。此时,REPLACE
块的内容将完全覆盖原文件内容。如果REPLACE
块也为空,则表示将文件清空。 -
REPLACE
块: -
内容: 包含将要替换 SEARCH
块内容的新代码。 -
删除操作: 如果 REPLACE
块为空(即=======
和+++++++ REPLACE
之间没有内容),则表示删除SEARCH
块中的所有代码。
3. 提示词结构设计 (Prompt Structure)
为了让LLM能够稳定地生成上述diff
格式并正确使用工具,系统提示词(System Prompt)必须经过精心设计。
3.1 提示词组成
一个完整的提示词由以下部分动态组合而成:
-
角色定义 (Role Definition):
-
开头部分,明确告知LLM其角色。 -
示例: "你是一个专业的AI代码编辑智能体,你的任务是帮助用户修改代码。你必须使用工具来完成任务。" -
核心规则 (Core Rules):
-
强制规定输出格式,特别是 diff
格式的使用。 -
示例: "当需要修改文件时,你必须使用 editTool
工具。editTool
的diffContent
参数必须严格遵守以下SEARCH/REPLACE
格式..." (此处插入2.1中的格式说明和示例)。 -
工具定义 (Tool Definitions):
-
以结构化格式(如JSON Schema)向LLM描述所有可用的工具及其参数。这使得LLM能够理解每个工具的功能和调用方式。 -
上下文注入 (Context Injection):
-
用户指令: 用户的原始请求。 -
文件/代码上下文: 由上下文管理器根据用户 @
提及或自动发现注入的相关文件内容或代码片段。 -
思维链与示例 (Chain of Thought & Few-shot Examples):
-
提供一到两个完整的“提问->思考->工具调用->结果”示例,向LLM展示标准的思考和工作流程。 -
示例: 用户: "请将main.ts中的calculate函数返回值改为string类型。"
AI思考: "好的。首先,我需要读取main.ts的内容以获取calculate函数的精确上下文。然后,我将构建一个editTool调用,使用SEARCH/REPLACE格式来修改函数签名和返回语句。"
AI工具调用: tool_code.readTool(filePath='main.ts')
(系统返回文件内容后)
AI思考: "我已经获取了文件内容。现在我将生成editTool的调用指令。"
AI工具调用: tool_code.editTool(filePath='main.ts', diffContent='...')
4. 核心工具集与调用
智能体的核心能力通过调用预定义的工具来实现。文件编辑操作的核心是editTool
。
4.1 editTool
工具定义
-
功能: 对指定文件应用一个或多个 diff
修改。 -
参数: -
filePath
(string, required): 需要修改的文件的相对路径。 -
diffContent
(string, required): 包含一个或多个diff
修改单元的字符串。
4.2 LLM输出到工具调用的映射
LLM不会直接执行Python代码。它会在其响应中生成一个特定格式的块,系统解析这个块并执行相应的工具函数。
-
LLM输出: <file-edit filePath="src/main.ts">
------- SEARCH
function calculate(): number {
return 42;
}
=======
function calculate(): string {
return "42";
}
+++++++ REPLACE
</file-edit> -
系统解析与调用: -
系统中的“模式二:意图到动作的解析”模块会识别到 <file-edit>
标签。 -
它会提取 filePath
属性值 "src/main.ts"。 -
它会提取标签内的全部内容作为 diffContent
参数。 -
最后在后端执行等效于 editTool(filePath="src/main.ts", diffContent=...)
的调用。
5. 端到端操作流程
以下是一个完整的、理想化的操作流程示例:
-
用户输入: 用户在聊天界面输入:“请将
utils.ts
里的add
函数重命名为sum
”。 -
指令->意图:
-
系统提示词(角色、规则、工具定义)。 -
用户指令:“请将 utils.ts
里的add
函数重命名为sum
”。 -
注入的上下文:“ utils.ts
的完整内容是:...add
函数的定义是:export function add(a: number, b: number): number { return a + b; }
”。 -
上下文管理器启动,识别到文件名 utils.ts
和函数名add
。 -
系统调用内部的 readTool
和代码解析工具(如Tree-sitter)来获取utils.ts
中add
函数的完整代码及其精确上下文。 -
提示词生成器将以下内容组合成一个完整的提示词: -
LLM处理: LLM接收到提示词,进行推理。
-
意图->动作:
-
LLM生成响应,其中包含一个 <file-edit>
块:<chat>好的,我将为您把 `add` 函数重命名为 `sum`。</chat>
<file-edit filePath="src/utils.ts">
------- SEARCH
export function add(a: number, b: number): number {
return a + b;
}
=======
export function sum(a: number, b: number): number {
return a + b;
}
+++++++ REPLACE
</file-edit> -
动作->执行:
-
尝试精确匹配,成功。 -
解析器捕获 <file-edit>
块,生成一个结构化的“编辑动作”对象。 -
执行引擎调用 editTool
,传入filePath
和diffContent
。 -
editTool
的执行器读取src/utils.ts
的最新内容。 -
它在文件中查找 SEARCH
块的内容。 -
级联回退匹配启动: -
找到匹配项后,执行引擎用 REPLACE
块的内容替换该部分。 -
修改后的新文件内容被写回磁盘。 -
反馈: 系统向用户界面报告:“文件
src/utils.ts
已成功修改。”
至此,一个从用户指令到文件修改的完整、可靠的操作闭环完成。
原文始发于微信公众号(先进攻防):编程智能体代码修改工作流程分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论