小白龙 Bailongma - 初始提交

自主操作员与思考搭档系统。
包含 orchestrator-v2 多Agent编排层、后台意识引擎、记忆系统、ACUI 组件。
This commit is contained in:
chengjiaxi
2026-05-22 19:22:06 +08:00
commit 664ffb3834
64 changed files with 111533 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
const fs = require('fs');
const path = require('path');
// ContextCompressor — automatic context window compression
// Monitors session token usage and produces structured summaries
// when token budgets are exceeded. Supports iterative compression
// (compressed sessions can be further compressed).
class ContextCompressor {
constructor(config) {
this.config = config || {};
this.maxTokensBeforeCompress = config.maxTokensBeforeCompress || 20000;
this.minCompressionGain = config.minCompressionGain || 0.1; // 10% minimum gain
this.summaryTemplate = config.summaryTemplate || this._defaultTemplate();
}
_defaultTemplate() {
return {
activeTask: '',
goal: '',
progress: [],
decisions: [],
blockers: [],
openQuestions: [],
keyFiles: [],
remainingWork: [],
notes: ''
};
}
// Check if a session needs compression
shouldCompress(sessionStore, sessionId) {
var tokenCount = sessionStore.getTotalTokenCount(sessionId);
return tokenCount > this.maxTokensBeforeCompress;
}
// Compress a session: produce summary and mark as compressed
async compress(sessionStore, sessionId, childSessionId) {
var session = sessionStore.getSession(sessionId);
if (!session) return null;
var messages = sessionStore.getMessages(sessionId);
var summary = this._buildSummary(session, messages);
// Mark current session as compressed
sessionStore.compressSession(sessionId, summary.summary, childSessionId);
// If there was a previous compression, propagate context
if (session.parent_id) {
var parentChain = sessionStore.getSessionChain(sessionId);
summary.compressionChain = parentChain.length;
}
return summary;
}
// Build a structured summary from session data and messages
_buildSummary(session, messages) {
var summary = JSON.parse(JSON.stringify(this.summaryTemplate));
// Extract from session metadata
summary.activeTask = session.task || '';
if (session.summary) {
summary.notes = session.summary;
}
// Extract from messages (last N messages for most recent context)
var recentMsgs = messages.slice(-20);
for (var i = 0; i < recentMsgs.length; i++) {
var msg = recentMsgs[i];
var content = msg.content || '';
// Look for decision patterns
var decisionMatch = content.match(/决定|选择|采用|使用|改用|确定|确认.*方案/i);
if (decisionMatch) {
var snippet = content.slice(Math.max(0, decisionMatch.index - 20), decisionMatch.index + 40);
summary.decisions.push(snippet);
}
// Look for blocker patterns
var blockerMatch = content.match(/阻塞|卡住|问题|错误|失败|报错|无法|不能|不行/i);
if (blockerMatch) {
var snippet = content.slice(Math.max(0, blockerMatch.index - 20), blockerMatch.index + 40);
summary.blockers.push(snippet);
}
// Look for file references
var fileMatch = content.match(/[A-Za-z]:\\[^\s,,。;;:!?()\[\]【】{}"']+/g);
if (fileMatch) {
for (var j = 0; j < fileMatch.length; j++) {
if (summary.keyFiles.indexOf(fileMatch[j]) < 0) {
summary.keyFiles.push(fileMatch[j]);
}
}
}
// Look for progress markers
if (content.includes('完成') || content.includes('成功') || content.includes('通过')) {
summary.progress.push(content.slice(0, 80));
}
}
// Deduplicate and trim
summary.decisions = this._unique(summary.decisions).slice(0, 10);
summary.blockers = this._unique(summary.blockers).slice(0, 10);
summary.keyFiles = this._unique(summary.keyFiles).slice(0, 15);
summary.progress = this._unique(summary.progress).slice(0, 10);
// Build condensed summary string
var summaryStr = 'Task: ' + (summary.activeTask || 'N/A');
if (summary.progress.length) summaryStr += ' | Progress: ' + summary.progress.length + ' items';
if (summary.decisions.length) summaryStr += ' | Decisions: ' + summary.decisions.length;
if (summary.blockers.length) summaryStr += ' | Blockers: ' + summary.blockers.length;
if (summary.keyFiles.length) summaryStr += ' | Files: ' + summary.keyFiles.length;
summary.summary = summaryStr;
return summary;
}
// Estimate token count for a string (approx: 1 token ~= 2 CJK chars or 4 ASCII chars)
estimateTokens(text) {
if (!text) return 0;
var cjkCount = (text.match(/[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff]/g) || []).length;
var asciiCount = text.length - cjkCount;
return Math.ceil(cjkCount / 1.5) + Math.ceil(asciiCount / 4);
}
// Check compression effectiveness (anti-thrash)
compressionGain(sessionStore, sessionId) {
var before = sessionStore.getTotalTokenCount(sessionId);
var session = sessionStore.getSession(sessionId);
if (!before || !session) return 0;
var summaryLen = this.estimateTokens(session.summary || '');
if (before === 0) return 0;
return (before - summaryLen) / before;
}
_unique(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
if (result.indexOf(arr[i]) < 0) result.push(arr[i]);
}
return result;
}
}
module.exports = ContextCompressor;