@mcpflow.io/mcp-mcp-reasoner
Version:
为Claude Desktop 实现的基于系统推理的MCP服务器,采用波束搜索和思维评估。
274 lines (273 loc) • 11.9 kB
JavaScript
import { v4 as uuidv4 } from 'uuid';
import { CONFIG } from '../../types.js';
import { MCTS002AlphaStrategy } from './mcts-002-alpha.js';
// Queue implementation for bidirectional search
class Queue {
constructor() {
this.items = [];
}
enqueue(item) {
this.items.push(item);
}
dequeue() {
return this.items.shift();
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}
export class MCTS002AltAlphaStrategy extends MCTS002AlphaStrategy {
constructor(stateManager, numSimulations = CONFIG.numSimulations) {
super(stateManager, numSimulations);
this.startNode = null;
this.goalNode = null;
this.bidirectionalStats = {
forwardExplorationRate: Math.sqrt(2),
backwardExplorationRate: Math.sqrt(2),
meetingPoints: 0,
pathQuality: 0
};
}
async processThought(request) {
// Get base response first to ensure proper MCTS initialization
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: [],
g: parentNode ? parentNode.g + 1 : 0,
h: 0,
f: 0,
policyScore: 0,
valueEstimate: 0,
priorActionProbs: new Map(),
actionHistory: parentNode ?
[...(parentNode.actionHistory || []), this.getActionKey(request.thought)] :
[this.getActionKey(request.thought)],
searchDepth: 0,
direction: parentNode ? parentNode.direction : 'forward'
};
// Track start and goal nodes for bidirectional search
if (!parentNode) {
this.startNode = node;
node.direction = 'forward';
}
if (node.isComplete) {
this.goalNode = node;
node.direction = 'backward';
}
// Run bidirectional search if we have both endpoints
if (this.startNode && this.goalNode) {
const path = await this.bidirectionalSearch(this.startNode, this.goalNode);
if (path.length > 0) {
await this.updatePathWithPolicyGuidance(path);
}
}
// Calculate enhanced path statistics
const currentPath = await this.stateManager.getPath(nodeId);
const enhancedScore = this.calculateBidirectionalPolicyScore(currentPath);
return {
...baseResponse,
score: enhancedScore,
bestScore: Math.max(baseResponse.bestScore || 0, enhancedScore)
};
}
getActionKey(thought) {
// Simple action extraction based on first few words
return thought.split(/\s+/).slice(0, 3).join('_').toLowerCase();
}
async searchLevel(queue, visited, otherVisited, direction) {
const levelSize = queue.size();
for (let i = 0; i < levelSize; i++) {
const current = queue.dequeue();
if (!current)
continue;
// Check if we've found a meeting point
if (otherVisited.has(current.id)) {
current.meetingPoint = true;
this.bidirectionalStats.meetingPoints++;
await this.saveNode(current);
return current;
}
// Get neighbors based on direction and policy scores
const neighbors = direction === 'forward' ?
await Promise.all(current.children.map(id => this.getNode(id))) :
await Promise.all([current.parentId].filter((id) => !!id).map(id => this.getNode(id)));
const validNeighbors = neighbors.filter((n) => !!n)
.sort((a, b) => b.policyScore - a.policyScore); // Use policy scores for neighbor selection
for (const neighbor of validNeighbors) {
if (!visited.has(neighbor.id)) {
visited.set(neighbor.id, neighbor);
neighbor.parent = current.id;
neighbor.direction = direction;
neighbor.searchDepth = (current.searchDepth || 0) + 1;
await this.saveNode(neighbor);
queue.enqueue(neighbor);
}
}
}
return null;
}
async bidirectionalSearch(start, goal) {
const forwardQueue = new Queue();
const backwardQueue = new Queue();
const forwardVisited = new Map();
const backwardVisited = new Map();
forwardQueue.enqueue(start);
backwardQueue.enqueue(goal);
forwardVisited.set(start.id, start);
backwardVisited.set(goal.id, goal);
while (!forwardQueue.isEmpty() && !backwardQueue.isEmpty()) {
// Search from both directions with policy guidance
const meetingPoint = await this.searchLevel(forwardQueue, forwardVisited, backwardVisited, 'forward');
if (meetingPoint) {
const path = this.reconstructPath(meetingPoint, forwardVisited, backwardVisited);
this.updateBidirectionalStats(path);
return path;
}
const backMeetingPoint = await this.searchLevel(backwardQueue, backwardVisited, forwardVisited, 'backward');
if (backMeetingPoint) {
const path = this.reconstructPath(backMeetingPoint, forwardVisited, backwardVisited);
this.updateBidirectionalStats(path);
return path;
}
// Adapt exploration rates based on progress
this.adaptBidirectionalExploration(forwardVisited, backwardVisited);
}
return [];
}
reconstructPath(meetingPoint, forwardVisited, backwardVisited) {
const path = [meetingPoint];
// Reconstruct forward path
let current = meetingPoint;
while (current.parent && forwardVisited.has(current.parent)) {
current = forwardVisited.get(current.parent);
path.unshift(current);
}
// Reconstruct backward path
current = meetingPoint;
while (current.parent && backwardVisited.has(current.parent)) {
current = backwardVisited.get(current.parent);
path.push(current);
}
return path;
}
async updatePathWithPolicyGuidance(path) {
const pathBonus = 0.2;
for (const node of path) {
// Boost both policy and value estimates for nodes along the path
node.policyScore += pathBonus;
node.valueEstimate = (node.valueEstimate + 1) / 2;
// Update action probabilities with path information
if (node.parentId) {
const parentNode = await this.getNode(node.parentId);
const actionKey = this.getActionKey(node.thought);
const currentProb = parentNode.priorActionProbs.get(actionKey) || 0;
const newProb = Math.max(currentProb, 0.8); // Strong preference for path actions
parentNode.priorActionProbs.set(actionKey, newProb);
await this.saveNode(parentNode);
}
await this.saveNode(node);
}
// Update path quality metric
this.bidirectionalStats.pathQuality = path.reduce((acc, node) => acc + node.policyScore + node.valueEstimate, 0) / (path.length * 2);
}
adaptBidirectionalExploration(forwardVisited, backwardVisited) {
// Adjust exploration rates based on search progress
const forwardProgress = Array.from(forwardVisited.values())
.reduce((acc, node) => acc + node.policyScore, 0) / forwardVisited.size;
const backwardProgress = Array.from(backwardVisited.values())
.reduce((acc, node) => acc + node.policyScore, 0) / backwardVisited.size;
// Increase exploration in the direction making less progress
if (forwardProgress > backwardProgress) {
this.bidirectionalStats.backwardExplorationRate *= 1.05;
this.bidirectionalStats.forwardExplorationRate *= 0.95;
}
else {
this.bidirectionalStats.forwardExplorationRate *= 1.05;
this.bidirectionalStats.backwardExplorationRate *= 0.95;
}
}
updateBidirectionalStats(path) {
const forwardNodes = path.filter(n => n.direction === 'forward');
const backwardNodes = path.filter(n => n.direction === 'backward');
// Update exploration rates based on path composition
const forwardQuality = forwardNodes.reduce((acc, n) => acc + n.policyScore, 0) / forwardNodes.length;
const backwardQuality = backwardNodes.reduce((acc, n) => acc + n.policyScore, 0) / backwardNodes.length;
this.bidirectionalStats.pathQuality = (forwardQuality + backwardQuality) / 2;
}
calculateBidirectionalPolicyScore(path) {
if (path.length === 0)
return 0;
return path.reduce((acc, node) => {
const biNode = node;
const baseScore = node.score;
const policyBonus = biNode.policyScore || 0;
const valueBonus = biNode.valueEstimate || 0;
const meetingPointBonus = biNode.meetingPoint ? 0.2 : 0;
const directionBonus = biNode.direction === 'forward' ?
this.bidirectionalStats.forwardExplorationRate * 0.1 :
this.bidirectionalStats.backwardExplorationRate * 0.1;
return acc + (baseScore +
policyBonus +
valueBonus +
meetingPointBonus +
directionBonus) / 5;
}, 0) / path.length;
}
async getMetrics() {
const baseMetrics = await super.getMetrics();
const nodes = await this.stateManager.getAllNodes();
const forwardNodes = nodes.filter(n => n.direction === 'forward');
const backwardNodes = nodes.filter(n => n.direction === 'backward');
const meetingPoints = nodes.filter(n => n.meetingPoint);
const bidirectionalMetrics = {
forwardSearch: {
nodesExplored: forwardNodes.length,
averagePolicyScore: forwardNodes.reduce((sum, n) => sum + n.policyScore, 0) / forwardNodes.length,
explorationRate: this.bidirectionalStats.forwardExplorationRate
},
backwardSearch: {
nodesExplored: backwardNodes.length,
averagePolicyScore: backwardNodes.reduce((sum, n) => sum + n.policyScore, 0) / backwardNodes.length,
explorationRate: this.bidirectionalStats.backwardExplorationRate
},
meetingPoints: {
count: this.bidirectionalStats.meetingPoints,
averageDepth: meetingPoints.reduce((sum, n) => sum + n.depth, 0) / (meetingPoints.length || 1)
},
pathQuality: this.bidirectionalStats.pathQuality
};
return {
...baseMetrics,
name: 'MCTS-002Alt-Alpha (Bidirectional + Policy Enhanced)',
hasStartNode: !!this.startNode,
hasGoalNode: !!this.goalNode,
bidirectionalMetrics
};
}
async clear() {
await super.clear();
this.startNode = null;
this.goalNode = null;
this.bidirectionalStats = {
forwardExplorationRate: Math.sqrt(2),
backwardExplorationRate: Math.sqrt(2),
meetingPoints: 0,
pathQuality: 0
};
}
}