Files
task-6a1864f78a94f887e50d46da/main.py
T
2026-06-15 12:20:37 +00:00

112 lines
3.9 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_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_tavily import TavilySearchResults
from langchain.tools import tool
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
# Load environment variables
load_dotenv()
# ---------- LLM and 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"),
)
# ---------- Vector Store ----------
CHROMA_DIR = Path("./chroma_db")
vector_store = Chroma(
collection_name="knowledge",
embedding_function=embeddings,
persist_directory=str(CHROMA_DIR),
)
# ---------- Tools ----------
@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 documents found in local KB."
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['content'][:200]}..." for i, res in enumerate(results))
# ---------- Backend ----------
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
# ---------- Agent ----------
agent = create_deep_agent(
model=llm,
tools=[search_local_kb, web_search],
backend=backend,
system_prompt=(
"You are a helpful assistant. For any user query, decide whether to use the local knowledge base or perform a web search. "
"If the query is about recent events, news, or requires uptodate information, use the web_search tool. "
"Otherwise, use search_local_kb. "
"Always return the source used in the response (either 'chromadb' or 'tavily')."
),
)
# ---------- Document Loader ----------
def load_documents(directory: str, vectorstore: Chroma):
"""Load .txt and .md files from a directory, chunk them, and add to the vector store."""
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
docs = []
for file_path in Path(directory).glob("**/*"):
if file_path.suffix.lower() in {".txt", ".md"}:
text = file_path.read_text(encoding="utf-8")
chunks = splitter.split_text(text)
docs.extend([Document(page_content=chunk, metadata={"source": str(file_path)}) for chunk in chunks])
if docs:
vectorstore.add_documents(docs)
vectorstore.persist()
# ---------- CLI ----------
async def main():
# Ensure vector store is loaded
if not CHROMA_DIR.exists() or not any(CHROMA_DIR.iterdir()):
print("Loading documents into ChromaDB…")
load_documents("./documents", vector_store)
print("RAG agent ready. Type 'exit' to quit.")
while True:
user_input = input("\nЗапрос: ")
if user_input.lower() in {"exit", "quit"}:
break
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": user_input}]},
{"configurable": {"thread_id": "session-1"}},
)
# Extract last message content
content = result["messages"][-1].content
print(f"\nОтвет:\n{content}")
if __name__ == "__main__":
asyncio.run(main())