from pathlib import Path import os from typing import List # LLM and embeddings via Ollama from langchain_ollama import ChatOllama, OllamaEmbeddings # Tools from langchain.tools import tool # Vector store from langchain_qdrant import QdrantVectorStore from qdrant_client import QdrantClient from qdrant_client.http.models import Distance, VectorParams # Text splitter from langchain_text_splitters import RecursiveCharacterTextSplitter # Agent from langchain.agents import create_agent # Document type from langchain_core.documents import Document # -------------------- 1. RAG tools -------------------- @tool def search_knowledge_base(query: str, max_results: int = 5) -> str: """Search the knowledge base for relevant documents.""" results = vector_store.similarity_search_with_score(query, k=max_results) if not results: return "No relevant documents found." response_lines = [] for doc, score in results: title = doc.metadata.get("title", "Untitled") snippet = doc.page_content[:200] + ("..." if len(doc.page_content) > 200 else "") response_lines.append(f"Score: {score:.4f}\nTitle: {title}\nContent: {snippet}") return "\n\n".join(response_lines) @tool def add_to_knowledge_base(content: str, title: str) -> str: """Add a new document to the knowledge base.""" doc = Document(page_content=content, metadata={"title": title}) vector_store.add_documents([doc]) return f"Document '{title}' added successfully." # -------------------- 2. Vector store setup -------------------- client = QdrantClient(":memory:") client.create_collection( collection_name="knowledge", vectors_config=VectorParams(size=384, distance=Distance.COSINE), ) embeddings = OllamaEmbeddings(model="nomic-embed-text") vector_store = QdrantVectorStore( client=client, collection_name="knowledge", embedding=embeddings, ) # -------------------- 3. Text splitter -------------------- splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100) # -------------------- 4. Agent -------------------- agent = create_agent( model=ChatOllama(model="llama3", temperature=0.2), tools=[search_knowledge_base, add_to_knowledge_base], system_prompt="You are a helpful assistant that can search and add documents to the knowledge base.", ) # -------------------- 5. Load docs from directory -------------------- def load_docs_from_dir(directory: str) -> List[Document]: docs = [] for file_path in Path(directory).rglob("*.txt"): text = file_path.read_text(encoding="utf-8") chunks = splitter.split_text(text) for i, chunk in enumerate(chunks): docs.append( Document(page_content=chunk, metadata={"title": f"{file_path.name} #{i+1}"}) ) return docs def init_knowledge_base(directory: str): docs = load_docs_from_dir(directory) vector_store.add_documents(docs) # -------------------- 6. Interactive CLI -------------------- def main(): print("Initializing knowledge base...") init_knowledge_base("./docs") # replace with your docs folder print("Ready! Use /add, /search, or /quit.") while True: user_input = input("> ").strip() if not user_input: continue if user_input.lower() == "/quit": break if user_input.startswith("/add"): try: _, title, content = user_input.split(" ", 2) result = add_to_knowledge_base(content=content, title=title) print(result) except ValueError: print("Usage: /add <content>") elif user_input.startswith("/search"): query = user_input[len("/search"):].strip() if not query: print("Provide a search query.") continue result = search_knowledge_base(query=query, max_results=3) print(result) else: # Regular chat with agent response = agent.invoke({"messages": [{"role": "human", "content": user_input}]}) ai_msg = response["messages"][-1] print(ai_msg.content) if __name__ == "__main__": main()