This commit is contained in:
+67
-59
@@ -1,72 +1,80 @@
|
||||
**What was implemented**
|
||||
- A FastAPI service exposing a single `/ask` endpoint that accepts a user question and returns an answer together with the sources used.
|
||||
- RAG (Retrieval‑Augmented Generation) logic built with LangChain: documents from `data/` are embedded with OpenAI embeddings, stored in a FAISS vector store, and queried by a `RetrievalQA` chain that feeds the retrieved passages to GPT‑4.
|
||||
- Automatic startup loading of documents, vector store creation, and agent construction so the API is ready to serve immediately after launch.
|
||||
**SOLUTION.md**
|
||||
|
||||
**Why the main parts satisfy the assignment**
|
||||
- **RAG memory**: `create_vectorstore` builds a FAISS index from the loaded documents, and `build_agent` wires this index into a `RetrievalQA` chain that retrieves relevant passages before generation.
|
||||
- **Course guidelines**: The solution follows the Deep Agents Virtual File System pattern – a single `src/index.py` module, clear separation of concerns (loading, vector store, agent, API), and use of environment variables for secrets.
|
||||
- **Python implementation**: All code is pure Python 3.11+, uses only standard libraries and well‑documented third‑party packages (`fastapi`, `langchain`, `openai`, `dotenv`).
|
||||
- **Individual assignment**: No shared state or external services beyond the OpenAI API; the repository contains only the student’s code.
|
||||
---
|
||||
|
||||
**Key code excerpts**
|
||||
### Что реализовано
|
||||
1. **Инструменты RAG**
|
||||
* `search_knowledge_base(query, topK)` – ищет наиболее релевантные документы в памяти.
|
||||
* `add_to_knowledge_base(content)` – добавляет новый контент в память.
|
||||
|
||||
*Loading documents* (`src/index.py`)
|
||||
```python
|
||||
def load_documents(path: Path) -> List:
|
||||
if not path.exists() or not path.is_dir():
|
||||
print(f"Warning: Data directory '{path}' not found. No documents loaded.")
|
||||
return []
|
||||
2. **Стек эмбеддингов**
|
||||
* Заменён `OpenAIEmbeddings` на `OllamaEmbeddings`.
|
||||
* В `package.json` добавлена зависимость `ollama-embeddings`.
|
||||
|
||||
loader = DirectoryLoader(str(path), glob="**/*.txt")
|
||||
documents = loader.load()
|
||||
print(f"Loaded {len(documents)} documents from '{path}'.")
|
||||
return documents
|
||||
3. **Интеграция**
|
||||
* Инструменты подключены в `src/index.js` и доступны через CLI‑команды `/search` и `/add`.
|
||||
* Все операции с эмбеддингами используют экземпляр `OllamaEmbeddings` из `src/embeddings.js`.
|
||||
|
||||
---
|
||||
|
||||
### Почему это соответствует требованиям
|
||||
* **Наличие инструментов** – файлы `searchKnowledgeBase.js` и `addToKnowledgeBase.js` экспортируют требуемые функции, которые можно вызывать из любого модуля.
|
||||
* **Использование OllamaEmbeddings** – в `embeddings.js` создаётся единственный экземпляр `OllamaEmbeddings`, а в инструментах вызывается `embeddings.embedQuery`.
|
||||
* **Обновлённые импорты** – все модули импортируют `embeddings` из `src/embeddings.js`, а не из OpenAI.
|
||||
* **Пакетная зависимость** – `ollama-embeddings` присутствует в `package.json`, что позволяет npm установить нужный пакет.
|
||||
|
||||
---
|
||||
|
||||
### Ключевые фрагменты кода
|
||||
|
||||
**src/embeddings.js** – инициализация OllamaEmbeddings
|
||||
```js
|
||||
import { OllamaEmbeddings } from 'ollama-embeddings';
|
||||
const modelName = process.env.OLLAMA_MODEL || 'all-minilm';
|
||||
export const embeddings = new OllamaEmbeddings({
|
||||
model: modelName,
|
||||
host: process.env.OLLAMA_HOST || 'http://localhost:11434'
|
||||
});
|
||||
```
|
||||
|
||||
*Creating the vector store* (`src/index.py`)
|
||||
```python
|
||||
def create_vectorstore(documents: List) -> FAISS:
|
||||
embeddings = OpenAIEmbeddings()
|
||||
vectorstore = FAISS.from_documents(documents, embeddings)
|
||||
print("FAISS vector store created.")
|
||||
return vectorstore
|
||||
**src/tools/searchKnowledgeBase.js** – поиск по памяти
|
||||
```js
|
||||
export async function search_knowledge_base(query, topK = 3) {
|
||||
const queryEmbedding = await embeddings.embedQuery(query);
|
||||
const scored = knowledgeBase.map(entry => ({
|
||||
id: entry.id,
|
||||
content: entry.content,
|
||||
score: cosineSimilarity(queryEmbedding, entry.embedding)
|
||||
}));
|
||||
scored.sort((a, b) => b.score - a.score);
|
||||
return scored.slice(0, topK);
|
||||
}
|
||||
```
|
||||
|
||||
*Building the RetrievalQA agent* (`src/index.py`)
|
||||
```python
|
||||
def build_agent(vectorstore: FAISS) -> RetrievalQA:
|
||||
llm = OpenAI(model_name="gpt-4", temperature=0, openai_api_key=OPENAI_API_KEY)
|
||||
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
|
||||
qa_chain = RetrievalQA.from_chain_type(
|
||||
llm=llm,
|
||||
chain_type="stuff",
|
||||
retriever=retriever,
|
||||
return_source_documents=True,
|
||||
)
|
||||
print("RetrievalQA agent constructed.")
|
||||
return qa_chain
|
||||
**src/tools/addToKnowledgeBase.js** – добавление контента
|
||||
```js
|
||||
export async function add_to_knowledge_base(content) {
|
||||
const embedding = await embeddings.embedQuery(content);
|
||||
const id = uuidv4();
|
||||
knowledgeBase.push({ id, content, embedding });
|
||||
return { id };
|
||||
}
|
||||
```
|
||||
|
||||
*FastAPI endpoint* (`src/index.py`)
|
||||
```python
|
||||
@app.post("/ask", response_model=AnswerResponse)
|
||||
def ask_question(request: QuestionRequest):
|
||||
if not agent:
|
||||
raise HTTPException(status_code=500, detail="Agent not initialized.")
|
||||
try:
|
||||
result = agent({"question": request.question})
|
||||
answer = result.get("answer", "")
|
||||
sources = [doc.metadata.get("source", "") for doc in result.get("source_documents", [])]
|
||||
return AnswerResponse(answer=answer, sources=sources)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
**src/index.js** – CLI‑интеграция инструментов
|
||||
```js
|
||||
import { search_knowledge_base } from './tools/searchKnowledgeBase.js';
|
||||
import { add_to_knowledge_base } from './tools/addToKnowledgeBase.js';
|
||||
...
|
||||
if (trimmed.startsWith('/search ')) { … }
|
||||
else if (trimmed.startsWith('/add ')) { … }
|
||||
```
|
||||
|
||||
**Honest limitations**
|
||||
- The vector store is rebuilt on every server restart; no persistence across restarts.
|
||||
- No caching of embeddings or query results, which may increase latency for repeated queries.
|
||||
- Error handling is minimal – any exception during a request returns a generic 500 error.
|
||||
- The solution assumes all documents are plain `.txt`; other formats would need additional loaders.
|
||||
---
|
||||
|
||||
These points are acceptable for the current assignment scope and can be refined in future iterations.
|
||||
### Ограничения
|
||||
* Память реализована как простая массив‑объект, поэтому данные не сохраняются между перезапусками.
|
||||
* Нет обработки ошибок при работе с Ollama (например, недоступность сервера).
|
||||
* Для больших наборов данных поиск выполняется линейно; в продакшене стоит использовать индексирование.
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user