import axios from 'axios'; import { withAiLog } from '../../services/ai-call-logger'; /** * AI 文本摘要服务 * 使用通义千问或讯飞星火 API 生成文本摘要 */ export class AISummaryService { private apiKey: string; private apiEndpoint: string; constructor() { // 这里可以使用多个 AI 服务,暂时使用 Mock 实现 this.apiKey = process.env.AI_API_KEY || ''; this.apiEndpoint = process.env.AI_API_ENDPOINT || ''; } /** * 生成文本摘要 * @param text 原始文本 * @param maxLength 最大长度(默认 200 字) */ async generateSummary(text: string, maxLength: number = 200): Promise { try { // 如果配置了真实的 AI API,调用真实服务 if (this.apiKey && this.apiEndpoint) { return await this.callAIAPI(text, maxLength); } // 否则使用 Mock 实现(提取前 200 字) return this.mockSummary(text, maxLength); } catch (error) { console.error('生成摘要失败:', error); // 降级处理:返回前 200 字 return this.mockSummary(text, maxLength); } } /** * 调用 AI API 生成摘要 */ private async callAIAPI(text: string, maxLength: number): Promise { const prompt = `请为以下文本生成一个${maxLength}字以内的摘要,要求简洁明了,突出核心内容:\n\n${text.slice(0, 1000)}`; const response = await withAiLog( () => axios.post( this.apiEndpoint, { prompt, max_tokens: maxLength, temperature: 0.7, }, { headers: { Authorization: `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }, } ), { callType: 'llm_chat', provider: 'ai-summary', model: 'summary-model', textLen: prompt.length } ); return response.data.result || response.data.choices?.[0]?.text || ''; } /** * Mock 摘要生成(简单截取) */ private mockSummary(text: string, maxLength: number): string { // 去除多余空格和换行 const cleaned = text.replace(/\s+/g, ' ').trim(); if (cleaned.length <= maxLength) { return cleaned; } // 截取到最后一个完整的句子 let summary = cleaned.substring(0, maxLength); const lastPeriod = summary.lastIndexOf('。'); const lastQuestion = summary.lastIndexOf('?'); const lastExclamation = summary.lastIndexOf('!'); const lastPunctuation = Math.max(lastPeriod, lastQuestion, lastExclamation); if (lastPunctuation > 0) { summary = summary.substring(0, lastPunctuation + 1); } return summary + '...'; } /** * 生成标题 * @param text 原始文本 */ async generateTitle(text: string): Promise { try { // 尝试从文本中提取关键信息作为标题 const lines = text.split('\n').filter(line => line.trim().length > 0); // 如果第一行比较短,可能是标题 if (lines.length > 0 && lines[0].length < 50) { return lines[0].trim(); } // 否则使用 AI 生成 if (this.apiKey && this.apiEndpoint) { return await this.callAITitle(text); } // Mock 实现:提取前 30 个字 return text.replace(/\s+/g, ' ').substring(0, 30).trim() + '...'; } catch (error) { console.error('生成标题失败:', error); return 'AI 生成的音频内容'; } } /** * 调用 AI 生成标题 */ private async callAITitle(text: string): Promise { const prompt = `请为以下文本生成一个吸引人的标题,不超过 30 个字:\n\n${text.slice(0, 500)}`; const response = await withAiLog( () => axios.post( this.apiEndpoint, { prompt, max_tokens: 30, temperature: 0.8, }, { headers: { Authorization: `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }, } ), { callType: 'llm_chat', provider: 'ai-summary', model: 'title-model', textLen: prompt.length } ); return response.data.result || response.data.choices?.[0]?.text || 'AI 生成的音频内容'; } /** * 提取关键词标签 * @param text 原始文本 */ async extractTags(text: string): Promise { try { // 简单的关键词提取(基于词频) const words = text.split(/[\s,,.。!??!]/).filter(word => word.length > 1); const wordFreq = new Map(); words.forEach(word => { wordFreq.set(word, (wordFreq.get(word) || 0) + 1); }); // 按频率排序,取前 5 个 const sorted = Array.from(wordFreq.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 5) .map(entry => entry[0]); return sorted.filter(tag => tag.length <= 10); // 过滤掉太长的词 } catch (error) { console.error('提取标签失败:', error); return ['AI 生成', '有声书']; } } } // 导出单例 export const aiSummaryService = new AISummaryService();