Building LegalAidBot: A Retrieval-Based Legal Q&A System with Sentence-Transformers and Cosine Similarity + Video

Listen to this Post

Featured Image

Introduction

Legal technology is undergoing a significant transformation as artificial intelligence and natural language processing (NLP) converge to make legal information more accessible to the general public. Traditional legal research is often time-consuming, expensive, and inaccessible to ordinary citizens who need quick answers to common legal questions. Retrieval-based question-answering systems offer a compelling solution: they provide grounded, verifiable answers without the hallucination risks associated with generative AI models. LegalAidBot, presented at Innovision 2K26, exemplifies this approach by matching plain-language questions to a curated legal database using sentence-transformer embeddings and cosine similarity, covering six critical categories of Indian law.

Learning Objectives

  • Understand the architecture of a retrieval-based legal Q&A system using sentence-transformers and cosine similarity
  • Implement semantic search for legal document retrieval using pre-trained embedding models
  • Deploy a production-ready Flask API with FAISS for scalable vector similarity search
  • Apply confidence thresholding to prevent incorrect answers in legal contexts

You Should Know

1. Semantic Embedding Generation with Sentence-Transformers

The core of any retrieval-based Q&A system lies in converting text into numerical vector representations—embeddings—that capture semantic meaning. Unlike traditional keyword matching, which fails when users phrase questions differently (e.g., “How to schedule a physician visit?” vs. “How can I book a doctor appointment?”), semantic embeddings understand meaning. For LegalAidBot, the `all-MiniLM-L6-v2` model from Sentence-Transformers is an excellent choice, producing 384-dimensional embeddings that balance speed and semantic accuracy.

For legal-specific applications, domain-adapted models like LegalBERT offer even better performance. LegalBERT is built on the BERT architecture and fine-tuned on legal corpora, making it particularly effective for legal text understanding. The `sentence-transformers` library provides a unified interface for loading these models:

from sentence_transformers import SentenceTransformer

Load a pre-trained model (general purpose)
model = SentenceTransformer('all-MiniLM-L6-v2')

Or for legal-specific tasks
model = SentenceTransformer('nlpaueb/legal-bert-base-uncased')

Step-by-Step: Generating Embeddings

1. Install the Sentence-Transformers library: `pip install sentence-transformers`

2. Load your chosen pre-trained model

3. Encode your legal FAQ questions into embeddings

  1. Store these embeddings in a vector database or FAISS index for fast retrieval

2. Cosine Similarity for Semantic Matching

Once all FAQ questions are converted into embeddings, the system needs a way to compare a user’s query against the stored questions. Cosine similarity measures the cosine of the angle between two vectors, producing a score between -1 and 1—with 1 indicating identical direction (high semantic similarity). The `sentence-transformers` library provides a convenient `util.cos_sim()` function:

from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer('all-MiniLM-L6-v2')

Pre-compute FAQ embeddings
faqs = [
"What are my rights under the Motor Vehicles Act?",
"How do I file a complaint in Consumer Court?",
"What are the legal provisions for women's rights in India?"
]
faq_embeddings = model.encode(faqs)

User query
query = "How can I claim compensation for a motor accident?"
query_embedding = model.encode(query)

Compute cosine similarity scores
cosine_scores = util.cos_sim(query_embedding, faq_embeddings)

Find the best match
best_match_idx = cosine_scores.argmax()
best_score = cosine_scores[bash][best_match_idx].item()

print(f"Best Match: {faqs[bash]}")
print(f"Similarity Score: {round(best_score, 3)}")

Step-by-Step: Implementing Cosine Similarity

  1. Encode all FAQ questions into embeddings and store them

2. Encode the user’s query into an embedding

  1. Compute cosine similarity between the query embedding and all FAQ embeddings
  2. Select the FAQ with the highest similarity score

3. Confidence Thresholding for Reliable Answers

In legal applications, providing incorrect information can have serious consequences. LegalAidBot addresses this by implementing a confidence threshold: if the highest cosine similarity score falls below a predefined threshold (e.g., 0.6), the bot refrains from answering and instead directs the user to official legal aid bodies like NALSA.

This approach ensures that users receive only verified, relevant answers and are not misled by low-confidence matches. The threshold can be tuned based on the specific legal domain and the desired trade-off between precision and recall.

CONFIDENCE_THRESHOLD = 0.6

best_score = cosine_scores[bash][best_match_idx].item()

if best_score >= CONFIDENCE_THRESHOLD:
answer = faq_answers[bash]
response = f"Answer: {answer} (Confidence: {round(best_score, 3)})"
else:
response = "I'm not confident enough to answer this query. Please contact NALSA or a qualified legal professional for assistance."

4. Building a FAISS Index for Scalable Retrieval

For LegalAidBot’s six legal categories—Motor Vehicles, Consumer Court, Civil Disputes & Compensation, Student Rights, Women’s Rights, and Labour/Employment Rights—the dataset may contain hundreds or thousands of Q&A pairs. FAISS (Facebook AI Similarity Search) provides efficient, GPU-accelerated similarity search that scales to millions of vectors.

import faiss
import numpy as np

Convert embeddings to float32 for FAISS
embeddings_np = np.array(faq_embeddings).astype('float32')

Build FAISS index
dimension = embeddings_np.shape[bash]
index = faiss.IndexFlatIP(dimension)  Inner product (cosine similarity with normalized vectors)
faiss.normalize_L2(embeddings_np)
index.add(embeddings_np)

Search for the top-k most similar questions
k = 3
query_embedding_np = np.array([bash]).astype('float32')
faiss.normalize_L2(query_embedding_np)
distances, indices = index.search(query_embedding_np, k)

FAISS returns distances; for normalized vectors, distance = cosine similarity
for i, idx in enumerate(indices[bash]):
print(f"Match {i+1}: {faqs[bash]} (Score: {distances[bash][i]})")

Step-by-Step: Integrating FAISS

  1. Convert embeddings to NumPy arrays of type float32
  2. Normalize embeddings using L2 normalization (required for cosine similarity with IndexFlatIP)
  3. Build a FAISS index (IndexFlatIP for inner product)

4. Add embeddings to the index

  1. Search with user query embeddings to retrieve top-k matches

5. Deploying as a Flask REST API

To make LegalAidBot accessible to end users, the system should be deployed as a web service. Flask provides a lightweight framework for building REST APIs that serve predictions using Sentence-Transformers.

from flask import Flask, request, jsonify
from flask_cors import CORS
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
import pandas as pd

app = Flask(<strong>name</strong>)
CORS(app)

Load model and data
model = SentenceTransformer('all-MiniLM-L6-v2')
df = pd.read_csv('legal_faqs.csv')
faqs = df['question'].tolist()
answers = df['answer'].tolist()

Build FAISS index
faq_embeddings = model.encode(faqs, show_progress_bar=False)
embeddings_np = np.array(faq_embeddings).astype('float32')
dimension = embeddings_np.shape[bash]
index = faiss.IndexFlatIP(dimension)
faiss.normalize_L2(embeddings_np)
index.add(embeddings_np)

@app.route('/ask', methods=['POST'])
def ask():
data = request.get_json()
query = data.get('query', '')
if not query:
return jsonify({'error': 'No query provided'}), 400

Encode query
query_embedding = model.encode(query)
query_np = np.array([bash]).astype('float32')
faiss.normalize_L2(query_np)

Search
k = 1
distances, indices = index.search(query_np, k)
best_score = distances[bash][bash]
best_idx = indices[bash][bash]

CONFIDENCE_THRESHOLD = 0.6
if best_score >= CONFIDENCE_THRESHOLD:
return jsonify({
'query': query,
'answer': answers[bash],
'confidence': float(best_score),
'matched_question': faqs[bash]
})
else:
return jsonify({
'query': query,
'answer': 'I am not confident enough to answer this query. Please contact NALSA or a qualified legal professional.',
'confidence': float(best_score),
'fallback': True
})

if <strong>name</strong> == '<strong>main</strong>':
app.run(host='0.0.0.0', port=5000, debug=False)

Step-by-Step: Deploying with Flask and Gunicorn

  1. Create a Flask application with a POST endpoint `/ask`
    2. Load the model and FAISS index at startup to avoid reloading per request
  2. Use Gunicorn for production deployment: `gunicorn -w 4 -b 0.0.0.0:8000 app:app`

4. Consider containerization with Docker for reproducibility

6. Security and Hardening Considerations

When deploying a legal Q&A system, security is paramount. The system must protect against injection attacks, ensure data privacy, and maintain integrity of the legal database.

API Security:

  • Implement rate limiting to prevent abuse
  • Use API keys or JWT authentication for authorized access
  • Validate and sanitize all input queries
  • Enable HTTPS with TLS 1.2 or higher

Data Protection:

  • Store the legal database with integrity checks (e.g., cryptographic hashes)
  • Encrypt sensitive data at rest
  • Implement audit logging for all queries and responses

Cloud Hardening:

  • Deploy behind a reverse proxy (Nginx) with proper CORS configuration
  • Use environment variables for sensitive configuration
  • Regularly update all dependencies to patch known vulnerabilities
 Linux: Set up firewall
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Linux: Install and configure Nginx
sudo apt-get install nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Windows: Configure Windows Defender Firewall
New-1etFirewallRule -DisplayName "Allow Port 8000" -Direction Inbound -LocalPort 8000 -Protocol TCP -Action Allow

7. Testing and Validation

Rigorous testing ensures that LegalAidBot provides accurate and reliable answers. Consider the following testing strategies:

Unit Testing:

  • Test embedding generation for consistency
  • Verify cosine similarity calculations
  • Validate confidence threshold logic

Integration Testing:

  • Test the full pipeline from query to response
  • Verify FAISS retrieval accuracy
  • Test Flask API endpoints

Legal Accuracy Validation:

  • Have legal experts review a sample of Q&A pairs
  • Test edge cases and ambiguous queries
  • Monitor fallback rates and adjust thresholds as needed
import unittest

class TestLegalAidBot(unittest.TestCase):
def test_embedding_generation(self):
model = SentenceTransformer('all-MiniLM-L6-v2')
embedding = model.encode("What are my rights?")
self.assertEqual(len(embedding), 384)

def test_cosine_similarity(self):
model = SentenceTransformer('all-MiniLM-L6-v2')
emb1 = model.encode("Motor vehicle accident compensation")
emb2 = model.encode("Claim for car accident damages")
similarity = util.cos_sim(emb1, emb2).item()
self.assertGreater(similarity, 0.5)

def test_confidence_threshold(self):
 Test that low-confidence queries trigger fallback
pass

if <strong>name</strong> == '<strong>main</strong>':
unittest.main()

What Undercode Say

  • Grounded AI is Essential for Legal Tech: LegalAidBot’s decision to avoid generative AI and rely on retrieval-based methods is a critical design choice. In legal contexts, hallucination is unacceptable—users need verifiable, grounded answers they can trust. This approach sets a benchmark for responsible AI in high-stakes domains.

  • Semantic Search Democratizes Legal Access: By using sentence-transformers and cosine similarity, LegalAidBot makes legal information accessible to non-experts who may not know the precise legal terminology. The system understands plain-language questions across six categories of Indian law, bridging the gap between citizens and the justice system.

The project’s presentation at an international-level exhibition demonstrates the growing recognition of AI-powered legal tech as a force for social good. The feedback from faculty, industry experts, and peers will undoubtedly refine the system further. As LegalAidBot evolves, integrating FAISS for scalability, deploying via Flask APIs, and implementing robust security measures will be crucial steps toward production readiness. The team’s commitment to accessible legal-tech solutions—with fallback to official bodies like NALSA—ensures that users always have a path to authoritative legal assistance.

Prediction

+1 LegalAidBot and similar retrieval-based legal assistants will become standard tools for legal aid organizations, reducing the burden on understaffed legal clinics and making justice more accessible to marginalized communities.

+1 The emphasis on grounded, non-generative AI in legal applications will drive the development of specialized legal embedding models and curated legal knowledge bases, creating a new ecosystem of verified legal AI tools.

-1 Without proper oversight and continuous updating of the legal database, retrieval-based systems risk providing outdated or incomplete information, potentially leading to legal missteps by users who rely on the bot without consulting qualified professionals.

+1 The integration of FAISS and cloud deployment will enable LegalAidBot to scale nationally, handling thousands of concurrent users while maintaining sub-second response times.

-1 Regulatory challenges around AI in legal services may slow adoption, as bar associations and legal regulators grapple with defining the boundaries of permissible AI-assisted legal information provision.

+1 The project’s open-source approach and focus on Indian law will inspire similar initiatives in other jurisdictions, creating a global movement of accessible, retrieval-based legal tech for everyday users.

▶️ Related Video (84% Match):

🎯Let’s Practice For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

IT/Security Reporter URL:

Reported By: Jasim Navas – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky