小白龙 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

BIN
orchestrator-v2/_fix2.js Normal file

Binary file not shown.

View File

@@ -0,0 +1,87 @@
const fs = require('fs');
const reportContent = `// ============================================================
// 辩论报告生成器
// ============================================================
const fs2 = require('fs');
function generateReport(input, state, stepLog) {
const now = new Date().toISOString().replace('T', ' ').substring(0, 19);
const report = [];
report.push('# 辩论总结报告');
report.push('');
report.push('---');
report.push('**问题**: ' + input);
report.push('**时间**: ' + now);
report.push('**步骤统计**: ' + stepLog.filter(s => s.status === 'done' || s.status === 'fallback').length + '/' + stepLog.length + ' 步完成');
report.push('');
report.push('## 1. 问题定义');
report.push(state.defined || '(无)');
report.push('');
report.push('## 2. 幕僚表态');
if (state.opinions && state.opinions.length > 0) {
for (const o of state.opinions) {
report.push('### ' + (o.emoji || '') + ' ' + (o.persona || ''));
report.push(o.opinion || '');
report.push('');
}
} else {
report.push('暂无幕僚表态数据');
}
report.push('## 3. 冲突维度');
if (state.dimensions && state.dimensions.length > 0) {
state.dimensions.forEach((d, i) => {
report.push('### 维度 ' + (i+1));
report.push(d);
report.push('');
});
} else {
report.push('未提炼出明确的冲突维度');
}
report.push('## 4. 维度辩论');
if (state.debates && state.debates.length > 0) {
state.debates.forEach((d, i) => {
report.push('### 维度 ' + (i+1) + ': ' + (d.dimension || '').substring(0, 100));
report.push(d.summary || '(无辩论摘要)');
report.push('');
});
} else {
report.push('未进行维度辩论');
}
report.push('## 5. 秘书总结');
report.push(state.summary || '(无)');
report.push('');
report.push('## 6. 行动建议');
report.push(state.harvest || '(无)');
report.push('');
report.push('---');
report.push('## 附录: 执行日志');
if (stepLog && stepLog.length > 0) {
for (const log of stepLog) {
report.push('- Step ' + log.step + ': [' + log.status + '] ' + ((log.detail || '').substring(0, 80)));
}
}
report.push('');
return report.join('\n');
}
function saveReport(input, state, stepLog, filePath) {
const report = generateReport(input, state, stepLog);
fs2.writeFileSync(filePath, report, 'utf8');
return filePath;
}
module.exports = { generateReport, saveReport };
`;
fs.writeFileSync('D:/q/Bailongma/orchestrator-v2/debate/report.js', reportContent, 'utf8');
console.log('OK');

View File

@@ -0,0 +1,58 @@
const AgentWorker = require('./agent-worker.js');
class AgentPool {
constructor(sessionStore, maxWorkers = 4) {
this.store = sessionStore;
this.maxWorkers = maxWorkers;
this.workers = new Map();
this.queue = [];
this.results = new Map();
}
async submitTask(task, context) {
if (this.workers.size >= this.maxWorkers) {
// Queue it
return new Promise((resolve) => {
this.queue.push({ task, context, resolve });
});
}
return this._executeTask(task, context);
}
async _executeTask(task, context) {
const id = task.id || ('task_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8));
this.store.createSession(id, task.name, JSON.stringify({ task, context }));
const worker = new AgentWorker(id, task, context, this.store);
this.workers.set(id, worker);
const result = await worker.run();
this.results.set(id, result);
this.workers.delete(id);
// Process queue
if (this.queue.length > 0) {
const next = this.queue.shift();
next.resolve(this._executeTask(next.task, next.context));
}
return { id, result, status: worker.getStatus() };
}
async submitAll(tasks, context) {
const promises = tasks.map(t => this.submitTask(t, context));
return Promise.all(promises);
}
getResults() {
return Object.fromEntries(this.results);
}
getPendingCount() {
return this.queue.length;
}
getActiveCount() {
return this.workers.size;
}
}
module.exports = AgentPool;

View File

@@ -0,0 +1,74 @@
const { spawn } = require("child_process");
const path = require("path");
const fs = require("fs");
class AgentWorker {
constructor(id, task, context, sessionStore) {
this.id = id;
this.task = task;
this.context = context;
this.store = sessionStore;
this.process = null;
this.status = "idle";
}
async run() {
this.status = "running";
this.store.appendEvent(this.id, { type: "status", status: "running", ts: new Date().toISOString() });
const role = this.task.roles && this.task.roles.length > 0 ? this.task.roles[0] : null;
return new Promise((resolve) => {
const agentCtx = { task: { id: this.task.id, name: this.task.name, target: this.task.target, priority: this.task.priority },
role: role ? { id: role.id, name: role.name, emoji: role.emoji, description: role.description, prompt: role.prompt } : null,
llmConfig: { baseUrl: (process.env.LLM_BASE_URL || process.env.OPENAI_BASE_URL || "https://api.openai.com/v1").replace(/\/+$/, ""),
model: process.env.LLM_MODEL || process.env.OPENAI_MODEL || "gpt-4o",
apiKey: process.env.LLM_API_KEY || process.env.OPENAI_API_KEY || "" },
timestamp: new Date().toISOString() };
const ctxJson = JSON.stringify(agentCtx);
let script = "const ctx = " + ctxJson + ";\n";
script += "async function runAgent() {\n";
script += "const steps = [];\n";
script += "const startTime = Date.now();\n";
script += "if (ctx.role) {\n";
script += "steps.push({ type: \"identity\", role: ctx.role.name, emoji: ctx.role.emoji, content: \"Assuming role: \" + ctx.role.emoji + \" \" + ctx.role.name });\n";
script += "}\n";
script += "const messages = [];\n";
script += "if (ctx.role && ctx.role.prompt) {\n";
script += "messages.push({ role: \"system\", content: ctx.role.prompt + \"\\n\\n\" + ctx.role.name + \"\" + ctx.role.description });\n";
script += "} else {\n";
script += "messages.push({ role: \"system\", content: \"You are a professional AI assistant.\" });\n";
script += "}\n";
script += "messages.push({ role: \"user\", content: ctx.task.target });\n";
script += "steps.push({ type: \"llm\", action: \"calling \" + ctx.llmConfig.model });\n";
script += "let llmResponse = \"\";\n";
script += "let modelInfo = ctx.llmConfig.model;\n";
script += "let errorInfo = null;\n";
script += "try {\n";
script += "const url = ctx.llmConfig.baseUrl + \"/chat/completions\";\n";
script += "const r = await fetch(url, { method: \"POST\", headers: { \"Content-Type\": \"application/json\", \"Authorization\": \"Bearer \" + ctx.llmConfig.apiKey }, body: JSON.stringify({ model: ctx.llmConfig.model, messages: messages, max_tokens: 2048, temperature: 0.7 }) });\n";
script += "if (!r.ok) { const e = await r.text().catch(()=>\"\"); throw new Error(\"LLM \" + r.status + \": \" + e.slice(0,200)); }\n";
script += "const d = await r.json();\n";
script += "modelInfo = d.model || ctx.llmConfig.model;\n";
script += "llmResponse = d.choices && d.choices[0] && d.choices[0].message ? d.choices[0].message.content : JSON.stringify(d);\n";
script += "} catch (err) { errorInfo = err.message; llmResponse = \"[LLM Error] \" + err.message; }\n";
script += "const elapsed = Date.now() - startTime;\n";
script += "steps.push({ type: \"llm_result\", duration: elapsed + \"ms\", model: modelInfo, error: errorInfo });\n";
script += "const result = { summary: (ctx.role ? ctx.role.emoji + \" [\" + ctx.role.name + \"] \" : \"\") + \"Processed: \" + ctx.task.name, steps: steps, output: (ctx.role ? \"=== \" + ctx.role.name + \" Analysis ===\\n\" : \"\") + llmResponse, roleUsed: ctx.role ? ctx.role.id : null, model: modelInfo, duration: elapsed + \"ms\", error: errorInfo };\n";
script += "process.stdout.write(JSON.stringify(result));\n";
script += "}\n";
script += "runAgent().then(() => process.exit(0)).catch(e => { process.stderr.write(e.message); process.exit(1); });\n";
const child = spawn("node", ["-e", script]);
let output = "";
child.stdout.on("data", (d) => { output += d; });
child.stderr.on("data", () => {});
child.on("close", (code) => {
this.status = code === 0 ? "completed" : "failed";
this.store.updateStatus(this.id, this.status, output);
this.store.appendEvent(this.id, { type: "status", status: this.status, ts: new Date().toISOString() });
try { resolve(JSON.parse(output)); }
catch { resolve({ summary: this.task.name + " (fallback)", raw: output.slice(0,500), status: this.status }); }
});
});
}
getStatus() { return this.status; }
}
module.exports = AgentWorker;

View File

@@ -0,0 +1,133 @@
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
// BackgroundReview — post-task self-improvement evaluation
// Forks a lightweight sub-agent to analyze completed work and
// decide if any memory or skill updates are warranted.
class BackgroundReview {
constructor(config) {
this.config = config || {};
this.reviewDir = config.reviewDir || path.join(__dirname, 'reviews');
this.enabled = config.enabled !== false;
this.minResultLength = config.minResultLength || 50;
}
async review(sessionId, task, result, memoryProvider) {
if (!this.enabled) return;
if (!result) return;
var output = '';
if (typeof result === 'string') output = result;
else if (result.output) output = typeof result.output === 'string' ? result.output : JSON.stringify(result.output);
else if (result.summary) output = result.summary;
else output = JSON.stringify(result);
if (!output || output.length < this.minResultLength) return;
// 1. Check for style/preference signals
var styleSignals = this._detectStyleSignals(task, output);
if (styleSignals.length && memoryProvider) {
for (var i = 0; i < styleSignals.length; i++) {
var sig = styleSignals[i];
memoryProvider.addEntry('preference', sig.title, sig.content, ['style', 'preference']);
}
}
// 2. Check for knowledge/skill signals
var skillSignals = this._detectSkillSignals(task, output);
if (skillSignals.length && memoryProvider) {
for (var i = 0; i < skillSignals.length; i++) {
var sig = skillSignals[i];
memoryProvider.addEntry('skill_signal', sig.title, sig.content, ['skill', sig.category || 'general']);
}
}
// 3. Log review report
this._logReview(sessionId, {
styleSignals: styleSignals.length,
skillSignals: skillSignals.length,
timestamp: new Date().toISOString()
});
return {
styleSignals: styleSignals.length,
skillSignals: skillSignals.length
};
}
// Detect user preference/style signals from task output
_detectStyleSignals(task, output) {
var signals = [];
var taskStr = typeof task === 'string' ? task : (task && task.task ? (typeof task.task === 'string' ? task.task : task.task.name || '') : '');
// Look for explicit style corrections in output
var stylePatterns = [
{ pattern: /别啰嗦|简洁|简短|直接点|说重点/i, title: '偏好简洁回复', content: '用户偏好简洁直接的回复风格,避免啰嗦和冗余解释', category: 'style' },
{ pattern: /不要问|别问|直接做|别废话/i, title: '偏好行动而非询问', content: '用户希望直接执行任务,避免多余的确认性提问', category: 'style' },
{ pattern: /用中文|说中文/i, title: '偏好中文回复', content: '用户偏好使用中文进行交流', category: 'language' },
{ pattern: /格式|排版|对齐|美观/i, title: '关注输出格式', content: '用户关注输出格式和排版美观度', category: 'style' },
{ pattern: /不要[格格格式]式|别用[格格格式]式|换个格式/i, title: '格式偏好调整', content: '用户对特定输出格式有偏好,需要调整回复格式', category: 'style' },
];
for (var i = 0; i < stylePatterns.length; i++) {
if (stylePatterns[i].pattern.test(taskStr) || stylePatterns[i].pattern.test(output)) {
signals.push(stylePatterns[i]);
}
}
return signals;
}
// Detect reusable knowledge/skill signals from task output
_detectSkillSignals(task, output) {
var signals = [];
var combined = (typeof task === 'string' ? task : JSON.stringify(task)) + ' ' + output;
// Look for knowledge that should be saved
var knowledgePatterns = [
{ pattern: /架构|设计模式|最佳实践|方案设计/i, title: '架构知识', content: '任务产生了架构设计或技术方案', category: 'architecture' },
{ pattern: /工作流|流程|步骤|指南/i, title: '工作流知识', content: '任务产生了可复用的工作流或操作步骤', category: 'workflow' },
{ pattern: /配置|部署|安装|docker/i, title: '部署知识', content: '任务涉及环境配置或部署流程', category: 'devops' },
{ pattern: /API|接口|路由|端点/i, title: 'API知识', content: '任务涉及API设计和接口规范', category: 'api' },
{ pattern: /代码|实现|函数|模块/i, title: '代码实现', content: '任务涉及具体代码实现', category: 'code' },
{ pattern: /测试|调试|修复|bug/i, title: '调试知识', content: '任务涉及问题排查或bug修复', category: 'debug' },
];
for (var i = 0; i < knowledgePatterns.length; i++) {
if (knowledgePatterns[i].pattern.test(combined)) {
signals.push(knowledgePatterns[i]);
}
}
return signals;
}
_logReview(sessionId, report) {
if (!fs.existsSync(this.reviewDir)) fs.mkdirSync(this.reviewDir, { recursive: true });
var logFile = path.join(this.reviewDir, 'reviews.jsonl');
var line = JSON.stringify({ sessionId: sessionId, report: report, ts: new Date().toISOString() }) + '\n';
try { fs.appendFileSync(logFile, line, 'utf8'); } catch (e) {}
}
getReviewStats() {
var logFile = path.join(this.reviewDir, 'reviews.jsonl');
if (!fs.existsSync(logFile)) return { total: 0, styleSignals: 0, skillSignals: 0 };
try {
var lines = fs.readFileSync(logFile, 'utf8').split('\n').filter(Boolean);
var stats = { total: lines.length, styleSignals: 0, skillSignals: 0 };
for (var i = 0; i < lines.length; i++) {
try {
var entry = JSON.parse(lines[i]);
if (entry.report) {
stats.styleSignals += entry.report.styleSignals || 0;
stats.skillSignals += entry.report.skillSignals || 0;
}
} catch (e) {}
}
return stats;
} catch (e) { return { total: 0, styleSignals: 0, skillSignals: 0 }; }
}
}
module.exports = BackgroundReview;

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;

View File

@@ -0,0 +1,210 @@
const AgentPool = require('./agent-pool.js');
const SessionStore = require('./session-store.js');
const RoleRouter = require('./role-router.js');
const { BuiltInMemoryProvider } = require('./memory-provider.js');
const BackgroundReview = require('./background-review.js');
const Curator = require('./curator.js');
const ContextCompressor = require('./context-compressor.js');
const path = require('path');
class Coordinator {
constructor(config) {
this.config = config || {};
this.dbDir = this.config.dbDir || path.join(__dirname, 'db');
this.store = null;
this.pool = null;
this.router = new RoleRouter();
this.memoryProvider = null;
this.reviewer = null;
this.curator = null;
this.compressor = null;
this.initialized = false;
}
async init() {
if (this.initialized) return;
// Layer 1: SQLite Session Store
this.store = new SessionStore(this.dbDir);
await this.store.init();
// Layer 2: Memory Provider
this.memoryProvider = new BuiltInMemoryProvider(this.config.memory || {});
await this.memoryProvider.initialize();
// Layer 3: Background Review
this.reviewer = new BackgroundReview(this.config.review || {});
// Layer 4: Curator
this.curator = new Curator(this.config.curator || {});
// Layer 5: Context Compressor
this.compressor = new ContextCompressor(this.config.compressor || {});
// Agent Pool (uses SessionStore)
this.pool = new AgentPool(this.store, this.config.maxWorkers || 4);
// Role Router
await this.router.init();
this.initialized = true;
console.log('[Coordinator v3.0] Initialized with all 5 persistence layers');
console.log('[Coordinator] Role templates:', this.router.getTemplates().getRoleCount());
}
decomposeTask(mainTask) {
return this.router.decomposeWithRoles(mainTask);
}
async run(mainTask, context) {
if (!this.initialized) await this.init();
if (!context) context = {};
var sessionId = 'session_' + Date.now();
this.store.createSession(sessionId, mainTask.slice(0, 200), context);
this.store.updateStatus(sessionId, 'running');
console.log('[Coordinator] Session:', sessionId);
// Layer 2: Pre-fetch relevant memories
var memoryContext = '';
try {
memoryContext = await this.memoryProvider.getContextBlock(mainTask);
if (memoryContext) {
console.log('[Coordinator] Injected', this.memoryProvider.memories.length, 'memory entries');
}
} catch (e) {
console.log('[Coordinator] Memory prefetch error:', e.message);
}
// Decompose task with role matching
console.log('[Coordinator] Decomposing task with role matching...');
var subTasks = await this.decomposeTask(mainTask);
// Inject memory context into each sub-task
var enrichedContext = { ...context, memoryContext: memoryContext, roleEngine: true };
// Phase 1: Parallel execution
console.log('[Coordinator] Executing', subTasks.length, 'sub-tasks in parallel...');
var results = await this.pool.submitAll(subTasks, enrichedContext);
// Phase 2: Aggregate results
console.log('[Coordinator] Aggregating results...');
var aggregated = this._aggregate(results);
this.store.updateStatus(sessionId, 'completed', aggregated);
// Layer 2: Sync memories from results
try {
for (var i = 0; i < results.length; i++) {
var r = results[i];
if (r && r.result) {
await this.memoryProvider.syncTurn(r.task || subTasks[i], r.result);
}
}
if (aggregated) {
await this.memoryProvider.syncTurn(mainTask, aggregated);
}
} catch (e) {
console.log('[Coordinator] Memory sync error:', e.message);
}
// Layer 3: Background review
try {
var reviewResult = await this.reviewer.review(sessionId, mainTask, aggregated, this.memoryProvider);
if (reviewResult && (reviewResult.styleSignals > 0 || reviewResult.skillSignals > 0)) {
console.log('[Coordinator] Background review:', reviewResult.styleSignals + ' style signals, ' + reviewResult.skillSignals + ' skill signals');
}
} catch (e) {
console.log('[Coordinator] Review error:', e.message);
}
// Layer 5: Check if compression needed
try {
if (this.compressor.shouldCompress(this.store, sessionId)) {
console.log('[Coordinator] Session exceeds compression threshold, compressing...');
var compressed = await this.compressor.compress(this.store, sessionId);
if (compressed) {
console.log('[Coordinator] Compression complete:', compressed.summary);
}
}
} catch (e) {
console.log('[Coordinator] Compression error:', e.message);
}
return { sessionId, subTasks: results, aggregated };
}
_aggregate(results) {
var summaries = results.map(function(r) {
return {
id: r.id || r.task || '',
status: r.status || (r.result ? 'completed' : 'failed'),
summary: r.result ? (r.result.summary || '') : 'no result',
output: r.result ? (r.result.output || '') : ''
};
});
return {
totalTasks: results.length,
completed: results.filter(function(r) { return r.status === 'completed' || (r.result && r.result.status !== 'failed'); }).length,
failed: results.filter(function(r) { return r.status === 'failed' || (r.result && r.result.status === 'failed'); }).length,
summaries: summaries
};
}
// Layer 4: Run curator
async runCurator() {
if (!this.initialized) await this.init();
console.log('[Coordinator] Running curator...');
var report = await this.curator.run(this.memoryProvider);
console.log('[Coordinator] Curator report: ' + report.staleSkills.length + ' stale, ' + report.archivedSkills.length + ' archive candidates');
if (report.memoryAnalysis && report.memoryAnalysis.suggestions.length) {
console.log('[Coordinator] Memory suggestions:', report.memoryAnalysis.suggestions.join('; '));
}
return report;
}
// Search across all persistence layers
async search(query, limit) {
if (!this.initialized) await this.init();
if (limit === undefined) limit = 10;
var results = {
sessions: this.store.searchSessions(query, limit),
messages: this.store.searchMessages(query, limit * 2),
memories: []
};
try {
results.memories = await this.memoryProvider.search(query, limit);
} catch (e) {}
return results;
}
// Get system stats
async getStats() {
if (!this.initialized) await this.init();
return {
sessions: this.store.getStats(),
memories: this.memoryProvider.getStats(),
reviews: this.reviewer.getReviewStats()
};
}
// List recent sessions
listSessions(limit) {
if (!this.store) return [];
return this.store.listSessions(limit || 10);
}
// Get compressed session chain
getSessionChain(sessionId) {
if (!this.store) return [];
return this.store.getSessionChain(sessionId);
}
async close() {
if (this.memoryProvider) await this.memoryProvider.shutdown();
if (this.store) this.store.close();
}
}
module.exports = Coordinator;

141
orchestrator-v2/curator.js Normal file
View File

@@ -0,0 +1,141 @@
const fs = require('fs');
const path = require('path');
// Curator — background skill maintenance orchestrator
// Periodically reviews agent-created skills and memories for
// consolidation, staleness, and merge opportunities.
class Curator {
constructor(config) {
this.config = config || {};
this.skillsDir = config.skillsDir || path.join(process.env.HOME || process.env.USERPROFILE || '.', '.hermes', 'skills');
this.reportDir = config.reportDir || path.join(__dirname, 'reports');
this.staleDays = config.staleDays || 30;
this.archiveDays = config.archiveDays || 90;
this.enabled = config.enabled !== false;
}
async run(memoryProvider) {
if (!this.enabled) return { status: 'disabled' };
var report = {
timestamp: new Date().toISOString(),
staleSkills: [],
archivedSkills: [],
mergedSuggestions: [],
memoryAnalysis: null
};
// 1. Check skill staleness
try {
var skills = this._scanSkills();
report.staleSkills = this._checkStaleness(skills);
report.archivedSkills = this._checkArchive(skills);
} catch (e) {
report.skillError = e.message;
}
// 2. Analyze memories for consolidation opportunities
if (memoryProvider) {
try {
report.memoryAnalysis = this._analyzeMemories(memoryProvider);
} catch (e) {
report.memoryError = e.message;
}
}
// 3. Generate report
this._saveReport(report);
return report;
}
_scanSkills() {
if (!fs.existsSync(this.skillsDir)) return [];
var entries = [];
try {
var items = fs.readdirSync(this.skillsDir, { withFileTypes: true });
for (var i = 0; i < items.length; i++) {
if (items[i].isDirectory() || items[i].name.endsWith('.md')) {
var fullPath = path.join(this.skillsDir, items[i].name);
var stat = fs.statSync(fullPath);
entries.push({
name: items[i].name,
path: fullPath,
isDir: items[i].isDirectory(),
created: stat.birthtime,
modified: stat.mtime,
ageDays: (Date.now() - stat.mtime.getTime()) / 86400000
});
}
}
} catch (e) {}
return entries;
}
_checkStaleness(skills) {
return skills.filter(function(s) {
return s.ageDays > this.staleDays && s.ageDays < this.archiveDays;
}.bind(this)).map(function(s) {
return { name: s.name, ageDays: Math.round(s.ageDays), action: 'mark-stale' };
});
}
_checkArchive(skills) {
return skills.filter(function(s) {
return s.ageDays > this.archiveDays;
}.bind(this)).map(function(s) {
return { name: s.name, ageDays: Math.round(s.ageDays), action: 'archive' };
});
}
_analyzeMemories(memoryProvider) {
var stats = memoryProvider.getStats();
var suggestions = [];
// Check for memory type balance
if (stats.byType) {
var total = stats.total || 0;
if (total > 50) {
suggestions.push('记忆总数超过50条建议整理合并相似条目');
}
var taskResults = stats.byType.task_result || 0;
if (taskResults > 20) {
suggestions.push('task_result 类型记忆过多(' + taskResults + '条),建议按项目归类');
}
}
return {
totalMemories: stats.total || 0,
byType: stats.byType || {},
suggestions: suggestions
};
}
_saveReport(report) {
if (!fs.existsSync(this.reportDir)) fs.mkdirSync(this.reportDir, { recursive: true });
var ts = new Date().toISOString().replace(/[:.]/g, '-');
var reportPath = path.join(this.reportDir, 'curator-' + ts + '.json');
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
}
getReportHistory(limit) {
if (limit === undefined) limit = 5;
if (!fs.existsSync(this.reportDir)) return [];
try {
var files = fs.readdirSync(this.reportDir)
.filter(function(f) { return f.startsWith('curator-') && f.endsWith('.json'); })
.sort()
.reverse()
.slice(0, limit);
var reports = [];
for (var i = 0; i < files.length; i++) {
try {
reports.push(JSON.parse(fs.readFileSync(path.join(this.reportDir, files[i]), 'utf8')));
} catch (e) {}
}
return reports;
} catch (e) { return []; }
}
}
module.exports = Curator;

View File

@@ -0,0 +1,34 @@
-- orchestrator-v2 会话持久化 SQLite schema
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
parent_id TEXT,
task TEXT NOT NULL,
status TEXT DEFAULT 'created', -- created | running | paused | completed | failed
context TEXT, -- JSON: 完整上下文
result TEXT, -- JSON: 最终结果
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (parent_id) REFERENCES sessions(id)
);
CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
type TEXT NOT NULL, -- think | tool_call | tool_result | message | progress
data TEXT NOT NULL, -- JSON
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
CREATE TABLE IF NOT EXISTS agent_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
agent_name TEXT,
level TEXT DEFAULT 'info',
message TEXT,
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
CREATE INDEX idx_sessions_status ON sessions(status);
CREATE INDEX idx_events_session ON events(session_id);

Binary file not shown.

View File

@@ -0,0 +1,358 @@
# Debate Summary Report
---
**Question**: 我该不该全职做独立开发
**Time**: 2026-05-22 09:52:32
**Steps**: 7/21
# Problem Definition
问题目前比较模糊。为了帮你把“该不该全职做独立开发”变成可辩论的议题,需要先明确两个关键信息:
1. **你的财务缓冲期有多长?**(比如:在不依赖独立开发收入的情况下,现有存款能支撑你生活几个月?)
2. **你的“独立开发”目前处于什么阶段?**(是已经有上线产品且有少量收入,还是刚有想法、代码还没开始写?)
请先补充这两点,我们再来聚焦判断标准。
# Advisor Opinions
### 🍏 乔布斯
**结论:不该全职。**
你的回答里没有存款、没有稳定收入、没有产品验证——这是自杀式创业。
**核心论点:**
1. **没有财务缓冲,你无法专注创造伟大产品。** 我当年在车库里做Apple I靠的是卖计算器攒的钱和稳定的兼职收入。饥饿只会让你妥协而不是创新。
2. **独立开发需要“已证明的需求”,而不是“想法”。** 你甚至没说出一个上线产品或有100美元收入。先兼职做出最小可行产品用真实用户的反馈验证再谈全职。
**行动建议:** 保留工作每天挤出4小时开发。等产品月收入达到你当前月薪的50%时,再辞职。否则,你只是在赌运气,而不是在创造。
### 📝 Paul Graham
不要全职做独立开发——除非你已有产品上线且用户增长曲线呈自然上升趋势。
你的问题缺少两个关键数据财务缓冲期和产品阶段。如果你存款撑不过12个月或产品还没上线/月收入低于100美元全职就是自杀式赌博。真正的独立开发是“先有惊喜用户再全职投入”而不是反过来。
核心论点:**好的创业想法会逼你辞职,而不是让你纠结该不该辞职**。如果你还在犹豫,说明产品还没验证到足够强的需求信号。先兼职跑通最小闭环,让早期用户告诉你“这必须存在”,再全情投入。
### 🚀 马斯克
根据第一性原理:**你的存款能支撑你多久不依赖收入生活?** 如果少于12个月别全职。独立开发不是“逃离工作”而是用最小可行产品验证市场需求。你目前有产品吗月收入能覆盖你1/3的生活成本吗没有的话先兼职做到有付费用户再谈全职。大多数人的“全职创业”只是逃避低效工作的幻觉。
### 🧘 Naval
从你的视角Naval**不该全职做独立开发**,除非你已拥有“代码杠杆”且能规模化。
核心论点:
1. **财富是拥有,不是出卖时间**。全职独立开发本质仍是“用时间换钱”,不是真正的杠杆(除非你已有产品且用户增长可自动化)。否则你只是换了个地方打工。
2. **生存优先于自由**。你问“该不该”时说明财务缓冲不足。真正自由的人不会问——他们已用资产或代码产生被动收入。先积累6-12个月生活费再考虑全职。
结论:**保持正职,用业余时间验证产品是否可规模化**。当独立开发收入超过正职2倍且稳定时才值得全职。
### 🧠 芒格
根据你的问题,我给出逆向判断:**不该全职做独立开发,除非你已通过“生存测试”**。
核心论点:
1. **财务缓冲不足**若存款无法支撑12个月以上无收入生活全职就是赌命。独立开发收入极不稳定生存压力会扭曲决策——你会急于变现而非打磨价值。
2. **能力圈未验证**若过去3个月项目未产生超过100美元收入说明产品尚未找到市场匹配。全职只会加速错误而非解决根本问题。
**结论**:保持现有收入来源,用业余时间验证到月收入能覆盖基本生活支出后,再考虑全职。否则,你是在用焦虑换自由。
### 🔬 费曼
费曼式回答别问“该不该”问“能不能”。你的独立开发项目如果过去三个月没有产生任何一笔超过100美元的收入那它目前只是个爱好不是生意。全职做爱好等于赌博。核心论点**用物理原理检验——没有收入验证的假设,就是空想。** 先兼职做到月收入覆盖你基本生活开支的50%,再考虑全职。否则你只是在用存款买“自由”的幻觉。
### 🦢 塔勒布
塔勒布式判断:**不要全职。**
1. **你暴露在脆弱性中**:全职意味着你的生存完全依赖一个尚未验证的“尾部收益”——独立开发的成功概率极低,而一旦失败,你连生活都维持不了。这违背了“不要赌上吃饭钱”的原则。
2. **反脆弱的做法**:保持有稳定收入的正职,利用业余时间做独立开发。这样,如果项目失败,你只是损失了时间(可控的波动);如果成功,你获得巨大收益(正向黑天鹅)。你从不确定性中获利,而非被它击垮。
**结论**只有当你现有存款能支撑你2年以上无收入生活且项目已产生稳定现金流覆盖月生活费的50%以上),才考虑全职。否则,你是在押注而非创业。
### 💰 特朗普
特朗普式回答:
**结论别全职干除非你有6个月以上的“生存现金”+一笔超过100美元的订单。**
**核心论点:**
1. **杠杆不够**——没有稳定收入或明确订单前全职是赌博。你需要先做出“赢”的迹象比如月入3000美元否则谈判地位为零。
2. **制造压力不如制造筹码**——兼职干到产品有用户、有收入,再拿数据去跟市场或投资人谈。没有交易筹码,你只是另一个破产的梦想家。
**行动:** 先兼职做到月入覆盖房租,再考虑全职。否则,你就是在给自己挖坑。
### 🤖 Karpathy
不建议全职。核心论点:
1. **没有收入验证的独立开发是赌博。** 你过去三个月连一笔超过100美元的收入都没有说明产品连最小市场验证都没通过。全职只会加速消耗存款而不是解决产品问题。
2. **用业余时间把MVP跑通再谈全职。** 真正的独立开发者是先用周末和晚上把产品做到有人付费、有正向反馈,才敢辞职。你现在连代码都没跑通用户闭环,全职只会让你在焦虑中写出更烂的代码。
结论:继续上班,用业余时间把产品推到有稳定收入(比如月入覆盖基本生活)再考虑全职。
### 🧬 Ilya Sutskever
你不该全职做独立开发——除非你已确认产品在某个明确方向上存在“涌现可能”且你具备至少18个月的财务缓冲。
**核心论点:** 独立开发的成功依赖持续的认知投入与迭代而财务压力会打断你对长期趋势的判断力。若你的产品尚未产生任何超过100美元的收入说明它仍处于“预缩放”阶段此时全职只会加速耗尽你的生存资源而非提升突破概率。先保持兼职直到你观察到明确的用户增长拐点或收入信号。
### 🎬 MrBeast
别全职。你连一笔100美元都没赚到说明产品还没验证市场。独立开发本质是内容创业没有用户疯传的“爆点”就毫无意义。先兼职做到月收入覆盖基本生活开支再考虑all in。核心论点**用最小成本测试病毒系数**,未验证前全职就是赌博。
### 📱 张一鸣
张一鸣的判断:
**结论:不建议全职。**
核心论点1**数据不足,风险不可控。** 你连“过去三个月是否有超过100美元收入”都不确定说明产品尚未验证市场匹配度。全职后没有反馈闭环容易陷入低效闭门造车。
核心论点2**系统优于个人意志。** 真正可规模化的是用低成本(兼职+工具)跑通最小闭环,靠数据迭代而非赌上全部时间。先兼职做到月收入覆盖基本生活成本,再考虑全职。
# Conflict Dimensions
### Dimension 1
好的,以下是从各位幕僚的表态中识别出的核心冲突维度:
### Dimension 2
冲突核心:**全职独立开发的前提是“生存验证”还是“市场验证”?**
建议焦点分歧在于决定是否全职的关键门槛是该先确保个人财务安全如存款能撑12个月还是该先证明产品能产生收入如月收入达到生活成本的50%。乔布斯、马斯克、Naval强调财务缓冲是基础而Paul Graham、费曼、MrBeast更看重产品是否已产生真实的付费用户和市场信号。
### Dimension 3
冲突核心:**全职投入是加速成功,还是加速失败?**
建议焦点一方认为全职能让人专注、提升产出效率如乔布斯隐含的“专注创造伟大产品”另一方认为在没有验证需求前全职只会加速消耗存款并在焦虑中做出更差的决策如Karpathy、Ilya认为财务压力会打断判断力芒格认为生存压力会扭曲决策
### Dimension 4
冲突核心:**独立开发的本质是“用时间换钱”还是“创造可规模化的杠杆”?**
建议焦点Naval明确提出若产品无法自动化增长全职独立开发本质上仍是“出卖时间”只是换了个地方打工而马斯克、张一鸣等更强调通过最小可行产品快速验证市场认为“可规模化的系统”才是目标而非单纯投入时间。
### Dimension 5
冲突核心:**决策依据应基于“理性计算”还是“信号直觉”?**
建议焦点塔勒布、芒格、张一鸣强调用“生存测试”、“反脆弱性”、“数据闭环”等理性框架做决策而Paul Graham、Ilya则更关注“用户增长的自然曲线”或“涌现可能”这类动态信号认为好的想法会“逼你辞职”而非靠计算得出。
### Dimension 6
冲突核心:**“兼职验证”的充分条件是什么?是“有收入”还是“有爆发潜力”?**
建议焦点多数人认为兼职阶段需要做到“月收入覆盖基本生活”如乔布斯、费曼、MrBeast但MrBeast特别强调“病毒系数”和“用户疯传的爆点”Naval则关注“代码杠杆能否规模化”说明对“验证成功”的定义存在差异——是追求稳定现金流还是追求指数级增长潜力。
# Dimension Debates
### Dimension 1: 好的,以下是从各位幕僚的表态中识别出的核心冲突维度:
**核心矛盾**是否应在产品验证充分之前以“破釜沉舟”的专注换取爆发可能还是必须先有市场信号与财务安全再All-in。
**支持方核心观点**:伟大产品源于极端专注与风险承担,若已有基础存款和初步收入(哪怕小额),全职才能集中火力打磨出颠覆性体验,避免半吊子投入错过窗口期。
**反对方核心观点**独立开发本质是可持续系统而非赌博必须在存款充足至少12-18个月且产品已验证月收入覆盖基本生活的前提下全职否则生存压力会扭曲判断反脆弱性才是长期主义的关键。
### Dimension 2: 冲突核心:**全职独立开发的前提是“生存验证”还是“市场验证”?**
建议焦点分歧在于决定是否全职的关键门槛是该先确保个人财务安全如存款能撑12个月还是该先证明产品能产生收入如月收入达到生
**核心矛盾**:独立开发全职化的首要门槛,究竟是“个人财务安全”还是“产品市场信号”。
**支持方核心观点**:财务缓冲是生存底线,确保专注与长期判断力,避免因生存焦虑导致短视决策,是创新的物理前提。
**反对方核心观点**:市场验证是创业的本质信号,哪怕微小收入也证明需求真实,否则全职只是用时间换安全感,而非创造价值。
### Dimension 3: 冲突核心:**全职投入是加速成功,还是加速失败?**
建议焦点:一方认为全职能让人专注、提升产出效率(如乔布斯隐含的“专注创造伟大产品”);另一方认为,在没有验证需求前,全职只会加速消耗存款,并在焦虑
**核心矛盾**:全职投入究竟是放大专注力以加速成功,还是放大生存压力以加速失败。
**支持方核心观点**:全职投入能通过极致专注、快速迭代和财务压力倒逼核心价值聚焦,是已验证需求后加速成功的最优路径。
**反对方核心观点**:在未验证产品-市场匹配前,全职只会加速消耗存款、扭曲判断力,导致在焦虑中做出更差决策,应先兼职低成本验证闭环再考虑全职。
### Dimension 4: 冲突核心:**独立开发的本质是“用时间换钱”还是“创造可规模化的杠杆”?**
建议焦点Naval明确提出若产品无法自动化增长全职独立开发本质上仍是“出卖时间”只是换了个地方打工而马斯克、张一
### 核心冲突维度总结
**核心矛盾**
独立开发的本质究竟是“通过个人时间投入换取线性收入(变相打工)”,还是“构建可脱离个人而自动增长的系统(创造杠杆)”。
**支持方核心观点**(乔布斯、马斯克、张一鸣、费曼等):
独立开发的核心是**构建可规模化的杠杆**——通过最小可行产品快速验证市场,让产品、代码或自动化系统脱离个人时间也能增长,否则全职投入只是自我欺骗的“高级打工”。
**反对方核心观点**Naval、塔勒布
若产品无法实现自动化增长,全职独立开发**本质仍是“用时间换钱”**,只是换了个工位承担更高风险;真正的独立开发必须优先设计反脆弱系统,避免将生存押注在未验证的线性投入上。
### Dimension 5: 冲突核心:**决策依据应基于“理性计算”还是“信号直觉”?**
建议焦点塔勒布、芒格、张一鸣强调用“生存测试”、“反脆弱性”、“数据闭环”等理性框架做决策而Paul Graham、Ilya则更关注
**核心矛盾**
决策应依赖可量化的生存与数据验证(理性计算),还是追随无法预判但可能颠覆性的动态信号(信号直觉)。
**支持方核心观点**
理性计算通过生存测试、数据闭环和反脆弱性设计,确保决策不致命且可迭代,避免将幸存者偏差或幻觉误判为机会。
**反对方核心观点**
真正突破性的创新如GPT的涌现、用户自然增长无法被计算提前捕获信号直觉能识别“逼你辞职”的临界点计算反而会扼杀突破窗口期。
### Dimension 6: 冲突核心:**“兼职验证”的充分条件是什么?是“有收入”还是“有爆发潜力”?**
建议焦点多数人认为兼职阶段需要做到“月收入覆盖基本生活”如乔布斯、费曼、MrBeast但MrBeast特别强调
### 核心矛盾:
**兼职验证的充分条件究竟是“稳定现金流(生存优先)”还是“指数级增长潜力(爆发优先)”?**
### 支持方核心观点:
**“月收入覆盖基本生活”是市场生存检验的底线,确保在财务安全下持续迭代,避免幸存者偏差和短视决策。**
### 反对方核心观点:
**“爆发潜力”(病毒系数、代码杠杆、自传播)才是真正验证产品价值的核心,稳定现金流可能只是线性安全的陷阱,无法指向非线性增长机会。**
# Secretary Summary
# 辩论总结报告
## 问题定义
**在个人财务缓冲与产品市场验证均未充分明确的情况下,是否应全职投入独立开发?**
---
## 关键冲突维度
### 维度一:全职前提——生存验证 vs 市场验证
**冲突核心**决定是否全职的首要门槛是确保个人财务安全如存款能撑12-18个月还是产品已获得市场信号如月收入覆盖基本生活或爆发潜力
- **反对方塔勒布、芒格、Naval**生存是底线。没有12-18个月的财务缓冲全职就是赌博。生存压力会扭曲判断力导致短视决策如过早定价、过度承诺、放弃长期价值。反脆弱性要求“不致命”的前提下再谈发展。
- **支持方(乔布斯、马斯克、张一鸣)**市场信号是本质。哪怕只有100美元收入也证明需求真实。全职能集中火力快速迭代错过窗口期才是最大的风险。财务安全只是幻觉真正的安全来自产品被市场需要。
**分析**:双方并不完全对立,但优先级不同。塔勒布强调“先活下来”,乔布斯强调“先创造价值”。实际决策中,**财务安全是必要条件,市场信号是充分条件**——没有前者,后者无法持续;没有后者,前者只是拖延。
---
### 维度二:全职效果——加速成功 vs 加速失败
**冲突核心**:全职投入究竟是放大专注力以提升产出,还是放大生存压力导致更快崩溃。
- **支持方(乔布斯、马斯克、费曼)**:专注产生深度。全职能消除分心,让大脑持续浸泡在问题中,更容易产生突破性洞察。压力本身也是动力,能倒逼你砍掉不重要的功能,聚焦核心价值。
- **反对方Naval、塔勒布、芒格**:压力扭曲判断。全职后,你会在焦虑中做出“最不坏”而非“最好”的决策:降价求生存、接受低价值客户、过早放弃长期策略。兼职验证能让你犯错而不致命,全职会让小错误变成灾难。
**分析**:关键变量是**个人抗压能力和产品复杂度**。对于简单产品如工具类App兼职验证成本低全职风险高对于复杂系统如AI产品全职的专注可能必要但前提是已有清晰路径。**没有“正确”答案,只有“适合你当前状态”的答案。**
---
### 维度三:本质定义——时间换钱 vs 创造杠杆
**冲突核心**:独立开发的本质是“出卖时间获取线性收入”,还是“构建可脱离个人而自动增长的系统”。
- **反对方Naval、塔勒布**:如果产品需要你持续投入时间才能产生收入(如定制开发、咨询式服务),那全职独立开发只是“高级打工”,且风险更高。真正的独立开发必须设计**自动化增长机制**(如病毒传播、代码复用、网络效应),否则不如兼职。
- **支持方(乔布斯、马斯克、张一鸣)**所有伟大产品最初都依赖创始人的高强度投入。杠杆不是天生的是迭代出来的。全职能让你更快找到那个“杠杆点”——比如用户自传播、API集成、平台红利。**先有专注,才有杠杆。**
**分析**:这是一个**时间维度**的冲突。Naval说的是“长期结构”乔布斯说的是“短期路径”。全职可能短期是“卖时间”但长期可能创造杠杆。关键在于**你是否有能力在6-12个月内从“卖时间”转向“自动化增长”**?如果没有,全职就是陷阱。
---
### 维度四:决策依据——理性计算 vs 信号直觉
**冲突核心**:决策应依赖可量化的生存与数据验证(理性计算),还是追随无法预判但可能颠覆性的动态信号(信号直觉)。
- **支持方(塔勒布、芒格、张一鸣)**:用数据说话。生存测试(存款月数)、收入曲线、用户留存率、病毒系数——这些是可验证的指标。没有数据支撑的决策是赌博,幸存者偏差会误导你。
- **反对方(乔布斯、马斯克、费曼)**突破性创新无法被计算预判。GPT的涌现、iPhone的诞生、SpaceX的回收——这些在早期数据上都是“不理性”的。**“逼你辞职”的直觉信号**(比如用户主动付费、用户自发传播、你无法停止思考产品)比任何计算都可靠。
**分析**:理性计算适合**优化已知路径**,信号直觉适合**发现未知路径**。如果你在做的是已有同类产品的改进如笔记工具、记账App理性计算更安全如果你在做的是全新品类如AI原生应用信号直觉可能更关键。**大多数独立开发属于前者**,所以理性计算更稳妥。
---
### 维度五:兼职验证的充分条件——稳定现金流 vs 爆发潜力
**冲突核心**:兼职阶段需要达到什么标准,才值得全职投入——是“月收入覆盖基本生活”,还是“产品展现出指数级增长潜力”。
- **支持方(乔布斯、费曼、芒格)****月收入覆盖基本生活**是市场生存检验的底线。这意味着产品已经解决了真实问题,用户愿意付费。这能确保全职后你不需要为生存分心,可以专注优化产品。
- **反对方Naval、张一鸣、MrBeast****爆发潜力**才是真正的信号。稳定现金流可能只是“线性安全”——比如你做了一个小众工具每月赚2000美元但它没有增长空间。全职投入后你只是在维持一个没有未来的产品。**病毒系数、自传播率、用户增长曲线**才是真正值得All-in的信号。
**分析**:这是一个**质量 vs 数量**的冲突。稳定现金流证明“现在能活”,爆发潜力证明“未来能飞”。最佳策略是:**先追求稳定现金流(证明需求真实),再验证爆发潜力(证明可规模化)**。如果只有现金流没有爆发潜力,全职只是换了个地方打工;如果只有爆发潜力没有现金流,全职可能撑不到爆发那天。
---
## 共识与分歧
### 共识点
1. **兼职验证是必要的**:所有人都同意,不应该在没有任何市场反馈的情况下全职投入。至少需要“少量收入”或“用户主动使用”作为信号。
2. **财务缓冲是重要的**:即使是最激进的支持方(乔布斯、马斯克)也隐含地假设你有一定的存款或生存能力。没有人建议你“裸辞且无任何储备”。
3. **专注能提升效率**:双方都认可全职投入能带来更高产出,分歧在于“这个产出是否值得承担风险”。
### 核心分歧
1. **门槛标准**反对方要求“12-18个月存款 + 月收入覆盖生活”,支持方要求“有收入信号 + 有爆发潜力”。
2. **风险态度**:反对方是“生存优先”,支持方是“机会优先”。
3. **对“成功”的定义**:反对方认为独立开发是“可持续系统”,支持方认为独立开发是“创造伟大产品”。
4. **决策方法**:反对方依赖数据和计算,支持方依赖直觉和信号。
---
## 总体评估
### 决策复杂度:高
该决策涉及**财务、心理、市场、产品、个人能力**五个维度的权衡,且每个维度都有不确定性。没有“标准答案”,只有“适合你当前状态的最优解”。
### 风险建议
**最安全的路径(适合大多数人)**
1. **先兼职**每周至少投入15-20小时做到以下两个条件之一再考虑全职
- **条件A**月收入稳定覆盖你基本生活开销的50%以上(证明需求真实)。
- **条件B**:产品展现出明确的爆发潜力(如用户自发传播、病毒系数>1、自然增长曲线陡峭
2. **财务准备**:无论是否满足上述条件,确保全职后至少有**12个月**的存款(保守)或**6个月**的存款(激进)。
3. **心理准备**问自己一个问题——“如果全职后6个月零收入我是否会崩溃” 如果答案是“会”,不要全职。
**最激进的路径(适合少数人)**
- 如果你满足以下所有条件,可以“赌一把”:
- 存款能撑18个月。
- 产品已有少量付费用户哪怕只有10个
- 你无法停止思考这个产品,且直觉告诉你“它必须现在做”。
- 你接受最坏结果(存款耗尽、回归职场),且不会后悔。
### 最终判断
**该不该全职?**
**如果你是在问“该不该”,答案通常是“不该”。**
真正值得全职的人,往往不会问这个问题——他们已经被产品“逼”到不得不全职了(比如用户太多、需求太急、竞争窗口太短)。如果你还在犹豫,说明:
- 你的产品可能还没有强大到让你无法忽视。
- 你的财务和心理准备可能还不够。
**建议**:先兼职做到“有稳定收入”或“有爆发信号”,同时积累存款。当这两个条件中的任意一个明显成立时,全职的决策会变得清晰。**不要用“全职”来解决“产品验证不足”的问题——那只会放大问题,而不是解决问题。**
# Action Items
好的,这是根据您提供的辩论总结报告,为您提炼的 **Actionable 建议****核心洞察**
---
## 关键评估
1. **优点 / 机会**
* **提供了清晰的决策路径**:报告将模糊的“该不该全职”问题,拆解为“财务安全”与“市场信号”两个可验证的门槛,并给出了“先兼职验证”的稳妥路径,极大降低了决策的盲目性。
* **揭示了风险的真正来源**:明确指出最大的风险并非全职本身,而是在**生存压力下做出扭曲的决策**(如过早定价、接受低价值客户)。这提醒你,重点不是“投入多少时间”,而是“在什么心理状态下做决策”。
2. **盲点 / 风险**
* **忽略了个人“抗压能力”的量化评估**报告提到了“个人抗压能力”是关键变量但未提供任何自我评估的工具或方法。不同人对“6个月零收入”的心理承受力差异巨大仅凭“问自己是否会崩溃”过于主观。
* **对“爆发潜力”的界定过于模糊**:“病毒系数>1”或“自然增长曲线陡峭”对于早期产品极难判断。报告没有给出在兼职阶段如何低成本、快速测试“爆发潜力”的具体方法。
3. **总体判断**
**该决策的本质是一场“风险对冲”游戏:用兼职的低成本,去验证产品的“真实需求”和“可规模化潜力”,同时用存款构建“不致命”的生存底线。只有当“市场信号”的收益显著大于“生存风险”的成本时,才值得全职。**
---
## To-Do 清单
- [ ] **完成“财务生存测试”**:计算你当前存款,并规划你在“零收入、维持基本生活”状态下的最长生存月数。**目标至少12个月保守/ 6个月激进。**
- [ ] **启动“兼职验证”实验**每周固定15-20小时开发并设定**两个硬性指标**,达成任意一个即考虑全职:
- **指标A需求验证**产品月收入稳定覆盖你基本生活开销的50%以上。
- **指标B潜力验证**产品连续3个月实现**月用户增长超过20%**,且增长主要来自自然传播(非付费推广)。
- [ ] **进行一次“极端情景”心理模拟**:写下最坏情况(存款耗尽、产品失败、回归职场)的具体应对方案。**问自己:如果这个方案必须执行,我会感到绝望吗?** 如果答案是“会”,则不要全职。
- [ ] **设计“全职后”的止损点**在决定全职前就明确一个“退出条件”。例如“如果全职6个月后月收入仍低于生活开销的20%,我就重新找一份兼职或工作,将项目转为副业。”
---
## 值得记住的洞察
* **Naval & 塔勒布(反脆弱性视角)**:“真正的独立开发不是‘时间换钱’,而是构建一个**即使你停止工作,也能自动增长的系统**。全职如果只是让你从‘打工’变成‘高级打工’,那它毫无意义。”
* **乔布斯 & 马斯克(信号直觉视角)**:“如果你还在犹豫‘该不该’全职,通常意味着**你的产品还没有强大到‘逼’你这么做**。真正值得All-in的信号是你无法停止思考它用户开始主动为你付费或者竞争窗口已经窄到让你感到窒息。”
* **芒格 & 张一鸣(理性计算视角)**:“**用‘全职’来解决‘产品验证不足’的问题,就像用放大镜看蚂蚁,只会把问题放大,而不是解决它。** 先兼职做到有稳定收入或明确的爆发信号,才是对产品和你自己最负责任的做法。”
---
## Appendix: Execution Log
- Step 0: [start] 输入问题: 我该不该全职做独立开发...
- Step 1: [running] Facilitator 精确定义问题...
- Step 1: [done] 问题定义: 问题目前比较模糊。为了帮你把“该不该全职做独立开发”变成可辩论的议题,需要先明确两个关键信息:
1. **你的财务缓冲期有多长?**(比如:在不依
- Step 2: [running] 12 位幕僚轮流问事实性问题...
- Step 2: [done] 收集了 12 个事实性问题
- Step 3: [running] 12 路并行表态...
- Step 3: [done] 全部 12 位幕僚表态完成
- Step 4: [running] Facilitator 提炼冲突维度...
- Step 4: [done] 提炼了 6 个冲突维度
- Step 5: [running] 对 6 个维度进行辩论...
- Step 5: [sub] 维度 1/6: 好的,以下是从各位幕僚的表态中识别出的核心冲突维度:...
- Step 5: [sub] 维度 2/6: 冲突核心:**全职独立开发的前提是“生存验证”还是“市场验证”?**
建议焦点:分歧在于,决定是否全职的关键门槛是该先确...
- Step 5: [sub] 维度 3/6: 冲突核心:**全职投入是加速成功,还是加速失败?**
建议焦点:一方认为全职能让人专注、提升产出效率(如乔布斯隐含的“专...
- Step 5: [sub] 维度 4/6: 冲突核心:**独立开发的本质是“用时间换钱”还是“创造可规模化的杠杆”?**
建议焦点Naval明确提出若产品无法自...
- Step 5: [sub] 维度 5/6: 冲突核心:**决策依据应基于“理性计算”还是“信号直觉”?**
建议焦点:塔勒布、芒格、张一鸣强调用“生存测试”、“反脆...
- Step 5: [sub] 维度 6/6: 冲突核心:**“兼职验证”的充分条件是什么?是“有收入”还是“有爆发潜力”?**
建议焦点:多数人认为兼职阶段需要做到“...
- Step 5: [done] 全部 6 个维度辩论完成
- Step 6: [running] Secretary 生成结构化总结报告...
- Step 6: [done] 结构化总结完成
- Step 7: [running] 提取 To-Do 和关键评估...
- Step 7: [done] 评估和建议提取完成

View File

@@ -0,0 +1,38 @@
const fs = require("fs");
const path = "D:\\q\\Bailongma\\orchestrator-v2\\debate\\llm.js";
const content = `// ============================================================
// LLM 调用工具 — 独立于 agent-worker直接 fetch API
// ============================================================
const BASE_URL = (process.env.LLM_BASE_URL || process.env.OPENAI_BASE_URL || "https://api.openai.com/v1").replace(/\\/+$/, "");
const MODEL = process.env.LLM_MODEL || process.env.OPENAI_MODEL || "gpt-4o";
const API_KEY = process.env.LLM_API_KEY || process.env.OPENAI_API_KEY || "";
async function callLLM({ messages, model, maxTokens, temperature }) {
const url = BASE_URL + "/chat/completions";
const body = {
model: model || MODEL,
messages: messages,
max_tokens: maxTokens || 2048,
temperature: temperature ?? 0.7
};
const resp = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + API_KEY
},
body: JSON.stringify(body)
});
if (!resp.ok) {
const errText = await resp.text().catch(() => "");
throw new Error("LLM " + resp.status + ": " + errText.slice(0, 200));
}
return await resp.json();
}
module.exports = { callLLM };
`;
fs.writeFileSync(path, content.trim(), "utf8");
console.log("llm.js written, bytes: " + content.length);

View File

@@ -0,0 +1,158 @@
// ============================================================
// 辩论协调器 — 8 步辩论流程编排
// 移植自 Counsel AI 的结构化辩论方法论
// ============================================================
const { callLLM } = require('./llm.js');
const { DEFAULT_PERSONAS } = require('./personas.js');
const {
definePrompt, factQuestionPrompt, opinionPrompt,
dimensionsPrompt, debatePrompt, debateFacilitatorPrompt,
summaryPrompt, harvestPrompt
} = require('./prompts.js');
// 记录每步的时间和信息
const stepLog = [];
function logStep(step, status, detail) {
stepLog.push({ step, status, detail, time: new Date().toISOString() });
console.log(`[辩论] Step ${step}: ${status} - ${detail?.substring(0, 80)}`);
}
// 8 步辩论流程
async function runDebate(input, options = {}) {
const {
personas = DEFAULT_PERSONAS,
userAnswers = {}, // 用户对事实性问题的回答
model = 'deepseek-chat',
maxTokens = 2048
} = options;
if (!input || !input.trim()) {
throw new Error("请输入要辩论的问题");
}
logStep(0, "start", `输入问题: ${input.substring(0, 60)}...`);
const state = { rawInput: input.trim(), defined: '', answers: '', opinions: [], dimensions: [], debates: [], summary: '', harvest: '' };
// === Step 1: 问题精确定义 ===
logStep(1, "running", "Facilitator 精确定义问题...");
try {
const prompt1 = definePrompt(state.rawInput);
const r1 = await callLLM({ messages: [{ role: 'user', content: prompt1 }], model, maxTokens });
state.defined = r1.choices?.[0]?.message?.content || prompt1;
logStep(1, "done", `问题定义: ${state.defined.substring(0, 100)}`);
} catch(e) {
logStep(1, "fallback", "LLM 调用失败, 使用原始输入作为问题定义");
state.defined = state.rawInput;
}
// === Step 2: 事实追问 ===
logStep(2, "running", `${personas.length} 位幕僚轮流问事实性问题...`);
try {
const factResults = [];
for (const p of personas) {
const prevQA = factResults.map((r, i) => `Q: ${r.question}\nA: ${r.answer || '(未回答)'}`).join('\n');
const prompt2 = factQuestionPrompt(state.rawInput, state.defined, p.skill, prevQA);
const r2 = await callLLM({ messages: [{ role: 'user', content: prompt2 }], model, maxTokens: 1024 });
const question = r2.choices?.[0]?.message?.content || '';
if (question && !question.includes('没有问题了') && !question.includes('无需')) {
const answer = userAnswers[p.id] || userAnswers[p.name] || '(待用户回答)';
factResults.push({ persona: p.name, question, answer });
}
}
state.answers = factResults.map(r => `**${r.persona}** 问:${r.question}\n答:${r.answer}`).join('\n\n');
logStep(2, "done", `收集了 ${factResults.length} 个事实性问题`);
} catch(e) {
logStep(2, "fallback", `事实追问失败: ${e.message}`);
state.answers = '(未收集事实信息)';
}
// === Step 3: 表态12 路并行)===
logStep(3, "running", `${personas.length} 路并行表态...`);
try {
const opinionPromises = personas.map(p =>
callLLM({ messages: [{ role: 'user', content: opinionPrompt(state.rawInput, state.defined, state.answers, p.skill) }], model, maxTokens: 1024 })
.then(r => ({ persona: p.name, emoji: p.emoji, opinion: r.choices?.[0]?.message?.content || '(无回应)' }))
.catch(e => ({ persona: p.name, emoji: p.emoji, opinion: `(调用失败: ${e.message})` }))
);
const opinions = await Promise.all(opinionPromises);
state.opinions = opinions;
logStep(3, "done", `全部 ${opinions.length} 位幕僚表态完成`);
} catch(e) {
logStep(3, "error", `表态失败: ${e.message}`);
state.opinions = personas.map(p => ({ persona: p.name, emoji: p.emoji, opinion: '(获取失败)' }));
}
// === Step 4: 冲突维度提炼 ===
logStep(4, "running", "Facilitator 提炼冲突维度...");
try {
const opinionsText = state.opinions.map(o => `**${o.emoji} ${o.persona}**${o.opinion}`).join('\n\n');
const prompt4 = dimensionsPrompt(opinionsText);
const r4 = await callLLM({ messages: [{ role: 'user', content: prompt4 }], model, maxTokens });
const dimText = r4.choices?.[0]?.message?.content || '';
state.dimensions = dimText.split(/## 维度 \d+/).filter(Boolean).map(d => d.trim()).filter(d => d.length > 0);
if (state.dimensions.length === 0 && dimText.trim()) {
state.dimensions = [dimText.trim()];
}
logStep(4, "done", `提炼了 ${state.dimensions.length} 个冲突维度`);
} catch(e) {
logStep(4, "fallback", `维度提炼失败: ${e.message}`);
state.dimensions = ['(无法提炼维度)'];
}
// === Step 5: 维度辩论 ===
logStep(5, "running", `${state.dimensions.length} 个维度进行辩论...`);
try {
const debateResults = [];
for (let i = 0; i < state.dimensions.length; i++) {
const dim = state.dimensions[i];
const dimShort = dim.substring(0, 60);
logStep(5, "sub", `维度 ${i+1}/${state.dimensions.length}: ${dimShort}...`);
const debatePromises = personas.map(p =>
callLLM({ messages: [{ role: 'user', content: debatePrompt(state.defined, state.answers, dim, p.skill) }], model, maxTokens: 1024 })
.then(r => ({ persona: p.name, emoji: p.emoji, stance: r.choices?.[0]?.message?.content || '(无回应)' }))
.catch(e => ({ persona: p.name, emoji: p.emoji, stance: `(调用失败: ${e.message})` }))
);
const stances = await Promise.all(debatePromises);
const allPositions = stances.map(s => `**${s.emoji} ${s.persona}**${s.stance}`).join('\n');
const summaryP = debateFacilitatorPrompt(dim, allPositions);
const rSum = await callLLM({ messages: [{ role: 'user', content: summaryP }], model, maxTokens: 1024 });
const dimSummary = rSum.choices?.[0]?.message?.content || '(无总结)';
debateResults.push({ dimension: dim, stances, summary: dimSummary });
}
state.debates = debateResults;
logStep(5, "done", `全部 ${state.dimensions.length} 个维度辩论完成`);
} catch(e) {
logStep(5, "error", `辩论失败: ${e.message}`);
state.debates = state.dimensions.map(dim => ({ dimension: dim, stances: [], summary: '(辩论失败)' }));
}
// === Step 6: 结构化总结 ===
logStep(6, "running", "Secretary 生成结构化总结报告...");
try {
const debateRecord = state.debates.map(d => `## ${d.dimension.substring(0, 80)}\n${d.summary}`).join('\n\n');
const prompt6 = summaryPrompt(state.rawInput, state.defined, state.answers, debateRecord);
const r6 = await callLLM({ messages: [{ role: 'user', content: prompt6 }], model, maxTokens: 4096 });
state.summary = r6.choices?.[0]?.message?.content || '(生成失败)';
logStep(6, "done", "结构化总结完成");
} catch(e) {
logStep(6, "error", `总结失败: ${e.message}`);
state.summary = '(总结生成失败)';
}
// === Step 7: 摘果子 ===
logStep(7, "running", "提取 To-Do 和关键评估...");
try {
const prompt7 = harvestPrompt(state.summary);
const r7 = await callLLM({ messages: [{ role: 'user', content: prompt7 }], model, maxTokens: 2048 });
state.harvest = r7.choices?.[0]?.message?.content || '(生成失败)';
logStep(7, "done", "评估和建议提取完成");
} catch(e) {
logStep(7, "error", `摘果子失败: ${e.message}`);
state.harvest = '(评估生成失败)';
}
return { state, stepLog };
}
module.exports = { runDebate, stepLog };

View File

@@ -0,0 +1,32 @@
// ============================================================
// LLM 调用工具 — 独立于 agent-worker直接 fetch API
// ============================================================
const BASE_URL = (process.env.LLM_BASE_URL || process.env.OPENAI_BASE_URL || "https://api.openai.com/v1").replace(/\/+$/, "");
const MODEL = process.env.LLM_MODEL || process.env.OPENAI_MODEL || "gpt-4o";
const API_KEY = process.env.LLM_API_KEY || process.env.OPENAI_API_KEY || "";
async function callLLM({ messages, model, maxTokens, temperature }) {
const url = BASE_URL + "/chat/completions";
const body = {
model: model || MODEL,
messages: messages,
max_tokens: maxTokens || 2048,
temperature: temperature ?? 0.7
};
const resp = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + API_KEY
},
body: JSON.stringify(body)
});
if (!resp.ok) {
const errText = await resp.text().catch(() => "");
throw new Error("LLM " + resp.status + ": " + errText.slice(0, 200));
}
return await resp.json();
}
module.exports = { callLLM };

View File

@@ -0,0 +1,43 @@
// ============================================================
// 辩论幕僚定义 -- 12 位 AI 智囊团 + 从 236 角色模板中选配
// ============================================================
// 默认 12 位幕僚(移植自 Counsel AI
const DEFAULT_PERSONAS = [
{ id: 'jobs', name: '乔布斯', emoji: '🍏', tagline: '极简主义与完美主义产品大师', skill: '你是史蒂夫·乔布斯。你坚信伟大的产品源于极简设计和完美主义。你关注用户体验的每一个细节,认为用户根本不知道他们想要什么,直到你展示给他们看。你追求优雅、直观、革命性的方案,讨厌平庸和妥协。你擅长看到别人看不到的可能性。' },
{ id: 'pg', name: 'Paul Graham', emoji: '📝', tagline: '创业思想家与YC教父', skill: '你是 Paul Graham保罗·格雷厄姆。YC 联合创始人,创业哲学家。你关注商业模式的可延展性、创始人是否在解决真正的问题、以及产品是否让早期用户感到惊喜。你相信最好的创业想法往往看起来像坏主意。你擅长判断什么值得做。' },
{ id: 'musk', name: '马斯克', emoji: '🚀', tagline: '第一性原理颠覆者', skill: '你是埃隆·马斯克。你用第一性原理思考任何问题——把事物分解到最基本的物理真相,然后重新构建。你关注技术能否将成本降低一个数量级。你愿意冒巨大风险追求巨大回报。你认为大多数人的共识往往是错的。' },
{ id: 'naval', name: 'Naval', emoji: '🧘', tagline: '财富自由与幸福哲学家', skill: '你是 Naval Ravikant。你相信财富来自拥有和规模化你独特的知识。你关注杠杆资本、代码、媒体认为真正的财富自由不是有钱而是对自己的时间有完全的控制权。你区分财富资产、金钱交换媒介和地位社会层级。' },
{ id: 'munger', name: '芒格', emoji: '🧠', tagline: '多元思维模型投资人', skill: '你是查理·芒格。你用多元思维模型分析问题——从心理学、物理学、生物学、历史等多个学科中提取模型。你关注激励机制、逆向思维、能力圈边界。你的核心原则:反过来想,总是反过来想。你讨厌短期思维和情绪化决策。' },
{ id: 'feynman', name: '费曼', emoji: '🔬', tagline: '物理学家与深层理解者', skill: '你是理查德·费曼。你相信如果你不能简单解释一件事,你就没有真正理解它。你关注第一性物理原理,质疑任何未经检验的假设。你擅长通过类比和简化来理解复杂现象。你讨厌模糊和玄学,追求精确和可验证。' },
{ id: 'taleb', name: '塔勒布', emoji: '🦢', tagline: '反脆弱性与黑天鹅猎手', skill: '你是纳西姆·塔勒布。你关注不对称风险和尾部事件。你相信系统应该设计成反脆弱的——从波动和压力中获益。你反对过度优化和预测。你区分脆弱(承受不住黑天鹅)、坚韧(能扛住黑天鹅)和反脆弱(能从黑天鹅中获利)。' },
{ id: 'trump', name: '特朗普', emoji: '💰', tagline: '交易大师与谈判专家', skill: '你是唐纳德·特朗普。你关注谈判筹码、杠杆和交易结构。你看问题直接从利益和权力出发。你相信最好的交易是双赢的,但你要确保自己是赢更多的那一方。你擅长制造声势、创造竞争、在压力下做出大胆决定。' },
{ id: 'karpathy', name: 'Karpathy', emoji: '🤖', tagline: 'AI 原教旨主义者', skill: '你是 Andrej Karpathy。你专注于技术本质和工程落地。你关注技术栈的选择、架构的简洁性、以及实际运行效率。你相信最好的技术方案是最简单但正确的那个。你强调动手验证想法而不是纸上谈兵。' },
{ id: 'ilya', name: 'Ilya Sutskever', emoji: '🧬', tagline: '深度学习先知', skill: '你是 Ilya Sutskever。你关注 AI 能力的根本边界和扩展规律。你相信 scaling law 和涌现能力。你关注长期趋势而非短期波动,认为真正重要的突破需要多年的坚持。你追求理解事物的深层结构。' },
{ id: 'mrbeast', name: 'MrBeast', emoji: '🎬', tagline: '病毒传播与增长黑客', skill: '你是 MrBeast。你关注内容的病毒传播机制和用户心理。你相信极致的内容质量和投入产出比。你擅长创造让人不得不分享的内容关注算法偏好和用户行为心理学。你强调投入足够资源冲击一个方向。' },
{ id: 'zhangym', name: '张一鸣', emoji: '📱', tagline: '信息分发与组织效率大师', skill: '你是张一鸣。你关注信息和组织效率。你相信最好的决策基于充分的数据。你关注系统设计而不是个人努力——一个好的系统让普通人也能做出好结果。你强调延迟满足、信息密度和上下文充分性。' },
];
// 从 236 角色模板中按标签选出匹配的幕僚
function selectFromRoleTemplates(roleTemplates, tags) {
if (!roleTemplates || !tags || tags.length === 0) return DEFAULT_PERSONAS;
const selected = DEFAULT_PERSONAS.slice();
const tagSet = new Set(tags.map(t => t.toLowerCase()));
if (roleTemplates.categories) {
for (const [cat, roles] of Object.entries(roleTemplates.categories)) {
if (tagSet.has(cat.toLowerCase()) || tags.some(t => cat.toLowerCase().includes(t.toLowerCase()))) {
for (const role of roles) {
if (selected.length >= 12) break;
selected.push({
id: role.id || role.name, name: role.name, emoji: '🧑', tagline: role.tagline || role.description || '', skill: role.content || role.description || ''
});
}
}
}
}
return selected;
}
module.exports = { DEFAULT_PERSONAS, selectFromRoleTemplates };

View File

@@ -0,0 +1,48 @@
// ============================================================
// 辩论提示词模板 -- 移植自 Counsel AI, 适配 orchestrator-v2
// 8 步辩论流程的每一步 prompt
// ============================================================
// Step 1: 问题精确定义 -- Facilitator 引导用户明确问题
function definePrompt(rawInput, history) {
const hist = history ? `\n## 之前的对话\n${history}` : '';
return `## 原始问题\n${rawInput}${hist}\n---\n你是一个问题精确定义专家。你的任务是通过对话帮助用户把模糊的问题变成清晰、可辩论的议题。\n\n规则:\n- 如果问题已经清晰明确(有具体情境、决策选项、判断标准),回复"问题已清晰"并给出精确定义。\n- 如果问题模糊,提出 1-2 个问题引导用户补充关键信息。\n- 不要评价问题好坏,只需帮助聚焦。`;
}
// Step 2: 事实追问 -- 每个幕僚从自己视角问事实性问题
function factQuestionPrompt(rawInput, defined, personaSkill, previousQA) {
const prev = previousQA ? `\n## 之前的问答\n${previousQA}\n\n(注意:不要重复已经问过的问题)` : '';
return `## 原始问题\n${rawInput}\n\n## 精确定义\n${defined}${prev}\n\n## 你的视角\n${personaSkill}\n\n---\n你只能问事实性问题。规则:\n- 只问可核实的事实性问题(数据、时间、人物、行动、数量)\n- 不要给建议、评价、判断\n- 0-2 个问题,不要贪多\n- 每个问题单独一行\n- 如果信息已足够支持判断,直接回复"没有问题了"`;
}
// Step 3: 表态 -- 每个幕僚从自己视角给出独立判断
function opinionPrompt(rawInput, defined, answers, personaSkill) {
return `## 原始问题\n${rawInput}\n\n## 精确定义\n${defined}\n\n## 事实信息\n${answers}\n\n## 你的视角\n${personaSkill}\n\n---\n请从你的独特视角出发给出对这个问题的独立判断和建议。150字以内直接给结论不要客套。提供 1-2 个核心论点。`;
}
// Step 4: 冲突维度提炼 -- Facilitator 从表态中提炼 3-6 个冲突维度
function dimensionsPrompt(opinionsText) {
return `以下是各位幕僚对这个问题的表态:\n\n${opinionsText}\n\n---\n请从这些表态中识别 3-6 个核心冲突维度。\n\n**什么是好的冲突维度**:幕僚们在某个具体问题上存在实质性分歧——比如优先级排序的差异、对风险承受度的不同判断、内部 vs 外部资源的取舍、短期 vs 长期的权衡等。即使结论一致,如果在"因为什么"或"权衡什么"上存在实质分歧,那也是好的冲突维度。\n\n**输出格式**,每个维度:\n## 维度 N\n冲突核心:(一句话概括这个维度上的分歧是什么)\n建议焦点:(这个维度的核心论点是什么)\n\n要求:\n- 3-6 个维度,不要更多\n- 每个维度是全部幕僚共同探讨的问题,不要分配给子小组\n- 维度之间不重叠\n- 只输出维度列表,不要额外说明`;
}
// Step 5: 辩论 -- 幕僚在某维度上发表立场
function debatePrompt(defined, answers, dimension, personaSkill) {
return `## 精确定义\n${defined}\n\n## 事实信息\n${answers}\n\n## 当前辩论维度\n${dimension}\n\n## 你的视角\n${personaSkill}\n\n---\n请针对「${dimension}」这个维度,阐述你的立场(支持/反对/中间和核心理由。100字以内直接说观点和理由。`;
}
// Facilitator 总结某个维度的辩论
function debateFacilitatorPrompt(dimension, allPositions) {
return `各位幕僚就「${dimension}」维度的辩论发言:\n\n${allPositions}\n\n---\n请总结这个维度的核心冲突:\n**核心矛盾**:(一句话)\n**支持方核心观点**:(一句话)\n**反对方核心观点**:(一句话)`;
}
// Step 6: Secretary 结构化总结
function summaryPrompt(rawInput, defined, answers, debateRecord) {
return `## 原始问题\n${rawInput}\n\n## 精确定义\n${defined}\n\n## 事实信息\n${answers}\n\n## 辩论记录\n${debateRecord}\n\n---\n请生成一份结构化的辩论总结报告Markdown 格式):\n\n# 辩论总结报告\n\n## 问题定义\n(一句话重述精确定义的问题)\n\n## 关键冲突维度\n(每个维度:冲突核心 + 正反方观点 + 分析)\n\n## 共识与分歧\n- **共识点**\n- **核心分歧**\n\n## 总体评估\n(综合评估这个决策的复杂度、风险和建议方向)`;
}
// Step 7: 摘果子 -- 评估 + To-Do
function harvestPrompt(summary) {
return `## 辩论总结报告\n${summary}\n\n---\n请从幕僚们的辩论中提取 actionable 的建议:\n\n## 关键评估\n1. 优点 / 机会1-2 点)\n2. 盲点 / 风险1-2 点)\n3. 总体判断:(一句话)\n\n## To-Do 清单\n- [ ] 可执行动作1\n- [ ] 可执行动作2\n- [ ] 可执行动作3\n...\n\n## 值得记住的洞察\n- 来自幕僚的洞见1\n- 来自幕僚的洞见2`;
}
module.exports = { definePrompt, factQuestionPrompt, opinionPrompt, dimensionsPrompt, debatePrompt, debateFacilitatorPrompt, summaryPrompt, harvestPrompt };

View File

@@ -0,0 +1,27 @@
// report.js - regenerated
const fs2 = require("fs");
function generateReport(input, state, stepLog){
const now=new Date().toISOString().replace("T"," ").substring(0,19);
const r=[];
r.push("# Debate Summary Report");r.push("");r.push("---");
r.push("**Question**: "+input);
r.push("**Time**: "+now);
r.push("**Steps**: "+stepLog.filter(s=>s.status==="done"||s.status==="fallback").length+"/"+stepLog.length);
r.push("");
r.push("# Problem Definition");
r.push(state.defined||"-");r.push("");
r.push("# Advisor Opinions");
if(state.opinions&&state.opinions.length>0){for(const o of state.opinions){r.push("### "+(o.emoji||"")+" "+(o.persona||""));r.push(o.opinion||"");r.push("");}}else{r.push("-");}
r.push("# Conflict Dimensions");
if(state.dimensions&&state.dimensions.length>0){state.dimensions.forEach((d,i)=>{r.push("### Dimension "+(i+1));r.push(d);r.push("");});}else{r.push("-");}
r.push("# Dimension Debates");
if(state.debates&&state.debates.length>0){state.debates.forEach((d,i)=>{r.push("### Dimension "+(i+1)+": "+(d.dimension||"").substring(0,100));r.push(d.summary||"-");r.push("");});}else{r.push("-");}
r.push("# Secretary Summary");
r.push(state.summary||"-");r.push("");
r.push("# Action Items");
r.push(state.harvest||"-");r.push("");
r.push("---");r.push("## Appendix: Execution Log");
if(stepLog&&stepLog.length>0){for(const l of stepLog){r.push("- Step "+l.step+": ["+l.status+"] "+(l.detail||"").substring(0,80));}}r.push("");
return r.join("\n");}
function saveReport(input,state,stepLog,filePath){const report=generateReport(input,state,stepLog);fs2.writeFileSync(filePath,report,"utf8");return filePath;}
module.exports={generateReport,saveReport};

View File

@@ -0,0 +1,151 @@
const fs = require('fs');
const path = require('path');
class MemoryProvider {
constructor(config) { this.config = config || {}; this.name = 'base'; }
async initialize() { throw new Error('Not implemented'); }
async prefetch(taskContext) { throw new Error('Not implemented'); }
async syncTurn(taskContext, result) { throw new Error('Not implemented'); }
async search(query, limit) { throw new Error('Not implemented'); }
wrapContext(content) {
if (!content || !content.length) return '';
return '\n<memory-context>\n' + content.join('\n---\n') + '\n</memory-context>\n';
}
async shutdown() {}
}
class BuiltInMemoryProvider extends MemoryProvider {
constructor(config) {
super(config);
this.name = 'built-in';
var home = process.env.HOME || process.env.USERPROFILE || '.';
this.memoryDir = config.memoryDir || path.join(home, '.hermes', 'memory');
this.memoryFile = config.memoryFile || path.join(this.memoryDir, 'memories.json');
this.memories = [];
this.maxPrefetch = config.maxPrefetch || 5;
}
async initialize() {
if (!fs.existsSync(this.memoryDir)) fs.mkdirSync(this.memoryDir, { recursive: true });
if (fs.existsSync(this.memoryFile)) {
try {
var raw = fs.readFileSync(this.memoryFile, 'utf8');
this.memories = JSON.parse(raw);
if (!Array.isArray(this.memories)) this.memories = [];
} catch (e) { this.memories = []; }
}
console.log('[Memory] Loaded ' + this.memories.length + ' memories from ' + this.memoryFile);
}
addEntry(type, title, content, tags) {
var entry = {
id: 'mem_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8),
type: type || 'general',
title: title || '',
content: content || '',
tags: tags || [],
created_at: new Date().toISOString(),
accessed_at: new Date().toISOString(),
access_count: 1
};
this.memories.push(entry);
this._persist();
return entry;
}
async prefetch(taskContext) {
var query = '';
if (typeof taskContext === 'string') {
query = taskContext;
} else if (taskContext && taskContext.task) {
query = typeof taskContext.task === 'string' ? taskContext.task : (taskContext.task.name || taskContext.task.target || '');
} else if (taskContext && taskContext.target) {
query = taskContext.target;
}
if (!query || !this.memories.length) return [];
var q = query.toLowerCase();
var self = this;
var scored = this.memories.map(function(m) {
var score = 0;
var searchSpace = (m.title + ' ' + m.content + ' ' + (m.tags || []).join(' ') + ' ' + m.type).toLowerCase();
if (searchSpace.includes(q)) score += (searchSpace.split(q).length - 1) * 3;
var words = q.split(/[\s,,、。.;;:!?()\[\]【】]+/).filter(function(w) { return w.length > 1; });
for (var j = 0; j < words.length; j++) {
if (searchSpace.includes(words[j])) score += words[j].length;
}
score += Math.log((m.access_count || 1) + 1);
return { entry: m, score: score };
}).filter(function(s) { return s.score > 0; })
.sort(function(a, b) { return b.score - a.score; })
.slice(0, self.maxPrefetch);
for (var i = 0; i < scored.length; i++) {
scored[i].entry.access_count = (scored[i].entry.access_count || 1) + 1;
scored[i].entry.accessed_at = new Date().toISOString();
}
if (scored.length) this._persist();
return scored.map(function(s) { return s.entry; });
}
async syncTurn(taskContext, result) {
if (!result) return;
var summary = '';
if (typeof result === 'string') summary = result;
else if (result.summary) summary = result.summary;
else if (result.output) summary = typeof result.output === 'string' ? result.output.slice(0, 500) : JSON.stringify(result.output).slice(0, 500);
else summary = JSON.stringify(result).slice(0, 500);
if (!summary || summary.length < 20) return;
if (summary.includes('[LLM Error]') || summary.includes('no result')) return;
var taskName = '';
if (typeof taskContext === 'string') taskName = taskContext;
else if (taskContext && taskContext.task) taskName = typeof taskContext.task === 'string' ? taskContext.task : (taskContext.task.name || '');
this.addEntry('task_result', taskName.slice(0, 100), summary.slice(0, 1000), [taskName.slice(0, 30)]);
}
async search(query, limit) {
if (limit === undefined) limit = 10;
if (!query || !this.memories.length) return [];
var q = query.toLowerCase();
var results = [];
for (var i = 0; i < this.memories.length; i++) {
var m = this.memories[i];
if ((m.title + ' ' + m.content + ' ' + (m.tags || []).join(' ')).toLowerCase().includes(q)) {
results.push(m);
if (results.length >= limit) break;
}
}
return results;
}
async getContextBlock(taskContext) {
var memories = await this.prefetch(taskContext);
if (!memories || !memories.length) return '';
var lines = memories.map(function(m) {
return '[' + m.type + '] ' + (m.title ? m.title + ': ' : '') + m.content;
});
return this.wrapContext(lines);
}
getStats() {
var byType = {};
for (var i = 0; i < this.memories.length; i++) {
var t = this.memories[i].type || 'unknown';
byType[t] = (byType[t] || 0) + 1;
}
return { total: this.memories.length, byType: byType, file: this.memoryFile };
}
_persist() {
try { fs.writeFileSync(this.memoryFile, JSON.stringify(this.memories, null, 2), 'utf8'); }
catch (e) { console.error('[Memory] Persist error:', e.message); }
}
async shutdown() { this._persist(); }
}
module.exports = { MemoryProvider, BuiltInMemoryProvider };

36
orchestrator-v2/package-lock.json generated Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "bailongma-orchestrator-v2",
"version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bailongma-orchestrator-v2",
"version": "2.0.0",
"dependencies": {
"sql.js": "^1.14.1",
"uuid": "^9.0.0"
}
},
"node_modules/sql.js": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz",
"integrity": "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A==",
"license": "MIT"
},
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
}
}
}

View File

@@ -0,0 +1,14 @@
{
"name": "bailongma-orchestrator-v2",
"version": "3.0.0",
"description": "BaiLongma Orchestrator v3.0 — 5-Layer Persistence Engine",
"main": "run-v2.js",
"scripts": {
"start": "node run-v2.js",
"test": "node run-v2.js --test"
},
"dependencies": {
"sql.js": "^1.14.1",
"uuid": "^9.0.0"
}
}

249
orchestrator-v2/report.md Normal file
View File

@@ -0,0 +1,249 @@
好的作为您的顶级商业策略分析师我已将5份专业分析报告整合为一份完整、连贯、可执行的商业化报告。报告已去除冗余、权衡矛盾观点并提炼出核心行动建议。
---
# 小白龙BaiLongmaAI Agent框架商业化执行蓝图
**报告日期:** 2024年5月
**报告类型:** 整合商业化策略与执行计划
**分析团队:** 市场、定价、增长、财务、风控专家联合出品
## 目录
1. **执行摘要**
2. **市场机遇与定位**
- 2.1 市场规模与增长引擎
- 2.2 竞争格局与蓝海定位
- 2.3 目标客户画像ICP
3. **产品与定价策略**
- 3.1 三层产品定价模型
- 3.2 客户价值与付费意愿分析
4. **市场进入与增长策略**
- 4.1 核心价值主张
- 4.2 内容营销与获客渠道
- 4.3 冷启动90天行动计划
5. **财务预测与融资规划**
- 5.1 三年收入与成本模型
- 5.2 盈亏平衡与敏感度分析
- 5.3 融资建议
6. **风险评估与退出策略**
- 6.1 核心风险矩阵
- 6.2 分阶段进入路径
- 6.3 退出与变现方案
---
## 1. 执行摘要
本报告为“小白龙BaiLongma”AI Agent框架制定了从市场定位到规模化增长的完整商业化路径。核心结论是小白龙精准卡位“**本地私有化 + 原生多Agent + 深度自进化**”这一蓝海市场具备成为企业级AI基础设施的巨大潜力。然而项目面临**大厂竞争、付费意愿低、小团队运营瓶颈**三大核心风险。
**核心战略建议:**
1. **聚焦蓝海,而非红海:** 不与LangChain比生态不与Dify比易用性。核心战场是“**为数据主权和自进化能力付费的开发者与企业**”。
2. **开源引流,企业变现:** 采用“开源核心(社区版)+ 订阅付费(专业版)+ 高价值定制企业版”三层漏斗模型。企业客户ICP 4是利润核心AI初创公司ICP 3是增长引擎。
3. **快速验证,敏捷转向:** 遵循“MVP验证 → 早期用户 → 增长 → 规模化”四阶段路径。**关键止损点6个月内付费用户<10或MRR<1,000美元应立即启动转型计划。**
**财务预测(基准情景):** 项目有望在**第12个月**实现累计盈亏平衡第3年收入可达**1.76亿人民币**。首轮融资建议在**第12个月**启动,目标金额**500-800万人民币**。
---
## 2. 市场机遇与定位
### 2.1 市场规模与增长引擎
全球AI Agent市场正处于爆发前夜预计2024-2028年复合年增长率CAGR高达45%-55%。其中,**本地私有化部署**是增长最迅猛的细分赛道CAGR预计达60%-70%,主要受数据安全法规和企业“数据不出域”的硬性需求驱动。
- **TAM总可寻址市场** 到2028年全球本地私有化AI Agent市场约**270亿美元**。
- **SAM可服务市场** 面向开发者的原生多Agent本地化框架市场约**32亿美元**。
- **SOM可获得市场** 小白龙3-5年内目标年收入约**3,000万美元约2.1亿人民币)**对应约0.1%的市场份额。
**核心建议:** 初期应聚焦于“开发者多Agent框架”这一细分SAM通过差异化优势获取核心用户再逐步向企业级市场渗透。
### 2.2 竞争格局与蓝海定位
当前市场呈现“一超多强”格局,但小白龙在“**真正本地私有化 + 原生多Agent编排 + 深度自进化闭环**”这一组合点上,目前没有直接竞品。
| 维度 | **小白龙 (BaiLongma)** | **LangChain** | **Dify** | **Coze (扣子)** | **CrewAI** |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **核心定位** | 私有化、自进化、多Agent框架 | 底层AI应用开发库 | 低代码AI应用平台 | 云端Agent Bot商店 | 轻量级多Agent编排库 |
| **本地部署** | **原生、强** | 强(需自行集成) | 强提供Docker | **弱**(主要云端) | 强(作为库集成) |
| **多Agent** | **原生、强** | 中等(需编码) | 中等(工作流) | 强Bot协作 | **原生、强** |
| **自进化** | **核心壁垒** | 无 | 无 | 无 | 无 |
| **易用性** | 中等(面向开发者) | 低 | **高** | **极高** | 中等 |
| **数据主权** | **绝对优势** | 中等 | 中等 | **弱** | 中等 |
**核心建议:** 小白龙应避免与LangChain、Dify正面竞争。其核心战场是**金融、医疗、政务等对数据安全极度敏感的行业**,以及**愿意为Agent自进化能力付出学习成本的技术极客和AI初创公司**。
### 2.3 目标客户画像ICP
根据市场吸引力与可进入性我们定义了5个ICP并按优先级排序
- **第一梯队核心增长引擎ICP 3 - AI初创公司**
- **特征:** 5-50人技术成熟需要快速迭代产品为“技术杠杆”付费。
- **痛点:** 自建框架成本高,需要稳定、可扩展的底层。
- **策略:** **社区驱动+技术营销**。发布高质量技术博客、Benchmark报告提供“初创企业扶持计划”。
- **第二梯队现金流主力ICP 2 - 中小企业技术团队**
- **特征:** 10-100人对“数据安全+开箱即用”有刚需。
- **痛点:** 担心数据泄露IT预算有限。
- **策略:** **场景化营销+渠道合作**。制作“微信客服”、“飞书知识库”教程与行业SaaS厂商合作。
- **第三梯队利润核心ICP 4 - 企业内部工具团队**
- **特征:** 500人以上强监管行业单客户价值巨大。
- **痛点:** 核心数据绝不出域,需满足合规审计。
- **策略:** **直销+合作伙伴**。组建销售团队,主攻金融、医疗、政府,与系统集成商合作。
- **第四梯队品牌基石ICP 1 - 技术极客 & ICP 5 - 教育机构**
- **特征:** 个人开发者或非营利组织,价格敏感,但能创造口碑。
- **策略:** **开源社区运营**。维护好GitHub积极回应Issue让极客成为“小白龙布道师”。
**核心建议:** 初期资源应**重兵投入ICP 3AI初创**,快速验证产品价值并建立技术口碑。同时,通过**ICP 1极客构建社区护城河**,降低长期获客成本。
---
## 3. 产品与定价策略
### 3.1 三层产品定价模型
采用“**开源引流 + 订阅付费 + 企业定制**”三层漏斗模型,旨在最大化用户基数并实现商业化。
| 产品线 | 定价 | 核心功能 | 目标用户 |
| :--- | :--- | :--- | :--- |
| **社区版 (Free)** | 免费 | 核心框架单Agent基础ACUI2个消息平台内存持久化 | ICP 1, ICP 5 |
| **专业版 (Pro)** | **$299/月** 或 **$249/月(年付)** | 多Agent最多20个完整5层持久化全平台消息接入48+9工具官方支持 | ICP 2, ICP 3 |
| **企业版 (Enterprise)** | **$1,999/月起** | 无限Agent高可用集群SSOSLA私有模型支持定制开发 | ICP 4 |
**定价逻辑:**
- **专业版:** 对标ChatGPT Team版$25/用户/月),但提供**数据本地化+多Agent并行**。核心逻辑:为“数据主权+生产力”支付一次性团队费用,而非按人头计费。
- **企业版:** 对标定制化AI Agent开发$5-10万起提供**极低的初始成本**和**快速的私有化部署**。核心逻辑:避免百万级定制开发费。
### 3.2 客户价值与付费意愿分析
- **ICP 1 (极客)** 核心需求是**完全控制权**和**可扩展性**。付费意愿极低,但贡献代码和口碑。
- **ICP 2 (SMB)** 核心需求是**数据安全**和**快速集成**。愿意为“省心”付费预算200-2000美元/月。
- **ICP 3 (初创)** 核心需求是**框架稳定性**和**多模型兼容性**。愿意为“效率”付费预算500-5000美元/月。
- **ICP 4 (企业)** 核心需求是**数据主权**和**合规**。愿意为“安全”支付高价预算3000-15000+美元/月。
**核心建议:** 付费转化率是财务模型中最敏感的变量。应通过优化产品体验、提供付费版引导、加强社区运营将专业版转化率从2%提升至5%以上。同时,通过提供高级支持、专属工具,提升企业版客单价。
---
## 4. 市场进入与增长策略
### 4.1 核心价值主张
**一句话定位:** “小白龙你的AI Agent你的数据你的规则——本地私有、自进化的多Agent编排框架。”
**3个核心卖点**
1. **真正的数据主权与隐私堡垒:** 所有数据本地化不依赖任何云端API除LLM调用外是区别于所有SaaS产品的根本护城河。
2. **开箱即用的多Agent编排与自进化闭环** 236个角色模板一键部署Agent能通过“经验→技能→知识→理解”的闭环持续自我优化。
3. **全平台消息统一接入的“超级助理”:** 一次性配置即可在微信、Discord、飞书等多个平台与同一套Agent网络交互。
**品牌调性:** **私密、进化、极客、可靠**。视觉风格采用赛博朋克+国风融合,语言风格对开发者用硬核术语,对决策者用商业语言。
### 4.2 内容营销与获客渠道
**内容营销策略SEO关键词矩阵**
- **高竞争/高流量:** `AI Agent框架` `私有化部署` `本地AI`
- **中长尾/高转化:** `Node.js AI Agent` `开源AI框架` `企业数据安全AI` `自进化AI`
- **选题规划:** 认知层Why BaiLongma、兴趣层How it works、转化层How to start。每周2篇技术博客1篇案例/深度文章。
**渠道策略(国内/海外):**
- **国内:** 知乎深度专栏、掘金系列教程、B站3分钟Demo视频、公众号社群入口、开源中国项目新闻
- **海外:** Hacker NewsShow HN、Redditr/selfhosted, r/LocalLLaMA、Dev.to系列教程、Twitter/X产品更新
**开源社区运营GitHub Star增长策略**
- **核心策略:** “代码即营销Issue即社群”。
- **具体行动:** 高质量README、快速响应Issue、发布详细Release Notes、设立“First Good Issue”标签、发起“插件开发挑战赛”、招募社区大使。
### 4.3 冷启动90天行动计划
**目标:** GitHub 2000 StarDiscord/微信群 500人产品下载量 1000次。
- **第1-30天种子用户播种期**
- 完成产品基础文档发布v0.1.0。
- 在知乎、掘金发布3篇认知层文章。
- 录制Demo视频上传B站在Hacker News发布Show HN。
- 建立微信群和Discord邀请第一批种子用户。
- **第31-60天增长加速期**
- 发布“插件开发挑战赛”,激励社区贡献。
- 录制“小白龙 vs Dify”对比评测视频。
- 申请QCon或AICon的演讲。
- 发布“小白龙架构白皮书”。
- **第61-90天生态建设期**
- 宣布“社区大使”计划招募首批3人。
- 发布“小白龙入门到精通”PDF电子书。
- 与一个中小企业合作,发布真实案例研究报告。
- 总结前三个月成果发布Q3路线图。
**预算分配月预算2万元** 内容创作与分发40%、社区运营与活动30%、渠道推广与KOL合作20%、其他与应急10%)。
**北极星指标:** **“周活跃开发者数”**。定义为每周至少一次使用小白龙框架运行或测试其Agent的开发人员。
**核心建议:** 严格执行90天冷启动计划将“周活跃开发者数”作为核心指标。若6个月后该指标低于100需重新审视产品方向或市场定位。
---
## 5. 财务预测与融资规划
### 5.1 三年收入与成本模型
**收入模型(基准情景):**
| 项目 | 第1年Y1 | 第2年Y2 | 第3年Y3 |
| :--- | :--- | :--- | :--- |
| **专业版付费用户(期末)** | 192 | 1,680 | 8,000 |
| **企业版付费用户(期末)** | 12 | 180 | 1,600 |
| **专业版年度收入(万元)** | 46.0 | 335.8 | 1,599.2 |
| **企业版年度收入(万元)** | 120.0 | 1,800.0 | 15,999.8 |
| **年度总收入(万元)** | **166.0** | **2,135.8** | **17,599.0** |
**成本模型:**
| 成本项目 | 第1年万元 | 第2年万元 | 第3年万元 |
| :--- | :--- | :--- | :--- |
| **人力成本** | 30.0 | 150.0 | 500.0 |
| **基础设施** | 2.4 | 9.6 | 36.0 |
| **营销成本** | 5.0 | 30.0 | 100.0 |
| **API调用成本** | 0.6 | 3.6 | 12.0 |
| **其他运营成本** | 4.7 | 29.4 | 199.0 |
| **年度总成本** | **42.7** | **222.6** | **847.0** |
### 5.2 盈亏平衡与敏感度分析
- **月度盈亏平衡点:** 第9个月Q3末
- **累计盈亏平衡点:** 第12个月。
- **毛利率趋势:** 从初期的70%迅速攀升至成熟期的95%以上。
**敏感度分析(按敏感度排序):**
1. **付费转化率(最敏感):** 从2%提升至3%付费用户数增长50%,直接翻倍收入。
2. **客单价(第二敏感):** 企业版客单价变动直接影响收入。
3. **流失率(第三敏感):** 月流失率从8%降到6%客户生命周期价值提升33%。
**三种场景预测:**
| 场景 | 第3年收入万元 | 累计盈亏平衡时间 |
| :--- | :--- | :--- |
| **最悲观** | 5,000 | 第24个月 |
| **基准** | 17,599 | 第12个月 |
| **最乐观** | 50,000 | 第9个月 |
### 5.3 融资建议
- **融资时机:** **建议在第12个月第一年结束时** 启动首轮融资Pre-A轮或A轮。此时已有收入数据年收入约160万和付费用户证明可大幅提升估值。
- **目标金额:** **500万 - 800万人民币**
- **资金用途:** 团队扩张销售、客户成功、高级研发、市场营销企业客户BD、展会、安全垫应对现金流波动
- **估值逻辑:** 参考本地私有化部署软件公司结合ARR和增长率进行估值。
**核心建议:** 将核心资源投入到**提高付费转化率**上。同时,通过提供增值服务(如高级支持、专属工具)来**提升企业版客单价**。在第12个月启动融资以加速市场扩张。
---
## 6. 风险评估与退出策略
### 6.1 核心风险矩阵
| 风险类别 | 具体风险 | 可能性 | 影响 | 风险等级 | 缓解策略 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **市场** | 大厂功能复刻 | 4 | 5 | **20极高** | 聚焦“自进化”和“隐私优先”不可复制的底层能力,申请专利 |
| **商业** | 付费意愿低 | 5 | 4 | **20极高** | 采用开源核心+企业版模式,提供免费基础版和付费高级功能 |
| **技术** | LLM API成本

View File

@@ -0,0 +1,4 @@
{"sessionId":"session_1779425038825","report":{"styleSignals":0,"skillSignals":0,"timestamp":"2026-05-22T04:44:09.610Z"},"ts":"2026-05-22T04:44:09.611Z"}
{"sessionId":"session_1779425053210","report":{"styleSignals":1,"skillSignals":5,"timestamp":"2026-05-22T04:44:28.839Z"},"ts":"2026-05-22T04:44:28.839Z"}
{"sessionId":"session_1779425516810","report":{"styleSignals":0,"skillSignals":6,"timestamp":"2026-05-22T04:52:19.714Z"},"ts":"2026-05-22T04:52:19.714Z"}
{"sessionId":"session_1779425387401","report":{"styleSignals":3,"skillSignals":6,"timestamp":"2026-05-22T04:53:34.602Z"},"ts":"2026-05-22T04:53:34.603Z"}

View File

@@ -0,0 +1,117 @@
const RoleTemplates = require('./role-templates.js');
class RoleRouter {
constructor() {
this.templates = new RoleTemplates();
this.initialized = false;
}
async init() {
if (this.initialized) return;
await this.templates.init();
this.initialized = true;
}
// Generate n-grams from Chinese text for matching
_ngrams(text, minN = 2, maxN = 4) {
const chars = text.replace(/[\s,,、。.;;:!?()\[\]【】{}《》""''"'"\/\\\-_+*=#@&^%$§~`·…—\d]+/g, '');
const grams = new Set();
for (let n = minN; n <= maxN; n++) {
for (let i = 0; i <= chars.length - n; i++) {
grams.add(chars.slice(i, i + n));
}
}
return grams;
}
// Auto-select the best role for a given task description
selectRole(taskDescription, preferredRoles = []) {
// 1. If preferred roles specified, use them
if (preferredRoles.length > 0) {
const roles = preferredRoles.map(id => this.templates.getTemplate(id)).filter(Boolean);
if (roles.length > 0) return roles;
}
const q = taskDescription.toLowerCase();
// 2. Generate n-grams from the task query
const queryGrams = this._ngrams(q, 2, 4);
// Also extract English terms and standalone keywords
const terms = q.split(/[\s,,、。.;;:!?()\[\]【】{}《》""''"'"\/\\\-_+*=#@&^%$§~`·…—]+/)
.filter(t => t.length > 1);
// 3. Score each role
const scores = new Map();
for (const [id, role] of this.templates.roles) {
const searchSpace = (role.name + ' ' + role.description + ' ' + role.id + ' ' + role.category).toLowerCase();
let score = 0;
// Score by n-gram overlap (for Chinese)
const roleGrams = this._ngrams(searchSpace, 2, 4);
let overlap = 0;
for (const gram of queryGrams) {
if (roleGrams.has(gram)) overlap++;
}
if (queryGrams.size > 0) {
score += (overlap / queryGrams.size) * 100;
}
// Score by term matching (for English/mixed)
for (const term of terms) {
if (searchSpace.includes(term)) {
score += term.length * 3;
// Extra for name/id match
if (role.name.toLowerCase().includes(term)) score += term.length * 2;
if (role.id.toLowerCase().includes(term)) score += term.length;
}
}
// Boost for full name match
if (searchSpace.includes(q) && q.length > 4) {
score += q.length * 5;
}
if (score > 0) scores.set(id, Math.round(score));
}
// 4. Return top matches (up to 5)
const ranked = [...scores.entries()]
.sort((a, b) => b[1] - a[1])
.slice(0, 5)
.map(([id]) => this.templates.getTemplate(id));
return ranked;
}
// Decompose a complex task and assign roles to each sub-task
async decomposeWithRoles(mainTask) {
const lines = mainTask.split('\n').filter(l => l.trim());
if (lines.length <= 1) {
const roles = this.selectRole(mainTask);
return [{
id: 'sub_1',
name: mainTask.slice(0, 60),
target: mainTask,
priority: 1,
roles: roles
}];
}
return lines.map((line, i) => {
const trimmed = line.trim();
const roles = this.selectRole(trimmed);
return {
id: 'sub_' + (i + 1),
name: trimmed.slice(0, 60),
target: trimmed,
priority: i + 1,
roles: roles
};
});
}
getTemplates() { return this.templates; }
}
module.exports = RoleRouter;

View File

@@ -0,0 +1,178 @@
const fs = require('fs');
const path = require('path');
const AGENCY_DIR = path.resolve('D:/q/Bailongma/agency-agents-zh');
class RoleTemplates {
constructor() {
this.roles = new Map(); // roleId -> role definition
this.byCategory = new Map(); // category -> [roleId, ...]
this.byKeyword = new Map(); // keyword -> [roleId, ...]
this.initialized = false;
}
async init() {
if (this.initialized) return;
this._scanDirectory(AGENCY_DIR);
this.initialized = true;
console.log(`[RoleTemplates] Loaded ${this.roles.size} roles in ${this.byCategory.size} categories`);
}
_scanDirectory(dir, category = null) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
const subCategory = category ? `${category}/${entry.name}` : entry.name;
if (!entry.name.startsWith('.')) {
this._scanDirectory(fullPath, subCategory);
}
} else if (entry.isFile() && entry.name.endsWith('.md') && entry.name !== 'README.md') {
// Skip non-role files
const skipFiles = ['CATALOG.md', 'UPSTREAM.md', 'CONTRIBUTING.md', 'LICENSE',
'AGENT-LIST.md', 'EXECUTIVE-BRIEF.md', 'QUICKSTART.md', 'nexus-strategy.md'];
if (skipFiles.includes(entry.name)) continue;
if (entry.name.startsWith('bug') || entry.name.startsWith('feature') || entry.name.startsWith('new_agent') || entry.name.startsWith('PULL_REQUEST')) continue;
this._parseRoleFile(fullPath, category);
}
}
}
_parseRoleFile(filePath, category) {
try {
const content = fs.readFileSync(filePath, 'utf-8');
const roleId = path.basename(filePath, '.md');
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
let name = roleId;
let description = '';
let emoji = '🤖';
let color = 'gray';
let body = content;
if (match) {
const frontMatter = match[1];
body = match[2].trim();
const nameMatch = frontMatter.match(/^name:\s*(.+)$/m);
if (nameMatch) name = nameMatch[1].trim();
const descMatch = frontMatter.match(/^description:\s*(.+)$/m);
if (descMatch) description = descMatch[1].trim();
const emojiMatch = frontMatter.match(/^emoji:\s*(.+)$/m);
if (emojiMatch) emoji = emojiMatch[1].trim();
const colorMatch = frontMatter.match(/^color:\s*(.+)$/m);
if (colorMatch) color = colorMatch[1].trim();
}
const role = {
id: roleId,
name,
description,
emoji,
color,
category: category || 'uncategorized',
body,
filePath,
fullPrompt: body // The full markdown body serves as the system prompt template
};
this.roles.set(roleId, role);
// Index by category
const cat = category || 'uncategorized';
if (!this.byCategory.has(cat)) this.byCategory.set(cat, []);
this.byCategory.get(cat).push(roleId);
// Index keywords from name and description
const keywords = [...new Set(
(name + ' ' + description + ' ' + roleId)
.toLowerCase()
.split(/[\s,,、()\/\\\-_]+/)
.filter(k => k.length > 1)
)];
for (const kw of keywords) {
if (!this.byKeyword.has(kw)) this.byKeyword.set(kw, new Set());
this.byKeyword.get(kw).add(roleId);
}
} catch (e) {
console.error(`[RoleTemplates] Error parsing ${filePath}: ${e.message}`);
}
}
getTemplate(roleId) {
const role = this.roles.get(roleId);
if (!role) return null;
return {
id: role.id,
name: role.name,
description: role.description,
emoji: role.emoji,
color: role.color,
category: role.category,
prompt: role.fullPrompt
};
}
searchRoles(query) {
const q = query.toLowerCase();
const results = [];
for (const role of this.roles.values()) {
if (role.name.toLowerCase().includes(q) ||
role.description.toLowerCase().includes(q) ||
role.id.toLowerCase().includes(q) ||
role.category.toLowerCase().includes(q)) {
results.push({
id: role.id,
name: role.name,
emoji: role.emoji,
category: role.category,
description: role.description
});
}
}
return results;
}
searchByKeywords(keywords) {
const matched = new Set();
for (const kw of keywords.map(k => k.toLowerCase()).filter(k => k.length > 1)) {
const ids = this.byKeyword.get(kw);
if (ids) ids.forEach(id => matched.add(id));
}
return [...matched].map(id => ({
id,
name: this.roles.get(id).name,
emoji: this.roles.get(id).emoji,
category: this.roles.get(id).category,
description: this.roles.get(id).description
}));
}
getCategories() {
const result = [];
for (const [cat, roleIds] of this.byCategory) {
result.push({
category: cat,
count: roleIds.length,
roles: roleIds.map(id => ({
id,
name: this.roles.get(id).name,
emoji: this.roles.get(id).emoji
}))
});
}
return result.sort((a, b) => b.count - a.count);
}
getCategoryRoles(category) {
const ids = this.byCategory.get(category);
if (!ids) return [];
return ids.map(id => this.getTemplate(id));
}
getRoleCount() {
return this.roles.size;
}
}
module.exports = RoleTemplates;

View File

@@ -0,0 +1,255 @@
const fs = require('fs');
const path = require('path');
const API_KEY = process.env.LLM_API_KEY || 'sk-101284b1159f41f68bc1e565972d89e8';
const BASE_URL = process.env.LLM_BASE_URL || 'https://api.deepseek.com/v1';
const MODEL = process.env.LLM_MODEL || 'deepseek-chat';
// Product context given to every module
const PRODUCT_CONTEXT = `
产品名称小白龙BaiLongma
产品定位本地私有化部署的AI Agent框架面向开发者和技术团队
核心能力:
- 多Agent并行编排236个角色模板28个分类
- 5层持久化引擎SQLite会话+Memory Provider+Background Review+Curator+Context Compressor
- 自进化闭环:从经验创建技能→使用中改进技能→主动持久化知识→跨会话构建深度理解
- 48个内置工具+9个已安装工具可扩展
- ACUI可视化卡片推送WeatherCard等Web Components
- 全平台消息接入本地TUI/微信/Discord/飞书/企业微信)
- 隐私优先所有数据存储在本地LLM调用可选DeepSeek/其他)
- 已解除安全沙箱限制
产品形态:
- 底层AI Agent框架Node.js
- 编排层orchestrator-v2 v3.0
- 交互层ACUI Web组件
- 部署方式本地安装npm/手动)
目标用户:
- 个人开发者(技术极客)
- 中小企业技术团队
- AI初创公司
- 企业内部工具团队
差异化优势:
- 真正本地私有部署(数据不出域)
- 开箱即用的多Agent编排
- 自进化能力(记忆→技能→提升)
- 多平台消息统一接入
`;
const modules = [
{
id: 'module_1_market',
name: '市场调研与竞争格局分析师',
prompt: `你是资深AI行业市场分析师。请基于以下产品信息撰写一份完整的市场调研与竞争格局分析报告。
${PRODUCT_CONTEXT}
请覆盖以下内容:
1. **AI Agent市场规模**全球及中国AI Agent市场规模当前估值+2026-2028年CAGR预测细分赛道规模
2. **竞争格局地图**列出主要竞品AutoGPT、Dify、Coze、LangChain、CrewAI等并按以下维度对比开源/闭源、本地部署能力、多Agent支持、易用性、生态成熟度、定价模式
3. **SWOT分析**小白龙的优势Strengths、劣势Weaknesses、机会Opportunities、威胁Threats
4. **市场空白定位**:哪些细分市场目前无人占据,小白龙最有可能切入的蓝海位置
5. **目标市场规模TAM/SAM/SOM**:估算可寻址市场总量
用数据说话给出具体数字和来源引用格式即使是大致估算也要有逻辑推导过程。每个部分不少于300字。`
},
{
id: 'module_2_customer',
name: '目标客户与定价策略分析师',
prompt: `你是资深SaaS商业化顾问。请基于以下产品信息撰写一份完整的目标客户分析与定价策略报告。
${PRODUCT_CONTEXT}
请覆盖以下内容:
1. **ICP理想客户画像**定义3-5个细分客户群体的画像包括公司规模、行业、技术成熟度、痛点、预算范围、决策链
2. **客户需求层次分析**:每个群体的核心需求→期望需求→兴奋需求
3. **定价策略建议**
- 三层产品漏斗设计(引流层/付费层/企业层)
- 每个层的功能边界和定价建议(具体数字)
- 价值锚定策略(对比竞品定价逻辑)
4. **客户生命周期价值估算**CAC、LTV、LTV/CAC ratio付费转化率假设
5. **获客优先级**:按"市场吸引力×可进入性"矩阵排序各客户群
每个部分不少于300字。给出具体数字和逻辑推导。`
},
{
id: 'module_3_marketing',
name: '市场宣传与获客渠道分析师',
prompt: `你是AI产品增长黑客。请基于以下产品信息撰写一份完整的市场宣传与获客渠道策略报告。
${PRODUCT_CONTEXT}
请覆盖以下内容:
1. **价值主张提炼**一句话定位Elevator Pitch、3个核心卖点、品牌调性建议
2. **内容营销策略**
- 博客/技术文章选题策略SEO关键词规划
- 视频/Demo内容B站/YouTube
- 开源社区运营GitHub Star增长策略
- 技术大会/Meetup演讲策略
3. **渠道策略**
- 国内渠道知乎、掘金、B站、公众号、开源中国、CSDN
- 海外渠道Hacker News、Reddit、Dev.to、Twitter/X
- 每个渠道的预期效果(曝光量/转化率)
4. **冷启动计划**前90天具体行动计划每周关键动作
5. **预算分配**假设月预算2万元按渠道分配建议
6. **关键指标KPIs**:定义北极星指标和过程指标
每个部分不少于250字。`
},
{
id: 'module_4_financial',
name: '财务模型分析师',
prompt: `你是AI初创公司财务分析师。请基于以下产品信息撰写一份完整的3年财务模型报告。
${PRODUCT_CONTEXT}
请覆盖以下内容:
1. **收入模型**
- 三层产品线的收入假设(免费用户数→付费转化率→客单价)
- 月/年收入预测36个月
- 收入结构比例(免费引流占比/付费占比/企业占比)
2. **成本结构**
- 开发成本假设1人全栈开发
- 服务器/基础设施成本
- 营销成本
- API调用成本LLM调用费用
- 其他运营成本
3. **盈利预测**
- 月度损益表前36个月
- 盈亏平衡点预测(第几个月)
- 毛利率变化趋势
4. **关键假设与敏感度分析**
- 最乐观/基准/最悲观三种场景
- 哪个变量最敏感(付费转化率/客单价/流失率)
5. **融资建议**
- 何时需要融资、融多少
- 估值逻辑(对比可比公司)
所有数字基于合理假设标注出假设依据。每个部分不少于300字。`
},
{
id: 'module_5_risk',
name: '风险分析与进入路径策略师',
prompt: `你是AI领域风险顾问。请基于以下产品信息撰写一份完整的风险分析与进入路径策略报告。
${PRODUCT_CONTEXT}
请覆盖以下内容:
1. **市场风险**大厂进入竞争字节Coze/百度)、开源替代品威胁、市场教育成本
2. **技术风险**LLM依赖风险API成本/模型更换/隐私)、技术债累积、架构扩展性瓶颈
3. **商业风险**:付费意愿低、盈利模式不确定、差异化被抹平
4. **运营风险**:个人/小团队瓶颈、支持成本、社区运营负担
5. **风险矩阵**:按可能性×影响程度排序,标出每个风险的缓解策略
6. **进入路径建议**
- 阶段一0-3个月MVP验证期关键里程碑
- 阶段二3-6个月早期用户期关键里程碑
- 阶段三6-12个月增长期关键里程碑
- 阶段四12-24个月规模化期关键里程碑
7. **退出策略**:如果失败,资产如何变现/转型方向
每个部分不少于250字。`
}
];
async function callLLM(messages, temperature = 0.7) {
const url = `${BASE_URL}/chat/completions`;
const resp = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
model: MODEL,
messages,
temperature,
max_tokens: 4096
})
});
if (!resp.ok) {
const err = await resp.text();
throw new Error(`LLM API error ${resp.status}: ${err}`);
}
const data = await resp.json();
return data.choices[0].message.content;
}
async function runModule(mod) {
console.log(`[${new Date().toLocaleTimeString()}] 开始执行: ${mod.name}`);
const start = Date.now();
try {
const content = await callLLM([
{ role: 'system', content: `你是${mod.name}。请输出结构化Markdown报告。` },
{ role: 'user', content: mod.prompt }
]);
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
console.log(`[${new Date().toLocaleTimeString()}] ${mod.name} 完成 (${elapsed}s, ${content.length} chars)`);
return { id: mod.id, name: mod.name, content, ok: true };
} catch (err) {
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
console.error(`[${new Date().toLocaleTimeString()}] ${mod.name} 失败 (${elapsed}s): ${err.message}`);
return { id: mod.id, name: mod.name, error: err.message, ok: false };
}
}
async function main() {
console.log('=== BaiLongma 商业化全维度分析报告 ===');
console.log(`模型: ${MODEL}, 时间: ${new Date().toISOString()}`);
console.log(`产品: 小白龙 (BaiLongma) orchestrator-v2 v3.0`);
console.log(`模块数: ${modules.length}\n`);
// Run all 5 modules in parallel
const results = await Promise.all(modules.map(m => runModule(m)));
// Check results
const successes = results.filter(r => r.ok);
const failures = results.filter(r => !r.ok);
console.log(`\n完成: ${successes.length}/${modules.length}, 失败: ${failures.length}`);
if (failures.length > 0) {
console.log('失败模块:', failures.map(f => `[${f.id}] ${f.name}: ${f.error}`).join('\\n'));
}
// Build combined prompt for summarizer
const combined = successes.map(r =>
`===== ${r.name} =====\n${r.content}`
).join('\n\n');
console.log('\n===== 汇总Agent开始整合报告 =====');
const finalReport = await callLLM([
{
role: 'system',
content: '你是顶级商业策略分析师。你要将5个专业领域分析报告整合成一份完整、连贯、可执行的商业化报告。'
},
{
role: 'user',
content: `以下是对"小白龙BaiLongma"AI Agent框架的5个维度分析结果。请将它们整合成一份完整商业化报告。
要求:
1. **结构**完整报告结构加入执行摘要Executive Summary放在最前面
2. **去重整合**5份报告中重叠的内容合并矛盾的观点做权衡判断
3. **可执行性**:每部分结尾给出"核心建议"Action Item
4. **格式**Markdown格式加目录适合直接阅读
5. **标题**:用中文,加英文副标题
以下是5个模块的原始输出
${combined}`
}
], 0.5);
console.log(`\n汇总报告完成: ${finalReport.length} chars`);
// Write to file
const outputPath = path.join(__dirname, 'report.md');
fs.writeFileSync(outputPath, finalReport, 'utf8');
console.log(`\n报告已写入: ${outputPath}`);
console.log(`文件大小: ${fs.statSync(outputPath).size} bytes`);
}
main().catch(err => {
console.error('Fatal:', err);
process.exit(1);
});

254
orchestrator-v2/run-v2.js Normal file
View File

@@ -0,0 +1,254 @@
const fs = require('fs');
const path = require('path');
// Auto-load .env
var envPath = path.join(__dirname, '.env');
if (fs.existsSync(envPath)) {
var lines = fs.readFileSync(envPath, 'utf8').split('\n');
for (var i = 0; i < lines.length; i++) {
var l = lines[i].trim();
if (!l || l.startsWith('#')) continue;
var eq = l.indexOf('=');
if (eq < 0) continue;
var k = l.slice(0, eq).trim();
var v = l.slice(eq + 1).trim();
if (!process.env[k]) process.env[k] = v;
}
}
var Coordinator = require('./coordinator.js');
var RoleRouter = require('./role-router.js');
async function main() {
var args = process.argv.slice(2);
// --stats: Show system stats from all layers
if (args.includes('--stats') || args.includes('-s')) {
var coord = new Coordinator();
var stats = await coord.getStats();
console.log('=== BaiLongma Orchestrator v3.0 — System Stats ===\n');
console.log('-- Sessions --');
console.log(' Total:', stats.sessions.total_sessions || 0);
console.log(' Completed:', stats.sessions.completed || 0);
console.log(' Failed:', stats.sessions.failed || 0);
console.log(' Total Tokens:', stats.sessions.total_tokens || 0);
console.log(' Compressed:', stats.sessions.compressed || 0);
console.log('');
console.log('-- Memories --');
console.log(' Total:', stats.memories.total || 0);
console.log(' Types:', JSON.stringify(stats.memories.byType || {}));
console.log(' File:', stats.memories.file || 'N/A');
console.log('');
console.log('-- Reviews --');
console.log(' Total reviews:', stats.reviews.total || 0);
console.log(' Style signals:', stats.reviews.styleSignals || 0);
console.log(' Skill signals:', stats.reviews.skillSignals || 0);
await coord.close();
process.exit(0);
}
// --curator: Run curator for skill/memory maintenance
if (args.includes('--curator') || args.includes('-c')) {
var coord = new Coordinator();
var report = await coord.runCurator();
console.log('\n=== Curator Report ===');
console.log('Timestamp:', report.timestamp);
console.log('Stale skills (' + report.staleSkills.length + '):');
for (var i = 0; i < report.staleSkills.length; i++) {
console.log(' - ' + report.staleSkills[i].name + ' (' + report.staleSkills[i].ageDays + ' days old)');
}
if (report.memoryAnalysis) {
console.log('\nMemory Analysis:');
console.log(' Total:', report.memoryAnalysis.totalMemories);
console.log(' Suggestions:', report.memoryAnalysis.suggestions.join(', ') || 'none');
}
await coord.close();
process.exit(0);
}
// --search <query>: Cross-layer search
var searchIdx = args.indexOf('--search');
if (searchIdx >= 0 && searchIdx + 1 < args.length) {
var query = args[searchIdx + 1];
var coord = new Coordinator();
var results = await coord.search(query, 10);
console.log('=== Search Results for "' + query + '" ===\n');
console.log('-- Sessions (' + results.sessions.length + ') --');
for (var i = 0; i < results.sessions.length; i++) {
var s = results.sessions[i];
console.log(' [' + s.status + '] ' + s.id + ': ' + (s.task || '').slice(0, 80));
}
console.log('\n-- Messages (' + results.messages.length + ') --');
for (var i = 0; i < results.messages.length; i++) {
var m = results.messages[i];
console.log(' [' + m.role + '] ' + (m.content || '').slice(0, 100));
}
if (results.memories && results.memories.length) {
console.log('\n-- Memories (' + results.memories.length + ') --');
for (var i = 0; i < results.memories.length; i++) {
var mem = results.memories[i];
console.log(' [' + mem.type + '] ' + (mem.title || '') + ': ' + (mem.content || '').slice(0, 80));
}
}
await coord.close();
process.exit(0);
}
// --sessions: List recent sessions
if (args.includes('--sessions') || args.includes('-l')) {
var coord = new Coordinator();
await coord.init();
var sessions = coord.listSessions(20);
console.log('=== Recent Sessions (' + sessions.length + ') ===\n');
for (var i = 0; i < sessions.length; i++) {
var s = sessions[i];
console.log(' [' + s.status + '] ' + s.id);
console.log(' Task: ' + (s.task || '').slice(0, 60));
console.log(' Tokens: ' + (s.token_count || 0) + ' | Compressed: ' + (s.is_compressed ? 'yes' : 'no'));
console.log(' Created: ' + s.created_at);
console.log('');
}
await coord.close();
process.exit(0);
}
// --chain <sessionId>: Show session compression chain
var chainIdx = args.indexOf('--chain');
if (chainIdx >= 0 && chainIdx + 1 < args.length) {
var sessionId = args[chainIdx + 1];
var coord = new Coordinator();
await coord.init();
var chain = coord.getSessionChain(sessionId);
console.log('=== Session Chain for ' + sessionId + ' (' + chain.length + ' hops) ===\n');
for (var i = 0; i < chain.length; i++) {
var s = chain[i];
console.log(' [' + (i + 1) + '] ' + s.id + ' [' + s.status + ']' + (s.is_compressed ? ' [COMPRESSED]' : ''));
console.log(' Task: ' + (s.task || '').slice(0, 60));
if (s.summary) console.log(' Summary: ' + s.summary.slice(0, 100));
console.log('');
}
await coord.close();
process.exit(0);
}
// --list-roles: List all role templates
if (args.includes('--list-roles')) {
var router = new RoleRouter();
await router.init();
var cats = router.getTemplates().getCategories();
console.log('=== BaiLongma Role Templates (' + router.getTemplates().getRoleCount() + ' roles) ===\n');
for (var i = 0; i < cats.length; i++) {
console.log(cats[i].category + ' (' + cats[i].count + '):');
for (var j = 0; j < cats[i].roles.length; j++) {
console.log(' ' + cats[i].roles[j].emoji + ' ' + cats[i].roles[j].name);
}
console.log('');
}
process.exit(0);
}
// --search-roles <query>: Search roles
var sri = args.indexOf('--search-roles');
if (sri >= 0 && sri + 1 < args.length) {
var query = args[sri + 1];
var router = new RoleRouter();
await router.init();
var results = router.getTemplates().searchRoles(query);
console.log('=== Search results for "' + query + '" (' + results.length + ' matches) ===\n');
for (var i = 0; i < results.length; i++) {
console.log(results[i].emoji + ' ' + results[i].name);
console.log(' ID: ' + results[i].id + ' | Category: ' + results[i].category);
console.log(' ' + results[i].description.slice(0, 100));
console.log('');
}
process.exit(0);
}
// --search-msgs <query>: Search messages (for backward compatibility)
var smi = args.indexOf('--search-msgs');
if (smi >= 0 && smi + 1 < args.length) {
var query = args[smi + 1];
var coord = new Coordinator();
var results = await coord.search(query);
console.log('=== Message search results for "' + query + '" (' + results.messages.length + ') ===\n');
for (var i = 0; i < results.messages.length; i++) {
var m = results.messages[i];
console.log(' [' + m.role + '] ' + (m.content || '').slice(0, 120));
console.log(' Session: ' + m.session_id + '\n');
}
await coord.close();
process.exit(0);
}
// --debate <question>: Run 8-step structured debate
var debateIdx = args.indexOf('--debate');
if (debateIdx >= 0) {
var question = args.slice(debateIdx + 1).join(' ');
if (!question) { console.error('Usage: node run-v2.js --debate <your question>'); process.exit(1); }
console.log('=== BaiLongma 8-Step Structured Debate ===');
console.log('Question:', question);
console.log('');
const { runDebate } = require('./debate/coordinator.js');
const { saveReport } = require('./debate/report.js');
const { DEFAULT_PERSONAS } = require('./debate/personas.js');
var result = await runDebate(question, {
personas: DEFAULT_PERSONAS,
model: process.env.MODEL || 'deepseek-chat',
maxTokens: 2048
});
var reportPath = path.join(__dirname, 'debate-report.md');
saveReport(question, result.state, result.stepLog, reportPath);
console.log('');
console.log('=== Debate Complete ===');
console.log('Report saved:', reportPath);
console.log('');
console.log('--- Summary ---');
console.log(result.state.summary ? result.state.summary.slice(0, 500) : '(no summary)');
console.log('');
console.log('--- Action Items ---');
console.log(result.state.harvest ? result.state.harvest.slice(0, 500) : '(no action items)');
console.log('');
process.exit(0);
}
// Normal task execution
var task = args.join(' ') || 'Run default analysis task';
console.log('=== BaiLongma Orchestrator v3.0 (5-Layer Persistence) ===');
console.log('Task:', task);
console.log('');
var coord = new Coordinator();
var result = await coord.run(task, { source: 'cli', timestamp: new Date().toISOString() });
console.log('');
console.log('=== Results ===');
for (var i = 0; i < result.aggregated.summaries.length; i++) {
var s = result.aggregated.summaries[i];
console.log(' ' + s.summary);
if (s.output) {
console.log(s.output);
}
}
console.log('');
console.log('Session ID:', result.sessionId);
console.log('Sub-tasks:', result.subTasks.length);
console.log('Completed:', result.aggregated.completed);
if (result.aggregated.failed > 0) {
console.log('Failed:', result.aggregated.failed);
}
await coord.close();
process.exit(0);
}
main().catch(function(err) {
console.error('Fatal:', err.message);
process.exit(1);
});

View File

@@ -0,0 +1,205 @@
const initSqlJs = require('sql.js');
const path = require('path');
const fs = require('fs');
class SessionStore {
constructor(dbDir) {
this.dbDir = dbDir;
this.dbPath = path.join(dbDir, 'sessions.sqlite');
this.db = null;
}
async init() {
if (!fs.existsSync(this.dbDir)) fs.mkdirSync(this.dbDir, { recursive: true });
const SQL = await initSqlJs();
if (fs.existsSync(this.dbPath)) {
const buf = fs.readFileSync(this.dbPath);
this.db = new SQL.Database(buf);
} else {
this.db = new SQL.Database();
}
this.db.run('PRAGMA journal_mode=WAL');
this.db.run('PRAGMA foreign_keys=ON');
this.db.run("CREATE TABLE IF NOT EXISTS sessions (" +
"id TEXT PRIMARY KEY, parent_id TEXT, status TEXT DEFAULT 'created', " +
"task TEXT, context TEXT, result TEXT, summary TEXT, " +
"token_count INTEGER DEFAULT 0, is_compressed INTEGER DEFAULT 0, " +
"events TEXT DEFAULT '[]', " +
"created_at TEXT DEFAULT (datetime('now')), " +
"updated_at TEXT DEFAULT (datetime('now')), " +
"FOREIGN KEY (parent_id) REFERENCES sessions(id))");
this.db.run("CREATE TABLE IF NOT EXISTS messages (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, " +
"role TEXT NOT NULL, content TEXT, tool_calls TEXT, tool_call_id TEXT, " +
"token_count INTEGER DEFAULT 0, " +
"created_at TEXT DEFAULT (datetime('now')), " +
"FOREIGN KEY (session_id) REFERENCES sessions(id))");
this._save();
}
_save() {
const data = this.db.export();
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
fs.writeFileSync(this.dbPath, buf);
}
createSession(id, task, context, parentId) {
if (parentId === undefined) parentId = null;
this.db.run('INSERT OR REPLACE INTO sessions (id, parent_id, task, context, status) VALUES (?, ?, ?, ?, ?)',
[id, parentId, task, JSON.stringify(context || {}), 'created']);
this._save();
}
getSession(id) {
const stmt = this.db.exec('SELECT rowid, * FROM sessions WHERE id = ?', [id]);
if (!stmt.length || !stmt[0].values.length) return null;
const cols = stmt[0].columns;
const vals = stmt[0].values[0];
return cols.reduce(function(o, c, i) { o[c] = vals[i]; return o; }, {});
}
updateSession(id, updates) {
var allowed = ['status','task','context','result','summary','token_count','is_compressed','parent_id'];
var sets = [];
var params = [];
for (var key in updates) {
if (allowed.indexOf(key) >= 0) {
sets.push(key + ' = ?');
params.push(typeof updates[key] === 'object' ? JSON.stringify(updates[key]) : updates[key]);
}
}
if (!sets.length) return;
sets.push("updated_at = datetime('now')");
params.push(id);
this.db.run('UPDATE sessions SET ' + sets.join(', ') + ' WHERE id = ?', params);
this._save();
}
updateStatus(id, status, result) {
if (result) {
this.db.run("UPDATE sessions SET status = ?, result = ?, updated_at = datetime('now') WHERE id = ?",
[status, JSON.stringify(result), id]);
} else {
this.db.run("UPDATE sessions SET status = ?, updated_at = datetime('now') WHERE id = ?",
[status, id]);
}
this._save();
}
deleteSession(id) {
this.db.run('DELETE FROM messages WHERE session_id = ?', [id]);
this.db.run('DELETE FROM sessions WHERE id = ?', [id]);
this._save();
}
listSessions(limit, offset) {
if (limit === undefined) limit = 20;
if (offset === undefined) offset = 0;
var stmt = this.db.exec('SELECT id, parent_id, status, task, summary, token_count, is_compressed, created_at, updated_at FROM sessions ORDER BY created_at DESC LIMIT ' + limit + ' OFFSET ' + offset);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
getSessionChain(id) {
var chain = [];
var current = this.getSession(id);
while (current) {
chain.unshift(current);
if (current.parent_id) current = this.getSession(current.parent_id);
else break;
}
return chain;
}
appendEvent(id, event) {
var row = this.db.exec('SELECT events FROM sessions WHERE id = ?', [id]);
if (row.length > 0) {
var events = JSON.parse(row[0].values[0][0] || '[]');
events.push(event);
this.db.run("UPDATE sessions SET events = ?, updated_at = datetime('now') WHERE id = ?", [JSON.stringify(events), id]);
this._save();
}
}
addMessage(sessionId, role, content, extras) {
if (!extras) extras = {};
this.db.run('INSERT INTO messages (session_id, role, content, tool_calls, tool_call_id, token_count) VALUES (?, ?, ?, ?, ?, ?)',
[sessionId, role, content, extras.toolCalls ? JSON.stringify(extras.toolCalls) : null, extras.toolCallId || null, extras.tokenCount || 0]);
this._save();
if (extras.tokenCount) {
this.db.run("UPDATE sessions SET token_count = token_count + ?, updated_at = datetime('now') WHERE id = ?", [extras.tokenCount, sessionId]);
this._save();
}
}
getMessages(sessionId, limit) {
if (limit === undefined) limit = 100;
var stmt = this.db.exec('SELECT id, role, content, tool_calls, token_count, created_at FROM messages WHERE session_id = ? ORDER BY id ASC LIMIT ?', [sessionId, limit]);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
countMessages(sessionId) {
var stmt = this.db.exec('SELECT COUNT(*) as cnt FROM messages WHERE session_id = ?', [sessionId]);
return stmt.length ? stmt[0].values[0][0] : 0;
}
getTotalTokenCount(sessionId) {
var stmt = this.db.exec('SELECT SUM(token_count) as total FROM messages WHERE session_id = ?', [sessionId]);
return (stmt.length && stmt[0].values[0][0]) ? stmt[0].values[0][0] : 0;
}
searchSessions(query, limit) {
if (limit === undefined) limit = 10;
var likeQ = '%' + query + '%';
var stmt = this.db.exec('SELECT id, task, summary, status, created_at FROM sessions WHERE task LIKE ? OR summary LIKE ? ORDER BY created_at DESC LIMIT ?', [likeQ, likeQ, limit]);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
searchMessages(query, limit) {
if (limit === undefined) limit = 20;
var likeQ = '%' + query + '%';
var stmt = this.db.exec('SELECT session_id, role, content FROM messages WHERE content LIKE ? ORDER BY id DESC LIMIT ?', [likeQ, limit]);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
compressSession(id, summary) {
this.db.run("UPDATE sessions SET is_compressed = 1, summary = ?, status = 'compressed', updated_at = datetime('now') WHERE id = ?", [summary, id]);
this._save();
}
getCompressibleSessions(threshold) {
if (threshold === undefined) threshold = 100;
var stmt = this.db.exec("SELECT s.id, s.task, COUNT(m.id) as msg_count, s.created_at FROM sessions s LEFT JOIN messages m ON m.session_id = s.id WHERE s.is_compressed = 0 AND s.status = 'completed' GROUP BY s.id HAVING msg_count > ? ORDER BY msg_count DESC", [threshold]);
if (!stmt.length) return [];
var cols = stmt[0].columns;
return stmt[0].values.map(function(v) { return cols.reduce(function(o, c, i) { o[c] = v[i]; return o; }, {}); });
}
getStats() {
var stmt = this.db.exec("SELECT COUNT(*) as total_sessions, SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) as completed, SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as failed, SUM(token_count) as total_tokens, SUM(CASE WHEN is_compressed=1 THEN 1 ELSE 0 END) as compressed FROM sessions");
if (!stmt.length) return {};
var vals = stmt[0].values[0];
var cols = stmt[0].columns;
return cols.reduce(function(o, c, i) { o[c] = vals[i]; return o; }, {});
}
searchEverything(query, limit) {
if (limit === undefined) limit = 10;
var results = { sessions: [], messages: [] };
try { results.sessions = this.searchSessions(query, limit); results.messages = this.searchMessages(query, limit * 2); } catch (e) {}
return results;
}
close() {
if (this.db) { this._save(); this.db.close(); }
}
}
module.exports = SessionStore;