mcp_llm-debate 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,218 @@
1
+ /**
2
+ * 辩论引擎 - 核心编排逻辑
3
+ * 负责根据辩论图定义,按轮次依次调用 LLM 生成各角色发言
4
+ */
5
+
6
+ import { LLMClient } from './llm-client.js';
7
+ import { SessionManager } from './session-manager.js';
8
+
9
+ export class DebateEngine {
10
+ constructor(llmClient, sessionManager) {
11
+ this.llm = llmClient;
12
+ this.sessions = sessionManager;
13
+ }
14
+
15
+ /**
16
+ * 执行一次完整的辩论
17
+ * @param {Object} graphDef - 辩论图定义
18
+ * @param {Object} input - 输入参数 {subject, context, previous_session_id}
19
+ * @param {Object} options - 选项 {parallel, timeout_ms, stream}
20
+ * @returns {Promise<Object>} 辩论结果 session
21
+ */
22
+ async runDebate(graphDef, input, options = {}) {
23
+ const { subject, context = {}, previous_session_id } = input;
24
+
25
+ // 创建或加载 session
26
+ let session;
27
+ if (previous_session_id) {
28
+ session = this.sessions.getSession(previous_session_id);
29
+ if (!session) {
30
+ throw new Error(`上一轮 Session 未找到: ${previous_session_id}`);
31
+ }
32
+ } else {
33
+ session = this.sessions.createSession(graphDef, subject, context);
34
+ }
35
+
36
+ const rounds = graphDef.rounds || [];
37
+ const roles = graphDef.roles || [];
38
+ const rules = graphDef.rules || {};
39
+ const sessionId = session.session_id;
40
+
41
+ // 构建辩论历史(用于上下文传递)
42
+ const history = session.rounds.map(r => ({
43
+ role: 'assistant',
44
+ content: `[${r.role}] ${r.content}`
45
+ }));
46
+
47
+ // 记录哪些阶段已完成,避免重复执行
48
+ const completedPhases = new Set(session.rounds.map(r => r.phase));
49
+
50
+ // 按顺序执行每个轮次
51
+ for (const round of rounds) {
52
+ // 跳过已完成的阶段
53
+ if (completedPhases.has(round.phase) && !options.force) {
54
+ continue;
55
+ }
56
+
57
+ const speakers = round.speakers || [];
58
+ const roundResults = [];
59
+
60
+ if (options.parallel) {
61
+ // 并行模式:同一轮所有角色同时发言
62
+ const roleMessages = speakers.map(speakerName => {
63
+ const role = roles.find(r => r.name === speakerName);
64
+ const systemPrompt = this._buildSystemPrompt(role, round, subject, context, rules, history);
65
+ const msg = {
66
+ messages: [
67
+ { role: 'system', content: systemPrompt },
68
+ { role: 'user', content: `辩论主题:${subject}\n当前环节:${round.description}\n请根据你的角色定位进行发言。` }
69
+ ]
70
+ };
71
+ // 如果角色指定了 model,传入该模型
72
+ if (role && role.model) {
73
+ msg.options = { model: role.model };
74
+ }
75
+ return msg;
76
+ });
77
+
78
+ const responses = await this.llm.chatBatch(roleMessages);
79
+
80
+ for (let i = 0; i < speakers.length; i++) {
81
+ const speakerResult = {
82
+ role: speakers[i],
83
+ phase: round.phase,
84
+ phase_description: round.description,
85
+ content: responses[i],
86
+ type: 'speech'
87
+ };
88
+ roundResults.push(speakerResult);
89
+ history.push({
90
+ role: 'assistant',
91
+ content: `[${speakers[i]}] ${responses[i]}`
92
+ });
93
+ }
94
+ } else {
95
+ // 串行模式:按顺序依次发言(后发言者看到先发言者的内容)
96
+ for (const speakerName of speakers) {
97
+ const role = roles.find(r => r.name === speakerName);
98
+ const systemPrompt = this._buildSystemPrompt(role, round, subject, context, rules, history);
99
+
100
+ // 构建当前发言的上下文,包含之前所有发言历史
101
+ const messages = [
102
+ { role: 'system', content: systemPrompt },
103
+ { role: 'user', content: `辩论主题:${subject}\n当前环节:${round.description}\n请根据你的角色定位进行发言。` }
104
+ ];
105
+
106
+ // 如果角色指定了 model,传入该模型
107
+ const chatOptions = role && role.model ? { model: role.model } : {};
108
+ const response = await this.llm.chat(messages, chatOptions);
109
+ const speakerResult = {
110
+ role: speakerName,
111
+ phase: round.phase,
112
+ phase_description: round.description,
113
+ content: response,
114
+ type: 'speech'
115
+ };
116
+
117
+ roundResults.push(speakerResult);
118
+
119
+ // 立即将本轮发言加入历史,后续发言者可以看到
120
+ history.push({
121
+ role: 'assistant',
122
+ content: `[${speakerName}] ${response}`
123
+ });
124
+ }
125
+ }
126
+
127
+ // 将本轮所有发言保存到 session
128
+ for (const result of roundResults) {
129
+ session = this.sessions.appendRound(sessionId, result);
130
+ }
131
+ }
132
+
133
+ // 生成总结并完成 session
134
+ const summary = this._generateSummary(session, graphDef);
135
+ session = this.sessions.finalizeSession(sessionId, {
136
+ summary,
137
+ round_count: session.rounds.length
138
+ });
139
+
140
+ return session;
141
+ }
142
+
143
+ /**
144
+ * 续辩:基于已有 session 继续辩论
145
+ */
146
+ async continueDebate(sessionId, graphDef, subject, context = {}) {
147
+ const existingSession = this.sessions.getSession(sessionId);
148
+ if (!existingSession) {
149
+ throw new Error(`Session 未找到: ${sessionId}`);
150
+ }
151
+
152
+ return await this.runDebate(graphDef, {
153
+ subject: subject || existingSession.subject,
154
+ context: { ...existingSession.context, ...context },
155
+ previous_session_id: sessionId
156
+ }, { force: true });
157
+ }
158
+
159
+ /**
160
+ * 构建角色的 system prompt
161
+ */
162
+ _buildSystemPrompt(role, round, subject, context, rules, history) {
163
+ let prompt = `## 角色设定\n`;
164
+ prompt += `你是辩论中的「${role ? role.name : '参与者'}」。\n`;
165
+ prompt += `角色描述:${role ? role.persona : '请基于理性和逻辑参与讨论'}\n\n`;
166
+ prompt += `## 辩论信息\n`;
167
+ prompt += `辩论主题:${subject}\n`;
168
+ prompt += `当前环节:${round ? round.description : '自由讨论'}\n`;
169
+
170
+ if (context && Object.keys(context).length > 0) {
171
+ prompt += `\n## 背景信息\n${JSON.stringify(context, null, 2)}\n`;
172
+ }
173
+
174
+ if (rules && Object.keys(rules).length > 0) {
175
+ prompt += `\n## 规则要求\n`;
176
+ if (rules.min_response_length) {
177
+ prompt += `- 每条发言不少于 ${rules.min_response_length} 字。\n`;
178
+ }
179
+ if (rules.requires_fact_checking) {
180
+ prompt += `- 请基于事实进行论证,避免主观臆断。\n`;
181
+ }
182
+ if (rules.turn_limit) {
183
+ prompt += `- 同一角色在每个环节最多发言 ${rules.turn_limit} 次。\n`;
184
+ }
185
+ }
186
+
187
+ if (history.length > 0) {
188
+ prompt += `\n## 辩论历史\n`;
189
+ // 只取最近 15 条历史,避免超出上下文窗口
190
+ const recentHistory = history.slice(-15);
191
+ for (const msg of recentHistory) {
192
+ prompt += `${msg.content}\n`;
193
+ }
194
+ }
195
+
196
+ return prompt;
197
+ }
198
+
199
+ /**
200
+ * 生成辩论摘要
201
+ */
202
+ _generateSummary(session, graphDef) {
203
+ const totalRounds = session.rounds.length;
204
+ const uniqueRoles = [...new Set(session.rounds.map(r => r.role))];
205
+ const completedPhases = [...new Set(session.rounds.map(r => r.phase))];
206
+ const startTime = new Date(session.created_at);
207
+ const endTime = new Date(session.updated_at);
208
+ const durationMs = endTime - startTime;
209
+
210
+ return {
211
+ total_speeches: totalRounds,
212
+ roles_involved: uniqueRoles,
213
+ phases_completed: completedPhases,
214
+ duration_seconds: (durationMs / 1000).toFixed(1),
215
+ total_phases: graphDef.rounds ? graphDef.rounds.length : 0
216
+ };
217
+ }
218
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * 辩论图加载器 - 负责加载内置辩论图模板和支持内联自定义定义
3
+ */
4
+
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const GRAPHS_DIR = path.join(__dirname, '..', 'graphs');
11
+
12
+ export class GraphLoader {
13
+ constructor(graphsDir = GRAPHS_DIR) {
14
+ this.graphsDir = graphsDir;
15
+ }
16
+
17
+ /**
18
+ * 按名称加载内置辩论图
19
+ * @param {string} name - 辩论图名称(不含 .json 后缀)
20
+ * @returns {Object|null}
21
+ */
22
+ loadGraph(name) {
23
+ const filePath = path.join(this.graphsDir, `${name}.json`);
24
+ if (!fs.existsSync(filePath)) {
25
+ return null;
26
+ }
27
+ try {
28
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * 列出所有内置辩论图模板
36
+ * @returns {Array<{name, description, roleCount, roundCount}>}
37
+ */
38
+ listGraphs() {
39
+ if (!fs.existsSync(this.graphsDir)) {
40
+ return [];
41
+ }
42
+
43
+ const files = fs.readdirSync(this.graphsDir)
44
+ .filter(f => f.endsWith('.json'))
45
+ .sort();
46
+
47
+ return files.map(f => {
48
+ try {
49
+ const graph = JSON.parse(fs.readFileSync(path.join(this.graphsDir, f), 'utf-8'));
50
+ return {
51
+ name: graph.name,
52
+ description: graph.description,
53
+ roleCount: graph.roles ? graph.roles.length : 0,
54
+ roundCount: graph.rounds ? graph.rounds.length : 0,
55
+ roles: graph.roles ? graph.roles.map(r => r.name) : []
56
+ };
57
+ } catch {
58
+ return { name: f.replace('.json', ''), description: '(加载失败)' };
59
+ }
60
+ });
61
+ }
62
+
63
+ /**
64
+ * 解析 graph 参数,支持字符串名称和 JSON 对象
65
+ * @param {string|Object} graphParam - 图名称或完整定义
66
+ * @returns {Object} 辩论图定义
67
+ */
68
+ parseGraphDefinition(graphParam) {
69
+ if (!graphParam) {
70
+ throw new Error('辩论图参数不能为空');
71
+ }
72
+
73
+ // 字符串:先尝试加载内置模板,再尝试解析为 JSON
74
+ if (typeof graphParam === 'string') {
75
+ const builtIn = this.loadGraph(graphParam);
76
+ if (builtIn) return builtIn;
77
+
78
+ try {
79
+ return JSON.parse(graphParam);
80
+ } catch {
81
+ throw new Error(`辩论图未找到: "${graphParam}"。使用 list_debate_graphs 查看可用模板。`);
82
+ }
83
+ }
84
+
85
+ // 对象:直接使用
86
+ if (typeof graphParam === 'object' && graphParam !== null) {
87
+ if (!graphParam.roles || !graphParam.rounds) {
88
+ throw new Error('自定义辩论图必须包含 roles 和 rounds 字段');
89
+ }
90
+ return graphParam;
91
+ }
92
+
93
+ throw new Error('无效的辩论图定义');
94
+ }
95
+ }
package/src/index.js ADDED
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * mcp_llm-debate - MCP 服务器入口
5
+ *
6
+ * 注册 5 个辩论相关工具,支持 stdio / http / rest 三种传输方式。
7
+ *
8
+ * 环境变量配置:
9
+ * LLM_API_KEY - LLM API 密钥(必填)
10
+ * LLM_BASE_URL - API 基础 URL(默认: https://api.openai.com/v1)
11
+ * LLM_MODEL - 模型名称(默认: gpt-4o)
12
+ * SERVER_TRANSPORT - 传输方式: stdio | http | rest(默认: stdio)
13
+ * SERVER_PORT - HTTP/REST 端口(默认: 3000)
14
+ * SERVER_HOST - 绑定地址(默认: 127.0.0.1)
15
+ */
16
+
17
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
18
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
+ import {
20
+ CallToolRequestSchema,
21
+ ListToolsRequestSchema,
22
+ } from "@modelcontextprotocol/sdk/types.js";
23
+ import { fileURLToPath } from 'url';
24
+
25
+ import { LLMClient } from './llm-client.js';
26
+ import { SessionManager } from './session-manager.js';
27
+ import { GraphLoader } from './graph-loader.js';
28
+ import { DebateEngine } from './debate-engine.js';
29
+ import { loadConfig, getLLMConfig } from './config.js';
30
+
31
+ // ============================================================
32
+ // 工具定义
33
+ // ============================================================
34
+ const TOOLS = [
35
+ {
36
+ name: "run_debate",
37
+ description: "执行一次辩论。根据辩论图定义,由多个 AI 角色围绕主题进行结构化辩论。支持内置模板(如 classic-debate)或自定义辩论图。",
38
+ inputSchema: {
39
+ type: "object",
40
+ properties: {
41
+ graph: {
42
+ type: "string",
43
+ description: "辩论图名称(如 classic-debate, panel-discussion, socratic-dialogue, brainstorming)或内联 JSON 定义"
44
+ },
45
+ input: {
46
+ type: "object",
47
+ properties: {
48
+ subject: { type: "string", description: "辩论主题" },
49
+ context: { type: "object", description: "额外的上下文信息(可选)" },
50
+ previous_session_id: { type: "string", description: "续辩时传入的上一轮 session_id(可选)" }
51
+ },
52
+ required: ["subject"]
53
+ },
54
+ options: {
55
+ type: "object",
56
+ properties: {
57
+ timeout_ms: { type: "number", description: "超时时间(毫秒)" },
58
+ parallel: { type: "boolean", description: "是否并行执行同一轮的所有角色发言,默认为串行" },
59
+ stream: { type: "boolean", description: "是否流式返回结果" }
60
+ }
61
+ }
62
+ },
63
+ required: ["graph", "input"]
64
+ }
65
+ },
66
+ {
67
+ name: "continue_debate",
68
+ description: "基于上轮结果续辩。传入已有 session_id 继续辩论,可以换主题或增加新上下文。",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ session_id: { type: "string", description: "历史 session ID" },
73
+ graph: { type: "string", description: "辩论图名称或内联定义" },
74
+ subject: { type: "string", description: "辩论主题(可选,默认使用原主题)" },
75
+ context: { type: "object", description: "额外上下文信息(可选)" }
76
+ },
77
+ required: ["session_id", "graph"]
78
+ }
79
+ },
80
+ {
81
+ name: "get_debate_graph",
82
+ description: "获取指定辩论图的完整定义,包括角色设定、轮次流程和规则。",
83
+ inputSchema: {
84
+ type: "object",
85
+ properties: {
86
+ name: { type: "string", description: "辩论图名称" }
87
+ },
88
+ required: ["name"]
89
+ }
90
+ },
91
+ {
92
+ name: "get_debate_result",
93
+ description: "拉取历史辩论 session 的完整结果,包括所有轮次的发言记录和最终总结。",
94
+ inputSchema: {
95
+ type: "object",
96
+ properties: {
97
+ session_id: { type: "string", description: "session ID" }
98
+ },
99
+ required: ["session_id"]
100
+ }
101
+ },
102
+ {
103
+ name: "list_debate_graphs",
104
+ description: "列出所有可用的内置辩论图模板,包括名称、描述和角色数量。",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {}
108
+ }
109
+ }
110
+ ];
111
+
112
+ // ============================================================
113
+ // 格式化帮助函数
114
+ // ============================================================
115
+
116
+ function _formatDebateResult(session) {
117
+ const lines = [];
118
+ lines.push(`=== 辩论结果 ===`);
119
+ lines.push(`Session ID: ${session.session_id}`);
120
+ lines.push(`主题: ${session.subject}`);
121
+ lines.push(`辩论图: ${session.graph_name || '自定义'}`);
122
+ lines.push(`状态: ${session.status}`);
123
+ lines.push(`创建时间: ${session.created_at}`);
124
+ lines.push('');
125
+
126
+ // 按阶段组织发言
127
+ const phases = [...new Set(session.rounds.map(r => r.phase))];
128
+ for (const phase of phases) {
129
+ const phaseRounds = session.rounds.filter(r => r.phase === phase);
130
+ const phaseDesc = phaseRounds[0]?.phase_description || phase;
131
+ lines.push(`\n--- ${phaseDesc} ---`);
132
+ for (const round of phaseRounds) {
133
+ lines.push(`\n[${round.role}]`);
134
+ lines.push(round.content);
135
+ }
136
+ }
137
+
138
+ // 总结
139
+ if (session.result && session.result.summary) {
140
+ lines.push(`\n--- 总结 ---`);
141
+ lines.push(`发言总数: ${session.result.summary.total_speeches}`);
142
+ lines.push(`参与角色: ${session.result.summary.roles_involved.join(', ')}`);
143
+ lines.push(`完成环节: ${session.result.summary.phases_completed.join(' -> ')}`);
144
+ lines.push(`耗时: ${session.result.summary.duration_seconds}秒`);
145
+ }
146
+
147
+ return lines.join('\n');
148
+ }
149
+
150
+ // ============================================================
151
+ // 工具执行器(可复用,供 MCP 和 REST 传输层共用)
152
+ // ============================================================
153
+
154
+ /**
155
+ * 创建工具执行上下文
156
+ * @param {Object} options - 可选覆盖配置
157
+ * @returns {{ llmClient, sessionManager, graphLoader, debateEngine, config }}
158
+ */
159
+ export function createToolContext(options = {}) {
160
+ const config = loadConfig(options);
161
+ const llmClient = new LLMClient(getLLMConfig(config));
162
+ const sessionManager = new SessionManager(config.dataDir);
163
+ const graphLoader = new GraphLoader(config.graphsDir);
164
+ const debateEngine = new DebateEngine(llmClient, sessionManager);
165
+
166
+ return { llmClient, sessionManager, graphLoader, debateEngine, config };
167
+ }
168
+
169
+ /**
170
+ * 执行单个工具调用(可被 MCP 和 REST 传输层共用)
171
+ * @param {string} name - 工具名称
172
+ * @param {Object} args - 工具参数
173
+ * @param {Object} ctx - 工具执行上下文(由 createToolContext 创建)
174
+ * @returns {Promise<{content: Array}>} MCP 格式的响应
175
+ */
176
+ export async function executeTool(name, args, ctx) {
177
+ const { graphLoader, debateEngine, sessionManager } = ctx;
178
+
179
+ switch (name) {
180
+ // ---- run_debate ----
181
+ case "run_debate": {
182
+ const graphDef = graphLoader.parseGraphDefinition(args.graph);
183
+ const result = await debateEngine.runDebate(graphDef, args.input, args.options || {});
184
+ const output = _formatDebateResult(result);
185
+ return { content: [{ type: "text", text: output }] };
186
+ }
187
+
188
+ // ---- continue_debate ----
189
+ case "continue_debate": {
190
+ const graphDef = graphLoader.parseGraphDefinition(args.graph);
191
+ const result = await debateEngine.continueDebate(
192
+ args.session_id, graphDef, args.subject, args.context || {}
193
+ );
194
+ return { content: [{ type: "text", text: _formatDebateResult(result) }] };
195
+ }
196
+
197
+ // ---- get_debate_graph ----
198
+ case "get_debate_graph": {
199
+ const graph = graphLoader.loadGraph(args.name);
200
+ if (!graph) throw new Error(`辩论图未找到: "${args.name}"`);
201
+ return { content: [{ type: "text", text: JSON.stringify(graph, null, 2) }] };
202
+ }
203
+
204
+ // ---- get_debate_result ----
205
+ case "get_debate_result": {
206
+ const session = sessionManager.getSession(args.session_id);
207
+ if (!session) throw new Error(`Session 未找到: "${args.session_id}"`);
208
+ return { content: [{ type: "text", text: JSON.stringify(session, null, 2) }] };
209
+ }
210
+
211
+ // ---- list_debate_graphs ----
212
+ case "list_debate_graphs": {
213
+ const graphs = graphLoader.listGraphs();
214
+ const output = graphs.map((g, i) =>
215
+ `${i + 1}. ${g.name} - ${g.description}\n 角色: ${g.roleCount}个 | 环节: ${g.roundCount}个`
216
+ ).join('\n\n');
217
+ return { content: [{ type: "text", text: output || '暂无可用辩论图模板。' }] };
218
+ }
219
+
220
+ default:
221
+ throw new Error(`未知工具: "${name}"`);
222
+ }
223
+ }
224
+
225
+ // ============================================================
226
+ // MCP 服务器工厂
227
+ // ============================================================
228
+
229
+ /**
230
+ * 创建已配置好工具处理器的 MCP Server 实例
231
+ * @param {Object} ctx - 工具执行上下文(由 createToolContext 创建)
232
+ * @returns {Server}
233
+ */
234
+ export function createServer(ctx) {
235
+ const server = new Server(
236
+ {
237
+ name: "mcp_llm-debate",
238
+ version: "1.0.0",
239
+ },
240
+ {
241
+ capabilities: {
242
+ tools: {},
243
+ },
244
+ }
245
+ );
246
+
247
+ // 列出工具
248
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
249
+ tools: TOOLS,
250
+ }));
251
+
252
+ // 调用工具
253
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
254
+ const { name, arguments: args } = request.params;
255
+ try {
256
+ return await executeTool(name, args, ctx);
257
+ } catch (error) {
258
+ return {
259
+ content: [{ type: "text", text: `错误: ${error.message}` }],
260
+ isError: true,
261
+ };
262
+ }
263
+ });
264
+
265
+ return server;
266
+ }
267
+
268
+ // ============================================================
269
+ // 启动 stdio 模式(默认,向后兼容)
270
+ // ============================================================
271
+
272
+ async function main() {
273
+ const config = loadConfig();
274
+
275
+ // 检查配置
276
+ if (!config.apiKey) {
277
+ console.error('警告: 未配置 LLM API 密钥。请设置环境变量 LLM_API_KEY 或 OPENAI_API_KEY。');
278
+ }
279
+
280
+ if (process.env.LLM_BASE_URL) {
281
+ console.error(`LLM API 端点: ${config.baseUrl}`);
282
+ }
283
+
284
+ console.error(`LLM 模型: ${config.model}`);
285
+ console.error('mcp_llm-debate 服务器启动中 (stdio 模式)...');
286
+
287
+ const ctx = createToolContext(config);
288
+ const server = createServer(ctx);
289
+
290
+ const transport = new StdioServerTransport();
291
+ await server.connect(transport);
292
+ console.error('mcp_llm-debate 服务器已就绪');
293
+ }
294
+
295
+ // 仅在直接执行 index.js 时启动 stdio 模式(不是被 import 时)
296
+ const isMainModule = process.argv[1] && (
297
+ process.argv[1] === fileURLToPath(import.meta.url) ||
298
+ process.argv[1].replace(/\\/g, '/').endsWith('/src/index.js')
299
+ );
300
+
301
+ if (isMainModule) {
302
+ main().catch((error) => {
303
+ console.error('服务器启动失败:', error);
304
+ process.exit(1);
305
+ });
306
+ }
package/src/lib.js ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * mcp_llm-debate 库导出入口
3
+ *
4
+ * 让其他 Node.js 项目可以直接 import 核心类使用:
5
+ *
6
+ * import { DebateEngine, LLMClient, SessionManager, GraphLoader } from 'mcp_llm-debate';
7
+ *
8
+ * 或者使用便捷工厂函数:
9
+ *
10
+ * import { createDebateEngine, createLLMClient } from 'mcp_llm-debate';
11
+ */
12
+
13
+ import { DebateEngine } from './debate-engine.js';
14
+ import { LLMClient } from './llm-client.js';
15
+ import { SessionManager } from './session-manager.js';
16
+ import { GraphLoader } from './graph-loader.js';
17
+ import { loadConfig, getLLMConfig } from './config.js';
18
+ import { createServer } from './index.js';
19
+
20
+ // 核心类
21
+ export { DebateEngine, LLMClient, SessionManager, GraphLoader };
22
+ // 配置
23
+ export { loadConfig, getLLMConfig };
24
+ // MCP 服务器工厂(用于自定义传输层)
25
+ export { createServer };
26
+
27
+ /**
28
+ * 便捷工厂:创建 LLMClient
29
+ * @param {Object} options - { apiKey, baseUrl, model }
30
+ */
31
+ export function createLLMClient(options = {}) {
32
+ const config = loadConfig(options);
33
+ return new LLMClient(getLLMConfig(config));
34
+ }
35
+
36
+ /**
37
+ * 便捷工厂:创建完整的辩论引擎
38
+ * @param {Object} options - { apiKey, baseUrl, model, dataDir, graphsDir }
39
+ */
40
+ export function createDebateEngine(options = {}) {
41
+ const config = loadConfig(options);
42
+ const llmClient = new LLMClient(getLLMConfig(config));
43
+ const sessionManager = new SessionManager(config.dataDir);
44
+ return new DebateEngine(llmClient, sessionManager);
45
+ }