Files
task-6a1864f78a94f887e50d46da/main.py
T
2026-06-04 16:45:35 +00:00

107 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import asyncio
from pathlib import Path
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
from langchain_tavily import TavilySearchResults
from langchain.tools import tool
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
# --------------------- 1. Загрузка переменных окружения ---------------------
load_dotenv()
# --------------------- 2. LLM и Embeddings ---------------------
llm = ChatOpenAI(
model="openai/gpt-oss-20b:free",
base_url="https://openrouter.ai/api/v1",
api_key=os.getenv("OPENAI_API_KEY"),
temperature=0.0,
)
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
base_url="https://openrouter.ai/api/v1",
api_key=os.getenv("OPENAI_API_KEY"),
)
# --------------------- 3. Векторное хранилище Chroma ---------------------
CHROMA_DIR = Path("./chroma_db")
CHROMA_DIR.mkdir(parents=True, exist_ok=True)
vector_store = Chroma(
collection_name="knowledge",
embedding_function=embeddings,
persist_directory=str(CHROMA_DIR),
)
# --------------------- 4. Загрузка документов ---------------------
DOCS_DIR = Path("./documents")
if DOCS_DIR.exists():
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
for file_path in DOCS_DIR.rglob("*.txt"):
text = file_path.read_text(encoding="utf-8")
docs = splitter.split_text(text)
documents = [Document(page_content=chunk, metadata={"source": str(file_path)}) for chunk in docs]
vector_store.add_documents(documents)
vector_store.persist()
# --------------------- 5. Инструменты ---------------------
@tool
def search_local_kb(query: str, top_k: int = 3) -> str:
"""Semantic search in the local knowledge base."""
docs = vector_store.similarity_search(query, k=top_k)
if not docs:
return "No relevant local knowledge found."
return "\n---\n".join(f"{i+1}. {doc.page_content[:200]}..." for i, doc in enumerate(docs))
@tool
def web_search(query: str) -> str:
"""Web search using Tavily."""
tavily = TavilySearchResults(api_key=os.getenv("TAVILY_API_KEY"))
results = tavily.run(query)
if not results:
return "No web results found."
return "\n---\n".join(f"{i+1}. {res['title']}\n{res['url']}\n{res['content'][:200]}..." for i, res in enumerate(results))
# --------------------- 6. Backend ---------------------
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
# --------------------- 7. Создание агента ---------------------
agent = create_deep_agent(
model=llm,
tools=[search_local_kb, web_search],
backend=backend,
system_prompt=(
"You are an assistant that answers user questions.\n"
"If the question is about information that should be in the local knowledge base, use the tool `search_local_kb`.\n"
"If the question requires uptodate information from the web, use the tool `web_search`.\n"
"Always indicate the source of the answer in the format: `Источник: chromadb` or `Источник: tavily`."
),
)
# --------------------- 8. CLI ---------------------
async def chat_loop():
print("Добро пожаловать в RAG‑агент. Введите 'exit' для выхода.")
while True:
user_input = input("\nЗапрос: ")
if user_input.lower() in {"exit", "quit", "q"}:
print("До свидания!")
break
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": user_input}]},
{"configurable": {"thread_id": "session-1"}},
)
# Последнее сообщение агента
content = result["messages"][-1]["content"]
print(content)
if __name__ == "__main__":
asyncio.run(chat_loop())