From d7a4e11b6f087492ac1a27942854b4b09059b312 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 17:08:48 +0000 Subject: [PATCH] =?UTF-8?q?=D0=AD=D0=BA=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD:=20R?= =?UTF-8?q?AG-=D0=B0=D0=B3=D0=B5=D0=BD=D1=82=20=D1=81=20ChromaDB=20=D0=B8?= =?UTF-8?q?=20=D0=B2=D0=B5=D0=B1-=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC:=20main.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..140c8f3 --- /dev/null +++ b/main.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +main.py – CLI чат‑цикл для RAG‑агента с ChromaDB и веб‑поиском через Tavily. +""" + +import os +from pathlib import Path + +from dotenv import load_dotenv +from langchain_ollama import ChatOllama, OllamaEmbeddings +from langchain_chroma import Chroma +from langchain_text_splitters import RecursiveCharacterTextSplitter +from langchain.tools import tool +from langchain.agents import AgentExecutor, create_openai_tools_agent + +# --------------------------------------------------------------------------- # +# 1. Загрузка переменных окружения (TAVILY_API_KEY) +# --------------------------------------------------------------------------- # +load_dotenv() +TAVILY_API_KEY = os.getenv("TAVILY_API_KEY") +if not TAVILY_API_KEY: + raise RuntimeError("Не найдено переменной окружения TAVILY_API_KEY") + +# --------------------------------------------------------------------------- # +# 2. Векторное хранилище ChromaDB +# --------------------------------------------------------------------------- # +CHROMA_DIR = Path("./chroma_db") +CHROMA_DIR.mkdir(parents=True, exist_ok=True) + +embeddings = OllamaEmbeddings(model="nomic-embed-text") +vectorstore = Chroma( + persist_directory=str(CHROMA_DIR), + embedding_function=embeddings, +) +# --------------------------------------------------------------------------- # +# 3. Инструменты агента +# --------------------------------------------------------------------------- # + +@tool("search_local_kb") +def search_local_kb(query: str, top_k: int = 5) -> str: + """ + Семантический поиск в локальной базе знаний (ChromaDB). + Возвращает объединённый текст найденных документов. + """ + retriever = vectorstore.as_retriever(search_kwargs={"k": top_k}) + docs = retriever.invoke(query) + if not docs: + return "Ничего не найдено." + # Собираем содержимое всех документов + return "\n\n".join(doc.page_content for doc in docs) + + +@tool("web_search") +def web_search(query: str) -> str: + """ + Поиск в интернете через Tavily. + Возвращает краткое резюме результатов поиска. + """ + from tavily import TavilyClient + + client = TavilyClient(api_key=TAVILY_API_KEY) + results = client.search(query=query, max_results=3) + if not results: + return "Ничего не найдено в интернете." + # Формируем строку с заголовками и ссылками + snippets = [] + for r in results: + snippets.append(f"{r['title']}\n{r['url']}") + return "\n\n".join(snippets) + + +# --------------------------------------------------------------------------- # +# 4. Создание агента +# --------------------------------------------------------------------------- # + +llm = ChatOllama(model="llama3", temperature=0) + +tools = [search_local_kb, web_search] + +agent = create_openai_tools_agent( + llm, + tools, + system_message=""" +Вы – интеллектуальный агент, который отвечает на вопросы пользователя. +Если вопрос относится к локальным документам (например, конспектам, заметкам), +используйте инструмент «search_local_kb». +Если требуется актуальная информация из интернета (новости, факты), используйте +инструмент «web_search». + +После выполнения запроса возвращайте ответ в формате: +[Источник] <текст ответа> + +Где источник – либо «chromadb», либо «tavily». +""", +) + +agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=False) + + +# --------------------------------------------------------------------------- # +# 5. Чат‑цикл CLI +# --------------------------------------------------------------------------- # + +def main() -> None: + print("=== RAG‑агент с ChromaDB и Tavily ===") + print("Введите запрос (или 'exit' для выхода):") + + while True: + user_input = input("\n> ").strip() + if not user_input or user_input.lower() == "exit": + print("До свидания!") + break + + # Запускаем агента + try: + result = agent_executor.invoke({"input": user_input}) + except Exception as e: + print(f"Ошибка при обработке запроса: {e}") + continue + + # Результат – строка вида "[Источник] ...". Если формат не соответствует, + # просто выводим как есть. + if isinstance(result, str) and result.startswith("["): + try: + source_part, answer = result.split("]", 1) + source = source_part.strip("[") + print(f"\nОтвет: {answer.strip()}") + print(f"Источник: {source.lower()}") + except ValueError: + # Не удалось распарсить – выводим полностью + print("\nОтвет:", result) + else: + print("\nОтвет:", result) + + +if __name__ == "__main__": + main() \ No newline at end of file