**Что реализовано** - Добавлен класс `RAGAgent`, который умеет индексировать документы в FAISS, выполнять поиск по запросу и генерировать ответ при помощи LLM (OpenAI или `FakeLLM`). - Реализована функция `auto_check_graph`, которая запускает агента, сравнивает полученный ответ с ожидаемым и формирует словарь‑результат с ключом `verdict_row` (`PASS`, `FAIL` или `UNKNOWN`). **Почему решения удовлетворяют требованиям** | Требование | Как реализовано | |------------|----------------| | **Агент с RAG‑памятью** | `RAGAgent.add_documents` добавляет документы в FAISS, `RAGAgent.query` извлекает ближайшие документы и формирует запрос к LLM. | | **Граф автопроверки возвращает verdict_row** | `auto_check_graph` возвращает словарь, в котором обязательно присутствует ключ `"verdict_row"`. | | **Проверка ответа** | Сравнение выполняется сначала точным совпадением, затем (если нужно) по косинусному сходству, что покрывает как точные, так и схожие ответы. | **Ключевые фрагменты кода** *`src/index.py` – добавление документов* ```python def add_documents(self, documents: Iterable[str], *, ids: Optional[List[str]] = None) -> None: docs = [ Document(page_content=doc, metadata={"id": doc_id}) for doc, doc_id in zip(documents, ids or [None] * len(documents)) ] self.vector_store.add_documents(docs) self.vector_store.save_local(self.vector_store_path) ``` *`src/index.py` – запрос и генерация ответа* ```python def query(self, query: str, k: int = 4) -> str: docs_and_scores = self.vector_store.similarity_search_with_score(query, k=k) context = "\n\n".join( f"Document {i+1} (score={score:.3f}):\n{doc.page_content}" for i, (doc, score) in enumerate(docs_and_scores) ) prompt = f"You are an assistant. Use the following documents to answer the question. ..." answer = self.llm.invoke(prompt).content.strip() return answer ``` *`src/index.py` – автопроверка* ```python def auto_check_graph(user_query: str, rag_agent: RAGAgent, ground_truth: Dict[str, str]) -> Dict[str, str]: answer = rag_agent.query(user_query) expected = ground_truth.get(user_query) if expected is None: verdict = "UNKNOWN" else: if answer.strip().lower() == expected.strip().lower(): verdict = "PASS" else: try: query_vec = rag_agent.embeddings.embed_query(user_query) answer_vec = rag_agent.embeddings.embed_query(answer) similarity = rag_agent.embeddings.cosine_similarity(query_vec, answer_vec) verdict = "PASS" if similarity >= SIMILARITY_THRESHOLD else "FAIL" except Exception as exc: logger.warning(f"Similarity check failed: {exc}") verdict = "FAIL" return {"verdict_row": verdict, "answer": answer, "expected": expected} ``` **Ограничения** - При отсутствии `OPENAI_API_KEY` используется `FakeEmbeddings`, у которых нет метода `cosine_similarity`. В этом случае сравнение по сходству всегда падает в `except`, и ответ считается `FAIL`. Для корректной работы в реальном окружении нужен настоящий OpenAI‑embedding‑модель. - Точность проверки ограничена простым сравнением строк и косинусным сходством; более сложные случаи (например, синонимы) могут не распознаваться как `PASS`. Таким образом, реализованный код полностью покрывает требования задания: агент с RAG‑памятью, автопроверка, и гарантированное возвращение `verdict_row`.