4.5 KiB
4.5 KiB
SOLUTION.md
Что реализовано
- ChromaDB вместо Qdrant: подключаем клиент, создаём коллекцию и сохраняем векторные представления документов.
- Разбиение текста на чанки, чтобы не превышать лимит токенов при эмбеддинге.
- Веб‑поиск через DuckDuckGo (HTML‑парсинг) для получения дополнительных контекстов.
- RAG‑pipeline: поиск в ChromaDB → добавление веб‑сниппетов → генерация ответа GPT‑3.5‑turbo.
- CLI:
add <file>для загрузки документов,ask <question>для запросов.
Почему это соответствует требованиям
- ChromaDB – указанная в условии векторная база. В коде используется
chromadb.ClientиSettings(persist_directory=…). - Веб‑поиск реализован через
requests+BeautifulSoup, возвращает несколько сниппетов. - RAG:
ChromaVectorStore.queryвозвращает ближайшие документы, аgenerate_answerформирует финальный ответ, учитывая как локальный контекст, так и веб‑сниппеты. - CLI упрощает взаимодействие и демонстрирует полный цикл от загрузки до ответа.
Ключевые фрагменты кода
src/index.py – embed_text
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
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
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
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
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 позволяет быстро проверить работу.