semillero-2-AI-ATS/docs/setup-guide.md

239 lines
9.4 KiB
Markdown

# Setup and Configuration Guide: AI Recruitment Platform
This guide covers the complete step-by-step setup process for the database (Supabase), the automation engine (n8n), the AI models, and the Next.js application.
---
## 1. Supabase (Database Setup)
Supabase provides the relational database, vector store, and API services.
### A. Extensions Needed
The semantic similarity matching uses vector calculations. You must enable the `vector` extension.
* Go to **Database** -> **Extensions** -> Search for `vector` -> Click **Enable**.
### B. Table Schemas
You need to create the following five tables. Run these SQL commands in the Supabase **SQL Editor**:
```sql
-- 1. Jobs Table
CREATE TABLE jobs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
requirements JSONB NOT NULL DEFAULT '{}'::jsonb, -- Store raw text + lowercase buzzwords list
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
);
-- 2. Candidates Table
CREATE TABLE candidates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
phone TEXT,
embedding VECTOR(1536), -- 1536 dimensions matching gemini-embedding-001
contact_info JSONB DEFAULT '{}'::jsonb, -- Store A-Z summary, skills list, phone, etc.
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
);
-- 3. Scores Table (Decoupled Evaluations)
CREATE TABLE scores (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
candidate_id UUID REFERENCES candidates(id) ON DELETE CASCADE NOT NULL,
job_id UUID REFERENCES jobs(id) ON DELETE CASCADE NOT NULL,
ai_score INTEGER NOT NULL CHECK (ai_score >= 0 AND ai_score <= 100),
risk_level TEXT NOT NULL, -- 'Low', 'Medium', 'High'
evaluation JSONB NOT NULL DEFAULT '{}'::jsonb, -- Store { summary, classification, suggestions }
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
);
-- 4. Interviews Table
CREATE TABLE interviews (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
candidate_id UUID REFERENCES candidates(id) ON DELETE CASCADE NOT NULL,
job_id UUID REFERENCES jobs(id) ON DELETE CASCADE NOT NULL,
interview_date TIMESTAMP WITH TIME ZONE NOT NULL,
stage TEXT DEFAULT 'Screening'::text NOT NULL, -- 'Screening', 'Technical', 'Cultural', 'Offer', 'Hired', 'Rejected'
feedback TEXT, -- Stores JSON string comments timeline array [{ id, text, timestamp, author, stage, isAi }]
pinned BOOLEAN DEFAULT false NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
);
```
### C. pgvector Similarity Match Function
Next, create the PostgreSQL RPC function to rank candidates against job requirements using cosine distance. Run this in the SQL Editor:
```sql
CREATE OR REPLACE FUNCTION match_candidates (
query_embedding VECTOR(1536),
match_threshold DOUBLE PRECISION,
match_count INT
)
RETURNS TABLE (
id UUID,
name TEXT,
email TEXT,
phone TEXT,
contact_info JSONB,
similarity DOUBLE PRECISION
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
SELECT
candidates.id,
candidates.name,
candidates.email,
candidates.phone,
candidates.contact_info,
1 - (candidates.embedding <=> query_embedding) AS similarity
FROM candidates
WHERE 1 - (candidates.embedding <=> query_embedding) > match_threshold
ORDER BY candidates.embedding <=> query_embedding
LIMIT match_count;
END;
$$;
```
---
## 2. n8n (Automation Engine Setup)
n8n acts as the central automation orchestrator, receiving hooks from Next.js, calling LLM chains, and saving responses.
### A. Minimum Requirements
* **n8n instance**: Self-hosted (Docker / npm package) or n8n Cloud.
* **Version**: n8n v1.0.0 or later (v1.30+ recommended for advanced LangChain integration features).
* **Network Access**: The n8n instance must be publicly reachable (using domain/tunnel like ngrok/Cloudflare) so Vercel can post webhook payloads.
### B. Supported LLM Models & Providers
The n8n Kickstarter script (`deploy-n8n-v2.ts`) supports credentials setup and deployment for:
| Provider | Choice ID | Default Model | Node Type | API Key Env Var |
| :--- | :--- | :--- | :--- | :--- |
| **Deepseek** | `1` | `deepseek-chat` | `@n8n/n8n-nodes-langchain.lmChatDeepSeek` | `DEEPSEEK_API_KEY` |
| **OpenAI** | `2` | `gpt-4o-mini` | `@n8n/n8n-nodes-langchain.lmChatOpenAi` | `OPENAI_API_KEY` |
| **Google Gemini** | `3` | `gemini-1.5-flash` | `@n8n/n8n-nodes-langchain.lmChatGoogleGemini` | `GEMINI_API_KEY` |
| **Anthropic** | `4` | `claude-3-5-sonnet-latest` | `@n8n/n8n-nodes-langchain.lmChatAnthropic` | `ANTHROPIC_API_KEY` |
### C. Deploying via Script
1. Configure the `.env` variables (`N8N_HOST`, `N8N_API_KEY`).
2. Run the deployment script:
```bash
npx ts-node -O '{"module": "commonjs"}' scripts/deploy-n8n-v2.ts \
--primary-provider=deepseek \
--fallback \
--fallback-provider=gemini
```
*Note: The script dynamically detects if a credential exists in n8n. If found, it safely reuses the credential to avoid entering keys repeatedly.*
### D. Email Ingestion & Notification Workflows
To deploy the email-based workflows (receiving CVs via IMAP and notifying candidates via SMTP on stage changes), run the dedicated email deployment script:
```bash
npx tsx scripts/deploy-email-automation.ts
```
This script will:
1. Create or update `Semillero2_IMAP_V2` credentials in n8n (utilizes standard secure port `993`).
2. Create or update `Semillero2_SMTP` credentials in n8n (utilizes `.env` SMTP variables).
3. Deploy and activate **Semillero2: Email Ingestion Listener** (listens for emails with PDF attachments, extracts text, and triggers candidate evaluation).
4. Deploy and activate **Semillero2: Stage Change Email Notifier** (listens for database webhooks on stage updates and sends templated HTML emails to candidates).
### E. Supabase Database Webhook Setup (Stage Changes)
The email notifier workflow is triggered by a Supabase Database Webhook when candidate stages are updated.
Run the following SQL in your Supabase SQL Editor to enable the trigger:
```sql
-- Enable pg_net extension for network calls
CREATE EXTENSION IF NOT EXISTS pg_net;
-- Create trigger function
CREATE OR REPLACE FUNCTION notify_interview_stage_change()
RETURNS TRIGGER AS $$
DECLARE
payload_body JSONB;
BEGIN
IF (TG_OP = 'INSERT' OR OLD.stage IS DISTINCT FROM NEW.stage) THEN
payload_body := jsonb_build_object(
'old_record', CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE to_jsonb(OLD) END,
'new_record', to_jsonb(NEW)
);
PERFORM net.http_post(
url := 'https://n8n.gaboggamer.online/webhook/stage-changed',
headers := '{"Content-Type": "application/json"}'::jsonb,
body := payload_body
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Drop existing trigger if it exists
DROP TRIGGER IF EXISTS tr_interview_stage_change ON public.interviews;
-- Bind trigger to public.interviews table
CREATE TRIGGER tr_interview_stage_change
AFTER INSERT OR UPDATE ON public.interviews
FOR EACH ROW
EXECUTE FUNCTION notify_interview_stage_change();
```
---
## 3. Supported Webhooks / API Endpoints
The n8n workflow exposes the following endpoints (automatically registered upon deployment):
### 1. `POST /webhook/evaluate-candidate`
Triggered by Next.js when parsing CVs or running AI evaluations.
* **Payload**:
```json
{
"candidateId": "uuid-here",
"jobId": "uuid-here",
"candidateName": "John Doe",
"jobTitle": "React Developer",
"jobRequirements": "Stack: React, TypeScript, Tailwind...",
"text": "Extracted text contents of candidate resume..."
}
```
* **Workflow Operations**:
* Extracts resume data (Structured Output Parser).
* Inserts parsed details (skills list, summary) into `candidates` table.
* Runs the primary evaluation chain (Deepseek) with a fallback to Google Gemini.
* Calculates calibrated scores (0-100), risk classifications (`Qualified`, `Unqualified`, `Review`), and appends next-step suggestions.
* Stores result in `scores` table.
### 2. `POST /webhook/suggest-next-steps`
Triggered by Next.js when clicking "Get AI Suggestion" inside Interviews.
* **Payload**:
```json
{
"candidateName": "John Doe",
"jobTitle": "React Developer",
"currentStage": "Technical",
"candidateSummary": "Extracted summary...",
"candidateSkills": ["react", "typescript"],
"jobRequirements": "Stack: React...",
"commentHistory": [ ... ],
"lang": "en" | "es"
}
```
* **Workflow Operations**:
* Assembles context of the candidate, job description, and interview timeline.
* Queries LLM to output a brief, actionable list of next steps.
* Instructs LLM to write in Spanish if `lang` is `"es"`, otherwise English.
---
## 4. Next.js (Application Setup)
### A. Environment variables
Make sure all items in `.env` are configured:
* `APP_PASSWORD`: Protects the lock screen. Default fallback is `Semillero2026!`.
* `GEMINI_API_KEY`: Mandatory for client-side embedding generation (`models/gemini-embedding-001`) and candidate comparisons.
* `NEXT_PUBLIC_SUPABASE_URL` & `SUPABASE_SECRET_KEY`: Service role keys to read/write without RLS checks.
### B. Deployment
Deploy to Vercel with all environment variables matching your local setup. Ensure the API routes can freely reach the database and the n8n webhooks.