test-model-comparison.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /**
  2. * 多模型大纲生成对比测试 - HTTP方式
  3. */
  4. const axios = require('axios');
  5. const fs = require('fs');
  6. // 只测试 MiniMax (使用官方API)
  7. const TEST_MODELS = [
  8. { id: 'MiniMax-M2.7', vendor: 'minimax', name: 'MiniMax M2.7' },
  9. ];
  10. // 优化后的提示词
  11. const OPTIMIZED_SYSTEM_PROMPT = `你是一位专业的图书策划编辑,擅长为各类主题设计清晰、严谨、有逻辑递进的书籍大纲。
  12. ## 你的职责
  13. 根据用户提供的书名、规模和主题,设计一份完整的书籍大纲。
  14. ## 工作要求
  15. 1. 分析主题复杂度,在允许的章节范围内决定最合适的章节数
  16. 2. 章节之间必须有清晰的逻辑递进关系(由浅入深、由理论到实践等)
  17. 3. 每章的知识点要具体,不能泛泛而谈
  18. 4. 预估字数要符合章节内容量
  19. 5. 核心知识点应独立成章,不要过度合并
  20. ## ⚠️ 输出格式要求(非常重要)
  21. 1. 必须且只能返回纯 JSON,不要任何解释、思考过程或其他文字
  22. 2. 不要使用 markdown 代码块标记
  23. 3. 不要包含任何标签(如details、think等)
  24. 4. 直接从 { 开始,到 } 结束
  25. 5. JSON 必须合法,可以被 JSON.parse 解析
  26. ## JSON 格式
  27. {
  28. "mainTheme": "主题一句话描述",
  29. "structureLogic": "章节组织逻辑说明",
  30. "chapters": [
  31. {
  32. "number": 1,
  33. "title": "章标题",
  34. "summary": "本章概述(2-3句话)",
  35. "keyPoints": ["知识点1", "知识点2"],
  36. "estimatedWords": 10000
  37. }
  38. ]
  39. }`;
  40. async function testModel(model) {
  41. console.log(`\n${'='.repeat(80)}`);
  42. console.log(`🤖 测试: ${model.name} (${model.vendor})`);
  43. console.log(`${'='.repeat(80)}`);
  44. // 读取models.json获取配置
  45. const modelsConfig = JSON.parse(fs.readFileSync('./src/config/models.json', 'utf8'));
  46. const vendor = Object.values(modelsConfig.vendors).find(v =>
  47. v.models.some(m => m.id === model.id)
  48. );
  49. if (!vendor) {
  50. console.log(`❌ 未找到模型配置: ${model.id}`);
  51. return null;
  52. }
  53. const modelConfig = vendor.models.find(m => m.id === model.id);
  54. try {
  55. const startTime = Date.now();
  56. const response = await axios.post(
  57. `${vendor.baseUrl}/chat/completions`,
  58. {
  59. model: model.id,
  60. messages: [
  61. { role: 'system', content: OPTIMIZED_SYSTEM_PROMPT },
  62. { role: 'user', content: '书名:《操作系统引论》\n规模:大学专业教材\n章节范围:10-12章\n\n请生成完整的书籍大纲。' }
  63. ],
  64. temperature: modelConfig.temperature || 0.7,
  65. max_tokens: modelConfig.maxTokens || 4096
  66. },
  67. {
  68. headers: {
  69. 'Authorization': `Bearer ${vendor.apiKey}`,
  70. 'Content-Type': 'application/json'
  71. },
  72. timeout: 120000
  73. }
  74. );
  75. const duration = ((Date.now() - startTime) / 1000).toFixed(2);
  76. const content = response.data.choices[0].message.content;
  77. console.log(`⏱️ 耗时: ${duration}秒`);
  78. console.log(`\n📝 生成内容:\n`);
  79. console.log(content);
  80. // 尝试解析
  81. try {
  82. let jsonStr = content.trim();
  83. const match = jsonStr.match(/\{[\s\S]*\}/);
  84. if (match) jsonStr = match[0];
  85. const outline = JSON.parse(jsonStr);
  86. console.log(`\n✅ JSON解析成功`);
  87. console.log(`📊 章节数: ${outline.chapters?.length || 0}`);
  88. console.log(`📖 主题: ${outline.mainTheme}`);
  89. if (outline.chapters) {
  90. const totalWords = outline.chapters.reduce((sum, c) => sum + (c.estimatedWords || 0), 0);
  91. console.log(`📈 总字数: ${totalWords}`);
  92. console.log(`\n📋 章节列表:`);
  93. outline.chapters.forEach(ch => {
  94. console.log(` ${ch.number}. ${ch.title} (${ch.estimatedWords}字)`);
  95. });
  96. }
  97. } catch (e) {
  98. console.log(`\n⚠️ JSON解析失败: ${e.message}`);
  99. }
  100. return { model: model.name, vendor: model.vendor, content, success: true, duration };
  101. } catch (error) {
  102. console.log(`\n❌ 失败: ${error.response?.data?.message || error.message}`);
  103. return { model: model.name, vendor: model.vendor, error: error.message, success: false };
  104. }
  105. }
  106. async function main() {
  107. console.log('🚀 多模型大纲生成对比测试\n');
  108. console.log('📚 测试主题: 操作系统引论');
  109. console.log(`🔢 测试模型: ${TEST_MODELS.length}个\n`);
  110. const results = [];
  111. for (const model of TEST_MODELS) {
  112. const result = await testModel(model);
  113. if (result) results.push(result);
  114. await new Promise(resolve => setTimeout(resolve, 2000));
  115. }
  116. // 汇总
  117. console.log(`\n\n${'='.repeat(80)}`);
  118. console.log('📊 测试结果汇总');
  119. console.log(`${'='.repeat(80)}\n`);
  120. results.forEach((r, i) => {
  121. console.log(`${i + 1}. ${r.model} [${r.vendor}]`);
  122. if (r.success) {
  123. console.log(` ✅ 成功 | 耗时: ${r.duration}秒`);
  124. } else {
  125. console.log(` ❌ 失败: ${r.error}`);
  126. }
  127. console.log();
  128. });
  129. // 保存结果
  130. const fs = require('fs');
  131. const path = require('path');
  132. // 确保目录存在
  133. const resultsDir = path.join(__dirname, 'test-results');
  134. if (!fs.existsSync(resultsDir)) {
  135. fs.mkdirSync(resultsDir, { recursive: true });
  136. }
  137. const resultFile = path.join(resultsDir, 'outline-comparison.json');
  138. fs.writeFileSync(resultFile, JSON.stringify(results, null, 2), 'utf8');
  139. console.log(`\n💾 结果已保存到: ${resultFile}`);
  140. // 同时保存一个可读的文本版本
  141. const textFile = path.join(resultsDir, 'outline-comparison.txt');
  142. let textContent = '=' .repeat(80) + '\n';
  143. textContent += '多模型大纲生成对比测试结果\n';
  144. textContent += '='.repeat(80) + '\n\n';
  145. textContent += `测试主题: 操作系统引论\n`;
  146. textContent += `书籍类型: 大学专业教材\n`;
  147. textContent += `章节范围: 10-12章\n`;
  148. textContent += `测试时间: ${new Date().toLocaleString('zh-CN')}\n\n`;
  149. results.forEach((r, i) => {
  150. textContent += `${'='.repeat(80)}\n`;
  151. textContent += `${i + 1}. ${r.model} [${r.vendor}]\n`;
  152. textContent += `${'='.repeat(80)}\n\n`;
  153. if (r.success) {
  154. textContent += `⏱️ 耗时: ${r.duration}秒\n`;
  155. textContent += `✅ 状态: 成功\n\n`;
  156. textContent += `📝 生成内容:\n\n`;
  157. textContent += r.content + '\n\n';
  158. } else {
  159. textContent += `❌ 状态: 失败\n`;
  160. textContent += `错误信息: ${r.error}\n\n`;
  161. }
  162. });
  163. fs.writeFileSync(textFile, textContent, 'utf8');
  164. console.log(`📄 可读版本已保存到: ${textFile}`);
  165. }
  166. main().catch(console.error);