import os import asyncio from pathlib import Path from dotenv import load_dotenv # LangChain imports from langchain_ollama import ChatOllama, OllamaEmbeddings from langchain_core.documents import Document from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_qdrant import QdrantVectorStore from langchain.agents import create_agent from langchain.tools import tool from langchain_core.messages import HumanMessage # Load environment variables (e.g., Ollama host) load_dotenv() # LLM and embeddings configuration llm = ChatOllama(model="llama3") embeddings = OllamaEmbeddings(model="nomic-embed-text") # Qdrant client and vector store initialization from qdrant_client import QdrantClient qdrant_client = QdrantClient(host="localhost", port=6333) vector_store = QdrantVectorStore( client=qdrant_client, collection_name="knowledge", embeddings=embeddings, ) # Text splitter for chunking documents splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) # Tool: Search knowledge base @tool def search_knowledge_base(query: str, max_results: int = 3) -> str: """Search the knowledge base for relevant information.""" docs = vector_store.similarity_search(query, k=max_results) if not docs: return "No relevant documents found." return "\n\n---\n\n".join(f"<{doc.metadata.get('title', 'Untitled')}> {doc.page_content}" for doc in docs) # Tool: Add document to knowledge base @tool def add_to_knowledge_base(content: str, title: str = "Document") -> str: """Add content to the knowledge base.""" # Split content into chunks chunks = splitter.split_text(content) documents = [Document(page_content=chunk, metadata={"title": title, "chunk_index": idx}) for idx, chunk in enumerate(chunks)] vector_store.add_documents(documents) return f"Added {len(chunks)} chunks from '{title}'." # Agent creation agent = create_agent( llm=llm, tools=[search_knowledge_base, add_to_knowledge_base], system_prompt="You are an AI assistant with access to a knowledge base. Use the provided tools to search and add information." ) # Helper to invoke agent asynchronously async def invoke_agent(user_input: str) -> str: result = await agent.ainvoke( {"messages": [HumanMessage(content=user_input)]}, {"configurable": {"thread_id": "session-1"}}, ) # The last message is the agent's reply return result["messages"][-1].content # CLI loop async def main(): print("Welcome to the RAG agent CLI. Commands: /add | /search | /quit") while True: user_input = input(">>> ") if not user_input: continue if user_input.lower() == "/quit": print("Goodbye!") break if user_input.lower().startswith("/add "): path = user_input[5:].strip() if Path(path).is_file(): content = Path(path).read_text(encoding="utf-8") title = Path(path).stem # Directly invoke tool via agent response = await invoke_agent(f"Add document: {title}\n{content}") else: print("File not found.") continue elif user_input.lower().startswith("/search "): query = user_input[8:].strip() response = await invoke_agent(f"Search for: {query}") else: print("Unknown command. Use /add, /search, or /quit.") continue print("\n--- Agent Response ---\n") print(response) print("\n-----------------------\n") if __name__ == "__main__": asyncio.run(main())