Files
BaiLongma/orchestrator-v2/debate/coordinator.js
chengjiaxi 664ffb3834 小白龙 Bailongma - 初始提交
自主操作员与思考搭档系统。
包含 orchestrator-v2 多Agent编排层、后台意识引擎、记忆系统、ACUI 组件。
2026-05-22 19:22:06 +08:00

159 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 辩论协调器 — 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 };