import OpenAI from 'openai';
import { CourseContent, Category, Tag } from '../types';
export class AIService {
private openai!: OpenAI;
private enabled: boolean;
private AIMODEL = 'gpt-4.1-mini';
constructor() {
const apiKey = process.env.OPENAI_API_KEY;
this.enabled = !!apiKey;
if (this.enabled) {
this.openai = new OpenAI({
apiKey: apiKey,
});
} else {
throw new Error('OPENAI_API_KEY é obrigatória para usar o servidor MCP de curadoria');
}
}
async suggestCategoryWithAI(content: CourseContent, categories: Category[]): Promise<{
categoryId: string;
confidence: number;
reasoning: string;
} | null> {
if (!this.enabled) return null;
try {
const categoryList = categories.map(cat => `${cat.id}: ${cat.name} - ${cat.description}`).join('\n');
const prompt = `
Analise o seguinte conteúdo de curso e sugira a categoria mais apropriada:
TÍTULO: ${content.title}
DESCRIÇÃO: ${content.description}
CATEGORIAS DISPONÍVEIS:
${categoryList}
Responda APENAS em formato JSON com:
{
"categoryId": "id_da_categoria",
"confidence": 0.85,
"reasoning": "explicação_detalhada"
}
A confiança deve ser um número entre 0 e 1. Seja específico na explicação.
`;
const response = await this.openai.chat.completions.create({
model: this.AIMODEL,
messages: [{
role: 'user',
content: prompt
}],
temperature: 0.3,
max_tokens: 200
});
const result = JSON.parse(response.choices[0].message.content || '{}');
return result;
} catch (error) {
console.error('Erro na sugestão de categoria via IA:', error);
return null;
}
}
async suggestTagsWithAI(content: CourseContent, availableTags: Tag[]): Promise<Array<{
tagId: string;
confidence: number;
reasoning: string;
}> | null> {
if (!this.enabled) return null;
try {
const tagList = availableTags.map(tag => `${tag.id}: ${tag.name}`).join(', ');
const prompt = `
Analise o conteúdo do curso e sugira as tags mais relevantes:
TÍTULO: ${content.title}
DESCRIÇÃO: ${content.description}
TAGS DISPONÍVEIS: ${tagList}
Sugira até 5 tags mais relevantes. Responda APENAS em formato JSON:
{
"tags": [
{
"tagId": "id_da_tag",
"confidence": 0.9,
"reasoning": "explicação"
}
]
}
Ordene por relevância (mais relevante primeiro).
`;
const response = await this.openai.chat.completions.create({
model: this.AIMODEL,
messages: [{
role: 'user',
content: prompt
}],
temperature: 0.3,
max_tokens: 300
});
const result = JSON.parse(response.choices[0].message.content || '{"tags": []}');
return result.tags || [];
} catch (error) {
console.error('Error in AI tag suggestion:', error);
return null;
}
}
async improveTitleWithAI(title: string, description: string): Promise<{
suggestion: string;
reasoning: string;
improvements: string[];
} | null> {
if (!this.enabled) return null;
try {
const prompt = `
Analise e melhore o seguinte título de curso:
TÍTULO ATUAL: ${title}
DESCRIÇÃO: ${description}
Considere as boas práticas:
- Comprimento ideal: 30-60 caracteres
- Incluir nível (iniciante, intermediário, avançado)
- Usar palavras-chave relevantes
- Ser específico e claro
- Usar números quando apropriado
Responda em formato JSON:
{
"suggestion": "título_melhorado",
"reasoning": "explicação_das_mudanças",
"improvements": ["lista", "de", "melhorias", "aplicadas"]
}
Se o título já estiver bom, mantenha similar mas otimizado.
`;
const response = await this.openai.chat.completions.create({
model: this.AIMODEL,
messages: [{
role: 'user',
content: prompt
}],
temperature: 0.4,
max_tokens: 250
});
const result = JSON.parse(response.choices[0].message.content || '{}');
return result;
} catch (error) {
console.error('Error in AI title improvement:', error);
return null;
}
}
async improveDescriptionWithAI(title: string, description: string): Promise<{
suggestion: string;
reasoning: string;
improvements: string[];
} | null> {
if (!this.enabled) return null;
try {
const prompt = `
Melhore a seguinte descrição de curso:
TÍTULO: ${title}
DESCRIÇÃO ATUAL: ${description}
Boas práticas para descrições:
- Comprimento ideal: 100-300 caracteres
- Começar com benefício/resultado
- Usar verbos de ação (aprenda, domine, desenvolva)
- Incluir o que será aprendido
- Call-to-action sutil
- Linguagem clara e envolvente
Responda em formato JSON:
{
"suggestion": "descrição_melhorada",
"reasoning": "explicação_das_mudanças",
"improvements": ["lista", "de", "melhorias", "aplicadas"]
}
`;
const response = await this.openai.chat.completions.create({
model: this.AIMODEL,
messages: [{
role: 'user',
content: prompt
}],
temperature: 0.4,
max_tokens: 350
});
const result = JSON.parse(response.choices[0].message.content || '{}');
return result;
} catch (error) {
console.error('Error in AI description improvement:', error);
return null;
}
}
isEnabled(): boolean {
return this.enabled;
}
}