From 99de3c96e590162a78b72b3ee3b48eaa416484f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D1=80=D0=B8=D1=8F=20=D0=91=D0=B5=D1=80=D0=B4?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2=D0=B0?= Date: Thu, 28 May 2026 16:59:11 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20solution?= =?UTF-8?q?.py:=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0=D1=82=D1=8C=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20vectorstore.py,=20tools.py,=20?= =?UTF-8?q?agent.py,=20init=5Fknow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- solution.py | 147 ++-------------------------------------------------- 1 file changed, 5 insertions(+), 142 deletions(-) diff --git a/solution.py b/solution.py index 94b5003..4b764ca 100644 --- a/solution.py +++ b/solution.py @@ -1,145 +1,8 @@ -import os import sys -from pathlib import Path -from typing import List - -import requests -from bs4 import BeautifulSoup -from langchain.embeddings.openai import OpenAIEmbeddings -from langchain.vectorstores import Chroma -from langchain.llms import OpenAI -from langchain.chains.question_answering import load_qa_chain -from rich.console import Console -from rich.prompt import Prompt - -# --------------------------------------------------------------------------- # -# Конфигурация и глобальные объекты -# --------------------------------------------------------------------------- # - -console = Console() - -CHROMA_DIR = Path("./chroma_db") -EMBEDDINGS_MODEL = "text-embedding-ada-002" -LLM_MODEL = "gpt-3.5-turbo" - -OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") -if not OPENAI_API_KEY: - console.print("[red]Ошибка: переменная окружения OPENAI_API_KEY не задана[/red]") - sys.exit(1) - -embeddings = OpenAIEmbeddings(model=EMBEDDINGS_MODEL, openai_api_key=OPENAI_API_KEY) -llm = OpenAI(model_name=LLM_MODEL, temperature=0.2, openai_api_key=OPENAI_API_KEY) - - -def init_vector_store() -> Chroma: - """ - Инициализирует или загружает существующую базу данных Chroma. - """ - return Chroma( - persist_directory=str(CHROMA_DIR), - embedding_function=embeddings, - ) - - -# --------------------------------------------------------------------------- # -# Веб‑парсинг и загрузка контента -# --------------------------------------------------------------------------- # - -def fetch_text_from_url(url: str) -> str: - """ - Загружает страницу по URL, удаляет скрипты/стили и возвращает чистый текст. - """ - try: - resp = requests.get(url, timeout=10) - resp.raise_for_status() - except Exception as e: - console.print(f"[yellow]Не удалось загрузить {url}: {e}[/yellow]") - return "" - - soup = BeautifulSoup(resp.text, "html.parser") - - # Удаляем скрипты и стили - for tag in soup(["script", "style"]): - tag.decompose() - - text = soup.get_text(separator="\n") - lines = [line.strip() for line in text.splitlines()] - cleaned = "\n".join([l for l in lines if l]) - return cleaned - - -def ingest_urls(urls: List[str], vector_store: Chroma) -> None: - """ - Загружает тексты по списку URL и сохраняет их в векторный хранилище. - """ - docs = [] - metadatas = [] - - for url in urls: - console.print(f"[cyan]Обрабатываем {url}[/cyan]") - content = fetch_text_from_url(url) - if not content: - continue - # Разбиваем на части по 1000 символов для удобства векторизации - chunk_size = 1000 - for i in range(0, len(content), chunk_size): - chunk = content[i : i + chunk_size] - docs.append(chunk) - metadatas.append({"source": url}) - - if docs: - vector_store.add_texts(docs, metadatas=metadatas) - console.print(f"[green]Добавлено {len(docs)} фрагментов в базу[/green]") - else: - console.print("[red]Нет данных для добавления[/red]") - - -# --------------------------------------------------------------------------- # -# RAG‑обработчик запросов -# --------------------------------------------------------------------------- # - -def answer_query(query: str, vector_store: Chroma) -> str: - """ - Получает ответ на вопрос, используя векторное хранение и LLM. - """ - # Поиск похожих документов - docs = vector_store.similarity_search(query, k=5) - if not docs: - return "Извините, я не нашёл подходящей информации." - - chain = load_qa_chain(llm, chain_type="stuff") - result = chain.run(input_documents=docs, question=query) - return result - - -# --------------------------------------------------------------------------- # -# Основная логика программы -# --------------------------------------------------------------------------- # - -def main() -> None: - vector_store = init_vector_store() - - # Если база пуста – запрашиваем URL для загрузки - if not list(vector_store.get_all_ids()): - console.print("[yellow]База данных пустая. Пожалуйста, укажите URL-адреса для индексации[/yellow]") - urls_input = Prompt.ask("Введите URL через запятую") - urls = [u.strip() for u in urls_input.split(",") if u.strip()] - ingest_urls(urls, vector_store) - vector_store.persist() - - console.print("[bold green]RAG‑агент готов к работе![/bold green]") - while True: - try: - query = Prompt.ask("\nВведите ваш вопрос (или 'выход' для завершения)") - if query.lower() in ("выход", "exit", "quit"): - console.print("[blue]До свидания![/blue]") - break - answer = answer_query(query, vector_store) - console.print(f"\n[bold]Ответ:[/bold]\n{answer}\n") - except KeyboardInterrupt: - console.print("\n[blue]Прервано пользователем. Завершение работы.[/blue]") - break - +from main import run_cli if __name__ == "__main__": - main() \ No newline at end of file + try: + run_cli() + except KeyboardInterrupt: + sys.exit(0) \ No newline at end of file