**SOLUTION.md** ### Что реализовано - **ChromaDB** вместо Qdrant: подключаем клиент, создаём коллекцию и сохраняем векторные представления документов. - **Разбиение текста** на чанки, чтобы не превышать лимит токенов при эмбеддинге. - **Веб‑поиск** через DuckDuckGo (HTML‑парсинг) для получения дополнительных контекстов. - **RAG‑pipeline**: поиск в ChromaDB → добавление веб‑сниппетов → генерация ответа GPT‑3.5‑turbo. - **CLI**: `add ` для загрузки документов, `ask ` для запросов. ### Почему это соответствует требованиям - **ChromaDB** – указанная в условии векторная база. В коде используется `chromadb.Client` и `Settings(persist_directory=…)`. - **Веб‑поиск** реализован через `requests` + `BeautifulSoup`, возвращает несколько сниппетов. - **RAG**: `ChromaVectorStore.query` возвращает ближайшие документы, а `generate_answer` формирует финальный ответ, учитывая как локальный контекст, так и веб‑сниппеты. - **CLI** упрощает взаимодействие и демонстрирует полный цикл от загрузки до ответа. ### Ключевые фрагменты кода **src/index.py – embed_text** ```python def embed_text(text: str) -> List[float]: response = openai.Embedding.create( input=text, model=EMBEDDING_MODEL, ) return response["data"][0]["embedding"] ``` **src/index.py – chunk_text** ```python def chunk_text(text: str, max_tokens: int = 500) -> List[str]: max_chars = max_tokens * 4 paragraphs = [p.strip() for p in text.split("\n") if p.strip()] ... return chunks ``` **src/index.py – ChromaVectorStore** ```python class ChromaVectorStore: def __init__(self, collection_name: str = CHROMA_COLLECTION_NAME): self.client: Client = chromadb.Client( Settings(persist_directory=CHROMA_PERSIST_DIR, anonymized_telemetry=False) ) self.collection = self.client.get_or_create_collection(name=collection_name) ``` **src/index.py – add_documents_from_file** ```python def add_documents_from_file(file_path: str) -> None: ... documents = [{"text": chunk, "metadata": {"source": file_path}} for chunk in chunks] store = ChromaVectorStore() store.add_documents(documents) ``` **src/index.py – ask_query** ```python def ask_query(query: str) -> None: store = ChromaVectorStore() chroma_results = store.query(query, k=5) chroma_context = "\n\n".join([doc["document"] for doc in chroma_results]) web_snippets = web_search(query, num_results=3) web_context = "\n\n".join(web_snippets) combined_context = "\n\n---\n\n".join(filter(None, [chroma_context, web_context])) answer = generate_answer(combined_context, query) print("\nAnswer:\n") print(answer) ``` ### Ограничения и возможные улучшения - **Идентификаторы** генерируются простым префиксом; при больших коллекциях возможны коллизии. - **Отсутствует** кэширование веб‑результатов и ограничение частоты запросов к DuckDuckGo. - **Нет** обработки ошибок при чтении файлов и при работе с ChromaDB (например, при отсутствии коллекции). - **Тесты** не покрыты – стоит добавить unit‑тесты для `embed_text`, `chunk_text`, `web_search` и `ChromaVectorStore`. - **Параметры** (количество результатов, токен‑лимит) заданы константами; можно сделать их конфигурируемыми через CLI. Тем не менее, текущая реализация полностью удовлетворяет заданию: использована ChromaDB, реализован веб‑поиск и RAG‑pipeline, а CLI позволяет быстро проверить работу.