Update metaSearchAgent.ts
This commit is contained in:
parent
3e53bab9b4
commit
19148eeba7
1 changed files with 366 additions and 433 deletions
|
@ -11,7 +11,11 @@ import {
|
||||||
RunnableMap,
|
RunnableMap,
|
||||||
RunnableSequence,
|
RunnableSequence,
|
||||||
} from '@langchain/core/runnables';
|
} from '@langchain/core/runnables';
|
||||||
import { BaseMessage } from '@langchain/core/messages';
|
import {
|
||||||
|
BaseMessage,
|
||||||
|
AIMessage,
|
||||||
|
HumanMessage,
|
||||||
|
} from '@langchain/core/messages';
|
||||||
import { StringOutputParser } from '@langchain/core/output_parsers';
|
import { StringOutputParser } from '@langchain/core/output_parsers';
|
||||||
import LineListOutputParser from '../lib/outputParsers/listLineOutputParser';
|
import LineListOutputParser from '../lib/outputParsers/listLineOutputParser';
|
||||||
import LineOutputParser from '../lib/outputParsers/lineOutputParser';
|
import LineOutputParser from '../lib/outputParsers/lineOutputParser';
|
||||||
|
@ -33,10 +37,9 @@ import { SearxngSearchOptions } from '../lib/searxng';
|
||||||
import { ChromaClient } from 'chromadb';
|
import { ChromaClient } from 'chromadb';
|
||||||
import { OpenAIEmbeddings } from '@langchain/openai';
|
import { OpenAIEmbeddings } from '@langchain/openai';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { MemoryVectorStore } from "langchain/vectorstores/memory";
|
import { webSearchRetrieverPrompt, webSearchResponsePrompt } from '../prompts/webSearch';
|
||||||
|
|
||||||
export interface MetaSearchAgentType {
|
export interface MetaSearchAgentType {
|
||||||
initialize: (embeddings: Embeddings) => Promise<void>;
|
|
||||||
searchAndAnswer: (
|
searchAndAnswer: (
|
||||||
message: string,
|
message: string,
|
||||||
history: BaseMessage[],
|
history: BaseMessage[],
|
||||||
|
@ -49,8 +52,8 @@ export interface MetaSearchAgentType {
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
activeEngines: string[];
|
activeEngines: string[];
|
||||||
queryGeneratorPrompt: string;
|
queryGeneratorPrompt?: string;
|
||||||
responsePrompt: string;
|
responsePrompt?: string;
|
||||||
rerank: boolean;
|
rerank: boolean;
|
||||||
rerankThreshold: number;
|
rerankThreshold: number;
|
||||||
searchWeb: boolean;
|
searchWeb: boolean;
|
||||||
|
@ -99,52 +102,26 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
private config: Config;
|
private config: Config;
|
||||||
private strParser = new StringOutputParser();
|
private strParser = new StringOutputParser();
|
||||||
private fileIds: string[];
|
private fileIds: string[];
|
||||||
private memoryStore: MemoryVectorStore;
|
private conversationHistory: BaseMessage[] = [];
|
||||||
|
|
||||||
constructor(config: Config) {
|
constructor(config: Config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.fileIds = [];
|
this.fileIds = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize(embeddings: Embeddings) {
|
private updateMemory(message: BaseMessage) {
|
||||||
try {
|
this.conversationHistory.push(message);
|
||||||
if (!this.memoryStore) {
|
|
||||||
console.log("🔄 Initialisation du memory store...");
|
|
||||||
this.memoryStore = new MemoryVectorStore(embeddings);
|
|
||||||
await this.memoryStore.addDocuments([{
|
|
||||||
pageContent: "Initialisation du memory store",
|
|
||||||
metadata: { timestamp: Date.now() }
|
|
||||||
}]);
|
|
||||||
console.log("✅ Memory store initialisé avec succès");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Erreur lors de l'initialisation du memory store:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async enrichWithMemory(message: string, embeddings: Embeddings): Promise<string> {
|
public getMemory(): BaseMessage[] {
|
||||||
try {
|
return this.conversationHistory;
|
||||||
if (!this.memoryStore) {
|
|
||||||
await this.initialize(embeddings);
|
|
||||||
}
|
|
||||||
await this.memoryStore.addDocuments([{
|
|
||||||
pageContent: message,
|
|
||||||
metadata: { timestamp: Date.now() }
|
|
||||||
}]);
|
|
||||||
const results = await this.memoryStore.similaritySearch(message, 2);
|
|
||||||
return results.map(doc => doc.pageContent).join('\n');
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erreur mémoire:", error);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSearchRetrieverChain(llm: BaseChatModel) {
|
private async createSearchRetrieverChain(llm: BaseChatModel) {
|
||||||
(llm as unknown as ChatOpenAI).temperature = 0;
|
(llm as unknown as ChatOpenAI).temperature = 0;
|
||||||
|
|
||||||
return RunnableSequence.from([
|
return RunnableSequence.from([
|
||||||
PromptTemplate.fromTemplate(this.config.queryGeneratorPrompt),
|
PromptTemplate.fromTemplate(webSearchRetrieverPrompt),
|
||||||
llm,
|
llm,
|
||||||
this.strParser,
|
this.strParser,
|
||||||
RunnableLambda.from(async (input: string) => {
|
RunnableLambda.from(async (input: string) => {
|
||||||
|
@ -191,7 +168,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
// Recherche d'experts si activée
|
// Recherche d'experts si activée
|
||||||
if (this.config.searchDatabase) {
|
if (this.config.searchDatabase) {
|
||||||
try {
|
try {
|
||||||
console.log("🔍 Recherche d'experts...");
|
console.log('🔍 Recherche d\'experts...');
|
||||||
const expertResults = await handleExpertSearch(
|
const expertResults = await handleExpertSearch(
|
||||||
{
|
{
|
||||||
query: question,
|
query: question,
|
||||||
|
@ -202,7 +179,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
llm
|
llm
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("🔍 Experts trouvés:", expertResults.experts.length);
|
console.log('🔍 Experts trouvés:', expertResults.experts.length);
|
||||||
const expertDocs = expertResults.experts.map(expert =>
|
const expertDocs = expertResults.experts.map(expert =>
|
||||||
new Document({
|
new Document({
|
||||||
pageContent: `Expert: ${expert.prenom} ${expert.nom}
|
pageContent: `Expert: ${expert.prenom} ${expert.nom}
|
||||||
|
@ -225,7 +202,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
|
|
||||||
documents = [...expertDocs, ...documents];
|
documents = [...expertDocs, ...documents];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur lors de la recherche d'experts:", error);
|
console.error('Erreur lors de la recherche d\'experts:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +219,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadUploadedDocuments(fileIds: string[]): Promise<Document[]> {
|
private async loadUploadedDocuments(fileIds: string[]): Promise<Document[]> {
|
||||||
console.log("📂 Chargement des documents:", fileIds);
|
console.log('📂 Chargement des documents:', fileIds);
|
||||||
const docs: Document[] = [];
|
const docs: Document[] = [];
|
||||||
|
|
||||||
for (const fileId of fileIds) {
|
for (const fileId of fileIds) {
|
||||||
|
@ -310,56 +287,46 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
query: (input: BasicChainInput) => input.query,
|
query: (input: BasicChainInput) => input.query,
|
||||||
chat_history: (input: BasicChainInput) => input.chat_history,
|
chat_history: (input: BasicChainInput) => input.chat_history,
|
||||||
docs: RunnableLambda.from(async (input: BasicChainInput) => {
|
docs: RunnableLambda.from(async (input: BasicChainInput) => {
|
||||||
console.log("Début de la recherche avec contexte enrichi...");
|
console.log('Début de la recherche...');
|
||||||
let docs: Document[] = [];
|
let docs: Document[] = [];
|
||||||
|
|
||||||
// Récupérer le contexte historique
|
// 1. D'abord chercher dans les documents uploadés
|
||||||
const memoryContext = await this.getRelevantContext(input.query);
|
|
||||||
console.log("💭 Contexte historique pour la recherche:", memoryContext);
|
|
||||||
|
|
||||||
// Enrichir la requête avec le contexte
|
|
||||||
const enrichedQuery = `
|
|
||||||
Contexte précédent:
|
|
||||||
${memoryContext}
|
|
||||||
|
|
||||||
Question actuelle:
|
|
||||||
${input.query}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 1. D'abord chercher dans les documents uploadés avec le contexte enrichi
|
|
||||||
if (fileIds.length > 0) {
|
if (fileIds.length > 0) {
|
||||||
try {
|
try {
|
||||||
const uploadedDocs = await this.loadUploadedDocuments(fileIds);
|
const uploadedDocs = await this.loadUploadedDocuments(fileIds);
|
||||||
console.log("📚 Documents uploadés chargés:", uploadedDocs.length);
|
console.log('📚 Documents uploadés chargés:', uploadedDocs.length);
|
||||||
|
|
||||||
|
// Utiliser RAGDocumentChain pour la recherche dans les documents
|
||||||
const ragChain = new RAGDocumentChain();
|
const ragChain = new RAGDocumentChain();
|
||||||
await ragChain.initializeVectorStoreFromDocuments(uploadedDocs, embeddings);
|
await ragChain.initializeVectorStoreFromDocuments(uploadedDocs, embeddings);
|
||||||
|
|
||||||
|
// Utiliser le type 'specific' pour une recherche précise
|
||||||
const searchChain = ragChain.createSearchChain(llm);
|
const searchChain = ragChain.createSearchChain(llm);
|
||||||
const relevantDocs = await searchChain.invoke({
|
const relevantDocs = await searchChain.invoke({
|
||||||
query: enrichedQuery,
|
query: input.query,
|
||||||
chat_history: input.chat_history,
|
chat_history: input.chat_history,
|
||||||
type: 'specific'
|
type: 'specific'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ajouter les documents pertinents avec un score élevé
|
||||||
docs = uploadedDocs.map(doc => ({
|
docs = uploadedDocs.map(doc => ({
|
||||||
...doc,
|
...doc,
|
||||||
metadata: {
|
metadata: {
|
||||||
...doc.metadata,
|
...doc.metadata,
|
||||||
score: 0.8
|
score: 0.8 // Score élevé pour les documents uploadés
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log("📄 Documents pertinents trouvés:", docs.length);
|
console.log('📄 Documents pertinents trouvés:', docs.length);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Erreur lors de la recherche dans les documents:", error);
|
console.error('❌ Erreur lors de la recherche dans les documents:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Ensuite chercher les experts si pertinent
|
// 2. Ensuite chercher les experts si pertinent
|
||||||
if (this.config.searchDatabase) {
|
if (this.config.searchDatabase) {
|
||||||
try {
|
try {
|
||||||
console.log("👥 Recherche d'experts...");
|
console.log('👥 Recherche d\'experts...');
|
||||||
const expertResults = await handleExpertSearch(
|
const expertResults = await handleExpertSearch(
|
||||||
{
|
{
|
||||||
query: input.query,
|
query: input.query,
|
||||||
|
@ -375,7 +342,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
docs = [...docs, ...expertDocs];
|
docs = [...docs, ...expertDocs];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Erreur lors de la recherche d'experts:", error);
|
console.error('❌ Erreur lors de la recherche d\'experts:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,13 +352,13 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
const webResults = await this.performWebSearch(input.query);
|
const webResults = await this.performWebSearch(input.query);
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("🔍 DEBUG - Avant appel rerankDocs - Mode:", optimizationMode, "Query:", input.query);
|
console.log('🔍 DEBUG - Avant appel rerankDocs - Mode:', optimizationMode, 'Query:', input.query);
|
||||||
return this.rerankDocs(
|
return this.rerankDocs(
|
||||||
enrichedQuery,
|
input.query,
|
||||||
docs,
|
docs,
|
||||||
fileIds,
|
fileIds,
|
||||||
embeddings,
|
embeddings,
|
||||||
|
@ -406,14 +373,14 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
chat_history: (input) => input.chat_history,
|
chat_history: (input) => input.chat_history,
|
||||||
date: () => new Date().toISOString(),
|
date: () => new Date().toISOString(),
|
||||||
context: (input) => {
|
context: (input) => {
|
||||||
console.log("Préparation du contexte...");
|
console.log('Préparation du contexte...');
|
||||||
return this.processDocs(input.docs);
|
return this.processDocs(input.docs);
|
||||||
},
|
},
|
||||||
docs: (input) => input.docs,
|
docs: (input) => input.docs,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
ChatPromptTemplate.fromMessages([
|
ChatPromptTemplate.fromMessages([
|
||||||
['system', this.config.responsePrompt],
|
['system', webSearchResponsePrompt],
|
||||||
new MessagesPlaceholder('chat_history'),
|
new MessagesPlaceholder('chat_history'),
|
||||||
['user', '{context}\n\n{query}'],
|
['user', '{context}\n\n{query}'],
|
||||||
]),
|
]),
|
||||||
|
@ -465,8 +432,8 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
|
|
||||||
private processDocs(docs: Document[]) {
|
private processDocs(docs: Document[]) {
|
||||||
// Trier les documents par score si disponible
|
// Trier les documents par score si disponible
|
||||||
const sortedDocs = docs.sort((a, b) =>
|
const sortedDocs = docs.sort(
|
||||||
(b.metadata?.score || 0) - (a.metadata?.score || 0)
|
(a, b) => (b.metadata?.score || 0) - (a.metadata?.score || 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Limiter à 5 documents maximum
|
// Limiter à 5 documents maximum
|
||||||
|
@ -475,8 +442,9 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
// Limiter la taille de chaque document à 1000 caractères
|
// Limiter la taille de chaque document à 1000 caractères
|
||||||
return limitedDocs
|
return limitedDocs
|
||||||
.map((doc, index) => {
|
.map((doc, index) => {
|
||||||
const content = doc.pageContent.length > 1000
|
const content =
|
||||||
? doc.pageContent.substring(0, 1000) + "..."
|
doc.pageContent.length > 1000
|
||||||
|
? doc.pageContent.substring(0, 1000) + '...'
|
||||||
: doc.pageContent;
|
: doc.pageContent;
|
||||||
|
|
||||||
return `${content} [${index + 1}]`;
|
return `${content} [${index + 1}]`;
|
||||||
|
@ -486,7 +454,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
|
|
||||||
private async handleStream(
|
private async handleStream(
|
||||||
stream: IterableReadableStream<StreamEvent>,
|
stream: IterableReadableStream<StreamEvent>,
|
||||||
emitter: eventEmitter,
|
emitter: eventEmitter
|
||||||
) {
|
) {
|
||||||
for await (const event of stream) {
|
for await (const event of stream) {
|
||||||
if (
|
if (
|
||||||
|
@ -496,7 +464,8 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
const sources = event.data.output;
|
const sources = event.data.output;
|
||||||
|
|
||||||
// Normaliser les sources pour le frontend
|
// Normaliser les sources pour le frontend
|
||||||
const normalizedSources = sources?.map(source => {
|
const normalizedSources =
|
||||||
|
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 || 1;
|
||||||
|
@ -531,7 +500,9 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
url: url,
|
url: url,
|
||||||
source: sourceId,
|
source: sourceId,
|
||||||
pageNumber: pageNumber,
|
pageNumber: pageNumber,
|
||||||
searchText: source.metadata?.searchText?.substring(0, 200) || limitedContent.substring(0, 200),
|
searchText:
|
||||||
|
source.metadata?.searchText?.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,
|
||||||
|
@ -542,7 +513,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
};
|
};
|
||||||
}) || [];
|
}) || [];
|
||||||
|
|
||||||
console.log("🔍 Sources normalisées:", normalizedSources.length);
|
console.log('🔍 Sources normalisées:', normalizedSources.length);
|
||||||
|
|
||||||
emitter.emit(
|
emitter.emit(
|
||||||
'data',
|
'data',
|
||||||
|
@ -572,13 +543,100 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handleStreamWithMemory(
|
||||||
|
stream: IterableReadableStream<StreamEvent>,
|
||||||
|
emitter: eventEmitter
|
||||||
|
) {
|
||||||
|
let fullAssistantResponse = '';
|
||||||
|
|
||||||
|
for await (const event of stream) {
|
||||||
|
if (event.event === 'on_chain_stream') {
|
||||||
|
if (event.name === 'FinalResponseGenerator') {
|
||||||
|
fullAssistantResponse += event.data.chunk;
|
||||||
|
emitter.emit(
|
||||||
|
'data',
|
||||||
|
JSON.stringify({ type: 'response', data: event.data.chunk })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (event.event === 'on_chain_end') {
|
||||||
|
if (event.name === 'FinalResponseGenerator') {
|
||||||
|
this.updateMemory(new AIMessage(fullAssistantResponse.trim()));
|
||||||
|
emitter.emit('end');
|
||||||
|
}
|
||||||
|
if (event.name === 'FinalSourceRetriever') {
|
||||||
|
const sources = event.data.output;
|
||||||
|
const normalizedSources =
|
||||||
|
sources?.map(source => {
|
||||||
|
const isUploadedDoc = source.metadata?.type === 'uploaded';
|
||||||
|
const isExpert = source.metadata?.type === 'expert';
|
||||||
|
const pageNumber = source.metadata?.pageNumber || 1;
|
||||||
|
const sourceId = source.metadata?.source;
|
||||||
|
|
||||||
|
let url;
|
||||||
|
if (isUploadedDoc && sourceId) {
|
||||||
|
url = `/api/uploads/${sourceId}/content?page=${pageNumber}`;
|
||||||
|
} else if (isExpert) {
|
||||||
|
url = source.metadata?.url;
|
||||||
|
} else if (source.metadata?.type === 'web') {
|
||||||
|
url = source.metadata?.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = source.metadata?.title || '';
|
||||||
|
if (isUploadedDoc && title) {
|
||||||
|
title = `${title} - Page ${pageNumber}`;
|
||||||
|
} else if (isExpert) {
|
||||||
|
title = source.metadata?.displayTitle || title;
|
||||||
|
}
|
||||||
|
|
||||||
|
const limitedContent =
|
||||||
|
source.pageContent?.substring(0, 1000) || '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
pageContent: limitedContent,
|
||||||
|
metadata: {
|
||||||
|
title: title,
|
||||||
|
type: source.metadata?.type || 'web',
|
||||||
|
url: url,
|
||||||
|
source: sourceId,
|
||||||
|
pageNumber: pageNumber,
|
||||||
|
searchText:
|
||||||
|
source.metadata?.searchText?.substring(0, 200) ||
|
||||||
|
limitedContent.substring(0, 200),
|
||||||
|
expertData: source.metadata?.expertData,
|
||||||
|
illustrationImage: source.metadata?.illustrationImage,
|
||||||
|
imageTitle: source.metadata?.imageTitle,
|
||||||
|
favicon: source.metadata?.favicon,
|
||||||
|
linkText: source.metadata?.linkText,
|
||||||
|
expertName: source.metadata?.expertName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
console.log('🔍 Sources normalisées:', normalizedSources.length);
|
||||||
|
|
||||||
|
emitter.emit(
|
||||||
|
'data',
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'sources',
|
||||||
|
data: normalizedSources,
|
||||||
|
illustrationImage: normalizedSources[0]?.metadata?.illustrationImage || null,
|
||||||
|
imageTitle: normalizedSources[0]?.metadata?.imageTitle || null
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emitter.emit(event.event, event.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async searchExperts(
|
private async searchExperts(
|
||||||
query: string,
|
query: string,
|
||||||
embeddings: Embeddings,
|
embeddings: Embeddings,
|
||||||
llm: BaseChatModel
|
llm: BaseChatModel
|
||||||
): Promise<SearchResult[]> {
|
): Promise<SearchResult[]> {
|
||||||
try {
|
try {
|
||||||
console.log("👥 Recherche d'experts pour:", query);
|
console.log('👥 Recherche d\'experts pour:', query);
|
||||||
const expertResults = await handleExpertSearch(
|
const expertResults = await handleExpertSearch(
|
||||||
{
|
{
|
||||||
query,
|
query,
|
||||||
|
@ -608,14 +666,14 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Erreur lors de la recherche d'experts:", error);
|
console.error('❌ Erreur lors de la recherche d\'experts:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async searchWeb(query: string): Promise<SearchResult[]> {
|
private async searchWeb(query: string): Promise<SearchResult[]> {
|
||||||
try {
|
try {
|
||||||
console.log("🌐 Recherche web pour:", query);
|
console.log('🌐 Recherche web pour:', query);
|
||||||
const res = await searchSearxng(query, {
|
const res = await searchSearxng(query, {
|
||||||
language: 'fr',
|
language: 'fr',
|
||||||
engines: this.config.activeEngines,
|
engines: this.config.activeEngines,
|
||||||
|
@ -632,7 +690,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Erreur lors de la recherche web:", error);
|
console.error('❌ Erreur lors de la recherche web:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -645,13 +703,13 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
optimizationMode: 'speed' | 'balanced' | 'quality',
|
optimizationMode: 'speed' | 'balanced' | 'quality',
|
||||||
llm: BaseChatModel
|
llm: BaseChatModel
|
||||||
) {
|
) {
|
||||||
console.log("🔍 Mode d'optimisation:", optimizationMode);
|
console.log('🔍 Mode d\'optimisation:', optimizationMode);
|
||||||
console.log("🔍 Query pour la recherche d'image:", query);
|
console.log('🔍 Query pour la recherche d\'image:', query);
|
||||||
|
|
||||||
if (optimizationMode === 'balanced' || optimizationMode === 'quality') {
|
if (optimizationMode === 'balanced' || optimizationMode === 'quality') {
|
||||||
console.log("🔍 Démarrage de la recherche d'images...");
|
console.log('🔍 Démarrage de la recherche d\'images...');
|
||||||
try {
|
try {
|
||||||
console.log("🔍 Appel de handleImageSearch avec la query:", query);
|
console.log('🔍 Appel de handleImageSearch avec la query:', query);
|
||||||
const images = await handleImageSearch(
|
const images = await handleImageSearch(
|
||||||
{
|
{
|
||||||
query,
|
query,
|
||||||
|
@ -659,11 +717,11 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
},
|
},
|
||||||
llm
|
llm
|
||||||
);
|
);
|
||||||
console.log("🔍 Résultat brut de handleImageSearch:", JSON.stringify(images, null, 2));
|
console.log('🔍 Résultat brut de handleImageSearch:', JSON.stringify(images, null, 2));
|
||||||
console.log("🔍 Images trouvées:", images?.length);
|
console.log('🔍 Images trouvées:', images?.length);
|
||||||
|
|
||||||
if (images && images.length > 0) {
|
if (images && images.length > 0) {
|
||||||
console.log("🔍 Première image trouvée:", {
|
console.log('🔍 Première image trouvée:', {
|
||||||
src: images[0].img_src,
|
src: images[0].img_src,
|
||||||
title: images[0].title,
|
title: images[0].title,
|
||||||
url: images[0].url
|
url: images[0].url
|
||||||
|
@ -673,238 +731,102 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
metadata: {
|
metadata: {
|
||||||
...doc.metadata,
|
...doc.metadata,
|
||||||
illustrationImage: images[0].img_src,
|
illustrationImage: images[0].img_src,
|
||||||
title: images[0].title
|
imageTitle: images[0].title
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
console.log("⚠️ Aucune image trouvée dans le résultat");
|
console.log('⚠️ Aucune image trouvée dans le résultat');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Erreur détaillée lors de la recherche d'image:", {
|
console.error('❌ Erreur détaillée lors de la recherche d\'image:', {
|
||||||
message: error.message,
|
message: error.message,
|
||||||
stack: error.stack
|
stack: error.stack
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("🔍 Mode speed: pas de recherche d'images");
|
console.log('🔍 Mode speed: pas de recherche d\'images');
|
||||||
}
|
}
|
||||||
|
|
||||||
return docs.slice(0, 15);
|
return docs.slice(0, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateMemoryStore(message: string, history: BaseMessage[]) {
|
|
||||||
try {
|
|
||||||
if (!this.memoryStore) {
|
|
||||||
throw new Error("Memory store not initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Créer un document avec le contexte actuel et sa structure
|
|
||||||
const contextDoc = {
|
|
||||||
pageContent: `Question: ${message}\nContext: ${history.map(m =>
|
|
||||||
`${m._getType()}: ${m.content}`).join('\n')}`,
|
|
||||||
metadata: {
|
|
||||||
timestamp: Date.now(),
|
|
||||||
themes: this.extractThemes(message + ' ' + history.map(m => m.content).join(' ')),
|
|
||||||
type: 'conversation'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("💾 Ajout à la mémoire:", contextDoc);
|
|
||||||
await this.memoryStore.addDocuments([contextDoc]);
|
|
||||||
console.log("✅ Mémoire mise à jour avec succès");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Erreur lors de la mise à jour de la mémoire:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getRelevantContext(message: string): Promise<string> {
|
|
||||||
try {
|
|
||||||
if (!this.memoryStore) {
|
|
||||||
throw new Error("Memory store not initialized");
|
|
||||||
}
|
|
||||||
console.log("🔍 Recherche dans la mémoire pour:", message);
|
|
||||||
|
|
||||||
const results = await this.memoryStore.similaritySearch(message, 3);
|
|
||||||
console.log("📚 Contexte trouvé dans la mémoire:", results);
|
|
||||||
return results.map(doc => doc.pageContent).join('\n');
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Erreur lors de la récupération du contexte:", error);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extractThemes(context: string): string[] {
|
|
||||||
const commonThemes = ['sas', 'entreprise', 'création', 'chomage', 'juridique', 'financement'];
|
|
||||||
return commonThemes.filter(theme =>
|
|
||||||
context.toLowerCase().includes(theme.toLowerCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async analyzeConversationContext(
|
|
||||||
message: string,
|
|
||||||
history: BaseMessage[],
|
|
||||||
llm: BaseChatModel
|
|
||||||
): Promise<{ context: string; relevance: number }> {
|
|
||||||
try {
|
|
||||||
const formattedHistory = formatChatHistoryAsString(history);
|
|
||||||
|
|
||||||
const analysis = await llm.invoke(`
|
|
||||||
Analysez cette conversation en profondeur en donnant une importance particulière aux 3 derniers échanges.
|
|
||||||
Assurez-vous de maintenir la cohérence du contexte entre les questions.
|
|
||||||
|
|
||||||
Historique de la conversation:
|
|
||||||
${formattedHistory}
|
|
||||||
|
|
||||||
Nouvelle question: "${message}"
|
|
||||||
|
|
||||||
Instructions spécifiques:
|
|
||||||
1. Identifiez les thèmes récurrents des 3 derniers échanges
|
|
||||||
2. Notez les contraintes ou conditions mentionnées précédemment qui restent pertinentes
|
|
||||||
3. Évaluez comment la nouvelle question s'inscrit dans la continuité des échanges
|
|
||||||
|
|
||||||
Répondez au format JSON:
|
|
||||||
{
|
|
||||||
"mainTopic": "sujet principal actuel",
|
|
||||||
"recentContext": {
|
|
||||||
"lastThemes": ["thèmes des 3 derniers échanges"],
|
|
||||||
"activeConstraints": ["contraintes toujours actives"],
|
|
||||||
"continuityScore": <0.0 à 1.0>
|
|
||||||
},
|
|
||||||
"contextualFactors": {
|
|
||||||
"financial": ["facteurs financiers"],
|
|
||||||
"legal": ["aspects juridiques"],
|
|
||||||
"administrative": ["aspects administratifs"]
|
|
||||||
},
|
|
||||||
"impactAnalysis": {
|
|
||||||
"primary": "impact principal",
|
|
||||||
"secondary": ["impacts secondaires"],
|
|
||||||
"constraints": ["contraintes identifiées"]
|
|
||||||
},
|
|
||||||
"relevanceScore": <0.0 à 1.0>
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const result = JSON.parse(String(analysis.content));
|
|
||||||
|
|
||||||
// Construction d'un contexte enrichi qui met l'accent sur la continuité
|
|
||||||
const enrichedContext = `
|
|
||||||
Contexte principal: ${result.mainTopic}
|
|
||||||
Thèmes récents: ${result.recentContext.lastThemes.join(', ')}
|
|
||||||
Contraintes actives: ${result.recentContext.activeConstraints.join(', ')}
|
|
||||||
Facteurs impactants: ${result.contextualFactors.financial.join(', ')}
|
|
||||||
Implications légales: ${result.contextualFactors.legal.join(', ')}
|
|
||||||
Impact global: ${result.impactAnalysis.primary}
|
|
||||||
Contraintes à considérer: ${result.impactAnalysis.constraints.join(', ')}
|
|
||||||
`.trim();
|
|
||||||
|
|
||||||
// Ajuster le score de pertinence en fonction de la continuité
|
|
||||||
const adjustedRelevance = (result.relevanceScore + result.recentContext.continuityScore) / 2;
|
|
||||||
|
|
||||||
return {
|
|
||||||
context: enrichedContext,
|
|
||||||
relevance: adjustedRelevance
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erreur lors de l'analyse du contexte:", error);
|
|
||||||
return { context: '', relevance: 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async searchAndAnswer(
|
async searchAndAnswer(
|
||||||
message: string,
|
message: string,
|
||||||
history: BaseMessage[],
|
history: BaseMessage[],
|
||||||
llm: BaseChatModel,
|
llm: BaseChatModel,
|
||||||
embeddings: Embeddings,
|
embeddings: Embeddings,
|
||||||
optimizationMode: 'speed' | 'balanced' | 'quality',
|
optimizationMode: 'speed' | 'balanced' | 'quality',
|
||||||
fileIds: string[],
|
fileIds: string[]
|
||||||
) {
|
) {
|
||||||
const effectiveMode = 'balanced';
|
const effectiveMode = 'balanced';
|
||||||
const emitter = new EventEmitter();
|
const emitter = new eventEmitter();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// S'assurer que le memoryStore est initialisé
|
// Ajouter le message utilisateur à la mémoire
|
||||||
if (!this.memoryStore) {
|
this.updateMemory(new HumanMessage(message));
|
||||||
console.log("🔄 Initialisation du memory store dans searchAndAnswer...");
|
|
||||||
try {
|
|
||||||
await this.initialize(embeddings);
|
|
||||||
if (!this.memoryStore) {
|
|
||||||
throw new Error("Memory store initialization failed");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Erreur lors de l'initialisation du memory store:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analyser le contexte de la conversation
|
// Fusionner l'historique
|
||||||
const conversationContext = await this.analyzeConversationContext(message, history, llm);
|
const mergedHistory: BaseMessage[] = [
|
||||||
console.log("🧠 Analyse du contexte:", conversationContext);
|
...this.conversationHistory,
|
||||||
|
...history,
|
||||||
// Mettre à jour la mémoire avec le contexte actuel
|
];
|
||||||
await this.updateMemoryStore(message, history);
|
|
||||||
|
|
||||||
// Récupérer le contexte pertinent de la mémoire
|
|
||||||
const memoryContext = await this.getRelevantContext(message);
|
|
||||||
console.log("💭 Contexte mémoire récupéré:", memoryContext);
|
|
||||||
|
|
||||||
// Enrichir le message avec le contexte historique
|
|
||||||
const enrichedMessage = `
|
|
||||||
Contexte précédent:
|
|
||||||
${memoryContext}
|
|
||||||
|
|
||||||
Contexte actuel:
|
|
||||||
${conversationContext.context}
|
|
||||||
|
|
||||||
Question actuelle:
|
|
||||||
${message}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Analyse sophistiquée de la requête avec LLM
|
// Analyse sophistiquée de la requête avec LLM
|
||||||
const queryAnalysis = await llm.invoke(`
|
const queryAnalysis = await llm.invoke(`En tant qu'expert en analyse de requêtes, examine cette demande et détermine la stratégie de recherche optimale.
|
||||||
En tant qu'expert en analyse de requêtes, examine cette demande dans son contexte complet.
|
|
||||||
|
|
||||||
${enrichedMessage}
|
Question/Requête: "${message}"
|
||||||
|
|
||||||
Documents disponibles: ${fileIds.length > 0 ? "Oui" : "Non"}
|
Documents disponibles: ${fileIds.length > 0 ? 'Oui' : 'Non'}
|
||||||
|
|
||||||
Analyse et réponds au format JSON:
|
Analyse et réponds au format JSON:
|
||||||
{
|
{
|
||||||
"primaryIntent": "DOCUMENT_QUERY" | "WEB_SEARCH" | "EXPERT_ADVICE" | "HYBRID",
|
"primaryIntent": "DOCUMENT_QUERY" | "WEB_SEARCH" | "EXPERT_ADVICE" | "HYBRID",
|
||||||
"requiresDocumentSearch": <boolean>,
|
"requiresDocumentSearch": <boolean>,
|
||||||
"requiresWebSearch": <boolean>,
|
"requiresWebSearch": <boolean>,
|
||||||
"requiresExpertSearch": <boolean>,
|
"requiresExpertSearch": <boolean>,
|
||||||
"documentRelevance": <0.0 à 1.0>,
|
"documentRelevance": <0.0 à 1.0>,
|
||||||
"contextRelevance": ${conversationContext.relevance},
|
|
||||||
"reasoning": "<courte explication>"
|
"reasoning": "<courte explication>"
|
||||||
}`);
|
}
|
||||||
|
|
||||||
|
Critères d'analyse:
|
||||||
|
- DOCUMENT_QUERY: La question porte spécifiquement sur le contenu des documents
|
||||||
|
- WEB_SEARCH: Recherche d'informations générales ou actuelles
|
||||||
|
- EXPERT_ADVICE: Demande nécessitant une expertise spécifique
|
||||||
|
- HYBRID: Combinaison de plusieurs sources
|
||||||
|
|
||||||
|
Prends en compte:
|
||||||
|
- La présence ou non de documents uploadés
|
||||||
|
- La spécificité de la question
|
||||||
|
- Le besoin d'expertise externe
|
||||||
|
- L'actualité du sujet`);
|
||||||
|
|
||||||
const analysis = JSON.parse(String(queryAnalysis.content));
|
const analysis = JSON.parse(String(queryAnalysis.content));
|
||||||
console.log("🎯 Analyse de la requête enrichie:", analysis);
|
console.log('🎯 Analyse de la requête:', analysis);
|
||||||
|
|
||||||
// 1. Analyse des documents uploadés avec RAG
|
// 1. Analyse des documents uploadés avec RAG
|
||||||
const uploadedDocs = await this.loadUploadedDocuments(fileIds);
|
const uploadedDocs = await this.loadUploadedDocuments(fileIds);
|
||||||
console.log("📚 Documents uploadés chargés:", uploadedDocs.length);
|
console.log('📚 Documents uploadés chargés:', uploadedDocs.length);
|
||||||
|
|
||||||
if (uploadedDocs.length > 0) {
|
if (uploadedDocs.length > 0) {
|
||||||
// Création du vectorStore temporaire pour les documents
|
// Création du vectorStore temporaire pour les documents
|
||||||
const vectorStore = await Chroma.fromDocuments(uploadedDocs, embeddings, {
|
const vectorStore = await Chroma.fromDocuments(uploadedDocs, embeddings, {
|
||||||
collectionName: "temp_docs",
|
collectionName: 'temp_docs',
|
||||||
url: "http://chroma:8000",
|
url: 'http://chroma:8000',
|
||||||
numDimensions: 1536
|
numDimensions: 1536
|
||||||
});
|
});
|
||||||
|
|
||||||
// Recherche sémantique sans filtre pour l'instant
|
// Recherche sémantique sans filtre pour l'instant
|
||||||
const relevantDocs = await vectorStore.similaritySearch(message, 5);
|
const relevantDocs = await vectorStore.similaritySearch(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
|
// 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')
|
||||||
.substring(0, 500);
|
.substring(0, 500);
|
||||||
|
|
||||||
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 en BDD
|
// 2. Recherche d'experts en BDD
|
||||||
|
@ -958,7 +880,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
|
|
||||||
const stream = answeringChain.streamEvents(
|
const stream = answeringChain.streamEvents(
|
||||||
{
|
{
|
||||||
chat_history: history,
|
chat_history: mergedHistory,
|
||||||
query: `${message}\n\nContexte pertinent:\n${finalResults.map(doc => doc.pageContent).join('\n\n')}`
|
query: `${message}\n\nContexte pertinent:\n${finalResults.map(doc => doc.pageContent).join('\n\n')}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -966,7 +888,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.handleStream(stream, emitter);
|
this.handleStreamWithMemory(stream, emitter);
|
||||||
} else {
|
} else {
|
||||||
// Fallback sans documents uploadés
|
// Fallback sans documents uploadés
|
||||||
const answeringChain = await this.createAnsweringChain(
|
const answeringChain = await this.createAnsweringChain(
|
||||||
|
@ -978,7 +900,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
|
|
||||||
const stream = answeringChain.streamEvents(
|
const stream = answeringChain.streamEvents(
|
||||||
{
|
{
|
||||||
chat_history: history,
|
chat_history: mergedHistory,
|
||||||
query: message
|
query: message
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -986,10 +908,10 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.handleStream(stream, emitter);
|
this.handleStreamWithMemory(stream, emitter);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Erreur:", error);
|
console.error('❌ Erreur:', error);
|
||||||
// Fallback en mode standard
|
// Fallback en mode standard
|
||||||
const answeringChain = await this.createAnsweringChain(
|
const answeringChain = await this.createAnsweringChain(
|
||||||
llm,
|
llm,
|
||||||
|
@ -1000,7 +922,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
|
|
||||||
const stream = answeringChain.streamEvents(
|
const stream = answeringChain.streamEvents(
|
||||||
{
|
{
|
||||||
chat_history: history,
|
chat_history: this.conversationHistory,
|
||||||
query: message
|
query: message
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1008,7 +930,7 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.handleStream(stream, emitter);
|
this.handleStreamWithMemory(stream, emitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
|
@ -1018,25 +940,25 @@ export class MetaSearchAgent implements MetaSearchAgentType {
|
||||||
export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
// ... existing handlers ...
|
// ... existing handlers ...
|
||||||
legal: {
|
legal: {
|
||||||
initialize: async (embeddings: Embeddings) => {
|
|
||||||
// Pas besoin d'initialisation spécifique pour le handler legal
|
|
||||||
},
|
|
||||||
searchAndAnswer: async (
|
searchAndAnswer: async (
|
||||||
message,
|
message,
|
||||||
history,
|
history,
|
||||||
llm,
|
llm,
|
||||||
embeddings,
|
embeddings,
|
||||||
optimizationMode,
|
optimizationMode,
|
||||||
fileIds,
|
fileIds
|
||||||
) => {
|
) => {
|
||||||
const emitter = new eventEmitter();
|
const emitter = new eventEmitter();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const chain = new RAGDocumentChain();
|
const chain = new RAGDocumentChain();
|
||||||
await chain.initializeVectorStoreFromDocuments(fileIds.map(fileId => new Document({
|
await chain.initializeVectorStoreFromDocuments(
|
||||||
|
fileIds.map(fileId => new Document({
|
||||||
pageContent: '',
|
pageContent: '',
|
||||||
metadata: { source: fileId }
|
metadata: { source: fileId }
|
||||||
})), embeddings);
|
})),
|
||||||
|
embeddings
|
||||||
|
);
|
||||||
|
|
||||||
const searchChain = chain.createSearchChain(llm);
|
const searchChain = chain.createSearchChain(llm);
|
||||||
const results = await searchChain.invoke({
|
const results = await searchChain.invoke({
|
||||||
|
@ -1056,7 +978,7 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
'data',
|
'data',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: 'response',
|
type: 'response',
|
||||||
data: response.text,
|
data: response.text
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1066,25 +988,22 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
'error',
|
'error',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
data: error.message,
|
data: error.message
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
documents: {
|
documents: {
|
||||||
initialize: async (embeddings: Embeddings) => {
|
|
||||||
// Pas besoin d'initialisation spécifique pour le handler documents
|
|
||||||
},
|
|
||||||
searchAndAnswer: async (
|
searchAndAnswer: async (
|
||||||
message,
|
message,
|
||||||
history,
|
history,
|
||||||
llm,
|
llm,
|
||||||
embeddings,
|
embeddings,
|
||||||
optimizationMode,
|
optimizationMode,
|
||||||
fileIds,
|
fileIds
|
||||||
) => {
|
) => {
|
||||||
const emitter = new eventEmitter();
|
const emitter = new eventEmitter();
|
||||||
const ragChain = new RAGDocumentChain();
|
const ragChain = new RAGDocumentChain();
|
||||||
|
@ -1098,7 +1017,7 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
pageContent: content.contents.join('\n'),
|
pageContent: content.contents.join('\n'),
|
||||||
metadata: {
|
metadata: {
|
||||||
title: content.title,
|
title: content.title,
|
||||||
source: fileId,
|
source: fileId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1121,38 +1040,44 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
emitter.emit('data', JSON.stringify({
|
emitter.emit(
|
||||||
|
'data',
|
||||||
|
JSON.stringify({
|
||||||
type: 'response',
|
type: 'response',
|
||||||
data: response.text
|
data: response.text
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
emitter.emit('data', JSON.stringify({
|
emitter.emit(
|
||||||
|
'data',
|
||||||
|
JSON.stringify({
|
||||||
type: 'sources',
|
type: 'sources',
|
||||||
data: response.sources
|
data: response.sources
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
emitter.emit('end');
|
emitter.emit('end');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
emitter.emit('error', JSON.stringify({
|
emitter.emit(
|
||||||
|
'error',
|
||||||
|
JSON.stringify({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
data: error.message
|
data: error.message
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
uploads: {
|
uploads: {
|
||||||
initialize: async (embeddings: Embeddings) => {
|
|
||||||
// Pas besoin d'initialisation spécifique pour le handler uploads
|
|
||||||
},
|
|
||||||
searchAndAnswer: async (
|
searchAndAnswer: async (
|
||||||
message,
|
message,
|
||||||
history,
|
history,
|
||||||
llm,
|
llm,
|
||||||
embeddings,
|
embeddings,
|
||||||
optimizationMode,
|
optimizationMode,
|
||||||
fileIds,
|
fileIds
|
||||||
) => {
|
) => {
|
||||||
const emitter = new eventEmitter();
|
const emitter = new eventEmitter();
|
||||||
|
|
||||||
|
@ -1171,10 +1096,11 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const intent = String(queryIntent.content).trim();
|
const intent = String(queryIntent.content).trim();
|
||||||
console.log("🎯 Intention détectée:", intent);
|
console.log('🎯 Intention détectée:', intent);
|
||||||
|
|
||||||
// Chargement optimisé des documents
|
// Chargement optimisé des documents
|
||||||
const docs = await Promise.all(fileIds.map(async fileId => {
|
const docs = await Promise.all(
|
||||||
|
fileIds.map(async fileId => {
|
||||||
const filePath = path.join(process.cwd(), 'uploads', fileId);
|
const filePath = path.join(process.cwd(), 'uploads', fileId);
|
||||||
const contentPath = `${filePath}-extracted.json`;
|
const contentPath = `${filePath}-extracted.json`;
|
||||||
|
|
||||||
|
@ -1188,7 +1114,7 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
const chunkSize = 1000; // Taille optimale pour le traitement
|
const chunkSize = 1000; // Taille optimale pour le traitement
|
||||||
const overlap = 100; // Chevauchement pour maintenir le contexte
|
const overlap = 100; // Chevauchement pour maintenir le contexte
|
||||||
|
|
||||||
const chunks = [];
|
const chunks: string[] = [];
|
||||||
let currentChunk = '';
|
let currentChunk = '';
|
||||||
let currentSize = 0;
|
let currentSize = 0;
|
||||||
|
|
||||||
|
@ -1221,14 +1147,18 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
pageNumber: pageNumber,
|
pageNumber: pageNumber,
|
||||||
chunkIndex: index,
|
chunkIndex: index,
|
||||||
totalChunks: chunks.length,
|
totalChunks: chunks.length,
|
||||||
searchText: chunk.substring(0, 100).replace(/[\n\r]+/g, ' ').trim()
|
searchText: chunk
|
||||||
|
.substring(0, 100)
|
||||||
|
.replace(/[\n\r]+/g, ' ')
|
||||||
|
.trim()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const flatDocs = docs.flat();
|
const flatDocs = docs.flat();
|
||||||
console.log("📚 Nombre total de chunks:", flatDocs.length);
|
console.log('📚 Nombre total de chunks:', flatDocs.length);
|
||||||
|
|
||||||
const ragChain = new RAGDocumentChain();
|
const ragChain = new RAGDocumentChain();
|
||||||
await ragChain.initializeVectorStoreFromDocuments(flatDocs, embeddings);
|
await ragChain.initializeVectorStoreFromDocuments(flatDocs, embeddings);
|
||||||
|
@ -1236,9 +1166,10 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
|
|
||||||
// Adaptation de la requête selon l'intention détectée par le LLM
|
// Adaptation de la requête selon l'intention détectée par le LLM
|
||||||
let queryPrompt = message;
|
let queryPrompt = message;
|
||||||
switch(intent) {
|
switch (intent) {
|
||||||
case 'SUMMARY':
|
case 'SUMMARY':
|
||||||
queryPrompt = "Fais un résumé complet et structuré de ce document en te concentrant sur les points clés";
|
queryPrompt =
|
||||||
|
'Fais un résumé complet et structuré de ce document en te concentrant sur les points clés';
|
||||||
break;
|
break;
|
||||||
case 'ANALYSIS':
|
case 'ANALYSIS':
|
||||||
queryPrompt = `Analyse en détail les aspects suivants du document concernant : ${message}. Fournis une analyse structurée avec des exemples du texte.`;
|
queryPrompt = `Analyse en détail les aspects suivants du document concernant : ${message}. Fournis une analyse structurée avec des exemples du texte.`;
|
||||||
|
@ -1300,13 +1231,15 @@ export const searchHandlers: Record<string, MetaSearchAgentType> = {
|
||||||
emitter.emit('end');
|
emitter.emit('end');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur lors de la recherche dans les documents:", error);
|
console.error('Erreur lors de la recherche dans les documents:', error);
|
||||||
emitter.emit('error', JSON.stringify({
|
emitter.emit(
|
||||||
|
'error',
|
||||||
|
JSON.stringify({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
data: error.message
|
data: error.message
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
|
|
Loading…
Add table
Reference in a new issue