AI Integration
AI tools and RAG integration for HRMS
AI Integration
This document defines how AI is integrated into the HRMS system, including function calling tools and RAG capabilities.
Full AI Chat Specification: For the complete AI Chat system including all tools, UI widgets, and implementation patterns, see AI Chat Specification.
Overview
The HRMS AI system consists of two main components:
- AI Chat/Copilot - User-facing conversational interface that can execute actions
- RAG Pipeline - Document search and Q&A using vector embeddings
All Available AI Tools
| Category | Tools | Description |
|---|---|---|
| Employee | employee_create, employee_search, employee_transfer, employee_terminate, employee_tag | Employee lifecycle management |
| Time-Off | timeoff_submit, timeoff_check_balance, timeoff_approve, timeoff_team_calendar, timeoff_cancel | Leave management |
| Workflows | workflow_create, workflow_track, workflow_complete_task, onboarding_initiate, offboarding_initiate | Onboarding/offboarding |
| Documents | document_search, document_ask, document_upload | RAG-powered document Q&A |
| Dashboard | dashboard_metrics, dashboard_insights, dashboard_pending_approvals | Analytics and insights |
| Organization | org_explain, org_validate, org_generate_structure, get_all_reports, get_reporting_chain | Org structure management |
See AI Chat Specification for full tool definitions.
AI Tools for Org Structure
These are the function definitions for AI assistants to interact with organizational data:
// packages/hrms-ai/src/tools/org-tools.ts
export const ORG_AI_TOOLS = [
{
type: 'function',
function: {
name: 'org_explain',
description: 'Generate narrative summary of employee org structure',
parameters: {
type: 'object',
properties: {
employeeId: { type: 'string' },
},
required: ['employeeId'],
},
},
},
{
type: 'function',
function: {
name: 'org_validate',
description: 'Validate org structure for issues',
parameters: {
type: 'object',
properties: {
checkCircular: { type: 'boolean', default: true },
checkOrphans: { type: 'boolean', default: true },
checkOverextended: { type: 'boolean', default: true },
maxReportsThreshold: { type: 'number', default: 15 },
},
},
},
},
{
type: 'function',
function: {
name: 'org_generate_structure',
description: 'Generate initial org structure from description',
parameters: {
type: 'object',
properties: {
description: { type: 'string' },
teamSize: { type: 'number' },
style: {
type: 'string',
enum: ['hierarchical', 'flat', 'matrix', 'squads'],
},
},
required: ['description'],
},
},
},
{
type: 'function',
function: {
name: 'get_all_reports',
description: 'Get all reports for a manager (primary + dotted + additional)',
parameters: {
type: 'object',
properties: {
managerId: { type: 'string' },
reportType: {
type: 'string',
enum: ['all', 'primary', 'dotted', 'additional'],
},
},
required: ['managerId'],
},
},
},
{
type: 'function',
function: {
name: 'get_reporting_chain',
description: 'Get reporting chain to top for an employee',
parameters: {
type: 'object',
properties: {
employeeId: { type: 'string' },
includeMatrixRelations: { type: 'boolean', default: false },
},
required: ['employeeId'],
},
},
},
]AI.org.explain
Generate a narrative summary of an employee's organizational position:
Input
{ employeeId: string }Output
{
summary: string // "John reports to Sarah (CTO) with a dotted line to..."
directManager: { name: string, title: string }
dottedLineManagers: [{ name: string, title: string, context: string }]
additionalManagers: [{ name: string, title: string, context: string }]
teams: [{ name: string, role: string }]
departments: [{ name: string, isPrimary: boolean }]
roles: [{ name: string, category: string }]
directReports: number
crossFunctionalInvolvement: string[]
}Example Output
{
"summary": "John Doe is a Senior Developer who reports directly to Jane Smith (Engineering Manager). He also has a dotted line relationship with the CTO for strategic technical decisions. John is a member of both the SaaS Team and Web Development Team, and belongs to the Engineering and Product departments.",
"directManager": {
"name": "Jane Smith",
"title": "Engineering Manager"
},
"dottedLineManagers": [
{
"name": "Bob Johnson",
"title": "CTO",
"context": "Strategic technical decisions"
}
],
"teams": [
{ "name": "SaaS Team", "role": "Tech Lead" },
{ "name": "Web Development", "role": "Member" }
],
"directReports": 3
}AI.org.validate
Validate the organizational structure for common issues:
Input
{
tenantId: string
checkCircular?: boolean // Detect circular relationships
checkOrphans?: boolean // Detect employees without managers
checkOverextended?: boolean // Detect managers with too many reports
maxReportsThreshold?: number // Default: 15
}Output
{
valid: boolean
issues: [{
type: 'ORPHAN' | 'OVEREXTENDED' | 'CIRCULAR' | 'MISSING_MANAGER'
severity: 'ERROR' | 'WARNING' | 'INFO'
employeeId: string
message: string
}]
summary: {
total: number
withManager: number
orphans: number
overextended: number
circular: number
}
}AI.org.generateStructure
Generate a suggested organizational structure from a description:
Input
{
description: string // "We're a 20-person startup with 3 squads..."
teamSize?: number
style?: 'hierarchical' | 'flat' | 'matrix' | 'squads'
}Output
{
departments: [{ name: string, description: string }]
teams: [{ name: string, type: string, parentTeam: string }]
roles: [{ name: string, category: string }]
suggestedHierarchy: {
topLevel: [{ role: string, responsibilities: string }]
middleLevel: [{ role: string, reportsTo: string }]
teamLevel: [{ role: string, team: string }]
}
}RAG Integration
Document Processing Pipeline
- Upload: Document uploaded to Google Cloud Storage
- Extract: Text extracted from PDF/DOCX
- Chunk: Split into overlapping chunks (512 tokens, 50 token overlap)
- Embed: Generate embeddings using OpenAI
text-embedding-3-small - Store: Store in MongoDB Atlas Vector Search
// Chunking configuration
const CHUNK_CONFIG = {
chunkSize: 512,
chunkOverlap: 50,
separators: ['\n\n', '\n', '. ', ' '],
}Vector Search
Query relevant documents for AI context:
// MongoDB Atlas Vector Search
const searchResults = await collection.aggregate([
{
$vectorSearch: {
index: 'vector_index',
path: 'embedding',
queryVector: queryEmbedding,
numCandidates: 100,
limit: 5,
filter: { tenantId: currentTenantId },
},
},
{
$project: {
text: 1,
documentId: 1,
score: { $meta: 'vectorSearchScore' },
},
},
])AI Chat Integration
// Combine RAG results with conversation
const systemPrompt = `
You are an HR assistant for ${tenantName}.
Use the following documents to answer questions:
${ragResults.map(r => r.text).join('\n\n')}
If the information is not in the documents, say so.
`
const response = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{ role: 'system', content: systemPrompt },
...conversationHistory,
{ role: 'user', content: userMessage },
],
tools: ORG_AI_TOOLS,
})AI Service Architecture
┌─────────────────────────────────────────────────┐
│ HRMS Backend │
│ (NestJS) │
└─────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ AI Service │
│ (Express) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Chat │ │ RAG │ │ Tools │ │
│ │ Handler │ │ Pipeline │ │ Executor │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ OpenAI │ │ MongoDB │ │ HRMS │
│ API │ │ (Vectors) │ │ Database │
└──────────────┘ └──────────────┘ └──────────────┘Security Considerations
- Tenant Isolation: All AI queries include tenant context
- Permission Checks: AI tools respect user permissions
- Data Filtering: Only return data user has access to
- Audit Logging: Log all AI interactions
- Rate Limiting: Prevent abuse of AI endpoints
// Always include tenant context in AI calls
const response = await aiService.chat({
message: userMessage,
tenantId: currentTenant.id,
userId: currentUser.id,
permissions: currentUser.permissions,
})Document Indexing Pipeline
When a document is uploaded via Phase 06 Document Management, it must be indexed for RAG:
Flow
Document Upload (Phase 06)
↓
DocumentService.upload() saves to GCS
↓
Create Document record (indexed=false)
↓
Call AI Service: POST /ai/rag/index
↓
AI Service extracts text, chunks, embeds
↓
Store chunks in MongoDB with tenantId
↓
Return vectorId
↓
Update Document: indexed=true, vectorId=vectorIdIntegration Point (Phase 06 → Phase 09)
// In DocumentService.upload() - add after GCS upload
if (document.category !== 'PRIVATE') {
await this.aiService.indexDocument({
sourceType: 'document',
sourceId: document.id,
title: document.title,
content: extractedText,
metadata: {
category: document.category,
visibility: document.visibility,
uploadedBy: document.uploadedById,
},
});
}AI Tool Access Control
All AI tools MUST enforce access control at the HRMS API level:
Tenant Isolation
- Every tool handler receives
context.tenantId - All HRMS API calls include
x-tenant-idheader - Vector searches filter by tenantId
Permission Enforcement
| Tool | Permission Check |
|---|---|
employee_search | Returns all tenant employees (MVP: no department filtering) |
org_explain | Requires read access to employee |
timeoff_balance | Only own balance OR manager/HR viewing subordinate |
document_search | Filter results by document visibility permissions |
Implementation Pattern
async execute(params, context: ChatContext) {
// 1. Validate user can perform this action
if (params.employeeId !== context.employeeId) {
const canView = await this.permissionService.canViewEmployee(
context.userId,
params.employeeId,
context.tenantId,
);
if (!canView) throw new ForbiddenError('Cannot view this employee');
}
// 2. Make API call with tenant context
const response = await fetch(endpoint, {
headers: {
'x-tenant-id': context.tenantId,
'Authorization': `Bearer ${context.token}`,
},
});
// 3. Filter results by user permissions if needed
return this.filterByPermissions(response.data, context);
}Technical Implementation Notes
MongoDB ID Handling
- Use HRMS CUID strings as document IDs in vector store
- Store
documentIdfield matching HRMS Document.id - Avoid MongoDB auto-generated ObjectIDs for cross-database joins
API Path Conventions
| Tool | Endpoint | Phase |
|---|---|---|
| employee_search | GET /api/v1/employees | Phase 02 |
| org_explain | GET /api/v1/org/employees/:id/summary | Phase 03 |
| timeoff_balance | GET /api/v1/timeoff/balances/:employeeId | Phase 05 |
| document_search | POST /ai/rag/query | Phase 09 |
Service-to-Service Authentication
- AI Service uses
x-service-secretheader for HRMS API calls - HRMS validates via ServiceSecretGuard (Phase 09, Step 147)
- Secret stored in environment variable
AI_SERVICE_SECRET
Future Enhancements (Phase 10+)
- SSE streaming for real-time chat responses
- Redis caching for frequent tool queries
- Extended employee_search with department/tag filtering
- Full org_explain response with roles and cross-functional data