Files
task-6a1864f78a94f887e50d46da/main.py
T
2026-06-04 15:55:29 +00:00

111 lines
4.0 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 langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain.tools import tool
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_ollama import OllamaEmbeddings
from langchain_tavily import TavilySearchResults
# ---------- LLM ----------
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,
)
# ---------- Backend ----------
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
# ---------- Vectorstore utilities ----------
PERSIST_DIR = Path("./chroma_db")
def create_vectorstore(persist_directory: str = "./chroma_db"):
embeddings = OllamaEmbeddings(model="nomic-embed-text")
return Chroma(persist_directory=persist_directory, embedding_function=embeddings)
def load_documents(directory: str, vectorstore):
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
docs = []
for file in Path(directory).glob("**/*"):
if file.suffix.lower() in {".txt", ".md"}:
text = file.read_text(encoding="utf-8")
docs.extend(splitter.split_text(text))
# Convert to Document objects
from langchain_core.documents import Document
documents = [Document(page_content=chunk) for chunk in docs]
vectorstore.add_documents(documents)
vectorstore.persist()
# ---------- Tools ----------
@tool
def search_local_kb(query: str, top_k: int = 3) -> str:
"""Semantic search in the local ChromaDB knowledge base."""
vectorstore = create_vectorstore(PERSIST_DIR)
retriever = vectorstore.as_retriever(search_kwargs={"k": top_k})
docs = retriever.invoke(query)
if not docs:
return "No relevant local documents found."
return "\n---\n".join(doc.page_content for doc in docs) + "\n[Source: chromadb]"
@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."
snippets = [f"{r['title']}\n{r['content']}" for r in results]
return "\n---\n".join(snippets) + "\n[Source: tavily]"
# ---------- Agent ----------
SYSTEM_PROMPT = (
"You are an AI assistant that can answer questions using either a local knowledge base or the web. "
"If the question refers to documents in the local folder, use the `search_local_kb` tool. "
"If the question is about recent events or requires uptodate information, use the `web_search` tool. "
"Always include the source tag (`[Source: chromadb]` or `[Source: tavily]`) in your answer."
)
agent = create_deep_agent(
model=llm,
tools=[search_local_kb, web_search],
backend=backend,
system_prompt=SYSTEM_PROMPT,
)
# ---------- CLI ----------
async def chat_loop():
print("RAG Agent ready. Type 'exit' to quit.")
while True:
user_input = input("\nЗапрос: ")
if user_input.strip().lower() == "exit":
print("Goodbye!")
break
result = await agent.ainvoke(
{"messages": [HumanMessage(content=user_input)]},
{"configurable": {"thread_id": "session-1"}},
)
# The last message is the assistant's reply
reply = result["messages"][-1].content
print(reply)
# ---------- Initialization ----------
if __name__ == "__main__":
# Ensure vectorstore exists and load documents if empty
vectorstore = create_vectorstore(PERSIST_DIR)
if not vectorstore.get_all_documents():
print("Loading documents into ChromaDB...")
load_documents("./documents", vectorstore)
print("Documents loaded.")
asyncio.run(chat_loop())