test-book27.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { bookStore } from './src/modules/book-generator/book-generator.store.js';
  2. import { callLLMWithMessages } from './src/services/llm/index.js';
  3. async function main() {
  4. const bookId = process.argv[2] || '27';
  5. const book = await bookStore.getById(bookId);
  6. if (!book) {
  7. console.error('Book not found');
  8. return;
  9. }
  10. console.log('Title:', book.title);
  11. console.log('Description:', book.description);
  12. console.log('bookAnalysis:', book.bookAnalysis ? JSON.parse(book.bookAnalysis) : null);
  13. // Build messages as richOutlineNode does
  14. const bookPlan = book.bookAnalysis ? JSON.parse(book.bookAnalysis) : null;
  15. const genLevel = bookPlan?.genLevel || 2;
  16. const config = { chapters: 1, totalWords: 1000 };
  17. const levelDesc: Record<number, string> = {
  18. 1: '仅章:只生成章节列表,不要节和小节。',
  19. 2: '章→节:每章下生成若干节(2-4节),节下不要拆小节。',
  20. 3: '章→节→小节:每章下生成节(2-4节),每节下生成小节(2-4小节),完整三层结构。',
  21. };
  22. let planSection = '';
  23. if (bookPlan) {
  24. const parts: string[] = [];
  25. if (bookPlan.goldenThread) parts.push(`黄金主线:${bookPlan.goldenThread}`);
  26. if (bookPlan.writingStyle) parts.push(`写作风格:${bookPlan.writingStyle}`);
  27. if (bookPlan.structureLogic) parts.push(`结构逻辑:${bookPlan.structureLogic}`);
  28. if (bookPlan.contentDepth) parts.push(`内容深度:${bookPlan.contentDepth}`);
  29. if (bookPlan.targetAudienceAnalysis) parts.push(`目标读者:${bookPlan.targetAudienceAnalysis}`);
  30. if (parts.length > 0) planSection = `\n\n## 前序深度规划参考\n${parts.join('\n')}`;
  31. }
  32. const systemPrompt = `你是一位专业的图书策划编辑。请为以下书籍生成富信息大纲。
  33. ## 本书约束
  34. - 总字数约${config.totalWords}字,目标约${config.chapters}章
  35. - 大纲层级:${levelDesc[genLevel] || levelDesc[2]}
  36. - genLevel 当前值:${genLevel}
  37. ${planSection}
  38. ## 输出格式
  39. 必须返回合法的 JSON,不要包含任何 markdown 代码块标记或其他文字:
  40. {
  41. "mainTheme": "主题一句话描述",
  42. "structureLogic": "章节组织逻辑说明",
  43. "chapters": [
  44. {
  45. "number": 1,
  46. "title": "章标题",
  47. "summary": "章摘要",
  48. "keyPoints": ["知识点1"],
  49. "estimatedWords": 2000,
  50. "sections": [
  51. {
  52. "number": 1,
  53. "title": "节标题",
  54. "summary": "节摘要",
  55. "keyPoints": ["知识点1"],
  56. "estimatedWords": 1000,
  57. "subsections": []
  58. }
  59. ]
  60. }
  61. ]
  62. }
  63. 注意:genLevel=1 时每章不要 sections
  64. genLevel=2 时每章的 sections 下不要 subsections
  65. genLevel=3 时才需要完整的 sections + subsections`;
  66. const messages = [
  67. { role: 'system', content: systemPrompt },
  68. {
  69. role: 'user',
  70. content: `书名:《${book.title}》\n${book.description ? `需求描述:${book.description}\n` : ''}\n请生成完整的富信息大纲。`,
  71. },
  72. ];
  73. console.log('\n=== Calling LLM ===');
  74. console.log('System prompt length:', systemPrompt.length);
  75. console.log('User content:', messages[1].content);
  76. try {
  77. const response = await callLLMWithMessages(messages);
  78. console.log('\n=== LLM Response ===');
  79. console.log('Response length:', response.length);
  80. console.log('Response preview:', response.substring(0, 300));
  81. // Try to parse(修复:indexOf('') → indexOf('</think>'))
  82. const thinkEnd = response.indexOf('</think>');
  83. console.log('\nthinkEnd index:', thinkEnd);
  84. if (thinkEnd !== -1) {
  85. console.log('After think removal:', response.substring(thinkEnd + 8, thinkEnd + 100));
  86. } else {
  87. console.log('No </think> tag found');
  88. }
  89. // Try regex match
  90. const match = response.match(/\{[\s\S]*\}/);
  91. if (match) {
  92. console.log('Found JSON match, length:', match[0].length);
  93. try {
  94. const data = JSON.parse(match[0]);
  95. console.log('Parse successful, chapters:', data.chapters?.length);
  96. } catch (e) {
  97. console.log('Parse failed:', e.message);
  98. }
  99. } else {
  100. console.log('No JSON match found');
  101. }
  102. } catch (e) {
  103. console.error('LLM call failed:', e.message);
  104. }
  105. }
  106. main();