feat: solution for 'Агент с RAG-памятью'
CI / build (push) Has been cancelled

This commit is contained in:
2026-06-28 12:54:50 +03:00
parent a8a8111eca
commit a811a5c07d
7 changed files with 175 additions and 148 deletions
+23 -50
View File
@@ -1,57 +1,30 @@
# Agent with RAG Memory
# Агент с RAG-памятью
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.
Главная
Мои задания
Агент с RAG-памятью
EN
Агент с RAG-памятью
Зачёт
Версия 9
Дедлайн сдачи: 31.08.2026
## Features
В работе
- **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.
Требуется доработка
## Prerequisites
Решение не соответствует ключевым требованиям задания: отсутствуют требуемые инструменты, некорректно реализован CLI и README содержит неверную информацию. Необходимо внести исправления.
- Node.js v18 or newer
- npm
Редактирование ответа
## Setup
Заполните ответ и отправьте работу на проверку преподавателю.
```bash
# 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
```
## Running the Agent
```bash
npm start
```
You should see output similar to:
```
=== Agent Response ===
Paris
```
## Project Structure
```
agent-rag-memory/
├── src/
│ └── index.ts # Main implementation
├── package.json
├── tsconfig.json
└── README.md
```
## License
MIT License
Тип ответа
Текст
Ссылка
Файлы
Ссылка (URL)
Прикреплённые файлы
Загрузить файл
Отправить
+16 -18
View File
@@ -1,26 +1,24 @@
{
"name": "agent-rag-memory",
"name": "rag-agent",
"version": "1.0.0",
"description": "A simple LangChain agent with RAG memory implemented in TypeScript",
"main": "dist/index.js",
"type": "commonjs",
"scripts": {
"build": "tsc",
"start": "ts-node src/index.ts"
"description": "A simple RAG agent with memory using OpenAI and FAISS",
"main": "src/index.js",
"bin": {
"rag-agent": "./src/cli.js"
},
"scripts": {
"start": "node src/cli.js"
},
"keywords": [
"langchain",
"rag",
"agent",
"typescript"
],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"@types/node": "^20.11.0",
"langchain": "^0.0.202",
"openai": "^4.20.0",
"ts-node": "^10.9.1",
"typescript": "^5.3.3"
"@langchain/core": "^0.0.0",
"@langchain/openai": "^0.0.0",
"@langchain/textsplitter": "^0.0.0",
"@langchain/vectorstores": "^0.0.0",
"commander": "^10.0.0",
"dotenv": "^16.0.0",
"faiss-node": "^1.0.0",
"fs-extra": "^11.0.0"
}
}
+29 -11
View File
@@ -1,14 +1,32 @@
const { getChatCompletion } = require('./utils');
const retriever = require('./retriever');
const { OpenAI } = require('@langchain/openai');
const { RetrievalQAChain } = require('@langchain/chains');
const { initVectorStore } = require('./vectorStore');
require('dotenv').config();
async function ask(question) {
const passages = await retriever.getRelevantPassages(question, 3);
const context = passages.join('\n---\n');
const prompt = `You are an assistant. Use the following context to answer the question.\n\nContext:\n${context}\n\nQuestion: ${question}\nAnswer:`;
const answer = await getChatCompletion(prompt);
return answer;
class Agent {
constructor() {
this.llm = new OpenAI({
temperature: 0.7,
openAIApiKey: process.env.OPENAI_API_KEY,
});
this.vectorStore = null;
this.chain = null;
}
module.exports = {
ask,
};
async init() {
if (!this.vectorStore) {
this.vectorStore = await initVectorStore();
}
if (!this.chain) {
this.chain = RetrievalQAChain.fromLLM(this.llm, this.vectorStore.asRetriever());
}
}
async ask(question) {
await this.init();
const result = await this.chain.invoke({ question });
return result.output;
}
}
module.exports = new Agent();
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env node
require('dotenv').config();
const { program } = require('commander');
const { addDocumentFromFile, clearMemory, agent } = require('./tools');
program
.name('rag-agent')
.description('CLI for a RAG agent with OpenAI and FAISS')
.version('1.0.0');
program
.command('add <file>')
.description('Add a document to the vector store')
.action(async (file) => {
try {
await addDocumentFromFile(file);
} catch (err) {
console.error('Error adding document:', err.message);
}
});
program
.command('query <question...>')
.description('Ask a question to the agent')
.action(async (question) => {
try {
const answer = await agent.ask(question.join(' '));
console.log('Answer:', answer);
} catch (err) {
console.error('Error querying agent:', err.message);
}
});
program
.command('clear')
.description('Clear all stored memory')
.action(async () => {
try {
await clearMemory();
} catch (err) {
console.error('Error clearing memory:', err.message);
}
});
program.parseAsync(process.argv);
+1 -43
View File
@@ -1,43 +1 @@
const readline = require('readline');
const agent = require('./agent');
const retriever = require('./retriever');
async function init() {
// Load knowledge base from ./knowledge directory
await retriever.loadKnowledgeBase('./knowledge');
console.log('Knowledge base loaded.');
}
async function main() {
await init();
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'You> ',
});
rl.prompt();
rl.on('line', async (line) => {
const trimmed = line.trim();
if (trimmed.toLowerCase() === 'exit') {
rl.close();
process.exit(0);
}
try {
const answer = await agent.ask(trimmed);
console.log(`Assistant: ${answer}\n`);
} catch (err) {
console.error(`Error: ${err.message}\n`);
}
rl.prompt();
});
rl.on('close', () => {
console.log('Goodbye!');
process.exit(0);
});
}
main();
module.exports = require('./agent');
+29
View File
@@ -0,0 +1,29 @@
const fs = require('fs');
const { RecursiveCharacterTextSplitter } = require('@langchain/textsplitter');
const { addDocuments, clearVectorStore } = require('./vectorStore');
const agent = require('./agent');
async function addDocumentFromFile(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}
const content = fs.readFileSync(filePath, 'utf-8');
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const docs = await splitter.splitText(content);
await addDocuments(docs);
console.log(`Added ${docs.length} chunks from ${filePath}`);
}
async function clearMemory() {
await clearVectorStore();
console.log('Cleared vector store memory.');
}
module.exports = {
addDocumentFromFile,
clearMemory,
agent,
};
+32 -26
View File
@@ -1,33 +1,39 @@
class VectorStore {
constructor() {
this.documents = [];
const { FAISS } = require('@langchain/vectorstores/faiss');
const { OpenAIEmbeddings } = require('@langchain/openai');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
const VECTORSTORE_DIR = path.join(__dirname, '..', 'data', 'vectorstore');
async function initVectorStore() {
if (!fs.existsSync(VECTORSTORE_DIR)) {
fs.mkdirSync(VECTORSTORE_DIR, { recursive: true });
}
const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
});
const vectorStore = await FAISS.load(embeddings, VECTORSTORE_DIR);
return vectorStore;
}
addDocument(id, embedding, text) {
this.documents.push({ id, embedding, text });
async function addDocuments(texts) {
const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
});
const vectorStore = await FAISS.load(embeddings, VECTORSTORE_DIR);
await vectorStore.addDocuments(texts);
await vectorStore.save();
}
cosineSimilarity(a, b) {
let dot = 0;
let normA = 0;
let normB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}
query(queryEmbedding, k) {
const sims = this.documents.map((doc) => ({
doc,
similarity: this.cosineSimilarity(queryEmbedding, doc.embedding),
}));
sims.sort((a, b) => b.similarity - a.similarity);
return sims.slice(0, k).map((s) => s.doc);
async function clearVectorStore() {
if (fs.existsSync(VECTORSTORE_DIR)) {
fs.rmdirSync(VECTORSTORE_DIR, { recursive: true });
}
}
const store = new VectorStore();
module.exports = store;
module.exports = {
initVectorStore,
addDocuments,
clearVectorStore,
};