UNPKG

openai-compatible-task-master

Version:

使用MCP解析PRD文档并生成任务列表

135 lines (129 loc) 6.14 kB
import { callOpenAIAPI, cleanJsonContent } from './llm_utils.js'; import chalk from 'chalk'; // 解析子任务JSON响应 export function parseSubTasksResponse(jsonContent, parentTask) { const cleanedContent = cleanJsonContent(jsonContent); try { // 尝试将内容解析为JSON const data = JSON.parse(cleanedContent); if (!data.subTasks || !Array.isArray(data.subTasks)) { throw new Error('JSON响应格式不正确,无法找到子任务列表'); } // 验证和处理子任务数据 const subTasks = data.subTasks.map((task, index) => { // 验证必要字段 if (!task.title || typeof task.title !== 'string') { throw new Error(`子任务响应格式不正确: 第${index + 1}个子任务缺少title字符串`); } if (!task.description || typeof task.description !== 'string') { throw new Error(`子任务响应格式不正确: 第${index + 1}个子任务缺少description字符串`); } if (!task.details || typeof task.details !== 'string') { throw new Error(`子任务响应格式不正确: 第${index + 1}个子任务缺少details字符串`); } if (!task.testStrategy || typeof task.testStrategy !== 'string') { throw new Error(`子任务响应格式不正确: 第${index + 1}个子任务缺少testStrategy字符串`); } // 验证优先级 if (!['high', 'medium', 'low'].includes(String(task.priority).toLowerCase())) { console.warn(chalk.yellow(`子任务 ${index + 1} 的优先级 "${task.priority}" 无效,将默认为 "medium"`)); task.priority = 'medium'; } // 生成子任务ID(复合ID) const subTaskId = `${parentTask.id}.${index + 1}`; // 返回规范化的子任务数据 return { id: subTaskId, title: String(task.title), description: String(task.description), status: 'pending', dependencies: task.dependencies ? task.dependencies.map(dep => `${parentTask.id}.${dep}`) : [], priority: String(task.priority).toLowerCase(), details: String(task.details), testStrategy: String(task.testStrategy), parentId: String(parentTask.id) // 设置父任务ID }; }); return subTasks; } catch (error) { const err = error; console.debug(chalk.blue("原始JSON内容:"), cleanedContent); // 记录清理后的JSON以供调试 throw new Error(`LLM返回的子任务内容无法解析: ${err.message}`); } } // 调用OpenAI Compatible API将任务分解为子任务 export async function breakupTaskWithLLM(parentTask, prompt, openaiUrl, apiKey, model, streamMode, maxRetries = 2) { if (!apiKey) { throw new Error('API密钥未提供,请使用 --api-key 参数提供有效的API密钥'); } // 构建系统提示 const systemPrompt = `你是一个AI助手,负责将软件开发任务分解为更小的子任务。 你将收到一个父任务和一个提示,描述如何将该任务分解为子任务。 你的工作是创建一组结构良好的子任务,这些子任务共同完成父任务的目标。 指南: 1. 每个子任务应该是原子性的,专注于单一职责 2. 子任务应该遵循与父任务相同的数据结构 3. 子任务应该有明确的实现细节和测试策略 4. 子任务应该按照逻辑顺序排列 5. 为每个子任务分配适当的优先级(high/medium/low) 6. 子任务可以有依赖关系,但依赖应该是其他子任务的索引(从1开始) 期望的输出格式: { "subTasks": [ { "title": "子任务标题", "description": "子任务描述", "priority": "high|medium|low", "dependencies": [子任务依赖索引数组,如 ["1", "2"],若无依赖则为空数组 []], "details": "实现细节", "testStrategy": "验证方法" }, ... // 其他子任务 ] } 重要: 你的回复必须仅包含有效的JSON,不要添加任何额外的解释、评论或代码块标记。必须使用简体中文回复。`; // 构建请求体 const messages = [ { role: 'system', content: systemPrompt, }, { role: 'user', content: `这是需要分解的父任务: 任务ID: ${parentTask.id} 标题: ${parentTask.title} 描述: ${parentTask.description} 优先级: ${parentTask.priority} 详细信息: ${parentTask.details} 测试策略: ${parentTask.testStrategy} 分解提示: ${prompt || "请将此任务分解为更小的子任务,每个子任务应该专注于单一职责,并且可以独立完成。"} 请根据上述信息将任务分解为子任务。`, }, ]; // 实现重试机制 let lastError = null; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const jsonContent = await callOpenAIAPI(messages, openaiUrl, apiKey, model, streamMode); return parseSubTasksResponse(jsonContent, parentTask); } catch (error) { lastError = error; console.warn(chalk.yellow(`尝试 ${attempt + 1}/${maxRetries + 1} 失败: ${lastError.message}`)); // 最后一次尝试失败,抛出错误 if (attempt === maxRetries) { throw new Error(`分解任务失败,已重试${maxRetries}次: ${lastError.message}`); } // 等待一段时间后重试 const delay = Math.pow(2, attempt) * 1000; // 指数退避策略 console.info(chalk.blue(`等待 ${delay / 1000} 秒后重试...`)); await new Promise(resolve => setTimeout(resolve, delay)); } } // 这行代码理论上不会执行,但为了类型安全添加 throw new Error(`分解任务失败: ${lastError?.message || '未知错误'}`); } //# sourceMappingURL=llm_breakup_task.js.map