Building a Personalized Bank FAQ Chat Agent with React.js, RAG, LLM, and Redis

Providing efficient and informative customer support is crucial for any financial institution. A well-designed FAQ chat agent can significantly enhance the user experience by offering instant answers to common queries. This article provides a comprehensive guide to building a personalized bank FAQ chat agent using React.js for the frontend, Retrieval-Augmented Generation () and a Large Language Model () for intelligent responses, and for robust session management and personalized chat history.

I. The Power of Intelligent Chat for Bank FAQs

Traditional FAQ pages can be cumbersome. An intelligent chat agent offers a more interactive and efficient way to find answers by understanding natural language queries and providing contextually relevant information drawn from the bank’s knowledge base. Leveraging Redis for session management allows for personalized interactions by remembering past conversations within a session.

II. Core Components

  1. Frontend (React.js): User interface for interaction.
  2. Backend ( with Flask): Orchestrates RAG, LLM, and session/chat history (Redis).
  3. Knowledge Source: Bank’s FAQ documents, policies, website content.
  4. Embedding Model: Converts text to vectors (e.g., OpenAI Embeddings).
  5. Vector : Stores and indexes vector embeddings (e.g., ChromaDB).
  6. Large Language Model (LLM): Generates responses (e.g., OpenAI’s GPT models).
  7. Redis: In-memory data store for sessions and chat history.
  8. Flask-Session: Flask extension for Redis-backed session management.
  9. LangChain: Framework for streamlining RAG and LLM interactions.

III. Backend Implementation (Python with Flask, Redis, and RAG)

Python

from flask import Flask, request, jsonify, session
from flask_session import Session
from redis import Redis
import uuid
import json
from flask_cors import CORS
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
from langchain.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import os

# --- Configuration ---
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 0
VECTOR_DB_PATH = "./bank_faq_db"
FAQ_DOCS_PATH = "./bank_faq_docs"

app = Flask(__name__)
CORS(app)
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_PERMANENT"] = True
app.config["SESSION_REDIS"] = Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB)
app.secret_key = "your_bank_faq_secret_key"  # Replace with a strong key
sess = Session(app)

# --- Initialize RAG Components ---
embeddings = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY)
if not os.path.exists(VECTOR_DB_PATH):
    # --- Data Ingestion (Run once to create the vector database) ---
    if not os.path.exists(FAQ_DOCS_PATH):
        os.makedirs(FAQ_DOCS_PATH)
        print(f"Please place your bank's FAQ documents (e.g., .txt files) in '{FAQ_DOCS_PATH}' and rerun the backend to process them.")
        vectordb = None
    else:
        loader = DirectoryLoader(FAQ_DOCS_PATH, glob="**/*.txt", loader_cls=TextLoader)
        documents = loader.load()
        text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
        chunks = text_splitter.split_documents(documents)
        vectordb = Chroma.from_documents(chunks, embeddings, persist_directory=VECTOR_DB_PATH)
        vectordb.persist()
else:
    vectordb = Chroma(persist_directory=VECTOR_DB_PATH, embedding_function=embeddings)

qa_chain = RetrievalQA.from_chain_type(llm=OpenAI(openai_api_key=OPENAI_API_KEY), chain_type="stuff", retriever=vectordb.as_retriever() if vectordb else None)

# --- Redis Helper Functions ---
def store_message(session_id, sender, text):
    redis_client = app.config["SESSION_REDIS"]
    key = f"bank_faq_chat:{session_id}"
    message = {"sender": sender, "text": text}
    redis_client.rpush(key, json.dumps(message))

def get_history(session_id):
    redis_client = app.config["SESSION_REDIS"]
    key = f"bank_faq_chat:{session_id}"
    history_bytes = redis_client.lrange(key, 0, -1)
    return [json.loads(hb.decode('utf-8')) for hb in history_bytes]

# ---  Endpoints ---
@app.route('/create_session')
def create_session():
    if 'bank_faq_session_id' not in session:
        session_id = str(uuid.uuid4())
        session['bank_faq_session_id'] = session_id
        return jsonify({"session_id": session_id})
    else:
        return jsonify({"session_id": session['bank_faq_session_id']})

@app.route('/get_chat_history')
def get_chat_history():
    if 'bank_faq_session_id' not in session:
        return jsonify({"history": []})
    session_id = session['bank_faq_session_id']
    history = get_history(session_id)
    return jsonify({"history": history})

@app.route('/bank_faq/chat', methods=['POST'])
def bank_faq_chat():
    if 'bank_faq_session_id' not in session:
        return jsonify({"error": "No active session."}), 401

    session_id = session['bank_faq_session_id']
    data = request.get_json()
    user_message = data.get('message')

    if not user_message:
        return jsonify({"error": "Message is required"}), 400

    store_message(session_id, "user", user_message)

    try:
        if qa_chain:
            response = qa_chain.run(user_message)
            store_message(session_id, "agent", response)
            return jsonify({"response": response})
        else:
            error_message = "Bank FAQ knowledge base not initialized. Please ensure FAQ documents are present and the backend is run to process them."
            store_message(session_id, "agent", error_message)
            return jsonify({"error": error_message}), 500

    except Exception as e:
        error_message = f"Sorry, I encountered an error: {str(e)}"
        store_message(session_id, "agent", error_message)
        return jsonify({"error": error_message}), 500

if __name__ == '__main__':
    print("Make sure you have your OpenAI API key set as an environment variable (OPENAI_API_KEY).")
    print(f"Place bank FAQ documents in '{FAQ_DOCS_PATH}' for processing.")
    app.run(debug=True)

IV. Frontend Implementation (React.js)

JavaScript

import React, { useState, useEffect, useRef } from 'react';

function BankFAQChat() {
  const [messages, setMessages] = useState([]);
  const [inputValue, setInputValue] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const chatWindowRef = useRef(null);
  const [sessionId, setSessionId] = useState(null);

  useEffect(() => {
    const fetchSessionAndHistory = async () => {
      try {
        const sessionResponse = await fetch('/create_session');
        if (sessionResponse.ok) {
          const sessionData = await sessionResponse.json();
          setSessionId(sessionData.session_id);
          if (sessionData.session_id) {
            const historyResponse = await fetch('/get_chat_history');
            if (historyResponse.ok) {
              const historyData = await historyResponse.json();
              setMessages(historyData.history);
            } else {
              console.error('Failed to fetch chat history:', historyResponse.status);
            }
          }
        } else {
          console.error('Failed to create/retrieve session:', sessionResponse.status);
        }
      } catch (error) {
        console.error('Error fetching session and history:', error);
      }
    };

    fetchSessionAndHistory();
  }, []);

  useEffect(() => {
    if (chatWindowRef.current) {
      chatWindowRef.current.scrollTop = chatWindowRef.current.scrollHeight;
    }
  }, [messages]);

  const sendMessage = async () => {
    if (inputValue.trim() && sessionId) {
      const newMessage = { sender: 'user', text: inputValue };
      setMessages([...messages, newMessage]);
      setInputValue('');
      setIsLoading(true);

      try {
        const response = await fetch('/bank_faq/chat', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ message: inputValue }),
        });

        if (response.ok) {
          const data = await response.json();
          const agentMessage = { sender: 'agent', text: data.response };
          setMessages([...messages, newMessage, agentMessage]);
        } else {
          console.error('Error sending message:', response.status);
          const errorMessage = { sender: 'agent', text: 'Sorry, I encountered an error.' };
          setMessages([...messages, newMessage, errorMessage]);
        }
      } catch (error) {
        console.error('Error sending message:', error);
        const errorMessage = { sender: 'agent', text: 'Sorry, I encountered an error.' };
      } finally {
        setIsLoading(false);
      }
    }
  };

  return (
    <div className="chat-container" style={styles.chatContainer}>
      <div ref={chatWindowRef} className="message-list" style={styles.messageList}>
        {messages.map((msg, index) => (
          <div key={index} className={`message ${msg.sender}`} style={msg.sender === 'user' ? styles.userMessage : styles.agentMessage}>
            {msg.text}
          </div>
        ))}
        {isLoading && <div className="message agent" style={styles.agentMessage}>Thinking...</div>}
      </div>
      <div className="input-area" style={styles.inputArea}>
        <input
          type="text"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          onKeyPress={(event) => event.key === 'Enter' && sendMessage()}
          placeholder="Ask a bank FAQ..."
          style={styles.input}
        />
        <button onClick={sendMessage} disabled={isLoading} style={styles.button}>Send</button>
      </div>
    </div>
  );
}

const styles = {
  chatContainer: { width: '400px', margin: '20px auto', border: '1px solid #ccc', borderRadius: '5px', overflow: 'hidden', display: 'flex', flexDirection: 'column' },
  messageList: { flexGrow: 1, padding: '10px', overflowY: 'auto' },
  userMessage: { backgroundColor: '#e0f7fa', padding: '8px', borderRadius: '5px', marginBottom: '5px', alignSelf: 'flex-end', maxWidth: '70%', wordBreak: 'break-word' },
  agentMessage: { backgroundColor: '#f5f5f5', padding: '8px', borderRadius: '5px', marginBottom: '5px', alignSelf: 'flex-start', maxWidth: '70%', wordBreak: 'break-word' },
  inputArea: { padding: '10px', borderTop: '1px solid #eee', display: 'flex' },
  input: { flexGrow: 1, padding: '8px', borderRadius: '3px', border: '1px solid #ddd', marginRight: '10px' },
  button: { padding: '8px 15px', borderRadius: '3px', border: 'none', backgroundColor: '#00bcd4', color: 'white', cursor: 'pointer', fontWeight: 'bold', '&:disabled': { backgroundColor: '#ccc', cursor: 'not-allowed' } },
};

export default BankFAQChat;

V. Running the Application

  1. Install Backend Dependencies: pip install Flask flask-session redis flask-cors langchain openai chromadb
  2. Set Up OpenAI API Key: Ensure you have an OpenAI API key and set it as an environment variable named OPENAI_API_KEY.
  3. Prepare Bank FAQ Documents: Create a directory ./bank_faq_docs and place your bank’s FAQ documents (as .txt files) inside.
  4. Run Backend (Initial Data Ingestion): Run the backend script once. It will attempt to create the vector database if it doesn’t exist. Ensure your FAQ documents are in the specified directory.
  5. Ensure Redis is Running: Start your Redis server.
  6. Run the Backend: Execute the backend script.
  7. Running the React Frontend
  8. Here are the instructions to get the React frontend of the Bank FAQ Chat Agent running:
  9. Navigate to your React project directory in your terminal. If you haven’t created a React project yet, you can do so using Create React App or a similar tool: Bashnpx create-react-app bank-faq-frontend cd bank-faq-frontend
  10. Install Dependencies: If you started with a fresh React project, you’ll need to install any necessary dependencies (though this example uses built-in React features like useState and useEffect). If you have a pre-existing project, ensure you have react and react-dom installed. Bashnpm install # Or yarn install
  11. Replace src/App.js (or your main component file): Open the src/App.js file (or the main component where you want to place the chat agent) and replace its entire content with the React code provided in the previous section. You might need to adjust the import path if your component is named differently or located in a different directory. For example, if you save the code in a file named BankFAQChat.js within a components folder, you would import it in App.js like this: JavaScriptimport BankFAQChat from './components/BankFAQChat'; function App() { return ( <div> <BankFAQChat /> </div> ); } export default App;
  12. Start the Development Server: Run the React development server from your terminal within the React project directory: Bashnpm start # Or yarn start This command will typically open your React application in a new tab in your web browser, usually at http://localhost:3000.
  13. Interact with the Chat Agent: Once the frontend is running, you should see the chat interface. You can type your bank-related questions in the input field and click the “Send” button (or press Enter) to send them to the backend. The agent’s responses and the conversation history will be displayed in the chat window.
  14. Important Notes for the Frontend:
  15. Backend URL: Ensure that the fetch calls in the BankFAQChat component (/create_session and /bank_faq/chat) are pointing to the correct URL where your Flask backend is running. If your backend is running on a different host or port than http://localhost:5000, you’ll need to update these URLs accordingly.
  16. Styling: The provided styles object in the React component offers basic styling. You can customize this further or use a CSS-in-JS library (like Styled Components) or a CSS framework (like Tailwind CSS or Material UI) to enhance the visual appearance of the chat agent.
  17. Error Handling: The frontend includes basic console.error logging for API request failures. You might want to implement more user-friendly error messages within the UI.
  18. Session Management: The frontend automatically fetches or creates a session on mount. The sessionId is managed in the component’s state.
  19. Create React App: Create a new React application if you haven’t already.
  20. Replace Frontend Code: Replace the content of your main React component file with the provided BankFAQChat component code.
  21. Start Frontend: Run your React development server. For Detail see below
Running the React Frontend

Here are the instructions to get the React frontend of the Bank FAQ Chat Agent running:
Navigate to your React project directory in your terminal. If you haven’t created a React project yet, you can do so using Create React App or a similar tool:
Bash
npx create-react-app bank-faq-frontend
cd bank-faq-frontend


Install Dependencies: If you started with a fresh React project, you’ll need to install any necessary dependencies (though this example uses built-in React features like useState and useEffect). If you have a pre-existing project, ensure you have react and react-dom installed.
Bash
npm install  # Or yarn install


Replace src/App.js (or your main component file): Open the src/App.js file (or the main component where you want to place the chat agent) and replace its entire content with the React code provided in the previous section. You might need to adjust the import path if your component is named differently or located in a different directory. For example, if you save the code in a file named BankFAQChat.js within a components folder, you would import it in App.js like this:
JavaScript
import BankFAQChat from ‘./components/BankFAQChat’;

function App() {
  return (
    <div>
      <BankFAQChat />
    </div>
  );
}

export default App;


Start the Development Server: Run the React development server from your terminal within the React project directory:
Bash
npm start  # Or yarn start

This command will typically open your React application in a new tab in your web browser, usually at http://localhost:3000.


Interact with the Chat Agent: Once the frontend is running, you should see the chat interface. You can type your bank-related questions in the input field and click the “Send” button (or press Enter) to send them to the backend. The agent’s responses and the conversation history will be displayed in the chat window.


Important Notes for the Frontend:
Backend URL: Ensure that the fetch calls in the BankFAQChat component (/create_session and /bank_faq/chat) are pointing to the correct URL where your Flask backend is running. If your backend is running on a different host or port than http://localhost:5000, you’ll need to update these URLs accordingly.


Styling: The provided styles object in the React component offers basic styling. You can customize this further or use a CSS-in-JS library (like Styled Components) or a CSS framework (like Tailwind CSS or Material UI) to enhance the visual appearance of the chat agent.


Error Handling: The frontend includes basic console.error logging for API request failures. You might want to implement more user-friendly error messages within the UI.


Session Management: The frontend automatically fetches or creates a session on mount. The sessionId is managed in the component’s state.
By following these instructions, you should be able to run the React frontend and interact with the Bank FAQ Chat Agent, provided that your Flask backend is also running and correctly configured.

This setup provides a functional bank FAQ chat agent with personalized history within a session, powered by RAG and an LLM. Remember to replace placeholders and configure API keys and file paths according to your specific environment and data.