Update metaSearchAgent.ts

This commit is contained in:
Lucas 2025-01-17 09:08:55 +01:00 committed by GitHub
parent fb958d0e79
commit 1b44361b6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -146,6 +146,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
// Recherche web si activée // Recherche web si activée
if (this.config.searchWeb) { if (this.config.searchWeb) {
console.log('🔍 Démarrage de la recherche web...');
const res = await searchSearxng(question, { const res = await searchSearxng(question, {
language: 'fr', language: 'fr',
engines: this.config.activeEngines, engines: this.config.activeEngines,
@ -159,60 +160,17 @@ export class MetaSearchAgent implements MetaSearchAgentType {
title: result.title, title: result.title,
url: result.url, url: result.url,
type: 'web', type: 'web',
source: 'web',
displayDomain: new URL(result.url).hostname.replace('www.', ''),
favicon: `https://s2.googleusercontent.com/s2/favicons?domain_url=${result.url}`,
linkText: 'Voir la page',
...(result.img_src && { img_src: result.img_src }), ...(result.img_src && { img_src: result.img_src }),
}, },
}), }),
); );
console.log('🌐 Sources web trouvées:', documents.length);
} }
// Recherche d'experts si activée
if (this.config.searchDatabase) {
try {
console.log('🔍 Recherche d\'experts...');
const expertResults = await handleExpertSearch(
{
query: question,
chat_history: [],
messageId: 'search_' + Date.now(),
chatId: 'chat_' + Date.now()
},
llm
);
console.log('🔍 Experts trouvés:', expertResults.experts.length);
const expertDocs = expertResults.experts.map(expert =>
new Document({
pageContent: `Expert: ${expert.prenom} ${expert.nom}
Spécialité: ${expert.specialite}
Ville: ${expert.ville}
Tarif: ${expert.tarif}
Expertises: ${expert.expertises}
Services: ${JSON.stringify(expert.services)}
${expert.biographie}`,
metadata: {
type: 'expert',
expert: true,
expertData: expert,
title: `${expert.specialite} - ${expert.ville}`,
url: `/expert/${expert.id_expert}`,
image_url: expert.image_url
}
})
);
documents = [...expertDocs, ...documents];
} catch (error) {
console.error('Erreur lors de la recherche d\'experts:', error);
}
}
// Trier pour mettre les experts en premier
documents.sort((a, b) => {
if (a.metadata?.type === 'expert' && b.metadata?.type !== 'expert') return -1;
if (a.metadata?.type !== 'expert' && b.metadata?.type === 'expert') return 1;
return 0;
});
return { query: question, docs: documents }; return { query: question, docs: documents };
}), }),
]); ]);
@ -347,9 +305,11 @@ export class MetaSearchAgent implements MetaSearchAgentType {
} }
// 3. Enfin, compléter avec la recherche web si nécessaire et si peu de résultats // 3. Enfin, compléter avec la recherche web si nécessaire et si peu de résultats
if (this.config.searchWeb && docs.length < 3) { if (this.config.searchWeb) {
try { try {
console.log('🌐 Démarrage de la recherche web...');
const webResults = await this.performWebSearch(input.query); const webResults = await this.performWebSearch(input.query);
console.log(`🌐 ${webResults.length} résultats web trouvés`);
docs = [...docs, ...webResults]; docs = [...docs, ...webResults];
} catch (error) { } catch (error) {
console.error('❌ Erreur lors de la recherche web:', error); console.error('❌ Erreur lors de la recherche web:', error);
@ -468,23 +428,30 @@ export class MetaSearchAgent implements MetaSearchAgentType {
sources?.map(source => { sources?.map(source => {
const isUploadedDoc = source.metadata?.type === 'uploaded'; const isUploadedDoc = source.metadata?.type === 'uploaded';
const isExpert = source.metadata?.type === 'expert'; const isExpert = source.metadata?.type === 'expert';
const pageNumber = source.metadata?.pageNumber || 1; const isWeb = source.metadata?.type === 'web';
const sourceId = source.metadata?.source; const sourceId = source.metadata?.source;
// Construire l'URL selon le type de source // Construire l'URL selon le type de source
let url; let url;
if (isUploadedDoc && sourceId) { if (isUploadedDoc && sourceId) {
url = `/api/uploads/${sourceId}/content?page=${pageNumber}`; const page = source.metadata?.pageNumber || source.metadata?.page || 1;
console.log(`🔍 Construction URL pour source ${sourceId} - Page ${page}`, source.metadata);
url = `/api/uploads/${sourceId}/content?page=${page}`;
} else if (isExpert) { } else if (isExpert) {
url = source.metadata?.url; url = source.metadata?.url;
} else if (source.metadata?.type === 'web') { } else if (isWeb) {
url = source.metadata?.url; url = source.metadata?.url;
console.log('🌐 Source web trouvée:', {
title: source.metadata?.title,
url: url
});
} }
// Construire un titre descriptif // Construire un titre descriptif
let title = source.metadata?.title || ''; let title = source.metadata?.title || '';
if (isUploadedDoc && title) { if (isUploadedDoc && title) {
title = `${title} - Page ${pageNumber}`; const page = source.metadata?.pageNumber || source.metadata?.page || 1;
title = `${title} - Page ${page}`;
} else if (isExpert) { } else if (isExpert) {
title = source.metadata?.displayTitle || title; title = source.metadata?.displayTitle || title;
} }
@ -498,22 +465,28 @@ export class MetaSearchAgent implements MetaSearchAgentType {
title: title, title: title,
type: source.metadata?.type || 'web', type: source.metadata?.type || 'web',
url: url, url: url,
source: sourceId, source: sourceId || (isWeb ? 'web' : undefined),
pageNumber: pageNumber, pageNumber: source.metadata?.pageNumber || source.metadata?.page || 1,
displayDomain: isUploadedDoc ? 'Document local' :
isWeb ? new URL(url).hostname.replace('www.', '') : undefined,
searchText: searchText:
source.metadata?.searchText?.substring(0, 200) || source.metadata?.searchText?.substring(0, 200) ||
limitedContent.substring(0, 200), limitedContent.substring(0, 200),
expertData: source.metadata?.expertData, expertData: source.metadata?.expertData,
illustrationImage: source.metadata?.illustrationImage, illustrationImage: source.metadata?.illustrationImage,
imageTitle: source.metadata?.imageTitle, imageTitle: source.metadata?.imageTitle,
favicon: source.metadata?.favicon, favicon: isWeb ? `https://s2.googleusercontent.com/s2/favicons?domain_url=${url}` : source.metadata?.favicon,
linkText: source.metadata?.linkText, linkText: isWeb ? 'Voir la page' : 'Voir la source',
expertName: source.metadata?.expertName expertName: source.metadata?.expertName,
fileId: sourceId,
page: source.metadata?.pageNumber || source.metadata?.page || 1,
isFile: isUploadedDoc
} }
}; };
}) || []; }) || [];
console.log('🔍 Sources normalisées:', normalizedSources.length); console.log('🔍 Sources normalisées:', normalizedSources.length);
console.log('🔍 Types de sources:', normalizedSources.map(s => s.metadata.type));
emitter.emit( emitter.emit(
'data', 'data',
@ -545,9 +518,12 @@ export class MetaSearchAgent implements MetaSearchAgentType {
private async handleStreamWithMemory( private async handleStreamWithMemory(
stream: IterableReadableStream<StreamEvent>, stream: IterableReadableStream<StreamEvent>,
emitter: eventEmitter emitter: eventEmitter,
llm: BaseChatModel,
originalQuery: string
) { ) {
let fullAssistantResponse = ''; let fullAssistantResponse = '';
let hasEmittedSuggestions = false;
for await (const event of stream) { for await (const event of stream) {
if (event.event === 'on_chain_stream') { if (event.event === 'on_chain_stream') {
@ -559,17 +535,45 @@ export class MetaSearchAgent implements MetaSearchAgentType {
); );
} }
} else if (event.event === 'on_chain_end') { } else if (event.event === 'on_chain_end') {
if (event.name === 'FinalResponseGenerator') { if (event.name === 'FinalResponseGenerator' && !hasEmittedSuggestions) {
try {
const suggestionsPrompt = `
Based on this conversation and response, suggest 3 relevant follow-up questions:
"${fullAssistantResponse}"
Return only the questions, one per line.`;
const suggestionsResponse = await llm.invoke(suggestionsPrompt);
const suggestions = String(suggestionsResponse.content)
.split('\n')
.filter(s => s.trim())
.slice(0, 3);
// Émettre uniquement les suggestions
emitter.emit(
'data',
JSON.stringify({
type: 'suggestions',
data: {
suggestions: suggestions,
suggestedExperts: []
}
})
);
hasEmittedSuggestions = true;
} catch (error) {
console.error('❌ Erreur lors de la génération des suggestions:', error);
}
this.updateMemory(new AIMessage(fullAssistantResponse.trim())); this.updateMemory(new AIMessage(fullAssistantResponse.trim()));
emitter.emit('end'); emitter.emit('end');
} }
if (event.name === 'FinalSourceRetriever') { if (event.name === 'FinalSourceRetriever') {
const sources = event.data.output; const sources = event.data.output;
const normalizedSources = const normalizedSources = sources?.map(source => {
sources?.map(source => {
const isUploadedDoc = source.metadata?.type === 'uploaded'; const isUploadedDoc = source.metadata?.type === 'uploaded';
const isExpert = source.metadata?.type === 'expert'; const isExpert = source.metadata?.type === 'expert';
const pageNumber = source.metadata?.pageNumber || 1; const pageNumber = source.metadata?.pageNumber || source.metadata?.page || 1;
const sourceId = source.metadata?.source; const sourceId = source.metadata?.source;
let url; let url;
@ -588,8 +592,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
title = source.metadata?.displayTitle || title; title = source.metadata?.displayTitle || title;
} }
const limitedContent = const limitedContent = source.pageContent?.substring(0, 1000) || '';
source.pageContent?.substring(0, 1000) || '';
return { return {
pageContent: limitedContent, pageContent: limitedContent,
@ -599,15 +602,17 @@ export class MetaSearchAgent implements MetaSearchAgentType {
url: url, url: url,
source: sourceId, source: sourceId,
pageNumber: pageNumber, pageNumber: pageNumber,
searchText: displayDomain: isUploadedDoc ? 'Document local' : undefined,
source.metadata?.searchText?.substring(0, 200) || searchText: source.metadata?.searchText?.substring(0, 200) || limitedContent.substring(0, 200),
limitedContent.substring(0, 200),
expertData: source.metadata?.expertData, expertData: source.metadata?.expertData,
illustrationImage: source.metadata?.illustrationImage, illustrationImage: source.metadata?.illustrationImage,
imageTitle: source.metadata?.imageTitle, imageTitle: source.metadata?.imageTitle,
favicon: source.metadata?.favicon, favicon: source.metadata?.favicon,
linkText: source.metadata?.linkText, linkText: isUploadedDoc ? 'Voir le document' : 'Voir la source',
expertName: source.metadata?.expertName expertName: source.metadata?.expertName,
fileId: sourceId,
page: pageNumber,
isFile: isUploadedDoc
} }
}; };
}) || []; }) || [];
@ -624,7 +629,8 @@ export class MetaSearchAgent implements MetaSearchAgentType {
}) })
); );
} }
} else { }
else {
emitter.emit(event.event, event.data); emitter.emit(event.event, event.data);
} }
} }
@ -809,10 +815,8 @@ Prends en compte:
if (uploadedDocs.length > 0) { if (uploadedDocs.length > 0) {
try { try {
// Utiliser l'instance unique de RAGDocumentChain
const ragChain = RAGDocumentChain.getInstance(); const ragChain = RAGDocumentChain.getInstance();
// Vérifier si déjà initialisé avec les mêmes documents
if (!ragChain.isInitialized()) { if (!ragChain.isInitialized()) {
console.log('🔄 Initialisation du vector store...'); console.log('🔄 Initialisation du vector store...');
await ragChain.initializeVectorStoreFromDocuments(uploadedDocs, embeddings); await ragChain.initializeVectorStoreFromDocuments(uploadedDocs, embeddings);
@ -820,11 +824,9 @@ Prends en compte:
console.log('✅ Vector store déjà initialisé'); console.log('✅ Vector store déjà initialisé');
} }
// Recherche sémantique
const relevantDocs = await ragChain.searchSimilarDocuments(message, 5); const relevantDocs = await ragChain.searchSimilarDocuments(message, 5);
console.log('📄 Documents pertinents trouvés:', relevantDocs.length); console.log('📄 Documents pertinents trouvés:', relevantDocs.length);
// Extraction du contexte pour enrichir la recherche
const documentContext = relevantDocs const documentContext = relevantDocs
.map(doc => doc.pageContent) .map(doc => doc.pageContent)
.join('\n') .join('\n')
@ -833,44 +835,49 @@ Prends en compte:
const documentTitle = uploadedDocs[0]?.metadata?.title || ''; const documentTitle = uploadedDocs[0]?.metadata?.title || '';
const enrichedQuery = `${message} ${documentTitle} ${documentContext}`; const enrichedQuery = `${message} ${documentTitle} ${documentContext}`;
// 2. Recherche d'experts si nécessaire // Recherche web si nécessaire
let expertResults = [];
if (analysis.requiresExpertSearch) {
expertResults = await this.searchExperts(message, embeddings, llm);
}
// 3. Recherche web si nécessaire
let webResults = []; let webResults = [];
if (analysis.requiresWebSearch) { if (analysis.requiresWebSearch) {
webResults = await this.searchWeb(enrichedQuery); console.log('🔍 Démarrage de la recherche web...');
const res = await searchSearxng(enrichedQuery, {
language: 'fr',
engines: this.config.activeEngines,
});
webResults = res.results.map(result =>
new Document({
pageContent: result.content,
metadata: {
title: result.title,
url: result.url,
type: 'web',
source: 'web',
...(result.img_src && { img_src: result.img_src }),
},
})
);
console.log('🌐 Résultats web trouvés:', webResults.length);
} }
// Combinaison des résultats avec les scores appropriés // Combinaison des résultats
const combinedResults = [ const combinedResults = [
...relevantDocs.map(doc => ({ ...relevantDocs.map(doc => ({
...doc, ...doc,
metadata: { metadata: {
...doc.metadata, ...doc.metadata,
score: 0.8 // Score élevé pour les documents uploadés type: doc.metadata.type || 'uploaded'
} }
})), })),
...expertResults.map(expert => ({ ...webResults
...expert,
metadata: {
...expert.metadata,
score: 0.6 // Score moyen pour les experts
}
})),
...webResults.map(web => ({
...web,
metadata: {
...web.metadata,
score: 0.4 // Score plus faible pour les résultats web
}
}))
]; ];
// Tri et sélection des meilleurs résultats console.log('🔄 Résultats combinés:', {
total: combinedResults.length,
uploaded: relevantDocs.length,
web: webResults.length,
types: combinedResults.map(doc => doc.metadata.type)
});
const finalResults = await this.rerankDocs( const finalResults = await this.rerankDocs(
message, message,
combinedResults, combinedResults,
@ -880,7 +887,6 @@ Prends en compte:
llm llm
); );
// Création de la chaîne de réponse
const answeringChain = await this.createAnsweringChain( const answeringChain = await this.createAnsweringChain(
llm, llm,
fileIds, fileIds,
@ -898,71 +904,17 @@ Prends en compte:
} }
); );
this.handleStreamWithMemory(stream, emitter); this.handleStreamWithMemory(stream, emitter, llm, message);
} catch (error) { } catch (error) {
console.error('❌ Erreur lors de la gestion des documents:', error); console.error('❌ Erreur lors de la gestion des documents:', error);
// Fallback en mode standard await this.handleFallback(llm, message, mergedHistory, emitter, fileIds, embeddings, effectiveMode);
const answeringChain = await this.createAnsweringChain(
llm,
fileIds,
embeddings,
effectiveMode
);
const stream = answeringChain.streamEvents(
{
chat_history: this.conversationHistory,
query: message
},
{
version: 'v1'
}
);
this.handleStreamWithMemory(stream, emitter);
} }
} else { } else {
// Fallback sans documents uploadés await this.handleFallback(llm, message, mergedHistory, emitter, fileIds, embeddings, effectiveMode);
const answeringChain = await this.createAnsweringChain(
llm,
fileIds,
embeddings,
effectiveMode
);
const stream = answeringChain.streamEvents(
{
chat_history: mergedHistory,
query: message
},
{
version: 'v1'
}
);
this.handleStreamWithMemory(stream, emitter);
} }
} catch (error) { } catch (error) {
console.error('❌ Erreur:', error); console.error('❌ Erreur:', error);
// Fallback en mode standard await this.handleFallback(llm, message, this.conversationHistory, emitter, fileIds, embeddings, effectiveMode);
const answeringChain = await this.createAnsweringChain(
llm,
fileIds,
embeddings,
effectiveMode
);
const stream = answeringChain.streamEvents(
{
chat_history: this.conversationHistory,
query: message
},
{
version: 'v1'
}
);
this.handleStreamWithMemory(stream, emitter);
} }
return emitter; return emitter;
@ -994,7 +946,7 @@ Prends en compte:
} }
); );
this.handleStreamWithMemory(stream, emitter); this.handleStreamWithMemory(stream, emitter, llm, message);
} }
private async ensureVectorStoreInitialized(documents: Document[], embeddings: Embeddings): Promise<RAGDocumentChain> { private async ensureVectorStoreInitialized(documents: Document[], embeddings: Embeddings): Promise<RAGDocumentChain> {
@ -1209,7 +1161,7 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
title: content.title || 'Document sans titre', title: content.title || 'Document sans titre',
source: fileId, source: fileId,
type: 'uploaded', type: 'uploaded',
url: `/api/uploads/${fileId}/view?page=${pageNumber}`, url: `/viewer/${fileId}?page=${pageNumber}`, // URL vers le viewer Next.js
pageNumber: pageNumber, pageNumber: pageNumber,
chunkIndex: index, chunkIndex: index,
totalChunks: chunks.length, totalChunks: chunks.length,