Skip to content

tsargent/RAG-demo

Repository files navigation

Mental Skills Coach — RAG Demo

Educational demo of a Retrieval-Augmented Generation (RAG) app that answers questions about evidence-based mental skills (e.g., box breathing, grounding, body scan). It retrieves relevant chunks from curated Markdown content stored in a Supabase Postgres database (with pgvector), then asks an LLM to answer strictly using those sources.

Important: This is for educational purposes only. It is not medical advice. If you’re in crisis, contact local emergency services or a crisis hotline.

What This Demo Shows

  • RAG flow end-to-end using Supabase + pgvector for similarity search.
  • Ingestion of Markdown content into a vectorized table.
  • A minimal Next.js UI that chats with an API route.
  • OpenAI models for embeddings and generation.

High-Level Architecture

  • Data: Markdown files under src/data/mental-skills/*.md.
  • Embeddings: OpenAI text-embedding-3-small.
  • Vector store: Supabase Postgres + pgvector with an RPC for similarity search.
  • Orchestration: answerQuestion() in src/lib/rag.ts embeds the query, retrieves top matches, and calls an LLM (gpt-4.1-mini).
  • API: POST /api/rag-query accepts { question: string }, returns { answer, sources }.
  • UI: src/app/page.tsx sends questions and renders answers with sources.

Prerequisites

  • Node 18+ and npm
  • A Supabase project with pgvector enabled
  • OpenAI API key

Environment Variables

Create ./.env.local with the following (replace values with your own):

# Supabase (project settings → API)
NEXT_PUBLIC_SUPABASE_URL="https://YOUR-PROJECT.ref.supabase.co"
NEXT_PUBLIC_SUPABASE_ANON_KEY="YOUR-ANON-KEY"
# Service key is required for ingestion (server-side writes)
SUPABASE_SERVICE_KEY="YOUR-SERVICE-ROLE-KEY"

# OpenAI
OPENAI_API_KEY="sk-..."

If you prefer, you can also put these into a plain .env file for the scripts; the app uses .env.local by default.

Supabase Setup (SQL)

Run these SQL statements in Supabase SQL Editor. Adjust dimensions if you change the embedding model.

-- 1) Extensions (pgvector)
create extension if not exists vector;

-- 2) Table for chunks
create table if not exists mental_skills_chunks (
  id uuid primary key default gen_random_uuid(),
  title text,
  source text,
  chunk text,
  embedding vector(1536) -- text-embedding-3-small dimension
);

-- 3) Optional: vector index for faster ANN search
create index if not exists mental_skills_chunks_embedding_idx
  on mental_skills_chunks using ivfflat (embedding vector_cosine_ops)
  with (lists = 100);

-- 4) RLS (recommended)
alter table mental_skills_chunks enable row level security;

-- Allow public/anon to read chunks (UI and API read via anon key)
create policy if not exists "Allow read for anon" on mental_skills_chunks
  for select using (true);

-- 5) Similarity search RPC (cosine distance)
create or replace function match_mental_skills_chunks(
  query_embedding vector(1536),
  match_count int default 5
)
returns table (
  id uuid,
  title text,
  source text,
  chunk text,
  similarity float
)
language sql stable as $$
  select
    m.id,
    m.title,
    m.source,
    m.chunk,
    1 - (m.embedding <=> query_embedding) as similarity
  from mental_skills_chunks m
  order by m.embedding <=> query_embedding
  limit match_count;
$$;

-- Allow anon to execute the RPC
grant execute on function match_mental_skills_chunks(vector(1536), int) to anon;

Install & Run

# Install deps
npm install

# Ingest content (reads Markdown, chunks, embeds, and inserts)
npm run ingest

# Optional: verify DB connectivity and a sample read
npm run db:test-read

# Start the UI
npm run dev

Then open http://localhost:3000 and try questions like:

  • “How can I use box breathing to manage anxiety?”
  • “What is a grounding exercise?”

Data Sources

Markdown files live here:

  • src/data/mental-skills/body-scan.md
  • src/data/mental-skills/box-breathing.md
  • src/data/mental-skills/grounding-54321.md
  • src/data/mental-skills/stop.md
  • src/data/mental-skills/urge-surfing.md

The ingestion script will chunk these and upsert them into mental_skills_chunks with embeddings.

Key Code Paths

  • src/lib/db.ts: Supabase client (anon key for reads in app).
  • src/lib/embeddings.ts: Embedding helper using OpenAI text-embedding-3-small.
  • src/lib/rag.ts: answerQuestion() → embed query → Supabase RPC → call gpt-4.1-mini with retrieved context.
  • src/app/api/rag-query/route.ts: API to handle POST questions.
  • src/app/page.tsx: Minimal chat-style UI showing answers and sources.
  • src/scripts/ingest.ts: Offline ingestion using SUPABASE_SERVICE_KEY.
  • src/scripts/test-read.ts: Quick connectivity/read test.

API Usage

Local example:

curl -s -X POST http://localhost:3000/api/rag-query \
  -H 'Content-Type: application/json' \
  -d '{"question":"How do I use box breathing?"}'

Returns JSON like:

{
  "answer": "...model-generated text grounded in sources...",
  "sources": [
    { "id": "...", "title": "box-breathing", "source": "box-breathing.md", "chunk": "...", "similarity": 0.87 }
  ]
}

Model & Safety Notes

  • Embeddings: text-embedding-3-small (1536-dim)
  • Chat: gpt-4.1-mini
  • The system prompt confines responses to provided context and reminds users this isn’t medical advice. Out-of-scope questions should receive a gentle deferral.

Troubleshooting

  • “Vector search failed: function match_mental_skills_chunks does not exist”
    • Ensure you created the RPC and granted execute to anon.
  • “column embedding is of type vector(1536) but expression is …”
    • Confirm the embedding model dimensions match your table definition.
  • No sources returned
    • Verify rows exist in mental_skills_chunks and that npm run ingest succeeded.
  • Auth/RLS issues
    • Reads use the anon key. Inserts require the service role key. With RLS enabled, service role bypasses policies; anon needs a select policy.

License / Use

This is a demo intended for learning and experimentation. Use responsibly and at your own risk.

About

RAG system for mental health skills

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published