import { Injectable, OnModuleInit } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; /** * RAG 检索结果 */ export interface RAGResult { content: string; sources: Array<{ articleId: string; title: string; similarity: number; }>; userMemories?: string[]; systemExperiences?: string[]; } /** * 离题检测结果 */ export interface OffTopicResult { isOffTopic: boolean; confidence: number; reason?: string; } /** * 用户记忆 */ export interface UserMemory { id: string; userId: string; memoryType: 'FACT' | 'PREFERENCE' | 'INTENT'; content: string; importance: number; createdAt: string; } /** * 系统经验 */ export interface SystemExperience { id: string; experienceType: string; content: string; scenario: string; confidence: number; relatedCategory?: string; } /** * API 响应包装 */ interface ApiResponse { success: boolean; data: T; message?: string; } /** * Knowledge Service 客户端 * 封装对 knowledge-service 的 HTTP 调用 */ @Injectable() export class KnowledgeClientService implements OnModuleInit { private baseUrl: string; constructor(private configService: ConfigService) {} onModuleInit() { this.baseUrl = this.configService.get('KNOWLEDGE_SERVICE_URL') || 'http://knowledge-service:3003'; console.log(`[KnowledgeClient] Initialized with base URL: ${this.baseUrl}`); } /** * RAG 知识检索 */ async retrieveKnowledge(params: { query: string; userId?: string; category?: string; includeMemories?: boolean; includeExperiences?: boolean; }): Promise { try { const response = await fetch(`${this.baseUrl}/api/v1/knowledge/retrieve`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params), }); if (!response.ok) { console.error(`[KnowledgeClient] RAG retrieve failed: ${response.status}`); return null; } const data = (await response.json()) as ApiResponse; return data.success ? data.data : null; } catch (error) { console.error('[KnowledgeClient] RAG retrieve error:', error); return null; } } /** * RAG 检索并格式化为提示词上下文 */ async retrieveForPrompt(params: { query: string; userId?: string; category?: string; }): Promise { try { const response = await fetch(`${this.baseUrl}/api/v1/knowledge/retrieve/prompt`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params), }); if (!response.ok) { console.error(`[KnowledgeClient] RAG retrieve/prompt failed: ${response.status}`); return null; } const data = (await response.json()) as ApiResponse<{ context: string }>; return data.success ? data.data.context : null; } catch (error) { console.error('[KnowledgeClient] RAG retrieve/prompt error:', error); return null; } } /** * 检查是否离题 */ async checkOffTopic(query: string): Promise { try { const response = await fetch(`${this.baseUrl}/api/v1/knowledge/check-off-topic`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query }), }); if (!response.ok) { console.error(`[KnowledgeClient] checkOffTopic failed: ${response.status}`); return { isOffTopic: false, confidence: 0 }; } const data = (await response.json()) as ApiResponse; return data.success ? data.data : { isOffTopic: false, confidence: 0 }; } catch (error) { console.error('[KnowledgeClient] checkOffTopic error:', error); return { isOffTopic: false, confidence: 0 }; } } /** * 保存用户记忆 */ async saveUserMemory(params: { userId: string; memoryType: 'FACT' | 'PREFERENCE' | 'INTENT'; content: string; importance?: number; sourceConversationId?: string; relatedCategory?: string; }): Promise { try { const response = await fetch(`${this.baseUrl}/api/v1/memory/user`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params), }); if (!response.ok) { console.error(`[KnowledgeClient] saveUserMemory failed: ${response.status}`); return null; } const data = (await response.json()) as ApiResponse; return data.success ? data.data : null; } catch (error) { console.error('[KnowledgeClient] saveUserMemory error:', error); return null; } } /** * 搜索用户相关记忆 */ async searchUserMemories(params: { userId: string; query: string; limit?: number; }): Promise { try { const response = await fetch(`${this.baseUrl}/api/v1/memory/user/search`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params), }); if (!response.ok) { console.error(`[KnowledgeClient] searchUserMemories failed: ${response.status}`); return []; } const data = (await response.json()) as ApiResponse; return data.success ? data.data : []; } catch (error) { console.error('[KnowledgeClient] searchUserMemories error:', error); return []; } } /** * 获取用户最重要的记忆 */ async getUserTopMemories(userId: string, limit = 5): Promise { try { const response = await fetch(`${this.baseUrl}/api/v1/memory/user/${userId}/top?limit=${limit}`); if (!response.ok) { console.error(`[KnowledgeClient] getUserTopMemories failed: ${response.status}`); return []; } const data = (await response.json()) as ApiResponse; return data.success ? data.data : []; } catch (error) { console.error('[KnowledgeClient] getUserTopMemories error:', error); return []; } } /** * 搜索相关系统经验 */ async searchExperiences(params: { query: string; experienceType?: string; category?: string; activeOnly?: boolean; limit?: number; }): Promise { try { const response = await fetch(`${this.baseUrl}/api/v1/memory/experience/search`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params), }); if (!response.ok) { console.error(`[KnowledgeClient] searchExperiences failed: ${response.status}`); return []; } const data = (await response.json()) as ApiResponse; return data.success ? data.data : []; } catch (error) { console.error('[KnowledgeClient] searchExperiences error:', error); return []; } } /** * 初始化用户节点(Neo4j) */ async initializeUser(userId: string): Promise { try { // 通过保存一个初始记忆来初始化用户 const response = await fetch(`${this.baseUrl}/api/v1/memory/user`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId, memoryType: 'FACT', content: '用户首次访问系统', importance: 10, }), }); return response.ok; } catch (error) { console.error('[KnowledgeClient] initializeUser error:', error); return false; } } }