iconsulting/packages/services/conversation-service/src/infrastructure/knowledge/knowledge-client.service.ts

290 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<T> {
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<string>('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<RAGResult | null> {
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<RAGResult>;
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<string | null> {
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<OffTopicResult> {
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<OffTopicResult>;
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<UserMemory | null> {
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<UserMemory>;
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<UserMemory[]> {
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<UserMemory[]>;
return data.success ? data.data : [];
} catch (error) {
console.error('[KnowledgeClient] searchUserMemories error:', error);
return [];
}
}
/**
* 获取用户最重要的记忆
*/
async getUserTopMemories(userId: string, limit = 5): Promise<UserMemory[]> {
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<UserMemory[]>;
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<SystemExperience[]> {
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<SystemExperience[]>;
return data.success ? data.data : [];
} catch (error) {
console.error('[KnowledgeClient] searchExperiences error:', error);
return [];
}
}
/**
* 初始化用户节点Neo4j
*/
async initializeUser(userId: string): Promise<boolean> {
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;
}
}
}