In this part, we’ll implement advanced features including persistent memory, intelligent agents, and custom tools to create sophisticated AI workflows.
Implementing Persistent Memory
// src/services/memoryService.js
import { ConversationSummaryBufferMemory } from "langchain/memory";
import { ChatMessageHistory } from "langchain/stores/message/in_memory";
export class AdvancedMemoryService {
constructor() {
this.sessions = new Map();
}
createConversationMemory(sessionId, options = {}) {
const memory = new ConversationSummaryBufferMemory({
llm: this.summaryLLM,
maxTokenLimit: 2000,
returnMessages: true,
memoryKey: "chat_history",
chatHistory: new ChatMessageHistory(),
});
this.sessions.set(sessionId, {
memory,
createdAt: new Date().toISOString(),
messageCount: 0,
});
return memory;
}
getMemory(sessionId) {
const session = this.sessions.get(sessionId);
if (!session) {
return this.createConversationMemory(sessionId);
}
return session.memory;
}
}
Building Custom Tools
// src/tools/customTools.js
import { Tool } from "@langchain/core/tools";
export class CalculatorTool extends Tool {
name = "calculator";
description = "Useful for performing mathematical calculations.";
async _call(input) {
try {
const result = Function(`"use strict"; return (${input})`)();
return `The result of ${input} is ${result}`;
} catch (error) {
return `Error calculating ${input}: ${error.message}`;
}
}
}
export class DocumentSummaryTool extends Tool {
constructor(documentManager) {
super();
this.documentManager = documentManager;
}
name = "document_summary";
description = "Useful for getting summaries of documents in the knowledge base.";
async _call(input) {
try {
const searchResults = await this.documentManager.searchDocuments(input, {
maxResults: 3,
});
if (searchResults.totalResults === 0) {
return `No documents found matching "${input}"`;
}
return `Found ${searchResults.totalResults} relevant documents`;
} catch (error) {
return `Error searching documents: ${error.message}`;
}
}
}
Creating an Agent
// src/services/agentService.js
import { AgentExecutor, createOpenAIFunctionsAgent } from "langchain/agents";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { CalculatorTool, DocumentSummaryTool } from '../tools/customTools.js';
export class AgentService {
constructor() {
this.setupAgent();
}
async setupAgent() {
this.tools = [
new CalculatorTool(),
new DocumentSummaryTool(this.documentManager),
];
this.prompt = ChatPromptTemplate.fromMessages([
["system", `You are a helpful AI assistant with access to tools:
- calculator: For mathematical calculations
- document_summary: For searching documents
Use tools when helpful and explain your reasoning.`],
new MessagesPlaceholder("chat_history"),
["human", "{input}"],
new MessagesPlaceholder("agent_scratchpad"),
]);
this.agent = await createOpenAIFunctionsAgent({
llm: llm,
tools: this.tools,
prompt: this.prompt,
});
}
async executeAgent(input, sessionId = 'default') {
try {
const memory = this.memoryService.getMemory(sessionId);
const agentExecutor = new AgentExecutor({
agent: this.agent,
tools: this.tools,
memory: memory,
maxIterations: 5,
});
const result = await agentExecutor.invoke({ input });
return {
response: result.output,
sessionId,
};
} catch (error) {
throw new Error('Failed to execute agent');
}
}
}
Agent API Endpoints
// src/routes/agent.js
import express from 'express';
import { AgentService } from '../services/agentService.js';
const router = express.Router();
const agentService = new AgentService();
router.post('/chat', async (req, res) => {
try {
const { message, sessionId = 'default' } = req.body;
if (!message) {
return res.status(400).json({ error: 'Message is required' });
}
const result = await agentService.executeAgent(message, sessionId);
res.json({
success: true,
...result,
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
export default router;
Multi-Agent Coordination
// src/services/multiAgentService.js
export class MultiAgentService {
constructor() {
this.agents = new Map();
this.setupSpecializedAgents();
}
async setupSpecializedAgents() {
this.agents.set('researcher', {
name: 'Document Researcher',
tools: ['document_summary'],
systemPrompt: 'You are a research specialist focused on document analysis.',
});
this.agents.set('calculator', {
name: 'Mathematical Calculator',
tools: ['calculator'],
systemPrompt: 'You are a mathematics specialist for calculations.',
});
}
async selectAgent(input) {
const inputLower = input.toLowerCase();
if (inputLower.includes('calculate') || /\d+/.test(input)) {
return 'calculator';
}
if (inputLower.includes('search') || inputLower.includes('find')) {
return 'researcher';
}
return 'researcher';
}
}
Testing Your Agents
# Test calculator
curl -X POST http://localhost:3000/api/agent/chat \
-H "Content-Type: application/json" \
-d '{"message": "What is 15 * 7?", "sessionId": "test"}'
# Test document search
curl -X POST http://localhost:3000/api/agent/chat \
-H "Content-Type: application/json" \
-d '{"message": "Find information about LangChain", "sessionId": "test"}'
In Part 8, we’ll cover deployment, production considerations, and scaling strategies!