2025-06-09 23:00:25 -06:00
|
|
|
import { Embeddings } from '@langchain/core/embeddings';
|
|
|
|
|
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
|
|
|
import {
|
|
|
|
|
BaseMessage,
|
|
|
|
|
HumanMessage,
|
|
|
|
|
SystemMessage,
|
|
|
|
|
} from '@langchain/core/messages';
|
2025-06-19 12:49:37 -06:00
|
|
|
import {
|
|
|
|
|
BaseLangGraphError,
|
|
|
|
|
END,
|
|
|
|
|
MemorySaver,
|
|
|
|
|
START,
|
|
|
|
|
StateGraph,
|
|
|
|
|
} from '@langchain/langgraph';
|
2025-06-09 23:00:25 -06:00
|
|
|
import { EventEmitter } from 'events';
|
2025-06-17 00:20:05 -06:00
|
|
|
import {
|
2025-06-15 11:53:00 -06:00
|
|
|
AgentState,
|
|
|
|
|
WebSearchAgent,
|
|
|
|
|
AnalyzerAgent,
|
2025-06-17 00:20:05 -06:00
|
|
|
SynthesizerAgent,
|
2025-06-21 16:12:19 -06:00
|
|
|
TaskManagerAgent,
|
2025-06-15 11:53:00 -06:00
|
|
|
} from '../agents';
|
2025-06-09 23:00:25 -06:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Agent Search class implementing LangGraph Supervisor pattern
|
|
|
|
|
*/
|
|
|
|
|
export class AgentSearch {
|
|
|
|
|
private llm: BaseChatModel;
|
|
|
|
|
private embeddings: Embeddings;
|
|
|
|
|
private checkpointer: MemorySaver;
|
|
|
|
|
private signal: AbortSignal;
|
2025-06-21 16:12:19 -06:00
|
|
|
private taskManagerAgent: TaskManagerAgent;
|
2025-06-15 11:53:00 -06:00
|
|
|
private webSearchAgent: WebSearchAgent;
|
|
|
|
|
private analyzerAgent: AnalyzerAgent;
|
|
|
|
|
private synthesizerAgent: SynthesizerAgent;
|
2025-06-19 12:49:37 -06:00
|
|
|
private emitter: EventEmitter;
|
2025-06-09 23:00:25 -06:00
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
llm: BaseChatModel,
|
|
|
|
|
embeddings: Embeddings,
|
|
|
|
|
emitter: EventEmitter,
|
|
|
|
|
systemInstructions: string = '',
|
|
|
|
|
personaInstructions: string = '',
|
|
|
|
|
signal: AbortSignal,
|
|
|
|
|
) {
|
|
|
|
|
this.llm = llm;
|
|
|
|
|
this.embeddings = embeddings;
|
|
|
|
|
this.checkpointer = new MemorySaver();
|
|
|
|
|
this.signal = signal;
|
2025-06-19 12:49:37 -06:00
|
|
|
this.emitter = emitter;
|
2025-06-09 23:00:25 -06:00
|
|
|
|
2025-06-15 11:53:00 -06:00
|
|
|
// Initialize agents
|
2025-06-21 16:12:19 -06:00
|
|
|
this.taskManagerAgent = new TaskManagerAgent(
|
|
|
|
|
llm,
|
|
|
|
|
emitter,
|
|
|
|
|
systemInstructions,
|
|
|
|
|
signal,
|
|
|
|
|
);
|
2025-06-15 11:53:00 -06:00
|
|
|
this.webSearchAgent = new WebSearchAgent(
|
|
|
|
|
llm,
|
|
|
|
|
emitter,
|
|
|
|
|
systemInstructions,
|
2025-06-17 00:20:05 -06:00
|
|
|
signal,
|
2025-06-09 23:00:25 -06:00
|
|
|
);
|
2025-06-15 11:53:00 -06:00
|
|
|
this.analyzerAgent = new AnalyzerAgent(
|
|
|
|
|
llm,
|
|
|
|
|
emitter,
|
|
|
|
|
systemInstructions,
|
2025-06-17 00:20:05 -06:00
|
|
|
signal,
|
2025-06-15 11:53:00 -06:00
|
|
|
);
|
|
|
|
|
this.synthesizerAgent = new SynthesizerAgent(
|
|
|
|
|
llm,
|
|
|
|
|
emitter,
|
|
|
|
|
personaInstructions,
|
2025-06-17 00:20:05 -06:00
|
|
|
signal,
|
2025-06-09 23:00:25 -06:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create and compile the agent workflow graph
|
|
|
|
|
*/
|
|
|
|
|
private createWorkflow() {
|
|
|
|
|
const workflow = new StateGraph(AgentState)
|
2025-06-21 16:12:19 -06:00
|
|
|
.addNode(
|
|
|
|
|
'task_manager',
|
|
|
|
|
this.taskManagerAgent.execute.bind(this.taskManagerAgent),
|
|
|
|
|
{
|
|
|
|
|
ends: ['web_search', 'analyzer'],
|
|
|
|
|
},
|
|
|
|
|
)
|
2025-06-17 00:20:05 -06:00
|
|
|
.addNode(
|
|
|
|
|
'web_search',
|
|
|
|
|
this.webSearchAgent.execute.bind(this.webSearchAgent),
|
|
|
|
|
{
|
2025-06-21 16:12:19 -06:00
|
|
|
ends: ['task_manager'],
|
2025-06-17 00:20:05 -06:00
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.addNode(
|
|
|
|
|
'analyzer',
|
|
|
|
|
this.analyzerAgent.execute.bind(this.analyzerAgent),
|
|
|
|
|
{
|
2025-06-21 16:12:19 -06:00
|
|
|
ends: ['task_manager', 'synthesizer'],
|
2025-06-17 00:20:05 -06:00
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.addNode(
|
|
|
|
|
'synthesizer',
|
|
|
|
|
this.synthesizerAgent.execute.bind(this.synthesizerAgent),
|
|
|
|
|
{
|
|
|
|
|
ends: [END],
|
|
|
|
|
},
|
|
|
|
|
)
|
2025-06-09 23:00:25 -06:00
|
|
|
.addEdge(START, 'analyzer');
|
|
|
|
|
|
|
|
|
|
return workflow.compile({ checkpointer: this.checkpointer });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute the agent search workflow
|
|
|
|
|
*/
|
|
|
|
|
async searchAndAnswer(query: string, history: BaseMessage[] = []) {
|
|
|
|
|
const workflow = this.createWorkflow();
|
|
|
|
|
|
2025-06-19 12:49:37 -06:00
|
|
|
const initialState = {
|
|
|
|
|
messages: [...history, new HumanMessage(query)],
|
|
|
|
|
query,
|
|
|
|
|
};
|
2025-06-09 23:00:25 -06:00
|
|
|
|
2025-06-19 12:49:37 -06:00
|
|
|
try {
|
|
|
|
|
await workflow.invoke(initialState, {
|
2025-06-09 23:00:25 -06:00
|
|
|
configurable: { thread_id: `agent_search_${Date.now()}` },
|
2025-06-21 16:12:19 -06:00
|
|
|
recursionLimit: 20,
|
2025-06-17 00:14:22 -06:00
|
|
|
signal: this.signal,
|
2025-06-09 23:00:25 -06:00
|
|
|
});
|
2025-06-19 12:49:37 -06:00
|
|
|
} catch (error: BaseLangGraphError | any) {
|
|
|
|
|
if (error instanceof BaseLangGraphError) {
|
|
|
|
|
console.error('LangGraph error occurred:', error.message);
|
|
|
|
|
if (error.lc_error_code === 'GRAPH_RECURSION_LIMIT') {
|
|
|
|
|
this.emitter.emit(
|
|
|
|
|
'data',
|
|
|
|
|
JSON.stringify({
|
|
|
|
|
type: 'response',
|
|
|
|
|
data: "I've been working on this for a while and can't find a solution. Please try again with a different query.",
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
this.emitter.emit('end');
|
|
|
|
|
}
|
|
|
|
|
} else if (error.name === 'AbortError') {
|
|
|
|
|
console.warn('Agent search was aborted:', error.message);
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Unexpected error during agent search:', error);
|
|
|
|
|
}
|
2025-06-09 23:00:25 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|