From 6d9af3917e3a0556d58aa5f9faaaffb76859f70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D0=B5=D0=BC=20=D0=92=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87=20=D0=91=D0=B0?= =?UTF-8?q?=D0=B1=D0=B0=D0=B9=D0=BA=D0=B8=D0=BD?= Date: Thu, 28 May 2026 16:35:26 +0000 Subject: [PATCH] feat: solution for 6a02e23da6fe2e4ac16acf65 --- .../6a02e23da6fe2e4ac16acf65/solution.py | 132 ++++++++++-------- 1 file changed, 73 insertions(+), 59 deletions(-) diff --git a/solutions/6a02e23da6fe2e4ac16acf65/solution.py b/solutions/6a02e23da6fe2e4ac16acf65/solution.py index b12048d..10eebc5 100644 --- a/solutions/6a02e23da6fe2e4ac16acf65/solution.py +++ b/solutions/6a02e23da6fe2e4ac16acf65/solution.py @@ -1,23 +1,21 @@ -from langchain_openai import ChatOpenAI -from pydantic import SecretStr -from langchain.tools import tool +from pathlib import Path +import sys + +from langchain_ollama import Ollama, OllamaEmbeddings from langchain_qdrant import QdrantVectorStore from qdrant_client import QdrantClient from qdrant_client.http.models import Distance, VectorParams -from langchain_ollama import OllamaEmbeddings -from langchain_core.documents import Document from langchain_text_splitters import RecursiveCharacterTextSplitter +from langchain.tools import tool from langchain.agents import create_agent +from langchain_core.documents import Document -# ---------- LLM ---------- -llm = ChatOpenAI( - model="openai/gpt-oss-20b", - base_url='https://platform.brojs.ru/jrnl-bh/api/inference/v1', - api_key=SecretStr("jrnl_30283ab953615cbb6846ff9940a1eedce0b76d7b2f59a2394f29e74643e6a90d"), +# ---------- LLM and embeddings ---------- +llm = Ollama( + model="llama3", # local Ollama model temperature=0.7, ) -# ---------- Embeddings ---------- embeddings = OllamaEmbeddings(model="nomic-embed-text") # ---------- Qdrant client ---------- @@ -26,83 +24,99 @@ client.create_collection( collection_name="knowledge_base", vectors_config=VectorParams(size=embeddings.embedding_size, distance=Distance.COSINE), ) -vector_store = QdrantVectorStore( - client=client, - collection_name="knowledge_base", - embedding=embeddings, -) +vector_store = QdrantVectorStore(client=client, collection_name="knowledge_base", embedding=embeddings) # ---------- Text splitter ---------- -splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) +splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) # ---------- Tools ---------- @tool def search_knowledge_base(query: str, max_results: int = 5) -> str: """Search the knowledge base for relevant documents.""" - docs_with_score = vector_store.similarity_search_with_score(query, k=max_results) - if not docs_with_score: - return "No results found." - result_lines = [ - f"{i+1}. {doc.page_content[:200]}..." for i, (doc, _) in enumerate(docs_with_score) - ] - return "\n".join(result_lines) + results = vector_store.similarity_search_with_score(query, k=max_results) + if not results: + return "No relevant information found." + out_lines = [] + for doc, score in results: + title = doc.metadata.get("title", "Untitled") + content_preview = doc.page_content[:200] + ("..." if len(doc.page_content) > 200 else "") + out_lines.append(f"Score: {score:.3f}\nTitle: {title}\nContent: {content_preview}") + return "\n\n".join(out_lines) @tool -def add_to_knowledge_base(content: str, title: str) -> str: +def add_to_knowledge_base(content: str, title: str = "Untitled") -> str: """Add a new document to the knowledge base.""" chunks = splitter.split_text(content) - documents = [Document(page_content=chunk, metadata={"title": title}) for chunk in chunks] + documents = [ + Document(page_content=chunk, metadata={"title": f"{title} (part {i+1})"}) + for i, chunk in enumerate(chunks) + ] vector_store.add_documents(documents) - return f"Added {len(documents)} chunks from '{title}'." + return f"Added {len(chunks)} chunks to the knowledge base under title '{title}'." # ---------- Agent ---------- +system_prompt = """ +You are an assistant that can search and add information to a local knowledge base. +Use the tools `search_knowledge_base` and `add_to_knowledge_base` as needed. +""" + agent = create_agent( model=llm, tools=[search_knowledge_base, add_to_knowledge_base], - system_prompt="You are an assistant that can search and add documents to the knowledge base.", + system_prompt=system_prompt, ) # ---------- CLI ---------- +def load_documents_from_dir(directory: Path): + for file_path in directory.rglob("*"): + if file_path.is_file() and file_path.suffix.lower() in {".txt", ".md"}: + content = file_path.read_text(encoding="utf-8") + title = file_path.stem + add_to_knowledge_base(content=content, title=title) + def main(): - print("RAG Agent CLI. Commands: /add <content>, /search <query>, /quit") + # Load initial docs if provided as first arg + if len(sys.argv) > 1: + load_documents_from_dir(Path(sys.argv[1])) + + print("Agent ready. Commands: /add <title> <file>, /search <query>, /quit") while True: - try: - inp = input("> ").strip() - except EOFError: - break - if not inp: + user_input = input("> ").strip() + if not user_input: continue - if inp.lower() in ("quit", "exit"): - print("Bye!") + if user_input.lower() in {"quit", "exit"} or user_input == "/quit": + print("Goodbye!") break - # Add document - if inp.startswith("/add "): - parts = inp[5:].split(None, 1) - if len(parts) != 2: - print("Usage: /add <title> <content>") + if user_input.startswith("/add"): + parts = user_input.split(maxsplit=2) + if len(parts) < 3: + print("Usage: /add <title> <file_path>") continue - title, content = parts - res = agent.invoke({"messages": [{"role": "human", "content": f"Add document '{title}'"}]}) - for msg in res["messages"]: - if hasattr(msg, "tool_calls"): - print(msg.content) - continue + title, file_path = parts[1], Path(parts[2]) + if not file_path.is_file(): + print(f"File {file_path} does not exist.") + continue + content = file_path.read_text(encoding="utf-8") + result = add_to_knowledge_base(content=content, title=title) + print(result) - # Search query - if inp.startswith("/search "): - query = inp[8:].strip() - res = agent.invoke({"messages": [{"role": "human", "content": f"Search: {query}"}]}) - for msg in res["messages"]: - if hasattr(msg, "tool_calls"): + elif user_input.startswith("/search"): + query = user_input[len("/search"):].strip() + if not query: + print("Usage: /search <query>") + continue + response = agent.invoke({"messages": [{"role": "human", "content": query}]}) + for msg in response["messages"]: + if hasattr(msg, "content"): print(msg.content) - continue - # General chat - res = agent.invoke({"messages": [{"role": "human", "content": inp}]}) - for msg in res["messages"]: - if hasattr(msg, "content"): - print(msg.content) + else: + # Regular chat with agent + response = agent.invoke({"messages": [{"role": "human", "content": user_input}]}) + for msg in response["messages"]: + if hasattr(msg, "content"): + print(msg.content) if __name__ == "__main__": main() \ No newline at end of file