pdca
Version:
🎯 AI 驅動的 PDCA 多代理開發系統 - 智能循環控制 + 成本管理 + Token 優化 + 多 AI 引擎支援
362 lines • 13.9 kB
JavaScript
/**
* PDCA 循環控制器
* 解決無限循環問題,提供智能停止機制
*/
import { EventEmitter } from 'events';
export class LoopController extends EventEmitter {
loopConfig;
costConfig;
iterations = [];
costTracker;
startTime;
currentIteration = 0;
constructor(loopConfig, costConfig, pricingModel) {
super();
this.loopConfig = loopConfig;
this.costConfig = costConfig;
this.startTime = new Date();
this.costTracker = {
totalTokens: 0,
totalCost: 0,
tokensByAgent: {},
costByAgent: {},
currency: costConfig.currency
};
// 設定定價模型
if (pricingModel && costConfig.show_realtime) {
this.setupPricingModel(pricingModel);
}
}
/**
* 檢查是否應該繼續下一輪迭代
*/
async shouldContinue(metrics) {
this.iterations.push(metrics);
this.currentIteration = metrics.iterationNumber;
// 更新成本追蹤
this.updateCostTracking(metrics);
// 顯示即時狀態
if (this.costConfig.show_realtime) {
this.displayRealTimeStatus(metrics);
}
// 1. 強制停止檢查
const forceStop = this.checkForceStop(metrics);
if (forceStop.continue === false) {
return forceStop;
}
// 2. 品質達標檢查
const qualityCheck = this.checkQualityTarget(metrics);
if (qualityCheck.continue === false) {
return qualityCheck;
}
// 3. 邊際效益檢查
const marginalCheck = this.checkMarginalImprovement(metrics);
if (marginalCheck.continue === false) {
return marginalCheck;
}
// 4. 自動繼續檢查
if (this.loopConfig.auto_continue) {
return {
continue: true,
reason: 'auto_continue_enabled',
confidence: 0.8,
suggestion: '自動繼續下一輪迭代'
};
}
// 5. 用戶決策
if (this.loopConfig.require_confirmation) {
return await this.askUserDecision(metrics);
}
// 預設繼續
return {
continue: true,
reason: 'default_continue',
confidence: 0.6,
suggestion: '條件允許,建議繼續'
};
}
/**
* 強制停止檢查
*/
checkForceStop(metrics) {
// 檢查最大迭代次數
if (this.loopConfig.max_iterations !== null &&
metrics.iterationNumber >= this.loopConfig.max_iterations) {
return {
continue: false,
reason: 'max_iterations_reached',
confidence: 1.0,
suggestion: `已達最大迭代次數 ${this.loopConfig.max_iterations}`
};
}
// 檢查 Token 預算
if (this.loopConfig.token_budget !== null &&
this.costTracker.totalTokens >= this.loopConfig.token_budget) {
return {
continue: false,
reason: 'token_budget_exceeded',
confidence: 1.0,
suggestion: `已超過 Token 預算 ${this.loopConfig.token_budget.toLocaleString()}`
};
}
// 檢查硬停止限制
if (this.costConfig.hard_stop_at_tokens !== null &&
this.costTracker.totalTokens >= this.costConfig.hard_stop_at_tokens) {
return {
continue: false,
reason: 'hard_stop_reached',
confidence: 1.0,
suggestion: `已達硬停止限制 ${this.costConfig.hard_stop_at_tokens.toLocaleString()} tokens`
};
}
// 檢查時間預算
if (this.loopConfig.time_budget_minutes !== null) {
const elapsed = (Date.now() - this.startTime.getTime()) / (1000 * 60);
if (elapsed >= this.loopConfig.time_budget_minutes) {
return {
continue: false,
reason: 'time_budget_exceeded',
confidence: 1.0,
suggestion: `已超過時間預算 ${this.loopConfig.time_budget_minutes} 分鐘`
};
}
}
return { continue: true, reason: 'no_force_stop', confidence: 1.0 };
}
/**
* 品質達標檢查
*/
checkQualityTarget(metrics) {
if (metrics.qualityScore >= this.loopConfig.quality_target) {
return {
continue: false,
reason: 'quality_target_achieved',
confidence: 0.9,
suggestion: `品質已達標 ${(metrics.qualityScore * 100).toFixed(1)}% >= ${(this.loopConfig.quality_target * 100).toFixed(0)}%`
};
}
return { continue: true, reason: 'quality_below_target', confidence: 0.8 };
}
/**
* 邊際效益檢查
*/
checkMarginalImprovement(metrics) {
if (this.iterations.length < 2) {
return { continue: true, reason: 'insufficient_data', confidence: 0.7 };
}
const previousMetrics = this.iterations[this.iterations.length - 2];
const improvement = metrics.qualityScore - previousMetrics.qualityScore;
if (improvement < this.loopConfig.marginal_threshold) {
const improvementPercent = (improvement * 100).toFixed(1);
const thresholdPercent = (this.loopConfig.marginal_threshold * 100).toFixed(1);
return {
continue: false,
reason: 'diminishing_returns',
confidence: 0.85,
suggestion: `改進幅度過小 ${improvementPercent}% < ${thresholdPercent}%,邊際效益遞減`
};
}
return { continue: true, reason: 'significant_improvement', confidence: 0.8 };
}
/**
* 詢問用戶決策
*/
async askUserDecision(metrics) {
// 顯示當前狀態摘要
this.displayIterationSummary(metrics);
// 生成建議
const recommendation = this.generateRecommendation(metrics);
// 發出事件讓 CLI 處理用戶輸入
return new Promise((resolve) => {
this.emit('user-decision-required', {
metrics,
recommendation,
resolve
});
});
}
/**
* 生成建議
*/
generateRecommendation(metrics) {
const suggestions = [];
// 品質分析
const qualityGap = this.loopConfig.quality_target - metrics.qualityScore;
if (qualityGap > 0.1) {
suggestions.push(`品質還有 ${(qualityGap * 100).toFixed(1)}% 改進空間`);
}
else if (qualityGap > 0) {
suggestions.push(`品質接近目標,還需 ${(qualityGap * 100).toFixed(1)}% 提升`);
}
// 成本分析
if (this.loopConfig.token_budget) {
const remaining = this.loopConfig.token_budget - this.costTracker.totalTokens;
const remainingPercent = (remaining / this.loopConfig.token_budget) * 100;
if (remainingPercent < 20) {
suggestions.push(`預算剩餘 ${remainingPercent.toFixed(0)}%,建議謹慎繼續`);
}
}
// 改進趨勢分析
if (this.iterations.length >= 2) {
const recentImprovement = this.calculateRecentImprovement();
if (recentImprovement > this.loopConfig.marginal_threshold * 2) {
suggestions.push('改進趨勢良好,建議繼續');
}
else {
suggestions.push('改進趨勢放緩,考慮結束');
}
}
return suggestions.length > 0 ? suggestions.join(';') : '建議根據實際需求決定';
}
/**
* 更新成本追蹤
*/
updateCostTracking(metrics) {
this.costTracker.totalTokens += metrics.tokensUsed;
// 按代理追蹤
if (this.costConfig.track_by_agent && metrics.agentResults) {
for (const [agent, result] of Object.entries(metrics.agentResults)) {
const tokens = result.tokensUsed || 0;
this.costTracker.tokensByAgent[agent] =
(this.costTracker.tokensByAgent[agent] || 0) + tokens;
}
}
// 計算總成本
this.costTracker.totalCost = this.calculateTotalCost();
// 檢查警告閾值
this.checkCostWarnings();
}
/**
* 檢查成本警告
*/
checkCostWarnings() {
if (this.costConfig.warn_at_percent && this.loopConfig.token_budget) {
const usedPercent = (this.costTracker.totalTokens / this.loopConfig.token_budget) * 100;
if (usedPercent >= this.costConfig.warn_at_percent) {
this.emit('cost-warning', {
usedPercent,
tokensUsed: this.costTracker.totalTokens,
budget: this.loopConfig.token_budget,
estimatedCost: this.costTracker.totalCost
});
}
}
}
/**
* 顯示即時狀態
*/
displayRealTimeStatus(metrics) {
console.log('\n' + '═'.repeat(60));
console.log(`🔄 第 ${metrics.iterationNumber} 輪迭代完成`);
console.log('─'.repeat(60));
console.log(`📊 品質分數: ${(metrics.qualityScore * 100).toFixed(1)}% / ${(this.loopConfig.quality_target * 100).toFixed(0)}%`);
console.log(`💰 Token 使用: ${this.costTracker.totalTokens.toLocaleString()} / ${this.loopConfig.token_budget?.toLocaleString() || '無限制'}`);
if (this.costTracker.totalCost > 0) {
console.log(`💸 預估成本: ${this.costTracker.totalCost.toFixed(4)} ${this.costConfig.currency}`);
}
const elapsed = (Date.now() - this.startTime.getTime()) / (1000 * 60);
console.log(`⏱️ 已用時間: ${elapsed.toFixed(1)} 分鐘`);
if (this.iterations.length >= 2) {
const improvement = this.calculateRecentImprovement();
const trend = improvement > 0 ? '📈' : improvement < 0 ? '📉' : '➡️';
console.log(`${trend} 改進幅度: ${(improvement * 100).toFixed(1)}%`);
}
console.log('═'.repeat(60));
}
/**
* 顯示迭代摘要
*/
displayIterationSummary(metrics) {
console.log('\n🎯 迭代摘要');
console.log('─'.repeat(50));
// 品質進展
if (this.iterations.length >= 2) {
const prev = this.iterations[this.iterations.length - 2];
const improvement = metrics.qualityScore - prev.qualityScore;
const trend = improvement > 0 ? '↗️' : improvement < 0 ? '↘️' : '→';
console.log(`${trend} 品質變化: ${(improvement * 100).toFixed(1)}%`);
}
// 成本效益
const costPerQuality = this.costTracker.totalTokens / metrics.qualityScore;
console.log(`💡 成本效益: ${costPerQuality.toFixed(0)} tokens/品質點`);
// 預計完成情況
if (this.loopConfig.quality_target > metrics.qualityScore) {
const remaining = this.loopConfig.quality_target - metrics.qualityScore;
const avgImprovement = this.calculateAverageImprovement();
if (avgImprovement > 0) {
const estimatedIterations = Math.ceil(remaining / avgImprovement);
console.log(`📈 預計還需 ${estimatedIterations} 輪達到目標`);
}
}
console.log('─'.repeat(50));
}
/**
* 計算最近改進幅度
*/
calculateRecentImprovement() {
if (this.iterations.length < 2)
return 0;
const current = this.iterations[this.iterations.length - 1];
const previous = this.iterations[this.iterations.length - 2];
return current.qualityScore - previous.qualityScore;
}
/**
* 計算平均改進幅度
*/
calculateAverageImprovement() {
if (this.iterations.length < 2)
return 0;
let totalImprovement = 0;
for (let i = 1; i < this.iterations.length; i++) {
totalImprovement += this.iterations[i].qualityScore - this.iterations[i - 1].qualityScore;
}
return totalImprovement / (this.iterations.length - 1);
}
/**
* 計算總成本
*/
calculateTotalCost() {
// 這裡需要實際的定價模型
return this.costTracker.totalTokens * 0.00003; // 預設每 token $0.00003
}
/**
* 設定定價模型
*/
setupPricingModel(pricingModel) {
// 儲存定價模型供後續使用
this.emit('pricing-model-updated', pricingModel);
}
/**
* 重置控制器
*/
reset() {
this.iterations = [];
this.currentIteration = 0;
this.startTime = new Date();
this.costTracker = {
totalTokens: 0,
totalCost: 0,
tokensByAgent: {},
costByAgent: {},
currency: this.costConfig.currency
};
}
/**
* 獲取統計信息
*/
getStatistics() {
return {
totalIterations: this.iterations.length,
totalTokens: this.costTracker.totalTokens,
totalCost: this.costTracker.totalCost,
averageQuality: this.iterations.length > 0
? this.iterations.reduce((sum, iter) => sum + iter.qualityScore, 0) / this.iterations.length
: 0,
totalTime: Date.now() - this.startTime.getTime(),
efficiency: this.costTracker.totalTokens > 0
? (this.iterations[this.iterations.length - 1]?.qualityScore || 0) / this.costTracker.totalTokens
: 0
};
}
}
//# sourceMappingURL=loop-controller.js.map