867 lines
30 KiB
Plaintext
867 lines
30 KiB
Plaintext
// AI Chat Service - Multilingual news assistant and conversation management
|
||
|
||
import {
|
||
ChatMessage,
|
||
ChatSession,
|
||
AIProvider,
|
||
AIResponse,
|
||
AIServiceConfig,
|
||
AIServiceError,
|
||
ContentInfo
|
||
} from '../types/ai-types.uts'
|
||
|
||
// 聊天配置选项
|
||
type ChatOptions = {
|
||
provider?: AIProvider
|
||
model?: string
|
||
temperature?: number
|
||
maxTokens?: number
|
||
language?: string
|
||
systemPrompt?: string
|
||
contextWindow?: number
|
||
streamResponse?: boolean
|
||
}
|
||
|
||
// 会话上下文
|
||
type SessionContext = {
|
||
recentNews?: ContentInfo[]
|
||
userPreferences?: UserPreferences
|
||
conversationHistory: ChatMessage[]
|
||
currentTopic?: string
|
||
activeLanguage: string
|
||
}
|
||
|
||
// 用户偏好
|
||
type UserPreferences = {
|
||
preferredLanguages: string[]
|
||
preferredCategories: string[]
|
||
preferredSources: string[]
|
||
newsStyle: 'brief' | 'detailed' | 'analytical'
|
||
updateFrequency: 'realtime' | 'hourly' | 'daily'
|
||
}
|
||
|
||
// 聊天统计
|
||
type ChatStats = {
|
||
totalSessions: number
|
||
totalMessages: number
|
||
avgSessionLength: number
|
||
avgResponseTime: number
|
||
totalCost: number
|
||
languageDistribution: Record<string, number>
|
||
topQuestionTypes: Record<string, number>
|
||
}
|
||
|
||
// 预定义的聊天模板
|
||
type ChatTemplate = {
|
||
id: string
|
||
name: string
|
||
description: string
|
||
systemPrompt: string
|
||
language: string
|
||
category: string
|
||
}
|
||
|
||
/**
|
||
* AI聊天服务类
|
||
* 提供多语言新闻助手功能,包括会话管理、上下文理解、个性化回复等
|
||
*/
|
||
export class AIChatService {
|
||
private config: AIServiceConfig
|
||
private activeSessions: Map<string, SessionContext> = new Map()
|
||
private chatTemplates: ChatTemplate[] = []
|
||
private stats: ChatStats = {
|
||
totalSessions: 0,
|
||
totalMessages: 0,
|
||
avgSessionLength: 0,
|
||
avgResponseTime: 0,
|
||
totalCost: 0,
|
||
languageDistribution: {},
|
||
topQuestionTypes: {}
|
||
}
|
||
|
||
constructor(config: AIServiceConfig) {
|
||
this.config = config
|
||
this.initializeChatTemplates()
|
||
}
|
||
|
||
/**
|
||
* 创建新的聊天会话
|
||
* @param userId 用户ID
|
||
* @param language 语言
|
||
* @param options 聊天选项
|
||
*/
|
||
async createChatSession(
|
||
userId: string,
|
||
language: string = 'zh-CN',
|
||
options: ChatOptions = {}
|
||
): Promise<AIResponse<ChatSession>> {
|
||
try {
|
||
const sessionId = this.generateSessionId(userId)
|
||
const session: ChatSession = {
|
||
id: sessionId,
|
||
userId,
|
||
name: this.generateSessionName(language),
|
||
language,
|
||
aiModel: options.model || this.getDefaultModel(options.provider),
|
||
contextSettings: {
|
||
temperature: options.temperature || 0.7,
|
||
maxTokens: options.maxTokens || 1000,
|
||
contextWindow: options.contextWindow || 10
|
||
},
|
||
totalMessages: 0,
|
||
totalTokensUsed: 0,
|
||
totalCostUSD: 0,
|
||
isActive: true,
|
||
startedAt: Date.now(),
|
||
lastMessageAt: Date.now()
|
||
}
|
||
|
||
// 初始化会话上下文
|
||
const context: SessionContext = {
|
||
conversationHistory: [],
|
||
activeLanguage: language,
|
||
currentTopic: undefined,
|
||
recentNews: [],
|
||
userPreferences: await this.loadUserPreferences(userId)
|
||
}
|
||
|
||
this.activeSessions.set(sessionId, context)
|
||
this.stats.totalSessions++
|
||
|
||
// 发送欢迎消息
|
||
const welcomeMessage = await this.generateWelcomeMessage(language)
|
||
await this.addSystemMessage(sessionId, welcomeMessage, language)
|
||
|
||
return { success: true, data: session }
|
||
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
error: error.message || 'Failed to create chat session'
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送消息
|
||
* @param sessionId 会话ID
|
||
* @param message 用户消息
|
||
* @param options 聊天选项
|
||
*/
|
||
async sendMessage(
|
||
sessionId: string,
|
||
message: string,
|
||
options: ChatOptions = {}
|
||
): Promise<AIResponse<ChatMessage>> {
|
||
try {
|
||
const context = this.activeSessions.get(sessionId)
|
||
if (!context) {
|
||
throw new Error('Session not found')
|
||
}
|
||
|
||
const startTime = Date.now()
|
||
|
||
// 添加用户消息到历史
|
||
const userMessage: ChatMessage = {
|
||
id: this.generateMessageId(),
|
||
sessionId,
|
||
type: 'user',
|
||
content: message,
|
||
language: context.activeLanguage,
|
||
timestamp: Date.now()
|
||
}
|
||
|
||
context.conversationHistory.push(userMessage)
|
||
this.stats.totalMessages++
|
||
|
||
// 分析用户意图
|
||
const intent = await this.analyzeUserIntent(message, context)
|
||
|
||
// 生成AI回复
|
||
const provider = options.provider || this.selectBestProvider()
|
||
const response = await this.generateAIResponse(message, context, intent, provider, options)
|
||
|
||
const processingTime = Date.now() - startTime
|
||
|
||
// 创建助手消息
|
||
const assistantMessage: ChatMessage = {
|
||
id: this.generateMessageId(),
|
||
sessionId,
|
||
type: 'assistant',
|
||
content: response.content,
|
||
language: context.activeLanguage,
|
||
timestamp: Date.now(),
|
||
responseTimeMs: processingTime,
|
||
tokensUsed: response.tokensUsed,
|
||
costUSD: response.cost
|
||
}
|
||
|
||
// 添加到历史并更新上下文
|
||
context.conversationHistory.push(assistantMessage)
|
||
this.updateSessionContext(context, message, response.content, intent)
|
||
|
||
// 保持上下文窗口大小
|
||
this.trimContextHistory(context, options.contextWindow || 10)
|
||
|
||
// 更新统计
|
||
this.updateChatStats(processingTime, response.tokensUsed || 0, response.cost || 0, context.activeLanguage, intent)
|
||
|
||
return {
|
||
success: true,
|
||
data: assistantMessage,
|
||
processingTimeMs: processingTime,
|
||
tokensUsed: response.tokensUsed,
|
||
costUSD: response.cost,
|
||
provider
|
||
}
|
||
|
||
} catch (error) {
|
||
const errorMessage: ChatMessage = {
|
||
id: this.generateMessageId(),
|
||
sessionId,
|
||
type: 'error',
|
||
content: this.getErrorMessage(error.message, this.activeSessions.get(sessionId)?.activeLanguage || 'zh-CN'),
|
||
language: this.activeSessions.get(sessionId)?.activeLanguage || 'zh-CN',
|
||
timestamp: Date.now()
|
||
}
|
||
|
||
return {
|
||
success: false,
|
||
data: errorMessage,
|
||
error: error.message || 'Failed to send message'
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取会话历史
|
||
* @param sessionId 会话ID
|
||
* @param limit 消息数量限制
|
||
*/
|
||
getChatHistory(sessionId: string, limit: number = 50): ChatMessage[] {
|
||
const context = this.activeSessions.get(sessionId)
|
||
if (!context) return []
|
||
|
||
return context.conversationHistory.slice(-limit)
|
||
}
|
||
|
||
/**
|
||
* 更新会话语言
|
||
* @param sessionId 会话ID
|
||
* @param language 新语言
|
||
*/
|
||
async switchLanguage(sessionId: string, language: string): Promise<AIResponse<boolean>> {
|
||
try {
|
||
const context = this.activeSessions.get(sessionId)
|
||
if (!context) {
|
||
throw new Error('Session not found')
|
||
}
|
||
|
||
const oldLanguage = context.activeLanguage
|
||
context.activeLanguage = language
|
||
|
||
// 发送语言切换确认消息
|
||
const confirmMessage = this.getLanguageSwitchMessage(oldLanguage, language)
|
||
await this.addSystemMessage(sessionId, confirmMessage, language)
|
||
|
||
return { success: true, data: true }
|
||
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
error: error.message || 'Failed to switch language'
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置会话的新闻上下文
|
||
* @param sessionId 会话ID
|
||
* @param newsItems 新闻列表
|
||
*/
|
||
setNewsContext(sessionId: string, newsItems: ContentInfo[]): void {
|
||
const context = this.activeSessions.get(sessionId)
|
||
if (context) {
|
||
context.recentNews = newsItems.slice(0, 10) // 保留最近10条新闻
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取推荐问题
|
||
* @param sessionId 会话ID
|
||
* @param category 新闻分类
|
||
*/
|
||
getSuggestedQuestions(sessionId: string, category?: string): string[] {
|
||
const context = this.activeSessions.get(sessionId)
|
||
if (!context) return []
|
||
|
||
const language = context.activeLanguage
|
||
const baseQuestions = this.getBaseQuestions(language)
|
||
|
||
if (category && context.recentNews) {
|
||
const categoryNews = context.recentNews.filter(news => news.categoryId === category)
|
||
if (categoryNews.length > 0) {
|
||
return this.generateCategoryQuestions(categoryNews, language)
|
||
}
|
||
}
|
||
|
||
return baseQuestions
|
||
}
|
||
|
||
/**
|
||
* 结束会话
|
||
* @param sessionId 会话ID
|
||
*/
|
||
async endChatSession(sessionId: string): Promise<AIResponse<ChatSession>> {
|
||
try {
|
||
const context = this.activeSessions.get(sessionId)
|
||
if (!context) {
|
||
throw new Error('Session not found')
|
||
}
|
||
|
||
// 生成会话总结
|
||
const summary = await this.generateSessionSummary(context)
|
||
|
||
// 添加结束消息
|
||
const endMessage = this.getSessionEndMessage(context.activeLanguage)
|
||
await this.addSystemMessage(sessionId, endMessage, context.activeLanguage)
|
||
|
||
// 更新会话状态
|
||
const session: ChatSession = {
|
||
id: sessionId,
|
||
userId: '', // 需要从其他地方获取
|
||
name: this.generateSessionName(context.activeLanguage),
|
||
language: context.activeLanguage,
|
||
aiModel: '',
|
||
contextSettings: {},
|
||
totalMessages: context.conversationHistory.length,
|
||
totalTokensUsed: this.calculateTotalTokens(context),
|
||
totalCostUSD: this.calculateTotalCost(context),
|
||
isActive: false,
|
||
startedAt: context.conversationHistory[0]?.timestamp || Date.now(),
|
||
lastMessageAt: Date.now(),
|
||
endedAt: Date.now()
|
||
}
|
||
|
||
// 清理活动会话
|
||
this.activeSessions.delete(sessionId)
|
||
|
||
return { success: true, data: session }
|
||
|
||
} catch (error) {
|
||
return {
|
||
success: false,
|
||
error: error.message || 'Failed to end chat session'
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取聊天统计
|
||
*/
|
||
getChatStatistics(): ChatStats {
|
||
return { ...this.stats }
|
||
}
|
||
|
||
// Private methods
|
||
|
||
private async analyzeUserIntent(message: string, context: SessionContext): Promise<string> {
|
||
// 简单的意图识别
|
||
const lowerMessage = message.toLowerCase()
|
||
|
||
if (lowerMessage.includes('新闻') || lowerMessage.includes('news')) {
|
||
if (lowerMessage.includes('最新') || lowerMessage.includes('latest')) return 'latest_news'
|
||
if (lowerMessage.includes('推荐') || lowerMessage.includes('recommend')) return 'recommend_news'
|
||
if (lowerMessage.includes('搜索') || lowerMessage.includes('search')) return 'search_news'
|
||
return 'general_news'
|
||
}
|
||
|
||
if (lowerMessage.includes('翻译') || lowerMessage.includes('translate')) return 'translate'
|
||
if (lowerMessage.includes('总结') || lowerMessage.includes('summary')) return 'summarize'
|
||
if (lowerMessage.includes('分析') || lowerMessage.includes('analyze')) return 'analyze'
|
||
if (lowerMessage.includes('解释') || lowerMessage.includes('explain')) return 'explain'
|
||
if (lowerMessage.includes('比较') || lowerMessage.includes('compare')) return 'compare'
|
||
|
||
return 'general_chat'
|
||
}
|
||
|
||
private async generateAIResponse(
|
||
message: string,
|
||
context: SessionContext,
|
||
intent: string,
|
||
provider: AIProvider,
|
||
options: ChatOptions
|
||
): Promise<{ content: string, tokensUsed?: number, cost?: number }> {
|
||
|
||
const systemPrompt = this.buildSystemPrompt(context, intent)
|
||
const contextMessages = this.buildContextMessages(context, options.contextWindow || 10)
|
||
|
||
switch (provider) {
|
||
case 'openai':
|
||
return await this.generateResponseWithOpenAI(message, systemPrompt, contextMessages, options)
|
||
case 'google':
|
||
return await this.generateResponseWithGoogle(message, systemPrompt, contextMessages, options)
|
||
case 'baidu':
|
||
return await this.generateResponseWithBaidu(message, systemPrompt, contextMessages, options)
|
||
default:
|
||
return await this.generateBasicResponse(message, context, intent)
|
||
}
|
||
}
|
||
|
||
private buildSystemPrompt(context: SessionContext, intent: string): string {
|
||
const language = context.activeLanguage
|
||
const newsContext = context.recentNews?.length ? `当前有${context.recentNews.length}条相关新闻可供参考。` : ''
|
||
|
||
let basePrompt = ''
|
||
|
||
if (language === 'zh-CN') {
|
||
basePrompt = `你是一个专业的多语言新闻助手。你可以帮助用户了解最新新闻、分析新闻内容、回答相关问题,并提供翻译服务。${newsContext}`
|
||
|
||
switch (intent) {
|
||
case 'latest_news':
|
||
basePrompt += '用户想了解最新新闻,请提供简洁准确的新闻摘要。'
|
||
break
|
||
case 'recommend_news':
|
||
basePrompt += '用户需要新闻推荐,请根据用户偏好推荐相关新闻。'
|
||
break
|
||
case 'search_news':
|
||
basePrompt += '用户想搜索特定新闻,请帮助找到相关内容。'
|
||
break
|
||
case 'translate':
|
||
basePrompt += '用户需要翻译服务,请提供准确的翻译。'
|
||
break
|
||
case 'analyze':
|
||
basePrompt += '用户需要新闻分析,请提供客观深入的分析。'
|
||
break
|
||
default:
|
||
basePrompt += '请提供有帮助的回复,保持友好和专业。'
|
||
}
|
||
} else {
|
||
basePrompt = `You are a professional multilingual news assistant. You can help users understand the latest news, analyze news content, answer related questions, and provide translation services. ${newsContext}`
|
||
}
|
||
|
||
return basePrompt
|
||
}
|
||
|
||
private buildContextMessages(context: SessionContext, windowSize: number): ChatMessage[] {
|
||
return context.conversationHistory.slice(-windowSize * 2) // 用户和助手消息
|
||
}
|
||
|
||
private async generateResponseWithOpenAI(
|
||
message: string,
|
||
systemPrompt: string,
|
||
contextMessages: ChatMessage[],
|
||
options: ChatOptions
|
||
): Promise<{ content: string, tokensUsed: number, cost: number }> {
|
||
|
||
// 构建消息数组
|
||
const messages = [
|
||
{ role: 'system', content: systemPrompt },
|
||
...contextMessages.map(msg => ({
|
||
role: msg.type === 'user' ? 'user' : 'assistant',
|
||
content: msg.content
|
||
})),
|
||
{ role: 'user', content: message }
|
||
]
|
||
|
||
// 模拟OpenAI API调用
|
||
await this.delay(Math.random() * 1000 + 500)
|
||
|
||
const responseContent = await this.generateMockResponse(message, systemPrompt, options.language || 'zh-CN')
|
||
const tokensUsed = this.estimateTokens(messages) + this.estimateTokens([{ content: responseContent }])
|
||
const cost = this.calculateOpenAICost(tokensUsed, options.model || 'gpt-3.5-turbo')
|
||
|
||
return {
|
||
content: responseContent,
|
||
tokensUsed,
|
||
cost
|
||
}
|
||
}
|
||
|
||
private async generateResponseWithGoogle(
|
||
message: string,
|
||
systemPrompt: string,
|
||
contextMessages: ChatMessage[],
|
||
options: ChatOptions
|
||
): Promise<{ content: string, tokensUsed: number, cost: number }> {
|
||
|
||
await this.delay(Math.random() * 800 + 400)
|
||
|
||
const responseContent = await this.generateMockResponse(message, systemPrompt, options.language || 'zh-CN')
|
||
const tokensUsed = this.estimateTokens([{ content: message }, { content: responseContent }])
|
||
const cost = this.calculateGoogleCost(tokensUsed)
|
||
|
||
return {
|
||
content: responseContent,
|
||
tokensUsed,
|
||
cost
|
||
}
|
||
}
|
||
|
||
private async generateResponseWithBaidu(
|
||
message: string,
|
||
systemPrompt: string,
|
||
contextMessages: ChatMessage[],
|
||
options: ChatOptions
|
||
): Promise<{ content: string, tokensUsed: number, cost: number }> {
|
||
|
||
await this.delay(Math.random() * 600 + 300)
|
||
|
||
const responseContent = await this.generateMockResponse(message, systemPrompt, options.language || 'zh-CN')
|
||
const tokensUsed = this.estimateTokens([{ content: message }, { content: responseContent }])
|
||
const cost = this.calculateBaiduCost(tokensUsed)
|
||
|
||
return {
|
||
content: responseContent,
|
||
tokensUsed,
|
||
cost
|
||
}
|
||
}
|
||
|
||
private async generateBasicResponse(
|
||
message: string,
|
||
context: SessionContext,
|
||
intent: string
|
||
): Promise<{ content: string }> {
|
||
|
||
const language = context.activeLanguage
|
||
|
||
// 基础响应生成
|
||
switch (intent) {
|
||
case 'latest_news':
|
||
return { content: this.getLatestNewsResponse(context) }
|
||
case 'recommend_news':
|
||
return { content: this.getRecommendNewsResponse(context) }
|
||
case 'general_chat':
|
||
return { content: this.getGeneralChatResponse(message, language) }
|
||
default:
|
||
return { content: this.getDefaultResponse(language) }
|
||
}
|
||
}
|
||
|
||
private async generateMockResponse(message: string, systemPrompt: string, language: string): Promise<string> {
|
||
// 模拟AI响应生成
|
||
const responses = {
|
||
'zh-CN': [
|
||
'我理解您的问题。根据最新的新闻信息,我可以为您提供以下回复:',
|
||
'这是一个很有趣的问题。让我为您分析一下相关情况:',
|
||
'基于当前的新闻数据和分析,我的建议是:',
|
||
'感谢您的提问。关于这个话题,我可以分享以下见解:'
|
||
],
|
||
'en': [
|
||
'I understand your question. Based on the latest news information, I can provide the following response:',
|
||
'That\'s an interesting question. Let me analyze the relevant situation for you:',
|
||
'Based on current news data and analysis, my recommendation is:',
|
||
'Thank you for your question. Regarding this topic, I can share the following insights:'
|
||
]
|
||
}
|
||
|
||
const langResponses = responses[language] || responses['zh-CN']
|
||
const baseResponse = langResponses[Math.floor(Math.random() * langResponses.length)]
|
||
|
||
// 添加针对消息的具体回复
|
||
return `${baseResponse}\n\n关于"${message.substring(0, 50)}${message.length > 50 ? '...' : ''}",我建议您关注相关的最新发展和官方信息。如果您需要更具体的信息或有其他问题,请随时告诉我。`
|
||
}
|
||
|
||
private getLatestNewsResponse(context: SessionContext): string {
|
||
if (!context.recentNews || context.recentNews.length === 0) {
|
||
return context.activeLanguage === 'zh-CN'
|
||
? '抱歉,目前没有最新的新闻信息。请稍后再试或询问其他问题。'
|
||
: 'Sorry, there is no latest news information available at the moment. Please try again later or ask other questions.'
|
||
}
|
||
|
||
const language = context.activeLanguage
|
||
const news = context.recentNews.slice(0, 3)
|
||
|
||
if (language === 'zh-CN') {
|
||
let response = '以下是最新的新闻信息:\n\n'
|
||
news.forEach((item, index) => {
|
||
response += `${index + 1}. ${item.title}\n${item.summary || item.content.substring(0, 100)}...\n\n`
|
||
})
|
||
return response
|
||
} else {
|
||
let response = 'Here is the latest news information:\n\n'
|
||
news.forEach((item, index) => {
|
||
response += `${index + 1}. ${item.title}\n${item.summary || item.content.substring(0, 100)}...\n\n`
|
||
})
|
||
return response
|
||
}
|
||
}
|
||
|
||
private getRecommendNewsResponse(context: SessionContext): string {
|
||
const language = context.activeLanguage
|
||
|
||
if (language === 'zh-CN') {
|
||
return '根据您的兴趣和阅读历史,我为您推荐以下新闻:\n\n1. 科技领域的最新发展\n2. 国际时事动态\n3. 经济政策解读\n\n如果您想了解特定领域的新闻,请告诉我您感兴趣的类别。'
|
||
} else {
|
||
return 'Based on your interests and reading history, I recommend the following news for you:\n\n1. Latest developments in technology\n2. International current affairs\n3. Economic policy analysis\n\nIf you want to know about news in a specific field, please tell me the category you are interested in.'
|
||
}
|
||
}
|
||
|
||
private getGeneralChatResponse(message: string, language: string): string {
|
||
if (language === 'zh-CN') {
|
||
return '我是您的新闻助手,可以帮助您了解最新新闻、分析新闻内容、提供翻译服务等。请告诉我您想了解什么,我会尽力为您提供帮助。'
|
||
} else {
|
||
return 'I am your news assistant and can help you understand the latest news, analyze news content, provide translation services, etc. Please tell me what you would like to know and I will do my best to help you.'
|
||
}
|
||
}
|
||
|
||
private getDefaultResponse(language: string): string {
|
||
if (language === 'zh-CN') {
|
||
return '感谢您的问题。作为您的新闻助手,我可以帮助您获取最新新闻、分析新闻内容、翻译文本等。请告诉我您需要什么帮助。'
|
||
} else {
|
||
return 'Thank you for your question. As your news assistant, I can help you get the latest news, analyze news content, translate text, etc. Please tell me what help you need.'
|
||
}
|
||
}
|
||
|
||
private async addSystemMessage(sessionId: string, content: string, language: string): Promise<void> {
|
||
const context = this.activeSessions.get(sessionId)
|
||
if (!context) return
|
||
|
||
const systemMessage: ChatMessage = {
|
||
id: this.generateMessageId(),
|
||
sessionId,
|
||
type: 'system',
|
||
content,
|
||
language,
|
||
timestamp: Date.now()
|
||
}
|
||
|
||
context.conversationHistory.push(systemMessage)
|
||
}
|
||
|
||
private updateSessionContext(context: SessionContext, userMessage: string, assistantResponse: string, intent: string): void {
|
||
// 更新当前话题
|
||
if (intent !== 'general_chat') {
|
||
context.currentTopic = intent
|
||
}
|
||
|
||
// 分析和更新用户偏好(简化版)
|
||
if (userMessage.includes('科技') || userMessage.includes('technology')) {
|
||
if (!context.userPreferences) context.userPreferences = { preferredLanguages: [], preferredCategories: [], preferredSources: [], newsStyle: 'brief', updateFrequency: 'daily' }
|
||
if (!context.userPreferences.preferredCategories.includes('technology')) {
|
||
context.userPreferences.preferredCategories.push('technology')
|
||
}
|
||
}
|
||
}
|
||
|
||
private trimContextHistory(context: SessionContext, windowSize: number): void {
|
||
if (context.conversationHistory.length > windowSize * 2) {
|
||
// 保留系统消息和最近的对话
|
||
const systemMessages = context.conversationHistory.filter(msg => msg.type === 'system')
|
||
const recentMessages = context.conversationHistory.slice(-windowSize * 2)
|
||
context.conversationHistory = [...systemMessages.slice(-2), ...recentMessages]
|
||
}
|
||
}
|
||
|
||
private async generateWelcomeMessage(language: string): Promise<string> {
|
||
if (language === 'zh-CN') {
|
||
return '您好!我是您的智能新闻助手。我可以帮助您:\n\n🔍 获取最新新闻\n📊 分析新闻内容\n🌐 翻译新闻文本\n💡 推荐相关新闻\n\n请告诉我您想了解什么,让我们开始对话吧!'
|
||
} else {
|
||
return 'Hello! I am your intelligent news assistant. I can help you:\n\n🔍 Get the latest news\n📊 Analyze news content\n🌐 Translate news text\n💡 Recommend related news\n\nPlease tell me what you want to know and let\'s start the conversation!'
|
||
}
|
||
}
|
||
|
||
private generateSessionName(language: string): string {
|
||
const now = new Date()
|
||
const timeStr = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`
|
||
|
||
if (language === 'zh-CN') {
|
||
return `新闻对话 ${timeStr}`
|
||
} else {
|
||
return `News Chat ${timeStr}`
|
||
}
|
||
}
|
||
|
||
private getLanguageSwitchMessage(oldLang: string, newLang: string): string {
|
||
if (newLang === 'zh-CN') {
|
||
return '语言已切换为中文。我会用中文继续为您服务。'
|
||
} else {
|
||
return 'Language has been switched to English. I will continue to serve you in English.'
|
||
}
|
||
}
|
||
|
||
private getSessionEndMessage(language: string): string {
|
||
if (language === 'zh-CN') {
|
||
return '感谢您使用新闻助手服务!如果您还有其他问题,随时可以开始新的对话。祝您有美好的一天!'
|
||
} else {
|
||
return 'Thank you for using the news assistant service! If you have any other questions, you can start a new conversation at any time. Have a great day!'
|
||
}
|
||
}
|
||
|
||
private getErrorMessage(error: string, language: string): string {
|
||
if (language === 'zh-CN') {
|
||
return '抱歉,处理您的请求时出现了问题。请稍后重试或联系技术支持。'
|
||
} else {
|
||
return 'Sorry, there was a problem processing your request. Please try again later or contact technical support.'
|
||
}
|
||
}
|
||
|
||
private getBaseQuestions(language: string): string[] {
|
||
if (language === 'zh-CN') {
|
||
return [
|
||
'今天有什么重要新闻?',
|
||
'请推荐一些科技新闻',
|
||
'最新的经济动态如何?',
|
||
'有什么国际新闻值得关注?',
|
||
'帮我分析这条新闻的影响',
|
||
'翻译这段英文新闻'
|
||
]
|
||
} else {
|
||
return [
|
||
'What important news is there today?',
|
||
'Please recommend some technology news',
|
||
'How are the latest economic developments?',
|
||
'What international news is worth paying attention to?',
|
||
'Help me analyze the impact of this news',
|
||
'Translate this Chinese news'
|
||
]
|
||
}
|
||
}
|
||
|
||
private generateCategoryQuestions(news: ContentInfo[], language: string): string[] {
|
||
if (language === 'zh-CN') {
|
||
return [
|
||
`分析一下"${news[0].title}"这条新闻`,
|
||
`这类新闻的趋势如何?`,
|
||
`相关的新闻还有哪些?`,
|
||
`这对普通人有什么影响?`
|
||
]
|
||
} else {
|
||
return [
|
||
`Analyze the news "${news[0].title}"`,
|
||
`What are the trends in this type of news?`,
|
||
`What other related news are there?`,
|
||
`What impact does this have on ordinary people?`
|
||
]
|
||
}
|
||
}
|
||
|
||
private async generateSessionSummary(context: SessionContext): Promise<string> {
|
||
const messageCount = context.conversationHistory.length
|
||
const topics = new Set(context.conversationHistory
|
||
.filter(msg => msg.type === 'user')
|
||
.map(msg => this.extractTopicFromMessage(msg.content))
|
||
)
|
||
|
||
const language = context.activeLanguage
|
||
|
||
if (language === 'zh-CN') {
|
||
return `本次对话共${messageCount}条消息,主要讨论了${Array.from(topics).join('、')}等话题。`
|
||
} else {
|
||
return `This conversation had ${messageCount} messages and mainly discussed topics such as ${Array.from(topics).join(', ')}.`
|
||
}
|
||
}
|
||
|
||
private extractTopicFromMessage(message: string): string {
|
||
// 简单的话题提取
|
||
if (message.includes('新闻') || message.includes('news')) return '新闻'
|
||
if (message.includes('科技') || message.includes('technology')) return '科技'
|
||
if (message.includes('经济') || message.includes('economy')) return '经济'
|
||
if (message.includes('政治') || message.includes('politics')) return '政治'
|
||
return '一般话题'
|
||
}
|
||
|
||
private calculateTotalTokens(context: SessionContext): number {
|
||
return context.conversationHistory
|
||
.filter(msg => msg.tokensUsed)
|
||
.reduce((total, msg) => total + (msg.tokensUsed || 0), 0)
|
||
}
|
||
|
||
private calculateTotalCost(context: SessionContext): number {
|
||
return context.conversationHistory
|
||
.filter(msg => msg.costUSD)
|
||
.reduce((total, msg) => total + (msg.costUSD || 0), 0)
|
||
}
|
||
|
||
private updateChatStats(processingTime: number, tokens: number, cost: number, language: string, intent: string): void {
|
||
this.stats.totalMessages++
|
||
this.stats.avgResponseTime = (this.stats.avgResponseTime * (this.stats.totalMessages - 1) + processingTime) / this.stats.totalMessages
|
||
this.stats.totalCost += cost
|
||
|
||
this.stats.languageDistribution[language] = (this.stats.languageDistribution[language] || 0) + 1
|
||
this.stats.topQuestionTypes[intent] = (this.stats.topQuestionTypes[intent] || 0) + 1
|
||
}
|
||
|
||
private estimateTokens(messages: Array<{ content: string }>): number {
|
||
return messages.reduce((total, msg) => total + Math.ceil(msg.content.length / 4), 0)
|
||
}
|
||
|
||
private calculateOpenAICost(tokens: number, model: string): number {
|
||
const pricing: Record<string, { input: number, output: number }> = {
|
||
'gpt-3.5-turbo': { input: 0.0015, output: 0.002 },
|
||
'gpt-4': { input: 0.03, output: 0.06 }
|
||
}
|
||
const modelPricing = pricing[model] || pricing['gpt-3.5-turbo']
|
||
return (tokens / 1000) * ((modelPricing.input + modelPricing.output) / 2)
|
||
}
|
||
|
||
private calculateGoogleCost(tokens: number): number {
|
||
return (tokens / 1000) * 0.01 // 估算
|
||
}
|
||
|
||
private calculateBaiduCost(tokens: number): number {
|
||
return (tokens / 1000) * 0.008 // 估算
|
||
}
|
||
|
||
private selectBestProvider(): AIProvider {
|
||
if (this.config.openai?.apiKey) return 'openai'
|
||
if (this.config.google?.apiKey) return 'google'
|
||
if (this.config.baidu?.apiKey) return 'baidu'
|
||
return 'openai'
|
||
}
|
||
|
||
private getDefaultModel(provider?: AIProvider): string {
|
||
switch (provider) {
|
||
case 'openai': return 'gpt-3.5-turbo'
|
||
case 'google': return 'gemini-pro'
|
||
case 'baidu': return 'ernie-bot'
|
||
default: return 'gpt-3.5-turbo'
|
||
}
|
||
}
|
||
|
||
private generateSessionId(userId: string): string {
|
||
return `session_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||
}
|
||
|
||
private generateMessageId(): string {
|
||
return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||
}
|
||
|
||
private async loadUserPreferences(userId: string): Promise<UserPreferences | undefined> {
|
||
// 模拟加载用户偏好
|
||
return {
|
||
preferredLanguages: ['zh-CN'],
|
||
preferredCategories: ['technology', 'economy'],
|
||
preferredSources: [],
|
||
newsStyle: 'brief',
|
||
updateFrequency: 'daily'
|
||
}
|
||
}
|
||
|
||
private async delay(ms: number): Promise<void> {
|
||
return new Promise(resolve => setTimeout(resolve, ms))
|
||
}
|
||
|
||
private initializeChatTemplates(): void {
|
||
this.chatTemplates = [
|
||
{
|
||
id: 'news_assistant',
|
||
name: '新闻助手',
|
||
description: '专业的新闻分析和推荐助手',
|
||
systemPrompt: '你是一个专业的新闻助手,可以帮助用户了解最新新闻、分析新闻内容、提供翻译服务。',
|
||
language: 'zh-CN',
|
||
category: 'news'
|
||
},
|
||
{
|
||
id: 'tech_news',
|
||
name: '科技新闻专家',
|
||
description: '专注于科技新闻的分析和解读',
|
||
systemPrompt: '你是一个科技新闻专家,专门分析科技行业动态、创新趋势和技术发展。',
|
||
language: 'zh-CN',
|
||
category: 'technology'
|
||
}
|
||
]
|
||
}
|
||
}
|