Экзамен: RAG-агент с ChromaDB и веб-поиском: main.py
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user