An intelligent virtual assistant developed to help internal employees at a ficticial e-commerce called BimBam Buy. This project uses LLMs (Large Language Models) to answer textual questions based on corporate documents (such as policies and manuals) via RAG (Retrieval-Augmented Generation), and performs analytical queries on data and metrics through Text-to-SQL in a high-performance database.
Deployment Note: CLICK HERE TO ACCESS THE WEBSITE.
- Key Features
- Technologies Used
- Project Structure
- Setup and Execution
- How the Agent Works (Core)
- Some Design Choices
- Chat ScreenShot
- Dual-Engine Querying:
- RAG (Retrieval-Augmented Generation): Searches unstructured textual documents (e.g., PDFs for warranties, payment methods) using ChromaDB and Cohere embeddings.
- SQL Agent: Executes read-only queries against a DuckDB database containing sales data to answer numerical and analytical questions.
- Interactive Web Interface: A lightweight frontend built with HTML/JS/CSS that supports Markdown rendering, code highlighting, and dynamic charting using Plotly.
- Conversational Memory: Retains full message history within the same session, enabling multi-turn context awareness and natural follow-up queries.
- Streaming Responses: Provides real-time streaming output using FastAPI and LangGraph.
- Intelligent Routing: The Groq-powered LangChain agent (using the Qwen model) automatically decides which tool to use based on the user's natural language input.
- Rate Limiting: Built-in rate limiting using
slowapito prevent API abuse.
- Backend & AI: Python 3.12, FastAPI, LangChain, ChatGroq (currently
qwen/qwen3.6-27b); - Data & Databases: DuckDB for the sales data, ChromaDB for the embeddings vector store;
- Frontend: HTML5, CSS3, JavaScript (Vanilla), Plotly.js, Marked.js for markdown formatting of the LLM's output;
- Infrastructure: Docker for deploying on Oracle Cloud, uv for managing Python dependencies;
app/
├── data/
│ ├── docs/ # PDFs with organizational rules and manuals
│ └── sales/ # Sales database directory (CSV → DuckDB)
├── rag/
│ └── vectorstore/ # Automatically generated persistent vector database (Chroma)
├── static/ # Frontend static files (HTML, CSS, JS, logo)
├── tools/
│ └── tools.py # LangChain tools definition (search_docs, query_sales_sql)
├── agent.py # Main agent configuration, system prompt, and session memory
├── ingest_sales.py # ETL script for data cleaning and conversion (CSV to DuckDB)
├── main.py # FastAPI API entrypoint and chat endpoints
└── vector_store.py # PDF processing and chunk synchronization with ChromaDB
- Docker and Docker Compose installed (Recommended).
- A Groq API Key (
GROQ_API_KEY). - A Cohere API Key (
COHERE_API_KEY) for embedding generation.
Copy the .env.example file to .env and fill in your API keys:
cp .env.example .envRequired keys:
GROQ_API_KEYCOHERE_API_KEY
The project includes a docker-compose.yml and an entrypoint.sh script configured to process and structure the data and initialize the application automatically.
At the root of the repository, run:
docker-compose up --buildAfter the docker image is built, the entrypoint.sh will automatically call:
ingest_sales.py, which will usesales.csvto ingest sales data intosales.duckdb;vector_store.py, which will use the pdfs fromapp/data/docs/to generate aChromaDBvector store inapp/rag/vectorstore;
The interactive web application will be immediately available at: http://localhost:8000
If you prefer to run without Docker, using the uv package manager:
# Sync and install all dependencies from pyproject.toml
uv sync
# Optional: Generate the base data and embeddings manually (if it's the first run)
uv run python -m app.ingest_sales
uv run python -m app.vector_store
# Start the FastAPI server
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000 --reloadThe system orchestrator is defined in the agent.py file. Based on the user's intent:
- Operational/Textual Questions: Trigger the
buscar_docstool, which performs a semantic similarity search (Top-K) in the ChromaDB base to bring context to the response. - Analytical Questions: Trigger the
consultar_vendas_sqltool, which autonomously generates a query supported by DuckDB, queries the highly efficient.parquetfile, and consolidates the numerical data. - Graphical Representation: If the user requests a chart in the response, the LLM is instructed by its System Prompt to return a special
json-chartMarkdown code block. The JavaScript inchat.jsintercepts this block and creates an interactive chart using the Plotly library without requiring extra server processing.
- LLM Providers:
CohereandGroqwere chosen as they provide a free tier API for LLM access.- For that reason, the agent is limited both via
max_tokensandrecursion_limit.
- For that reason, the agent is limited both via
- Embeddings: The text files are split with
RecursiveCharacterTextSplitterwith(chunk_size=1000, chunk_overlap=200). Although this was an arbitrary first test, it managed to return satisfying results. In the future I might look into optimizing it, if my goal is to go deeper into RAG systems. - Sales Data: The source of the sales data is the Superstore Sales Dataset. I took the liberty to make some changes along with the data cleaning inside
ingest_sales.pyto make the interactions more interesting, like appending the dates to be more recent, adding cents to the value column, and other changes to remove confusion during agent interactions. - LangChain: The agent was designed using best practices suggested by the LangChain docs after v1 changes, like using checkpointer for message history and the
create_agentmodule. - Agent Memory: In-memory storage that holds a limited amount of thread ids, and with the agent.touch_thread() function I can control the number of threads to mitigate memory leaks over time.
- Agent Security & Safety: Database connections to DuckDB are strictly set to
read_onlywith host file-system access disabled, preventing unauthorized schema mutations or file access during text-to-SQL execution.
