Files
task-6a02e23da6fe2e4ac16acf65/main.py
T
2026-06-15 12:21:44 +00:00

148 lines
5.4 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.
"""
# main.py RAGagent with Qdrant, OpenRouter, and deepagents
# ----------------------------------------------------------
# 1. Imports and configuration
# 2. Qdrant vector store wrapper (embedding, add, search)
# 3. Text splitter (RecursiveCharacterTextSplitter)
# 4. LangChain tools: search_knowledge_base, add_to_knowledge_base
# 5. DeepAgent creation (create_deep_agent)
# 6. CLI client for /add, /search, /quit
# ----------------------------------------------------------
"""
import os
import asyncio
import json
from pathlib import Path
from typing import List
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
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
from langchain_qdrant import QdrantVectorStore
# ------------------------------------------------------------------
# 1. Configuration
# ------------------------------------------------------------------
# Load environment variables (e.g. OPENAI_API_KEY)
from dotenv import load_dotenv
load_dotenv()
# LLM OpenRouter (free tier)
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 OpenAI via OpenRouter
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
base_url="https://openrouter.ai/api/v1",
api_key=os.getenv("OPENAI_API_KEY"),
)
# Qdrant client assumes Qdrant is running locally on default port
qdrant_url = os.getenv("QDRANT_URL", "http://localhost:6333")
collection_name = "knowledge_base"
vector_store = QdrantVectorStore(
url=qdrant_url,
collection_name=collection_name,
embeddings=embeddings,
)
# Text splitter 1000 chars max, 200 overlap
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
# ------------------------------------------------------------------
# 2. Tools
# ------------------------------------------------------------------
@tool
def search_knowledge_base(query: str, max_results: int = 3) -> str:
"""Semantic search in the Qdrant knowledge base."""
docs: List[Document] = 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')}:\n{doc.page_content}" for doc in docs])
@tool
def add_to_knowledge_base(content: str, title: str = "Untitled") -> str:
"""Add a new document to the knowledge base.
The content is split into chunks before being stored.
"""
chunks = text_splitter.split_text(content)
docs = [Document(page_content=chunk, metadata={"title": title}) for chunk in chunks]
vector_store.add_documents(docs)
return f"Added {len(docs)} chunks for document '{title}'."
# ------------------------------------------------------------------
# 3. DeepAgent setup
# ------------------------------------------------------------------
backend = CompositeBackend([
LocalShellBackend(workspace_dir="./workspace"),
FilesystemBackend(),
])
agent = create_deep_agent(
model=llm,
tools=[search_knowledge_base, add_to_knowledge_base],
backend=backend,
system_prompt="You are a helpful assistant with access to a knowledge base. Use the provided tools to search and add information.",
)
# ------------------------------------------------------------------
# 4. CLI client
# ------------------------------------------------------------------
async def run_cli():
print("Welcome to the RAG Agent CLI. Commands: /add <title> <file>, /search <query>, /quit")
thread_id = "cli-session"
while True:
try:
user_input = input("> ")
except EOFError:
break
if not user_input:
continue
if user_input.startswith("/quit"):
print("Goodbye!")
break
if user_input.startswith("/add"):
parts = user_input.split(maxsplit=2)
if len(parts) < 3:
print("Usage: /add <title> <file_path>")
continue
title, file_path = parts[1], parts[2]
try:
content = Path(file_path).read_text(encoding="utf-8")
except Exception as e:
print(f"Error reading file: {e}")
continue
# Invoke tool directly
result = add_to_knowledge_base(content, title)
print(result)
continue
if user_input.startswith("/search"):
query = user_input[len("/search"):].strip()
if not query:
print("Usage: /search <query>")
continue
# Use agent to perform search via tool
response = await agent.ainvoke(
{"messages": [{"role": "user", "content": f"search {query}"}]},
{"configurable": {"thread_id": thread_id}},
)
print(response["messages"][-1].content)
continue
# Default: treat as normal user message
response = await agent.ainvoke(
{"messages": [{"role": "user", "content": user_input}]},
{"configurable": {"thread_id": thread_id}},
)
print(response["messages"][-1].content)
if __name__ == "__main__":
asyncio.run(run_cli())