| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- 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<string> {
- 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<string> {
- 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<string> {
- 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<string> {
- 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<string[]> {
- try {
- // 简单的关键词提取(基于词频)
- const words = text.split(/[\s,,.。!??!]/).filter(word => word.length > 1);
- const wordFreq = new Map<string, number>();
- 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();
|