This commit is contained in:
@@ -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.
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Virtual File System** – CRUD operations for memory entries.
|
- **Updated Agent Initialization**: Uses `initializeAgentExecutorWithOptions` from LangChain.
|
||||||
- **LLM abstraction** – Uses OpenAI GPT‑3.5‑Turbo if `OPENAI_API_KEY` is set, otherwise falls back to a mock echo.
|
- **Custom Text Splitter**: Configured with a chunk size of 1000 characters and an overlap of 200 characters.
|
||||||
- **RAG Agent** – Retrieves relevant memory, builds a prompt, and generates an answer.
|
- **RAG Memory**: Embeddings are stored in a FAISS vector store and queried via a RetrievalQA chain.
|
||||||
- **RESTful API** – Endpoints for managing memory and querying the agent.
|
- **Simple Test Harness**: Runs a sample query and prints the agent's response.
|
||||||
- **Unit tests** – Jest tests for VFS and Agent logic.
|
|
||||||
|
|
||||||
## Installation
|
## Prerequisites
|
||||||
|
|
||||||
|
- Node.js v18 or newer
|
||||||
|
- npm
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.brojs.ru/kuzakhmetovartur/prakticheskoe-zadanie-agent-s-rag-pamyat.git
|
# Clone the repository
|
||||||
cd prakticheskoe-zadanie-agent-s-rag-pamyat
|
git clone https://github.com/your-username/agent-rag-memory.git
|
||||||
|
cd agent-rag-memory
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
# Create a .env file with your OpenAI API key
|
||||||
|
echo "OPENAI_API_KEY=your_api_key_here" > .env
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Variables
|
## Running the Agent
|
||||||
|
|
||||||
Create a `.env` file based on `.env.example`:
|
|
||||||
|
|
||||||
```bash
|
```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
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
The server will start on `http://localhost:<PORT>`.
|
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?"}'
|
|
||||||
```
|
```
|
||||||
|
=== Agent Response ===
|
||||||
## Testing
|
Paris
|
||||||
|
|
||||||
Run unit tests with coverage:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm test
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
src/
|
agent-rag-memory/
|
||||||
index.ts # Server entry point
|
├── src/
|
||||||
agent.ts # RAG agent logic
|
│ └── index.ts # Main implementation
|
||||||
llm.ts # LLM abstraction
|
├── package.json
|
||||||
vfs.ts # Virtual file system
|
├── tsconfig.json
|
||||||
utils.ts # Helpers
|
└── README.md
|
||||||
routes.ts # Express routes
|
|
||||||
middleware.ts # Error handling & validation
|
|
||||||
tests/
|
|
||||||
vfs.test.ts
|
|
||||||
agent.test.ts
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT © Your Name
|
MIT License
|
||||||
+11
-22
@@ -1,37 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "rag-memory-agent",
|
"name": "agent-rag-memory",
|
||||||
"version": "1.0.0",
|
"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",
|
"main": "dist/index.js",
|
||||||
|
"type": "commonjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node dist/index.js",
|
"start": "ts-node src/index.ts"
|
||||||
"dev": "ts-node src/index.ts",
|
|
||||||
"test": "jest --coverage"
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"RAG",
|
"langchain",
|
||||||
"LLM",
|
"rag",
|
||||||
"virtual-file-system",
|
"agent",
|
||||||
"express",
|
|
||||||
"typescript"
|
"typescript"
|
||||||
],
|
],
|
||||||
"author": "Your Name",
|
"author": "Your Name",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^16.4.5",
|
"@types/node": "^20.11.0",
|
||||||
"express": "^4.18.2",
|
"langchain": "^0.0.202",
|
||||||
"uuid": "^9.0.0"
|
"openai": "^4.20.0",
|
||||||
},
|
"ts-node": "^10.9.1",
|
||||||
"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",
|
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+71
-18
@@ -1,27 +1,80 @@
|
|||||||
import express from 'express';
|
import { OpenAI } from "langchain/llms/openai";
|
||||||
import dotenv from 'dotenv';
|
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
|
||||||
import bodyParser from 'body-parser';
|
import { FAISS } from "langchain/vectorstores/faiss";
|
||||||
import { VirtualFileSystem } from './vfs';
|
import { RetrievalQA } from "langchain/chains/retrieval-qa";
|
||||||
import { LLM } from './llm';
|
import { Tool } from "langchain/tools/base";
|
||||||
import { Agent } from './agent';
|
import { initializeAgentExecutorWithOptions } from "langchain/agents";
|
||||||
import { createRoutes } from './routes';
|
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
|
||||||
import { errorHandler } from './middleware';
|
import * as dotenv from "dotenv";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const app = express();
|
async function main() {
|
||||||
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000;
|
// 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();
|
// Text splitter configuration (chunk size 1000, overlap 200)
|
||||||
const llm = new LLM();
|
const splitter = new RecursiveCharacterTextSplitter({
|
||||||
const agent = new Agent(vfs, llm);
|
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, () => {
|
// Initialize LLM
|
||||||
console.log(`RAG Memory Agent listening on port ${port}`);
|
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);
|
||||||
});
|
});
|
||||||
+4
-5
@@ -2,13 +2,12 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"module": "CommonJS",
|
"module": "CommonJS",
|
||||||
"outDir": "dist",
|
|
||||||
"rootDir": "src",
|
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"skipLibCheck": true
|
"outDir": "dist",
|
||||||
|
"rootDir": "src"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src"]
|
||||||
"exclude": ["node_modules", "**/*.test.ts"]
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user