@mcpflow.io/mcp-mcp-reasoner
Version:
为Claude Desktop 实现的基于系统推理的MCP服务器,采用波束搜索和思维评估。
295 lines (294 loc) • 12.3 kB
JavaScript
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
};
}
}