Files
task-6a1864f78a94f887e50d46da/main.py
T

105 lines
3.7 KiB
Python

import os
import asyncio
from dotenv import load_dotenv
from langchain_ollama import ChatOllama
from langchain_ollama import OllamaEmbeddings
from langchain_chroma import Chroma
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.tools import tool
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend
# Load environment variables
load_dotenv()
# ---------- Vector Store ----------
def create_vectorstore(persist_directory="./chroma_db"):
"""Create a Chroma vector store with Ollama embeddings."""
embeddings = OllamaEmbeddings(model="nomic-embed-text")
return Chroma(persist_directory=persist_directory, embedding_function=embeddings)
def load_documents(directory, vectorstore):
"""Load .txt and .md files from *directory*, chunk them, and add to *vectorstore*.
The function preserves the file name in metadata for later reference.
"""
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
for root, _, files in os.walk(directory):
for fname in files:
if fname.lower().endswith(('.txt', '.md')):
path = os.path.join(root, fname)
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
docs = splitter.split_text(text)
documents = [Document(page_content=chunk, metadata={"source": fname}) for chunk in docs]
vectorstore.add_documents(documents)
# ---------- Tools ----------
vectorstore = create_vectorstore()
@tool
def search_local_kb(query: str, top_k: int = 3) -> str:
"""Semantic search in the local knowledge base (ChromaDB)."""
retriever = vectorstore.as_retriever(search_kwargs={"k": top_k})
docs = retriever.get_relevant_documents(query)
if not docs:
return "No relevant local knowledge found."
return "\n---\n".join([f"{doc.metadata.get('source', 'unknown')}\n{doc.page_content}" for doc in docs])
@tool
def web_search(query: str) -> str:
"""Web search using Tavily."""
from tavily import TavilyClient
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
results = client.search(query, max_results=3)
if not results:
return "No web results found."
return "\n---\n".join([f"{r['title']}\n{r['content']}" for r in results])
# ---------- Agent ----------
llm = ChatOllama(model="llama3", temperature=0.0)
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
system_prompt = (
"You are an AI assistant with access to two tools: "
"search_local_kb for local knowledge and web_search for up-to-date information. "
"When answering a user query, first decide which tool is appropriate. "
"If the answer can be derived from the local documents, use search_local_kb; "
"otherwise use web_search. "
"Always indicate the source of the information in your response: "
"[Local KB] or [Web Search]."
)
agent = create_deep_agent(
model=llm,
tools=[search_local_kb, web_search],
backend=backend,
system_prompt=system_prompt,
)
async def main():
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": [{"role": "user", "content": user_input}]},
{"configurable": {"thread_id": "session-1"}},
)
# The last message is the assistant's reply
reply = result["messages"][-1].content
print(f"\n{reply}")
if __name__ == "__main__":
asyncio.run(main())