feat: fase 3 - integração de observações com chat da IA
- Nova API GET /api/observacoes/buscar (filtros: crianca, mes, tags) - Nova API GET /api/observacoes/resumo (compila relatório pedagógico via GPT) - Resumo pedagógico estruturado: perfil, aspectos sociais/cognitivos/motores, pontos de atenção, pontos fortes, sugestões para planejamento - System prompt atualizado: IA reconhece comandos como 'observações da Maria', 'resumo pedagógico do João' e 调用a API para buscar/compilar registros
This commit is contained in:
@@ -383,6 +383,157 @@ app.get('/api/observacoes/:yearMonth/:filename', requireAuth, (req, res) => {
|
|||||||
res.sendFile(filePath);
|
res.sendFile(filePath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Rota: GET /api/observacoes/buscar — busca observações por criança e/ou período
|
||||||
|
// Query: ?crianca=Nome&mes=YYYY-MM&tags=social,cognitivo&limit=20
|
||||||
|
app.get('/api/observacoes/buscar', requireAuth, (req, res) => {
|
||||||
|
const { crianca, mes, tags, limit = 20 } = req.query;
|
||||||
|
const idx = readObsIndex();
|
||||||
|
let results = idx.observations || [];
|
||||||
|
|
||||||
|
// Filtra por criança
|
||||||
|
if (crianca) {
|
||||||
|
const searchName = crianca.toLowerCase();
|
||||||
|
results = results.filter(o =>
|
||||||
|
o.criancas && o.criancas.some(c => c.toLowerCase().includes(searchName))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtra por mês
|
||||||
|
if (mes) {
|
||||||
|
results = results.filter(o => o.date && o.date.startsWith(mes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtra por tags
|
||||||
|
if (tags) {
|
||||||
|
const tagList = tags.split(',').map(t => t.trim());
|
||||||
|
results = results.filter(o =>
|
||||||
|
o.tags && tagList.some(t => o.tags.includes(t))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limita resultados
|
||||||
|
results = results.slice(0, parseInt(limit));
|
||||||
|
|
||||||
|
// Carrega conteúdo completo de cada resultado
|
||||||
|
const enriched = results.map(o => {
|
||||||
|
const filePath = path.join(OBSERVACOES_DIR, o.date?.slice(0, 7), o.filename);
|
||||||
|
let fullContent = '';
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
fullContent = fs.readFileSync(filePath, 'utf8');
|
||||||
|
}
|
||||||
|
return { ...o, fullContent };
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ observations: enriched, total: idx.observations?.length || 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rota: GET /api/observacoes/resumo — compila observações em resumo para documentos
|
||||||
|
// Query: ?crianca=Nome&mes=YYYY-MM
|
||||||
|
// Retorna resumo estruturado que a IA pode usar em HTPC/planejamentos
|
||||||
|
app.get('/api/observacoes/resumo', requireAuth, async (req, res) => {
|
||||||
|
const { crianca, mes } = req.query;
|
||||||
|
if (!crianca) return res.status(400).json({ error: 'Nome da criança é obrigatório' });
|
||||||
|
|
||||||
|
// Busca observações
|
||||||
|
const idx = readObsIndex();
|
||||||
|
let results = idx.observations || [];
|
||||||
|
const searchName = crianca.toLowerCase();
|
||||||
|
results = results.filter(o =>
|
||||||
|
o.criancas && o.criancas.some(c => c.toLowerCase().includes(searchName))
|
||||||
|
);
|
||||||
|
if (mes) results = results.filter(o => o.date?.startsWith(mes));
|
||||||
|
|
||||||
|
if (results.length === 0) {
|
||||||
|
return res.json({ resumo: null, total: 0, message: `Nenhuma observação encontrada para "${crianca}"${mes ? ` em ${mes}` : ''}.` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carrega conteúdo completo
|
||||||
|
const observations = results.slice(0, 15).map(o => {
|
||||||
|
const filePath = path.join(OBSERVACOES_DIR, o.date?.slice(0, 7), o.filename);
|
||||||
|
let fullContent = '';
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
fullContent = fs.readFileSync(filePath, 'utf8');
|
||||||
|
}
|
||||||
|
return { ...o, fullContent };
|
||||||
|
});
|
||||||
|
|
||||||
|
// Compila textos
|
||||||
|
const texts = observations.map(o => o.fullContent.replace(/^---[\s\S]*?---\n/, '')).filter(Boolean);
|
||||||
|
const combined = texts.join('\n\n---\n\n');
|
||||||
|
|
||||||
|
// Pede para IA compilar resumo pedagógico
|
||||||
|
const compilePrompt = `Você é a Kemily, assistente pedagógica. Compilar um RESUMO PEDAGÓGICO consolidado a partir de várias observações de uma criança.
|
||||||
|
|
||||||
|
CRIANÇA: ${crianca}
|
||||||
|
NÚMERO DE OBSERVAÇÕES: ${observations.length}
|
||||||
|
${mes ? `PERÍODO: ${mes}` : ''}
|
||||||
|
|
||||||
|
COLEÇÃO DE OBSERVAÇÕES:
|
||||||
|
"""
|
||||||
|
${combined.slice(0, 4000)}
|
||||||
|
"""
|
||||||
|
|
||||||
|
GERE UM RESUMO ESTRUTURADO COM:
|
||||||
|
|
||||||
|
## Perfil da Criança
|
||||||
|
[Padrões de comportamento, habilidades, desenvolvimento observado]
|
||||||
|
|
||||||
|
## Aspectos Sociais e Emocionais
|
||||||
|
[Interações com outras crianças, gestão emocional, socialização]
|
||||||
|
|
||||||
|
## Aspectos Cognitivos e de Aprendizagem
|
||||||
|
[Curiosidade, resolução de problemas, atenção, concentração]
|
||||||
|
|
||||||
|
## Aspectos Motores e de Linguagem
|
||||||
|
[Desenvolvimento motor, linguagem oral, comunicação]
|
||||||
|
|
||||||
|
## Pontos de Atenção
|
||||||
|
[Dificuldades, necessidades de suporte, áreas a desenvolver]
|
||||||
|
|
||||||
|
## Pontos Fortes
|
||||||
|
[Conquistas, habilidades desarrolladas, aspectos positivos]
|
||||||
|
|
||||||
|
## Sugestões para Planejamento
|
||||||
|
[Recomendações para próximos passos, atividades sugeridas, seguimento]
|
||||||
|
|
||||||
|
-use linguagem formal-pedagógica
|
||||||
|
-Máximo 300 palavras
|
||||||
|
-Sea objetivo e baseado nas evidências das observações`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const compileRes = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
|
||||||
|
messages: [{ role: 'user', content: compilePrompt }],
|
||||||
|
temperature: 0.4,
|
||||||
|
max_tokens: 800
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const compileData = await compileRes.json();
|
||||||
|
const resumoText = compileData.choices?.[0]?.message?.content || '';
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
crianca,
|
||||||
|
mes: mes || 'todos',
|
||||||
|
total: observations.length,
|
||||||
|
datas: observations.map(o => o.date).filter(Boolean),
|
||||||
|
resumo: resumoText,
|
||||||
|
observations: observations.map(o => ({
|
||||||
|
date: o.date, time: o.time, tags: o.tags, turma: o.turma, preview: o.preview
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Erro ao compilar resumo:', err);
|
||||||
|
res.status(500).json({ error: 'Erro ao gerar resumo pedagógico' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Rota POST para upload de imagem em base64
|
// Rota POST para upload de imagem em base64
|
||||||
app.post('/api/upload-avatar', requireAuth, (req, res) => {
|
app.post('/api/upload-avatar', requireAuth, (req, res) => {
|
||||||
const { avatarType, base64Data } = req.body;
|
const { avatarType, base64Data } = req.body;
|
||||||
@@ -688,6 +839,34 @@ ESCRITA DA CRIANÇA:
|
|||||||
- Hipótese de escrita silábico-alfabética
|
- Hipótese de escrita silábico-alfabética
|
||||||
- Hipótese de escrita alfabética
|
- Hipótese de escrita alfabética
|
||||||
|
|
||||||
|
═══════════════════════════════════════════════════
|
||||||
|
SISTEMA DE OBSERVAÇÕES POR VOZ — Como acessar registros
|
||||||
|
═══════════════════════════════════════════════════
|
||||||
|
|
||||||
|
A Camila possui um sistema de OBSERVAÇÕES POR VOZ onde grava registros sobre crianças durante o dia. Esses registros são salvos e ficam disponíveis para consulta.
|
||||||
|
|
||||||
|
QUANDO A CAMILA PEDIR "observações da [nome da criança]", "resumo da [criança]", "ler observações da [nome]" ou similar:
|
||||||
|
→ Use a API: GET /api/observacoes/resumo?crianca=[nome]&mes=YYYY-MM
|
||||||
|
→ O sistema retorna um RESUMO PEDAGÓGICO compilado de todas as observações daquela criança no período
|
||||||
|
→ Use esse resumo para ajudar a elaborar HTPCs, relatórios, planejamentos
|
||||||
|
|
||||||
|
EXEMPLOS DE COMANDO:
|
||||||
|
- "mostre as observações da Maria deste mês"
|
||||||
|
- "resumo pedagógico da Ana"
|
||||||
|
- "ler observações do João"
|
||||||
|
- "compilar observações da Sofia em maio"
|
||||||
|
|
||||||
|
AO RECEBER O RESUMO:
|
||||||
|
- Analise o conteúdo returned
|
||||||
|
- Use para fundamentar documentos que a Camila pedir
|
||||||
|
- Se não houver observações, diga: "Não encontrei observações registradas para [criança] neste período. Posso ajudar de outra forma?"
|
||||||
|
- Integre o resumo de forma natural no documento que estiver criando
|
||||||
|
|
||||||
|
LIMITES:
|
||||||
|
- Máximo 15 observações por consulta
|
||||||
|
- Resumos são compactados para caber no contexto
|
||||||
|
- Se a Camila pedir mais detalhes, pode mostrar a lista de observações individuais
|
||||||
|
|
||||||
═══════════════════════════════════════════════════
|
═══════════════════════════════════════════════════
|
||||||
REGRAS DE ATUAÇÃO
|
REGRAS DE ATUAÇÃO
|
||||||
═══════════════════════════════════════════════════
|
═══════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user