From a8a8111eca213d3b90d529ac5c145e2cce48726e Mon Sep 17 00:00:00 2001 From: kuzakhmetovartur Date: Thu, 25 Jun 2026 12:41:22 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20solution=20for=20'=D0=90=D0=B3=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=20=D1=81=20RAG-=D0=BF=D0=B0=D0=BC=D1=8F=D1=82?= =?UTF-8?q?=D1=8C=D1=8E'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 98 ++++++++++++++++----------------------------------- package.json | 33 ++++++----------- src/index.ts | 89 ++++++++++++++++++++++++++++++++++++---------- tsconfig.json | 9 +++-- 4 files changed, 117 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index b6dc529..1fed409 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,57 @@ -# RAG Memory Agent +# Agent with RAG Memory -A simple Retrieval-Augmented Generation (RAG) memory system built with Node.js, TypeScript, and Express. -It stores user data in an in‑memory virtual file system and uses a language model (OpenAI or a mock) to answer queries based on stored memory. +This project demonstrates a simple LangChain agent that uses Retrieval-Augmented Generation (RAG) to answer questions based on a small set of documents. The implementation is written in TypeScript and follows the latest LangChain initialization patterns. ## Features -- **Virtual File System** – CRUD operations for memory entries. -- **LLM abstraction** – Uses OpenAI GPT‑3.5‑Turbo if `OPENAI_API_KEY` is set, otherwise falls back to a mock echo. -- **RAG Agent** – Retrieves relevant memory, builds a prompt, and generates an answer. -- **RESTful API** – Endpoints for managing memory and querying the agent. -- **Unit tests** – Jest tests for VFS and Agent logic. +- **Updated Agent Initialization**: Uses `initializeAgentExecutorWithOptions` from LangChain. +- **Custom Text Splitter**: Configured with a chunk size of 1000 characters and an overlap of 200 characters. +- **RAG Memory**: Embeddings are stored in a FAISS vector store and queried via a RetrievalQA chain. +- **Simple Test Harness**: Runs a sample query and prints the agent's response. -## Installation +## Prerequisites + +- Node.js v18 or newer +- npm + +## Setup ```bash -git clone https://git.brojs.ru/kuzakhmetovartur/prakticheskoe-zadanie-agent-s-rag-pamyat.git -cd prakticheskoe-zadanie-agent-s-rag-pamyat +# Clone the repository +git clone https://github.com/your-username/agent-rag-memory.git +cd agent-rag-memory + +# Install dependencies npm install + +# Create a .env file with your OpenAI API key +echo "OPENAI_API_KEY=your_api_key_here" > .env ``` -## Environment Variables - -Create a `.env` file based on `.env.example`: +## Running the Agent ```bash -cp .env.example .env -``` - -- `OPENAI_API_KEY` – (optional) Your OpenAI API key. If omitted, the agent will use a mock LLM. -- `PORT` – Port number for the server (default: 3000). - -## Running the Server - -```bash -npm run dev # Development with ts-node -# or -npm run build npm start ``` -The server will start on `http://localhost:`. +You should see output similar to: -## API Endpoints - -| Method | Path | Description | Body (JSON) | -|--------|-----------|---------------------------------------------|---------------------------------| -| GET | `/memory` | List all memory entries (id, snippet). | – | -| POST | `/memory` | Create a new memory entry. | `{ "content": "string" }` | -| DELETE | `/memory/:id` | Delete a memory entry by ID. | – | -| POST | `/query` | Query the agent. | `{ "query": "string" }` | - -### Example Requests - -```bash -# Add memory -curl -X POST http://localhost:3000/memory \ - -H "Content-Type: application/json" \ - -d '{"content":"I love programming in TypeScript."}' - -# Query -curl -X POST http://localhost:3000/query \ - -H "Content-Type: application/json" \ - -d '{"query":"What do I like?"}' ``` - -## Testing - -Run unit tests with coverage: - -```bash -npm test +=== Agent Response === +Paris ``` ## Project Structure ``` -src/ - index.ts # Server entry point - agent.ts # RAG agent logic - llm.ts # LLM abstraction - vfs.ts # Virtual file system - utils.ts # Helpers - routes.ts # Express routes - middleware.ts # Error handling & validation - tests/ - vfs.test.ts - agent.test.ts +agent-rag-memory/ +├── src/ +│ └── index.ts # Main implementation +├── package.json +├── tsconfig.json +└── README.md ``` ## License -MIT © Your Name \ No newline at end of file +MIT License \ No newline at end of file diff --git a/package.json b/package.json index a13949b..999f740 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,26 @@ { - "name": "rag-memory-agent", + "name": "agent-rag-memory", "version": "1.0.0", - "description": "Retrieval-Augmented Generation (RAG) memory system with virtual file system and RESTful API", + "description": "A simple LangChain agent with RAG memory implemented in TypeScript", "main": "dist/index.js", + "type": "commonjs", "scripts": { "build": "tsc", - "start": "node dist/index.js", - "dev": "ts-node src/index.ts", - "test": "jest --coverage" + "start": "ts-node src/index.ts" }, "keywords": [ - "RAG", - "LLM", - "virtual-file-system", - "express", + "langchain", + "rag", + "agent", "typescript" ], "author": "Your Name", "license": "MIT", "dependencies": { - "dotenv": "^16.4.5", - "express": "^4.18.2", - "uuid": "^9.0.0" - }, - "devDependencies": { - "@types/express": "^4.17.21", - "@types/jest": "^29.5.12", - "@types/node": "^20.11.5", - "@types/supertest": "^2.0.12", - "jest": "^29.7.0", - "supertest": "^6.3.3", - "ts-jest": "^29.1.1", - "ts-node": "^10.9.2", + "@types/node": "^20.11.0", + "langchain": "^0.0.202", + "openai": "^4.20.0", + "ts-node": "^10.9.1", "typescript": "^5.3.3" } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index f224d25..f9cab61 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,27 +1,80 @@ -import express from 'express'; -import dotenv from 'dotenv'; -import bodyParser from 'body-parser'; -import { VirtualFileSystem } from './vfs'; -import { LLM } from './llm'; -import { Agent } from './agent'; -import { createRoutes } from './routes'; -import { errorHandler } from './middleware'; +import { OpenAI } from "langchain/llms/openai"; +import { OpenAIEmbeddings } from "langchain/embeddings/openai"; +import { FAISS } from "langchain/vectorstores/faiss"; +import { RetrievalQA } from "langchain/chains/retrieval-qa"; +import { Tool } from "langchain/tools/base"; +import { initializeAgentExecutorWithOptions } from "langchain/agents"; +import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; +import * as dotenv from "dotenv"; dotenv.config(); -const app = express(); -const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000; +async function main() { + // Ensure API key is set + const apiKey = process.env.OPENAI_API_KEY; + if (!apiKey) { + console.error("Error: OPENAI_API_KEY environment variable is not set."); + process.exit(1); + } -app.use(bodyParser.json()); + // Sample document + const sampleText = ` + The quick brown fox jumps over the lazy dog. This sentence is often used to test typing and fonts. + The capital of France is Paris. Paris is known for its art, gastronomy, and culture. + The Earth revolves around the Sun every 365.25 days. The Moon orbits the Earth approximately every 27.3 days. + `; -const vfs = new VirtualFileSystem(); -const llm = new LLM(); -const agent = new Agent(vfs, llm); + // Text splitter configuration (chunk size 1000, overlap 200) + const splitter = new RecursiveCharacterTextSplitter({ + chunkSize: 1000, + chunkOverlap: 200, + }); -app.use('/', createRoutes(agent)); + // Split the document into chunks + const docs = await splitter.splitText(sampleText); -app.use(errorHandler); + // Initialize embeddings and vector store + const embeddings = new OpenAIEmbeddings({ openAIApiKey: apiKey }); + const vectorStore = await FAISS.fromTexts(docs, [], embeddings); -app.listen(port, () => { - console.log(`RAG Memory Agent listening on port ${port}`); + // Initialize LLM + const llm = new OpenAI({ + openAIApiKey: apiKey, + temperature: 0, + }); + + // Create RetrievalQA chain + const qaChain = RetrievalQA.fromLLM(llm, vectorStore); + + // Define a tool that uses the QA chain + const ragTool = new Tool({ + name: "RAG", + description: "Answer questions based on the provided documents using Retrieval-Augmented Generation.", + func: async (input: string) => { + const result = await qaChain.invoke({ query: input }); + return result.output as string; + }, + }); + + // Initialize the agent with the updated method + const agent = await initializeAgentExecutorWithOptions( + [ragTool], + llm, + { + agentType: "zero-shot-react-description", + verbose: true, + } + ); + + // Run a sample query + const query = "What is the capital of France?"; + const response = await agent.invoke({ input: query }); + + console.log("\n=== Agent Response ==="); + console.log(response.output); +} + +main().catch((err) => { + console.error("Error in main execution:", err); + process.exit(1); }); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 450bd55..0e33d4f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,13 +2,12 @@ "compilerOptions": { "target": "ES2020", "module": "CommonJS", - "outDir": "dist", - "rootDir": "src", "strict": true, "esModuleInterop": true, + "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "skipLibCheck": true + "outDir": "dist", + "rootDir": "src" }, - "include": ["src/**/*"], - "exclude": ["node_modules", "**/*.test.ts"] + "include": ["src"] } \ No newline at end of file