@@ -871,3 +871,336 @@ agent的数据结构有
871871理解我的设计,你觉得有没有不合理的地方,能否改进?帮我完善他。
872872给出关键代码的实现。
873873
874+ ----------------------------------------
875+
876+
877+ 以下是一个重构多文件工程的vscode插件的相关代码(typescript)
878+
879+ export async function queryCodeReDesign(
880+ cvbContent: string,
881+ userRequest: string,
882+ outputChannel: vscode.OutputChannel,
883+ abortSignal?: AbortSignal
884+ ): Promise<string | null> {
885+ const requestContent = `
886+ 【格式说明】
887+ - CVB 格式说明:${Cvb.getFormatDescription()}
888+ - TCVB 格式说明:${TCVB.getFormatDescription()}
889+
890+ 【任务说明】
891+ 请读取以下 CVB 格式代码,并根据需求修改代码。注意:
892+ 1. 如果需求涉及“移动代码”,请务必修改原始代码,将代码重新封装到新位置,而非简单复制;
893+ 2. 修改后的代码必须完整、可执行,不能有任何省略;
894+ 3. 输出内容必须严格遵守 TCVB 格式(仅正文部分含 TCVB 标记,其他地方如有 TCVB 开始或结束符需转义),以确保后续合并正确;
895+ 4. 注意不要将某文件的修改内容误认为是其他文件,请一条一条列出具体修改项及对应文件路径。
896+
897+ 【输出要求】
898+ 1. 先输出你对需求及相关代码的理解,请按层级缩进列出笔记,便于整理思路;
899+ 2. 再输出详细的方案大纲,格式如下:
900+ 需求理解:
901+ …
902+ 查询资料:
903+ 列出每个关键修改点所在的文件路径
904+ 修改方案:
905+ 文件路径1:
906+ 描述修改点,避免用大块代码,注意只输出关键修改,不要太长, 不要加载无用的上下文。不要输出没有改动部分的代码
907+ 文件路径2:
908+ 描述修改点,同上
909+ …
910+ 最后检查:
911+ 对以上输出的方案大纲进行反思,重新阅读输入代码,结合以上方案大纲,逐条检查有没有和原文对不上的地方。检查方案是否完备、文件路径是否正确,设计思路是否无误,如有问题请提出修正意见
912+ 3. 请确保输出中既包含错误部分的修正说明,又完整保留原有正确部分,不得遗漏任何内容;
913+ 4. 用最小改动实现需求目的。
914+
915+ 【输入部分】
916+ - 输入代码:${cvbContent}
917+ - 需求描述:${userRequest}
918+
919+ 【最终输出】
920+ 请先输出思路与方案大纲,最后汇总输出符合 TCVB 格式的精确代码。
921+ `;
922+
923+ return callDeepSeekApi(requestContent, undefined, outputChannel, true, '## END_TCVB', abortSignal); // 添加结束字符串
924+ }
925+
926+ export class Cvb {
927+ private m_recMetadata: Record<string, string>;
928+ private m_recFiles: Record<string, string>;
929+
930+ constructor(cvbContent?: string) {
931+ this.m_recMetadata = {};
932+ this.m_recFiles = {};
933+ if (cvbContent) {
934+ const { metadata, files } = this.parse(cvbContent);
935+ this.m_recMetadata = metadata;
936+ this.m_recFiles = files;
937+ }
938+ }
939+
940+ public static getFormatDescription(): string {
941+ return `
942+ CVB 格式介绍:
943+ - 文件以 "## BEGIN_CVB" 开头,以 "## END_CVB" 结尾。
944+ - 元数据部分以 "## META" 开头,以 "## END_META" 结尾,包含用户需求和时间戳。
945+ - 每个文件以 "## FILE:文件路径" 开头,紧接着是 Markdown 格式的代码块,包含文件内容。
946+ - 多个文件按顺序拼接在一起。
947+ `;
948+ }
949+ }
950+
951+ export class TCVB {
952+ private m_arrOperations: TcvbOperation[] = [];
953+ public static getFormatDescription(): string {
954+ return `
955+ TCVB 格式规范:
956+
957+ ## BEGIN_TCVB
958+ [文件块1]
959+ [文件块2]
960+ ...
961+ ## END_TCVB
962+
963+ 文件块格式:
964+ ## FILE:<文件绝对路径>
965+ [操作1]
966+ [操作2]
967+ ...
968+
969+ 操作类型:
970+
971+ 1. 全局替换操作(GLOBAL-REPLACE):
972+ ## OPERATION:GLOBAL-REPLACE
973+ ## OLD_CONTENT
974+ [markdown代码块:被全局替换的内容, 可以在需要被替换的文本前后包含一些上下文帮助精确替换,一般是上下各3行。不要太长,不要带太多不必要的上下文,因为输出越长就越可能出错导致匹配不上。总长度不要超过10行,尽量不要大块的替换代码,而是切成很多小块替换。]
975+ ## NEW_CONTENT
976+ [markdown代码块:新内容]
977+
978+ 2. 创建操作(CREATE):
979+ ## OPERATION:CREATE
980+ [markdown代码块:直接跟正文内容,表示新文件的全部内容]
981+
982+ 注意:
983+ 1. 所有OPERATION操作以行为单位
984+ 2. 一个'## FILE'下可以有多个'## OPERATION'
985+ 3. 锚点为连续的多行内容:使用至少3行唯一文本作为锚点,用来标定范围,防止混淆(如果需要可以超过3行)
986+ 4. [markdown代码块], 一定要用\`\`\` ... \`\`\` 包裹,仔细检查不要漏掉。
987+ 5. 注意TCVB和CVB的区别。CVB是完整的内容,而TCVB是用来生成差量同步的,通过多个OPERATION去操作已有CVB合成新CVB
988+ 6. 插入和删除操作都可以转化为替换操作
989+ 7. 用来匹配的锚点必须和原文的格式完全一致,不能有缺失,不能丢弃注释。
990+ 8. 注意不要丢失OPERATION而直接输出代码块
991+ 9. 不要私自加入不必要的空行
992+ 10.如果是在一个已有文件里插入大块代码,不应该用CREATE,而是用替换的方式插入
993+ `;
994+ }
995+ }
996+ }
997+
998+
999+ /**
1000+ * 调用 DeepSeek API
1001+ * @param userContent 用户输入内容,可以是字符串或字符串数组
1002+ * @param systemContent 系统提示内容
1003+ * @param outputChannel 输出通道,用于实时显示流式内容
1004+ * @param streamMode 是否启用流式模式
1005+ * @param endstring 结束字符串,用于检查输出是否包含特定字符串
1006+ * @param abortSignal 用于中断请求的信号
1007+ * @returns API 返回的完整内容
1008+ */
1009+ export async function callDeepSeekApi(
1010+ userContent: string | {role:string, content: string}[], // 修改为支持 string 或 string[]
1011+ systemContent: string = 'You are a helpful assistant.',
1012+ outputChannel?: vscode.OutputChannel,
1013+ streamMode: boolean = true,
1014+ endstring?: string,
1015+ abortSignal?: AbortSignal
1016+ ): Promise<string | null> {
1017+ const { modelName, apiBaseURL, apiKey } = getDeepSeekModelConfig();
1018+ const userStopException = 'operation stop by user';
1019+
1020+ if (!apiKey) {
1021+ vscode.window.showErrorMessage('DeepSeek API Key is not configured. Please set it in the settings.');
1022+ return null;
1023+ }
1024+
1025+ if (!modelName || !apiBaseURL) {
1026+ vscode.window.showErrorMessage('DeepSeek Model Name or API Base URL is not configured.');
1027+ return null;
1028+ }
1029+
1030+ try {
1031+ const openai = new OpenAI({
1032+ apiKey: apiKey,
1033+ baseURL: apiBaseURL,
1034+ });
1035+
1036+ if (outputChannel) {
1037+ outputChannel.clear();
1038+ outputChannel.show();
1039+ }
1040+
1041+ // 构造消息体
1042+ let messages_body: OpenAI.ChatCompletionMessageParam[] = [];
1043+ if (Array.isArray(userContent)) {
1044+ messages_body.push({ role: 'system', content: systemContent });
1045+ // 如果 userContent 是数组,按交替方式生成消息
1046+ for (let i = 0; i < userContent.length; i++) {
1047+ const role = (userContent[i].role === 'user') ? 'user' : 'assistant';
1048+ messages_body.push({ role, content: userContent[i].content });
1049+ }
1050+ } else {
1051+ // 如果是单个字符串,默认是 'user' 角色
1052+ messages_body = [
1053+ { role: 'system', content: systemContent },
1054+ { role: 'user', content: userContent },
1055+ ];
1056+ }
1057+
1058+ let fullResponse = '';
1059+ let maxAttempts = 5;
1060+ let attempts = 0;
1061+
1062+ vscode.window.showInformationMessage('开始上传DeepSeek API');
1063+
1064+ while (attempts < maxAttempts) {
1065+ attempts++;
1066+ const response = await openai.chat.completions.create({
1067+ model: modelName,
1068+ messages: messages_body,
1069+ stream: streamMode,
1070+ max_tokens: 8192,
1071+ temperature: 0
1072+ });
1073+ let thinking = false;
1074+
1075+ vscode.window.showInformationMessage('DeepSeek API 正在处理...');
1076+
1077+ let chunkResponse = '';
1078+ let finishReason: string | null = null;
1079+
1080+ if (streamMode) {
1081+ for await (const chunk of response as AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>) {
1082+ if (abortSignal?.aborted) {
1083+ throw new Error(userStopException);
1084+ }
1085+ const content = chunk.choices[0]?.delta?.content || '';
1086+ const delta = chunk.choices[0]?.delta;
1087+ const think = ('reasoning_content' in delta! && delta.reasoning_content) as string || "";
1088+
1089+ if (!thinking && chunkResponse.length === 0 && think.length > 0){
1090+ if (outputChannel) {
1091+ outputChannel.append("<think>");
1092+ }
1093+ thinking = true;
1094+ }
1095+
1096+ chunkResponse += content;
1097+ if (outputChannel) {
1098+ outputChannel.append(content + think);
1099+ }
1100+
1101+ if (thinking && content.length > 0){
1102+ thinking = false;
1103+ if (outputChannel) {
1104+ outputChannel.append("</think>");
1105+ }
1106+ }
1107+
1108+ finishReason = chunk.choices[0]?.finish_reason || null;
1109+ }
1110+ } else {
1111+ const completion = response as OpenAI.Chat.Completions.ChatCompletion;
1112+ chunkResponse = completion.choices[0].message.content || "";
1113+ finishReason = completion.choices[0].finish_reason || null;
1114+ if (outputChannel) {
1115+ outputChannel.append(chunkResponse);
1116+ }
1117+ }
1118+
1119+ // 累积完整响应
1120+ fullResponse += chunkResponse;
1121+
1122+ // 检查终止条件
1123+ const shouldContinue =
1124+ finishReason === 'length' ||
1125+ (endstring && !fullResponse.includes(endstring));
1126+
1127+ if (!shouldContinue) {break;};
1128+
1129+ if (abortSignal?.aborted) {
1130+ throw new Error(userStopException);
1131+ }
1132+
1133+ vscode.window.showWarningMessage('超过最大Token数,正在重试...');
1134+
1135+ // 准备下一次请求
1136+ messages_body.push(
1137+ { role: 'assistant', content: fullResponse },
1138+ { role: 'user', content: '你的输出被截断了,请继续输出剩余部分, 不需要```做起始,直接继续输出纯内容:' }
1139+ );
1140+ }
1141+
1142+ // 最终检查
1143+ if (endstring && !fullResponse.includes(endstring)) {
1144+ vscode.window.showWarningMessage('响应未包含结束标记');
1145+ }
1146+
1147+ messages_body.push({ role: 'assistant', content: fullResponse });
1148+ lastMessageBody = messages_body;
1149+ return fullResponse;
1150+
1151+ } catch (error) {
1152+ if (error instanceof Error && error.message === userStopException) {
1153+ vscode.window.showInformationMessage('operation stop by user');
1154+ return null;
1155+ }
1156+ vscode.window.showErrorMessage('API调用失败: ' + (error as Error).message);
1157+ return null;
1158+ }
1159+ }
1160+
1161+ 我需要你设计一种通用的agent,来把上面的功能兼容到长上下文
1162+ 也就是把调用api改为调用agent
1163+
1164+ agent的大致数据结构和设计思路如下:
1165+
1166+ 我希望设计一个agent框架处理这个问题
1167+ 把多个文件、长上下文的代码块切成很多块,逐个调用agent
1168+
1169+ agent的数据结构有
1170+ 核心工作提示词,指导工作的方式
1171+ 目标(用户输入的最终目标)
1172+ 互通信息记忆(和目标有关的一些记忆,用来在多轮对话中长期存储的记忆,比如某个函数的潜规则)
1173+ 笔记记忆(用来做长期记忆的笔记,用缩进表示层级结构)
1174+ 问题列表(从当前输入和互动记忆、笔记记忆中都找不到线索的遗留问题,留待后续输入中寻找线索的问题列表,并标注是否已经被解决)
1175+ 任务列表(列出为了达到目标设计的子任务清单,按执行顺序排序,其实就是一个调用栈,允许有层级结构,也就是某个任务下面可以拆成子任务,每个任务后面都有一个括号,里面标注是否已完成)
1176+ 当前agent正在执行的子任务
1177+ 输入数据:可能是代码块,如果输入完毕,会输入<InputFinish>
1178+
1179+ 输出:
1180+ 如果当前任务有子任务,就唤起一个新agent,设置他的子任务。直到所有子任务执行完,他才汇总返回
1181+ 对以上每一个数据的修改,要求结构化输出,方便处理
1182+ 比如根据新输入的块,修改记忆,问题列表添加新问题,或者标记问题已解答
1183+ 还可以增删任务列表,修改任务列表的状态(是否已解决)
1184+ 不要提出重复问题等
1185+ 根据当前子任务,返回应该返回的值
1186+
1187+ 可以看出,他是一个树状搜索的控制流
1188+ 对于长上下文,他如果想通读一遍,他可以每次唤起一个子任务,读取下一个分块,而这是个tool,可以自动给下一个唤起的子任务装填下一个代码分块
1189+
1190+ 理解我的设计,你觉得有没有不合理的地方,能否改进?帮我完善他。
1191+ 给出关键代码的实现。
1192+
1193+ 一些建议:
1194+ 把CVB进行扩展 -> ECVB (Extend CVB)
1195+ 把每个文件再加入切页,明确每个分页有一个压缩信息
1196+ 每个文件有一个总的压缩信息
1197+ 再引入一个Group,把相似功能的文件归类到一起,给一个总结(比如同名的头文件和cpp,或者一个子系统拆出来的MVC几个文件,其实是同一个功能的)
1198+ Group里有文件列表和压缩信息
1199+ 压缩信息分两种
1200+ 1.摘要,列出所有的类、变量、函数,以及他们的含义和作用分析
1201+ 2.缩句,类似摘要,但是是代码连续的,可以让大模型帮忙生成,是一种缩句,把不重要的部分略去
1202+ 压缩信息是为了方便大模型能用小窗口加载
1203+
1204+ ECVB提供接口设置和获取压缩信息,以及对应的metadata(文件路径和页码,或者对应的group等)
1205+
1206+ 我想设计的就是一个通用行的agent,他甚至能把别的chain,workflow也消化成自己的
0 commit comments