pdca
Version:
🎯 AI 驅動的 PDCA 多代理開發系統 - 智能循環控制 + 成本管理 + Token 優化 + 多 AI 引擎支援
439 lines • 15.6 kB
JavaScript
/**
* 增強型 PDCA 協調器
* 整合配置系統、循環控制器和成本管理
*/
import { EventEmitter } from 'events';
import { mkdirSync, existsSync } from 'fs';
import * as readline from 'readline';
import { AIEngineManager } from './ai-engine-adapter.js';
import { CommunicationManager } from './communication-manager.js';
import { AdvancedConfigLoader } from './advanced-config-loader.js';
import { LoopController } from './loop-controller.js';
export class EnhancedOrchestrator extends EventEmitter {
config;
configLoader;
loopController;
engineManager;
selectedEngine;
communicationManager;
sessionName;
communicationDir;
activeAgents = new Map();
rl;
constructor(options = {}) {
super();
this.sessionName = options.sessionName || 'pdca';
this.communicationDir = options.communicationDir || '.raiy-pdca/communication';
// 初始化組件
this.configLoader = new AdvancedConfigLoader();
this.engineManager = new AIEngineManager();
this.communicationManager = new CommunicationManager({
baseDir: this.communicationDir
});
// 設定 readline 介面
this.setupReadlineInterface();
}
/**
* 初始化系統
*/
async initialize(configOptions = {}) {
console.log('🚀 初始化 PDCA 增強型系統...\n');
try {
// 1. 載入配置
this.config = await this.configLoader.loadRuntimeConfig(configOptions);
// 2. 顯示配置摘要
this.configLoader.displayConfigSummary(this.config);
// 3. 初始化循環控制器
this.initializeLoopController();
// 4. 選擇 AI 引擎
await this.initializeAIEngine(configOptions.cliOverrides?.engine);
// 5. 設置目錄
this.setupDirectories();
console.log('\n✅ 系統初始化完成!\n');
}
catch (error) {
console.error('❌ 系統初始化失敗:', error);
throw error;
}
}
/**
* 執行 PDCA 任務
*/
async executeTask(taskDescription) {
const taskContext = {
taskDescription,
sessionId: this.config.sessionId,
startTime: new Date(),
workingDirectory: this.config.workingDirectory
};
console.log(`📋 開始執行任務: ${taskDescription}\n`);
try {
// 重置循環控制器
this.loopController?.reset();
let iterationNumber = 1;
let continueLoop = true;
while (continueLoop) {
console.log(`\n🔄 第 ${iterationNumber} 輪 PDCA 循環`);
console.log('═'.repeat(60));
// 執行 PDCA 循環
const iterationResult = await this.executePDCACycle(taskContext, iterationNumber);
// 檢查是否繼續
const decision = await this.loopController.shouldContinue(iterationResult);
if (!decision.continue) {
console.log(`\n🛑 停止迭代: ${decision.suggestion || decision.reason}`);
break;
}
if (decision.suggestion) {
console.log(`\n💡 ${decision.suggestion}`);
}
iterationNumber++;
}
// 顯示最終統計
this.displayFinalStatistics();
}
catch (error) {
console.error('❌ 任務執行失敗:', error);
throw error;
}
}
/**
* 執行單次 PDCA 循環
*/
async executePDCACycle(context, iterationNumber) {
const startTime = Date.now();
const agentResults = {};
let totalTokens = 0;
let overallQuality = 0;
// 決定要執行的代理
const agentsToRun = this.determineAgentsToRun(context, iterationNumber);
console.log(`🎯 執行代理: ${agentsToRun.join(', ')}`);
// 順序執行代理
for (const agentName of agentsToRun) {
console.log(`\n ⚡ 執行 ${agentName} 代理...`);
const result = await this.executeAgent(agentName, context, agentResults);
agentResults[agentName] = result;
totalTokens += result.tokensUsed;
if (result.success) {
console.log(` ✅ ${agentName} 完成 (品質: ${(result.qualityScore * 100).toFixed(0)}%, tokens: ${result.tokensUsed})`);
}
else {
console.log(` ❌ ${agentName} 失敗: ${result.errors?.join(', ')}`);
}
}
// 計算整體品質
const successfulAgents = Object.values(agentResults).filter(r => r.success);
overallQuality = successfulAgents.length > 0
? successfulAgents.reduce((sum, r) => sum + r.qualityScore, 0) / successfulAgents.length
: 0;
// 計算改進幅度
const improvement = iterationNumber > 1 ? this.calculateImprovement(overallQuality) : 0;
const metrics = {
iterationNumber,
qualityScore: overallQuality,
tokensUsed: totalTokens,
timeElapsed: Date.now() - startTime,
improvement,
agentResults
};
return metrics;
}
/**
* 決定要執行的代理
*/
determineAgentsToRun(context, iterationNumber) {
const { execution } = this.config;
const maxAgents = execution.max_agents;
// 第一輪通常執行完整流程
if (iterationNumber === 1) {
if (maxAgents >= 4) {
return ['plan', 'do', 'check', 'act'];
}
else if (maxAgents >= 2) {
return ['do', 'check'];
}
else {
return ['do'];
}
}
// 後續輪次根據配置和需求決定
if (maxAgents >= 5) {
return ['plan', 'do', 'check', 'act', 'knowledge'];
}
else if (maxAgents >= 3) {
return ['do', 'check', 'act'];
}
else {
return ['check', 'act'];
}
}
/**
* 執行單個代理
*/
async executeAgent(agentName, context, previousResults) {
const startTime = Date.now();
try {
// 獲取代理配置
const agentConfig = this.getAgentConfig(agentName);
// 建構 prompt
const prompt = this.buildAgentPrompt(agentName, context, previousResults, agentConfig);
// 執行 AI 引擎
const output = await this.executeAIEngine(prompt);
// 評估結果品質
const qualityScore = this.evaluateOutputQuality(output, agentName);
// 估算 token 使用量
const tokensUsed = this.estimateTokenUsage(prompt, output);
return {
agent: agentName,
success: true,
output,
tokensUsed,
qualityScore,
executionTime: Date.now() - startTime
};
}
catch (error) {
return {
agent: agentName,
success: false,
output: '',
tokensUsed: 0,
qualityScore: 0,
executionTime: Date.now() - startTime,
errors: [error instanceof Error ? error.message : String(error)]
};
}
}
/**
* 初始化循環控制器
*/
initializeLoopController() {
const { loop_control, cost_control } = this.config.execution;
this.loopController = new LoopController(loop_control, cost_control, cost_control.pricing_model);
// 監聽事件
this.loopController.on('cost-warning', (warning) => {
console.log(`\n⚠️ 成本警告: 已使用 ${warning.usedPercent.toFixed(0)}% 預算`);
console.log(` Token: ${warning.tokensUsed.toLocaleString()} / ${warning.budget.toLocaleString()}`);
if (warning.estimatedCost > 0) {
console.log(` 預估成本: $${warning.estimatedCost.toFixed(4)}`);
}
});
this.loopController.on('user-decision-required', async (data) => {
const decision = await this.promptUserDecision(data.metrics, data.recommendation);
data.resolve(decision);
});
}
/**
* 初始化 AI 引擎
*/
async initializeAIEngine(engineName) {
if (engineName) {
this.selectedEngine = await this.engineManager.selectEngineByName(engineName);
}
else {
this.selectedEngine = await this.engineManager.selectBestEngine();
}
}
/**
* 獲取代理配置
*/
getAgentConfig(agentName) {
const agentConfig = this.config.agents[agentName];
if (!agentConfig) {
throw new Error(`找不到代理配置: ${agentName}`);
}
return agentConfig;
}
/**
* 建構代理 prompt
*/
buildAgentPrompt(agentName, context, previousResults, agentConfig) {
let prompt = agentConfig.prompts.initial + '\n\n';
// 添加任務上下文
prompt += `任務描述: ${context.taskDescription}\n\n`;
// 添加前面代理的結果
if (Object.keys(previousResults).length > 0) {
prompt += '前面代理的執行結果:\n';
for (const [agent, result] of Object.entries(previousResults)) {
if (result.success) {
prompt += `${agent}: ${result.output.substring(0, 500)}...\n`;
}
}
prompt += '\n';
}
// 使用任務特定的 prompt
if (agentConfig.prompts.mission) {
prompt += agentConfig.prompts.mission.replace('{{mission}}', context.taskDescription);
}
return prompt;
}
/**
* 執行 AI 引擎
*/
async executeAIEngine(prompt) {
if (!this.selectedEngine) {
throw new Error('AI 引擎未初始化');
}
if (this.selectedEngine.promptFlag) {
// 支援直接執行的引擎(如 Gemini)
return await this.selectedEngine.executePrompt(prompt);
}
else {
// 互動模式引擎(如 Claude)
throw new Error('互動模式引擎需要特殊處理');
}
}
/**
* 評估輸出品質
*/
evaluateOutputQuality(output, agentName) {
// 簡單的品質評估邏輯
let score = 0.5; // 基礎分數
// 長度檢查
if (output.length > 100)
score += 0.1;
if (output.length > 500)
score += 0.1;
// 結構檢查
if (output.includes('\n'))
score += 0.1; // 有換行
if (output.match(/\d+\./))
score += 0.1; // 有編號列表
if (output.includes('```'))
score += 0.1; // 有程式碼
// 代理特定檢查
switch (agentName) {
case 'plan':
if (output.includes('步驟') || output.includes('計畫'))
score += 0.1;
break;
case 'do':
if (output.includes('實作') || output.includes('程式碼'))
score += 0.1;
break;
case 'check':
if (output.includes('測試') || output.includes('檢查'))
score += 0.1;
break;
case 'act':
if (output.includes('優化') || output.includes('改善'))
score += 0.1;
break;
}
return Math.min(1.0, score);
}
/**
* 估算 token 使用量
*/
estimateTokenUsage(prompt, output) {
// 簡單的 token 估算(大約 4 字元 = 1 token)
return Math.ceil((prompt.length + output.length) / 4);
}
/**
* 計算改進幅度
*/
calculateImprovement(currentQuality) {
// 這裡需要與上一輪的品質比較
// 暫時返回隨機值,實際需要從歷史數據計算
return Math.random() * 0.1; // 0-10% 改進
}
/**
* 詢問用戶決策
*/
async promptUserDecision(metrics, recommendation) {
console.log('\n🤔 需要您的決策');
console.log('─'.repeat(50));
console.log(`💡 建議: ${recommendation}`);
console.log('─'.repeat(50));
const answer = await this.askQuestion('是否繼續下一輪迭代? [y/N]: ', 'n');
const shouldContinue = answer.toLowerCase() === 'y';
return {
continue: shouldContinue,
reason: shouldContinue ? 'user_approved' : 'user_declined',
confidence: 1.0,
suggestion: shouldContinue ? '用戶選擇繼續' : '用戶選擇停止'
};
}
/**
* 詢問用戶問題
*/
async askQuestion(question, defaultAnswer) {
return new Promise((resolve) => {
this.rl.question(question, (answer) => {
resolve(answer.trim() || defaultAnswer || '');
});
});
}
/**
* 設置 readline 介面
*/
setupReadlineInterface() {
this.rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
}
/**
* 設置必要目錄
*/
setupDirectories() {
const dirs = [
'.raiy-pdca',
'.raiy-pdca/communication',
'.raiy-pdca/scripts',
'.raiy-pdca/logs',
'.raiy-pdca/agents'
];
dirs.forEach(dir => {
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
});
}
/**
* 顯示最終統計
*/
displayFinalStatistics() {
if (!this.loopController)
return;
const stats = this.loopController.getStatistics();
console.log('\n📊 最終統計');
console.log('═'.repeat(60));
console.log(`🔢 總迭代次數: ${stats.totalIterations}`);
console.log(`💰 總 Token 使用: ${stats.totalTokens.toLocaleString()}`);
console.log(`💸 總成本: $${stats.totalCost.toFixed(4)}`);
console.log(`📈 平均品質: ${(stats.averageQuality * 100).toFixed(1)}%`);
console.log(`⏱️ 總時間: ${(stats.totalTime / 1000 / 60).toFixed(1)} 分鐘`);
console.log(`⚡ 效率: ${(stats.efficiency * 1000).toFixed(2)} 品質/千tokens`);
console.log('═'.repeat(60));
}
/**
* 停止系統
*/
async stop() {
console.log('\n🛑 停止系統...');
// 關閉所有活動代理
for (const [name, process] of this.activeAgents) {
console.log(`停止代理: ${name}`);
process.kill();
}
this.activeAgents.clear();
// 關閉 readline
if (this.rl) {
this.rl.close();
}
console.log('✅ 系統已停止');
}
/**
* 獲取系統狀態
*/
getStatus() {
return {
sessionId: this.config?.sessionId,
engine: this.selectedEngine?.name || '未選擇',
session: this.sessionName,
activeAgents: Array.from(this.activeAgents.keys()),
statistics: this.loopController?.getStatistics()
};
}
}
//# sourceMappingURL=enhanced-orchestrator.js.map