UNPKG

@mcpflow.io/mcp-mcp-reasoner

Version:

为Claude Desktop 实现的基于系统推理的MCP服务器,采用波束搜索和思维评估。

295 lines (294 loc) 12.3 kB
import { v4 as uuidv4 } from 'uuid'; import { CONFIG } from '../../types.js'; import { MonteCarloTreeSearchStrategy } from '../mcts.js'; export class MCTS002AlphaStrategy extends MonteCarloTreeSearchStrategy { constructor(stateManager, numSimulations = CONFIG.numSimulations) { super(stateManager, numSimulations); this.temperature = 1.0; this.explorationRate = Math.sqrt(2); this.learningRate = 0.1; this.noveltyBonus = 0.2; this.simulationCount = numSimulations; this.policyMetrics = this.initializePolicyMetrics(); } initializePolicyMetrics() { return { averagePolicyScore: 0, averageValueEstimate: 0, actionDistribution: {}, explorationStats: { temperature: this.temperature, explorationRate: this.explorationRate, noveltyBonus: this.noveltyBonus }, convergenceMetrics: { policyEntropy: 0, valueStability: 0 } }; } async processThought(request) { // Get base MCTS response const baseResponse = await super.processThought(request); const nodeId = uuidv4(); const parentNode = request.parentId ? await this.getNode(request.parentId) : undefined; const node = { id: nodeId, thought: request.thought, depth: request.thoughtNumber - 1, score: 0, children: [], parentId: request.parentId, isComplete: !request.nextThoughtNeeded, visits: 0, totalReward: 0, untriedActions: [], policyScore: 0, valueEstimate: 0, priorActionProbs: new Map(), actionHistory: parentNode ? [...(parentNode.actionHistory || []), this.extractAction(request.thought)] : [this.extractAction(request.thought)] }; // Initialize node with policy guidance node.score = this.evaluateThought(node, parentNode); node.visits = 1; node.totalReward = node.score; node.policyScore = this.calculatePolicyScore(node, parentNode); node.valueEstimate = this.estimateValue(node); node.noveltyScore = this.calculateNovelty(node); await this.saveNode(node); // Update parent if exists if (parentNode) { parentNode.children.push(node.id); await this.saveNode(parentNode); await this.updatePolicyMetrics(node, parentNode); } // Run policy-guided search if (!node.isComplete) { await this.runPolicyGuidedSearch(node); } // Calculate enhanced path statistics const currentPath = await this.stateManager.getPath(nodeId); const enhancedScore = this.calculatePolicyEnhancedScore(currentPath); return { ...baseResponse, score: enhancedScore, bestScore: Math.max(baseResponse.bestScore || 0, enhancedScore) }; } extractAction(thought) { // Simple action extraction based on first few words return thought.split(/\s+/).slice(0, 3).join('_').toLowerCase(); } calculatePolicyScore(node, parent) { // Combine multiple policy factors const depthFactor = Math.exp(-0.1 * node.depth); const parentAlignment = parent ? this.thoughtCoherence(node.thought, parent.thought) : 1; const noveltyBonus = node.noveltyScore || 0; return (0.4 * depthFactor + 0.4 * parentAlignment + 0.2 * noveltyBonus); } estimateValue(node) { // Combine immediate score with future potential const immediateValue = node.score; const depthPotential = 1 - (node.depth / CONFIG.maxDepth); const noveltyValue = node.noveltyScore || 0; return (0.5 * immediateValue + 0.3 * depthPotential + 0.2 * noveltyValue); } calculateNovelty(node) { // Measure thought novelty based on action history const uniqueActions = new Set(node.actionHistory).size; const historyLength = node.actionHistory?.length || 1; const uniquenessRatio = uniqueActions / historyLength; // Combine with linguistic novelty const complexityScore = (node.thought.match(/[.!?;]|therefore|because|if|then/g) || []).length / 10; return (0.7 * uniquenessRatio + 0.3 * complexityScore); } thoughtCoherence(thought1, thought2) { const words1 = new Set(thought1.toLowerCase().split(/\W+/)); const words2 = new Set(thought2.toLowerCase().split(/\W+/)); const intersection = new Set([...words1].filter(x => words2.has(x))); const union = new Set([...words1, ...words2]); return intersection.size / union.size; } async runPolicyGuidedSearch(node) { for (let i = 0; i < this.simulationCount; i++) { const selectedNode = await this.selectWithPUCT(node); const expandedNode = await this.expandWithPolicy(selectedNode); const reward = await this.simulateWithValueGuidance(expandedNode); await this.backpropagateWithPolicyUpdate(expandedNode, reward); // Adapt exploration rate this.adaptExplorationRate(expandedNode); } } async selectWithPUCT(root) { let node = root; while (node.children.length > 0) { const children = await Promise.all(node.children.map(id => this.getNode(id))); node = this.selectBestPUCTChild(children); } return node; } selectBestPUCTChild(nodes) { const totalVisits = nodes.reduce((sum, node) => sum + node.visits, 0); return nodes.reduce((best, node) => { const exploitation = node.valueEstimate; const exploration = Math.sqrt(Math.log(totalVisits) / node.visits); const policyTerm = node.policyScore * this.explorationRate; const noveltyBonus = (node.noveltyScore || 0) * this.noveltyBonus; const puct = exploitation + exploration * policyTerm + noveltyBonus; node.puct = puct; return puct > (best.puct || 0) ? node : best; }); } async expandWithPolicy(node) { if (node.isComplete) return node; const newNode = { ...node, id: uuidv4(), depth: node.depth + 1, parentId: node.id, children: [], visits: 1, totalReward: 0, policyScore: 0, valueEstimate: 0, priorActionProbs: new Map(), actionHistory: [...(node.actionHistory || [])] }; newNode.policyScore = this.calculatePolicyScore(newNode, node); newNode.score = this.evaluateThought(newNode, node); newNode.valueEstimate = this.estimateValue(newNode); newNode.noveltyScore = this.calculateNovelty(newNode); await this.saveNode(newNode); return newNode; } async simulateWithValueGuidance(node) { let current = node; let totalReward = 0; let depth = 0; while (!current.isComplete && depth < CONFIG.maxDepth) { const reward = current.valueEstimate; totalReward += reward; const expanded = await this.expandWithPolicy(current); current = expanded; depth++; } return totalReward / depth; } async backpropagateWithPolicyUpdate(node, reward) { let current = node; while (current) { current.visits++; current.totalReward += reward; // Update value estimate with temporal difference const newValue = (1 - this.learningRate) * current.valueEstimate + this.learningRate * reward; current.valueEstimate = newValue; // Update action probabilities if (current.parentId) { const parentNode = await this.getNode(current.parentId); const actionKey = this.extractAction(current.thought); const currentProb = parentNode.priorActionProbs.get(actionKey) || 0; const newProb = currentProb + this.learningRate * (reward - currentProb); parentNode.priorActionProbs.set(actionKey, newProb); await this.saveNode(parentNode); } await this.saveNode(current); current = current.parentId ? await this.getNode(current.parentId) : undefined; } } adaptExplorationRate(node) { const successRate = node.totalReward / node.visits; const targetRate = 0.6; if (successRate > targetRate) { // Reduce exploration when doing well this.explorationRate = Math.max(0.5, this.explorationRate * 0.95); } else { // Increase exploration when results are poor this.explorationRate = Math.min(2.0, this.explorationRate / 0.95); } } async updatePolicyMetrics(node, parent) { // Update running averages this.policyMetrics.averagePolicyScore = (this.policyMetrics.averagePolicyScore + node.policyScore) / 2; this.policyMetrics.averageValueEstimate = (this.policyMetrics.averageValueEstimate + node.valueEstimate) / 2; // Update action distribution const action = this.extractAction(node.thought); this.policyMetrics.actionDistribution[action] = (this.policyMetrics.actionDistribution[action] || 0) + 1; // Update exploration stats this.policyMetrics.explorationStats = { temperature: this.temperature, explorationRate: this.explorationRate, noveltyBonus: this.noveltyBonus }; // Calculate policy entropy and value stability const probs = Array.from(parent.priorActionProbs.values()); this.policyMetrics.convergenceMetrics = { policyEntropy: this.calculateEntropy(probs), valueStability: Math.abs(node.valueEstimate - parent.valueEstimate) }; } calculateEntropy(probs) { const sum = probs.reduce((a, b) => a + b, 0); return -probs.reduce((acc, p) => { const norm = p / sum; return acc + (norm * Math.log2(norm + 1e-10)); }, 0); } calculatePolicyEnhancedScore(path) { if (path.length === 0) return 0; return path.reduce((acc, node) => { const policyNode = node; const baseScore = node.score; const policyBonus = policyNode.policyScore || 0; const valueBonus = policyNode.valueEstimate || 0; const noveltyBonus = (policyNode.noveltyScore || 0) * this.noveltyBonus; return acc + (baseScore + policyBonus + valueBonus + noveltyBonus) / 4; }, 0) / path.length; } async getMetrics() { const baseMetrics = await super.getMetrics(); const nodes = await this.stateManager.getAllNodes(); // Calculate additional policy-specific metrics const currentNode = nodes[nodes.length - 1]; const policyStats = { currentNode: currentNode ? { policyScore: currentNode.policyScore, valueEstimate: currentNode.valueEstimate, noveltyScore: currentNode.noveltyScore, actionHistory: currentNode.actionHistory } : null, averages: { policyScore: nodes.reduce((sum, n) => sum + n.policyScore, 0) / nodes.length, valueEstimate: nodes.reduce((sum, n) => sum + n.valueEstimate, 0) / nodes.length, noveltyScore: nodes.reduce((sum, n) => sum + (n.noveltyScore || 0), 0) / nodes.length }, policyMetrics: this.policyMetrics }; return { ...baseMetrics, name: 'MCTS-002-Alpha (Policy Enhanced)', temperature: this.temperature, explorationRate: this.explorationRate, learningRate: this.learningRate, policyStats }; } }