ai-summary.service.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.aiSummaryService = exports.AISummaryService = void 0;
  7. const axios_1 = __importDefault(require("axios"));
  8. /**
  9. * AI 文本摘要服务
  10. * 使用通义千问或讯飞星火 API 生成文本摘要
  11. */
  12. class AISummaryService {
  13. apiKey;
  14. apiEndpoint;
  15. constructor() {
  16. // 这里可以使用多个 AI 服务,暂时使用 Mock 实现
  17. this.apiKey = process.env.AI_API_KEY || '';
  18. this.apiEndpoint = process.env.AI_API_ENDPOINT || '';
  19. }
  20. /**
  21. * 生成文本摘要
  22. * @param text 原始文本
  23. * @param maxLength 最大长度(默认 200 字)
  24. */
  25. async generateSummary(text, maxLength = 200) {
  26. try {
  27. // 如果配置了真实的 AI API,调用真实服务
  28. if (this.apiKey && this.apiEndpoint) {
  29. return await this.callAIAPI(text, maxLength);
  30. }
  31. // 否则使用 Mock 实现(提取前 200 字)
  32. return this.mockSummary(text, maxLength);
  33. }
  34. catch (error) {
  35. console.error('生成摘要失败:', error);
  36. // 降级处理:返回前 200 字
  37. return this.mockSummary(text, maxLength);
  38. }
  39. }
  40. /**
  41. * 调用 AI API 生成摘要
  42. */
  43. async callAIAPI(text, maxLength) {
  44. const prompt = `请为以下文本生成一个${maxLength}字以内的摘要,要求简洁明了,突出核心内容:\n\n${text.slice(0, 1000)}`;
  45. const response = await axios_1.default.post(this.apiEndpoint, {
  46. prompt,
  47. max_tokens: maxLength,
  48. temperature: 0.7,
  49. }, {
  50. headers: {
  51. Authorization: `Bearer ${this.apiKey}`,
  52. 'Content-Type': 'application/json',
  53. },
  54. });
  55. return response.data.result || response.data.choices?.[0]?.text || '';
  56. }
  57. /**
  58. * Mock 摘要生成(简单截取)
  59. */
  60. mockSummary(text, maxLength) {
  61. // 去除多余空格和换行
  62. const cleaned = text.replace(/\s+/g, ' ').trim();
  63. if (cleaned.length <= maxLength) {
  64. return cleaned;
  65. }
  66. // 截取到最后一个完整的句子
  67. let summary = cleaned.substring(0, maxLength);
  68. const lastPeriod = summary.lastIndexOf('。');
  69. const lastQuestion = summary.lastIndexOf('?');
  70. const lastExclamation = summary.lastIndexOf('!');
  71. const lastPunctuation = Math.max(lastPeriod, lastQuestion, lastExclamation);
  72. if (lastPunctuation > 0) {
  73. summary = summary.substring(0, lastPunctuation + 1);
  74. }
  75. return summary + '...';
  76. }
  77. /**
  78. * 生成标题
  79. * @param text 原始文本
  80. */
  81. async generateTitle(text) {
  82. try {
  83. // 尝试从文本中提取关键信息作为标题
  84. const lines = text.split('\n').filter(line => line.trim().length > 0);
  85. // 如果第一行比较短,可能是标题
  86. if (lines.length > 0 && lines[0].length < 50) {
  87. return lines[0].trim();
  88. }
  89. // 否则使用 AI 生成
  90. if (this.apiKey && this.apiEndpoint) {
  91. return await this.callAITitle(text);
  92. }
  93. // Mock 实现:提取前 30 个字
  94. return text.replace(/\s+/g, ' ').substring(0, 30).trim() + '...';
  95. }
  96. catch (error) {
  97. console.error('生成标题失败:', error);
  98. return 'AI 生成的音频内容';
  99. }
  100. }
  101. /**
  102. * 调用 AI 生成标题
  103. */
  104. async callAITitle(text) {
  105. const prompt = `请为以下文本生成一个吸引人的标题,不超过 30 个字:\n\n${text.slice(0, 500)}`;
  106. const response = await axios_1.default.post(this.apiEndpoint, {
  107. prompt,
  108. max_tokens: 30,
  109. temperature: 0.8,
  110. }, {
  111. headers: {
  112. Authorization: `Bearer ${this.apiKey}`,
  113. 'Content-Type': 'application/json',
  114. },
  115. });
  116. return response.data.result || response.data.choices?.[0]?.text || 'AI 生成的音频内容';
  117. }
  118. /**
  119. * 提取关键词标签
  120. * @param text 原始文本
  121. */
  122. async extractTags(text) {
  123. try {
  124. // 简单的关键词提取(基于词频)
  125. const words = text.split(/[\s,,.。!??!]/).filter(word => word.length > 1);
  126. const wordFreq = new Map();
  127. words.forEach(word => {
  128. wordFreq.set(word, (wordFreq.get(word) || 0) + 1);
  129. });
  130. // 按频率排序,取前 5 个
  131. const sorted = Array.from(wordFreq.entries())
  132. .sort((a, b) => b[1] - a[1])
  133. .slice(0, 5)
  134. .map(entry => entry[0]);
  135. return sorted.filter(tag => tag.length <= 10); // 过滤掉太长的词
  136. }
  137. catch (error) {
  138. console.error('提取标签失败:', error);
  139. return ['AI 生成', '有声书'];
  140. }
  141. }
  142. }
  143. exports.AISummaryService = AISummaryService;
  144. // 导出单例
  145. exports.aiSummaryService = new AISummaryService();