mirror of
https://github.com/Shubhamsaboo/awesome-llm-apps.git
synced 2026-03-09 07:25:00 -05:00
LLM Powered Hybrid Search RAG Assistant - CLAUDE
This commit is contained in:
2
rag_tutorials/hybrid_search_rag/.gitignore
vendored
Normal file
2
rag_tutorials/hybrid_search_rag/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.flashrank_cache
|
||||
|
||||
93
rag_tutorials/hybrid_search_rag/README.md
Normal file
93
rag_tutorials/hybrid_search_rag/README.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# LLM Hybrid Search-RAG Assistant - Claude 🤖
|
||||
|
||||
A powerful document Q&A application that leverages Hybrid Search (RAG) and Claude's advanced language capabilities to provide comprehensive answers. Built with RAGLite for robust document processing and retrieval, and Streamlit for an intuitive chat interface, this system seamlessly combines document-specific knowledge with Claude's general intelligence to deliver accurate and contextual responses.
|
||||
|
||||
## Features
|
||||
|
||||
- **Hybrid Search Question Answering**
|
||||
- RAG-based answers for document-specific queries
|
||||
- Fallback to Claude for general knowledge questions
|
||||
|
||||
- **Document Processing**:
|
||||
- PDF document upload and processing
|
||||
- Automatic text chunking and embedding
|
||||
- Hybrid search combining semantic and keyword matching
|
||||
- Reranking for better context selection
|
||||
|
||||
- **Multi-Model Integration**:
|
||||
- Claude for text generation - tested with Claude 3 Opus
|
||||
- OpenAI for embeddings - tested with text-embedding-3-large
|
||||
- Cohere for reranking - tested with Cohere 3.5 reranker
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You'll need the following API keys and database setup:
|
||||
|
||||
1. **Database**: Create a free PostgreSQL database at [Neon](https://neon.tech):
|
||||
- Sign up/Login at Neon
|
||||
- Create a new project
|
||||
- Copy the connection string (looks like: `postgresql://user:pass@ep-xyz.region.aws.neon.tech/dbname`)
|
||||
|
||||
2. **API Keys**:
|
||||
- [OpenAI API key](https://platform.openai.com/api-keys) for embeddings
|
||||
- [Anthropic API key](https://console.anthropic.com/settings/keys) for Claude
|
||||
- [Cohere API key](https://dashboard.cohere.com/api-keys) for reranking
|
||||
|
||||
## How to get Started?
|
||||
|
||||
1. **Clone the Repository**:
|
||||
```bash
|
||||
git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git
|
||||
cd rag_tutorials/hybrid_search_rag
|
||||
```
|
||||
|
||||
2. **Install Dependencies**:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. **Install spaCy Model**:
|
||||
```bash
|
||||
pip install https://github.com/explosion/spacy-models/releases/download/xx_sent_ud_sm-3.7.0/xx_sent_ud_sm-3.7.0-py3-none-any.whl
|
||||
```
|
||||
|
||||
4. **Run the Application**:
|
||||
```bash
|
||||
streamlit run main.py
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. Start the application
|
||||
2. Enter your API keys in the sidebar:
|
||||
- OpenAI API key
|
||||
- Anthropic API key
|
||||
- Cohere API key
|
||||
- Database URL (optional, defaults to SQLite)
|
||||
3. Click "Save Configuration"
|
||||
4. Upload PDF documents
|
||||
5. Start asking questions!
|
||||
- Document-specific questions will use RAG
|
||||
- General questions will use Claude directly
|
||||
|
||||
## Database Options
|
||||
|
||||
The application supports multiple database backends:
|
||||
|
||||
- **PostgreSQL** (Recommended):
|
||||
- Create a free serverless PostgreSQL database at [Neon](https://neon.tech)
|
||||
- Get instant provisioning and scale-to-zero capability
|
||||
- Connection string format: `postgresql://user:pass@ep-xyz.region.aws.neon.tech/dbname`
|
||||
|
||||
- **MySQL**:
|
||||
```
|
||||
mysql://user:pass@host:port/db
|
||||
```
|
||||
- **SQLite** (Local development):
|
||||
```
|
||||
sqlite:///path/to/db.sqlite
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
175
rag_tutorials/hybrid_search_rag/main.py
Normal file
175
rag_tutorials/hybrid_search_rag/main.py
Normal file
@@ -0,0 +1,175 @@
|
||||
import os
|
||||
import logging
|
||||
import streamlit as st
|
||||
from raglite import RAGLiteConfig, insert_document, hybrid_search, retrieve_chunks, rerank_chunks, rag
|
||||
from rerankers import Reranker
|
||||
from typing import List
|
||||
from pathlib import Path
|
||||
import anthropic
|
||||
import time
|
||||
import warnings
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
warnings.filterwarnings("ignore", message=".*torch.classes.*")
|
||||
|
||||
RAG_SYSTEM_PROMPT = """
|
||||
You are a friendly and knowledgeable assistant that provides complete and insightful answers.
|
||||
Answer the user's question using only the context below.
|
||||
When responding, you MUST NOT reference the existence of the context, directly or indirectly.
|
||||
Instead, you MUST treat the context as if its contents are entirely part of your working memory.
|
||||
""".strip()
|
||||
|
||||
def initialize_config(openai_key: str, anthropic_key: str, cohere_key: str, db_url: str) -> RAGLiteConfig:
|
||||
try:
|
||||
os.environ["OPENAI_API_KEY"] = openai_key
|
||||
os.environ["ANTHROPIC_API_KEY"] = anthropic_key
|
||||
os.environ["COHERE_API_KEY"] = cohere_key
|
||||
|
||||
return RAGLiteConfig(
|
||||
db_url=db_url,
|
||||
llm="claude-3-opus-20240229",
|
||||
embedder="text-embedding-3-large",
|
||||
embedder_normalize=True,
|
||||
chunk_max_size=2000,
|
||||
embedder_sentence_window_size=2,
|
||||
reranker=Reranker("cohere", api_key=cohere_key, lang="en")
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValueError(f"Configuration error: {e}")
|
||||
|
||||
def process_document(file_path: str) -> bool:
|
||||
try:
|
||||
if not st.session_state.get('my_config'):
|
||||
raise ValueError("Configuration not initialized")
|
||||
insert_document(Path(file_path), config=st.session_state.my_config)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing document: {str(e)}")
|
||||
return False
|
||||
|
||||
def perform_search(query: str) -> List[dict]:
|
||||
try:
|
||||
chunk_ids, scores = hybrid_search(query, num_results=10, config=st.session_state.my_config)
|
||||
if not chunk_ids:
|
||||
return []
|
||||
chunks = retrieve_chunks(chunk_ids, config=st.session_state.my_config)
|
||||
return rerank_chunks(query, chunks, config=st.session_state.my_config)
|
||||
except Exception as e:
|
||||
logger.error(f"Search error: {str(e)}")
|
||||
return []
|
||||
|
||||
def handle_fallback(query: str) -> str:
|
||||
try:
|
||||
client = anthropic.Anthropic(api_key=st.session_state.user_env["ANTHROPIC_API_KEY"])
|
||||
system_prompt = """You are a helpful AI assistant. When you don't know something,
|
||||
be honest about it. Provide clear, concise, and accurate responses. If the question
|
||||
is not related to any specific document, use your general knowledge to answer."""
|
||||
|
||||
message = client.messages.create(
|
||||
model="claude-3-sonnet-20240229",
|
||||
max_tokens=1024,
|
||||
system=system_prompt,
|
||||
messages=[{"role": "user", "content": query}],
|
||||
temperature=0.7
|
||||
)
|
||||
return message.content[0].text
|
||||
except Exception as e:
|
||||
logger.error(f"Fallback error: {str(e)}")
|
||||
st.error(f"Fallback error: {str(e)}") # Show error in UI
|
||||
return "I apologize, but I encountered an error while processing your request. Please try again."
|
||||
|
||||
def main():
|
||||
st.set_page_config(page_title="LLM-Powered Hybrid Search-RAG Assistant", layout="wide")
|
||||
|
||||
for state_var in ['chat_history', 'documents_loaded', 'my_config', 'user_env']:
|
||||
if state_var not in st.session_state:
|
||||
st.session_state[state_var] = [] if state_var == 'chat_history' else False if state_var == 'documents_loaded' else None if state_var == 'my_config' else {}
|
||||
|
||||
with st.sidebar:
|
||||
st.title("Configuration")
|
||||
openai_key = st.text_input("OpenAI API Key", value=st.session_state.get('openai_key', ''), type="password", placeholder="sk-...")
|
||||
anthropic_key = st.text_input("Anthropic API Key", value=st.session_state.get('anthropic_key', ''), type="password", placeholder="sk-ant-...")
|
||||
cohere_key = st.text_input("Cohere API Key", value=st.session_state.get('cohere_key', ''), type="password", placeholder="Enter Cohere key")
|
||||
db_url = st.text_input("Database URL", value=st.session_state.get('db_url', 'sqlite:///raglite.sqlite'), placeholder="sqlite:///raglite.sqlite")
|
||||
|
||||
if st.button("Save Configuration"):
|
||||
try:
|
||||
if not all([openai_key, anthropic_key, cohere_key, db_url]):
|
||||
st.error("All fields are required!")
|
||||
return
|
||||
|
||||
for key, value in {'openai_key': openai_key, 'anthropic_key': anthropic_key, 'cohere_key': cohere_key, 'db_url': db_url}.items():
|
||||
st.session_state[key] = value
|
||||
|
||||
st.session_state.my_config = initialize_config(openai_key=openai_key, anthropic_key=anthropic_key, cohere_key=cohere_key, db_url=db_url)
|
||||
st.session_state.user_env = {"ANTHROPIC_API_KEY": anthropic_key}
|
||||
st.success("Configuration saved successfully!")
|
||||
except Exception as e:
|
||||
st.error(f"Configuration error: {str(e)}")
|
||||
|
||||
st.title("LLM-Powered Hybrid Search-RAG Assistant")
|
||||
|
||||
if st.session_state.my_config:
|
||||
uploaded_files = st.file_uploader("Upload PDF documents", type=["pdf"], accept_multiple_files=True, key="pdf_uploader")
|
||||
|
||||
if uploaded_files:
|
||||
success = False
|
||||
for uploaded_file in uploaded_files:
|
||||
with st.spinner(f"Processing {uploaded_file.name}..."):
|
||||
temp_path = f"temp_{uploaded_file.name}"
|
||||
with open(temp_path, "wb") as f:
|
||||
f.write(uploaded_file.getvalue())
|
||||
|
||||
if process_document(temp_path):
|
||||
st.success(f"Successfully processed: {uploaded_file.name}")
|
||||
success = True
|
||||
else:
|
||||
st.error(f"Failed to process: {uploaded_file.name}")
|
||||
os.remove(temp_path)
|
||||
|
||||
if success:
|
||||
st.session_state.documents_loaded = True
|
||||
st.success("Documents are ready! You can now ask questions about them.")
|
||||
|
||||
if st.session_state.documents_loaded:
|
||||
for msg in st.session_state.chat_history:
|
||||
with st.chat_message("user"): st.write(msg[0])
|
||||
with st.chat_message("assistant"): st.write(msg[1])
|
||||
|
||||
user_input = st.chat_input("Ask a question about the documents...")
|
||||
if user_input:
|
||||
with st.chat_message("user"): st.write(user_input)
|
||||
with st.chat_message("assistant"):
|
||||
message_placeholder = st.empty()
|
||||
try:
|
||||
reranked_chunks = perform_search(query=user_input)
|
||||
if not reranked_chunks or len(reranked_chunks) == 0:
|
||||
logger.info("No relevant documents found. Falling back to Claude.")
|
||||
st.info("No relevant documents found. Using general knowledge to answer.")
|
||||
full_response = handle_fallback(user_input)
|
||||
else:
|
||||
formatted_messages = [{"role": "user" if i % 2 == 0 else "assistant", "content": msg}
|
||||
for i, msg in enumerate([m for pair in st.session_state.chat_history for m in pair]) if msg]
|
||||
|
||||
response_stream = rag(prompt=user_input,
|
||||
system_prompt=RAG_SYSTEM_PROMPT,
|
||||
search=hybrid_search,
|
||||
messages=formatted_messages,
|
||||
max_contexts=5,
|
||||
config=st.session_state.my_config)
|
||||
|
||||
full_response = ""
|
||||
for chunk in response_stream:
|
||||
full_response += chunk
|
||||
message_placeholder.markdown(full_response + "▌")
|
||||
|
||||
message_placeholder.markdown(full_response)
|
||||
st.session_state.chat_history.append((user_input, full_response))
|
||||
except Exception as e:
|
||||
st.error(f"Error: {str(e)}")
|
||||
else:
|
||||
st.info("Please configure your API keys and upload documents to get started." if not st.session_state.my_config else "Please upload some documents to get started.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
12
rag_tutorials/hybrid_search_rag/requirements.txt
Normal file
12
rag_tutorials/hybrid_search_rag/requirements.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
raglite==0.2.1
|
||||
pydantic==2.10.1
|
||||
sqlalchemy>=2.0.0
|
||||
psycopg2-binary>=2.9.9
|
||||
openai>=1.0.0
|
||||
cohere>=4.37
|
||||
pypdf>=3.0.0
|
||||
python-dotenv>=1.0.0
|
||||
rerankers==0.6.0
|
||||
spacy>=3.7.0
|
||||
streamlit
|
||||
anthropic
|
||||
Reference in New Issue
Block a user