diff --git a/PROMPT.txt b/PROMPT.txt index 16c774f..5c2a9b3 100644 --- a/PROMPT.txt +++ b/PROMPT.txt @@ -8,14 +8,9 @@ SEGUINDO OS CRITÉRIOS QUE VÃO ESTAR ABAIXO, AVALIE ATENDIMENTOS PRESTADOS VIA 04 (PROTOCOLO) - O AGENTE DEVE INFORMAR O PROTOCOLO DE ATENDIMENTO DURANTE A CONVERSA. -05 (USO DO PORTUGUÊS) – ESTE CRITÉRIO SE APLICA SOMENTE ÀS MENSAGENS ENVIADAS PELO AGENTE (TYPE = "OUT"). -AO AVALIAR ESTE CRITÉRIO, IGNORE COMPLETAMENTE QUALQUER ERRO COMETIDO PELO CLIENTE. -NÃO ANALISE MENSAGENS COM TYPE = "IN" E NÃO ANALISE MENSAGENS DO BOT AUTOMÁTICO. - -O AGENTE DEVE UTILIZAR CORRETAMENTE O PORTUGUÊS NAS MENSAGENS TYPE OUT, INCLUINDO PONTUAÇÃO, ACENTUAÇÃO E INICIO DE FRASES COM LETRA MAIÚSCULA. - +05 (USO DO PORTUGUÊS) – O AGENTE DEVE PONTUAR CORRETAMENTE, ACENTUAR CORRETAMENTE E INICIAR AS FRASES COM LETRA MAIÚSCULA. É PERMITIDO O USO DE LINGUAGEM REGIONAL COMO “TU”, “TEU”, “TUA”, POIS ISSO FAZ PARTE DA IDENTIDADE DA EMPRESA. -IMPORTANTE: NÃO AVALIE MENSAGENS DO CLIENTE (TYPE IN), NÃO AVALIE MENSAGENS DO PIPEBOT, NEM AVALIE MENSAGENS TYPE “SYSTEM”. AVALIE SOMENTE AS MENSAGENS TYPE OUT. +ESTE CRITÉRIO SE APLICA SOMENTE ÀS MENSAGENS ENVIADAS PELO AGENTE (TYPE OUT). AO AVALIAR ESTE CRITÉRIO, IGNORE COMPLETAMENTE QUALQUER ERRO COMETIDO PELO CLIENTE. 06 (PACIÊNCIA E EDUCAÇÃO) - O AGENTE DEVE SER PACIENTE E EDUCADO, UTILIZANDO O USO DE AGRADECIMENTOS, SAUDAÇÕES, PEDIDOS DE DESCULPAS E LINGUAGEM RESPEITOSA. COMO POR EXEMPLO "COMO POSSO TE AJUDAR?", "PERFEITO", "POR GENTILEZA", "OBRIGADO PELAS INFORMAÇÕES", "VOU VERIFICAR, AGUARDE UM MOMENTO POR GENTILEZA", "DESCULPE, MAS NÃO TE COMPREENDI", "PODERIA EXPLICAR DE NOVO?". @@ -36,12 +31,14 @@ As mensagens do chat estão estruturadas no formato JSON com os campos: O fluxo de mensagens inicia-se com o cliente interagindo com o BOT, e então a mensagem é transferida para o atendente. -Em cada categoria, atribua 0 quando o agente não tiver atendido o critétio e 1 caso tenha atendido. +Em cada categoria, atribua 0 quando o agente não tiver atendido o critétio e 1 caso tenha atendido. Não atribua nenhum outro valor além destes dois . Devem ser avaliados somente os agentes que o nome de usuario inicie com "FIN -" . A sua resposta deve ser apenas uma tabela CSV e nada mais, utilizando como separador o caracter ';' com as colunas: CATEGORIA;PONTOS;MOTIVO;EVIDENCIA; on> +Portanto crie essas colunas para todo agente que for avaliado. + Na saída CSV, na coluna categoria, utilize o nome correspondente ao invés do número A seguir estão as mensagens do atendimento, em JSON, avalie e retorne apenas um CSV. diff --git a/PROMPT_DATA_SANITIZATION.txt b/PROMPT_DATA_SANITIZATION.txt index 5f76fde..6176d4f 100644 --- a/PROMPT_DATA_SANITIZATION.txt +++ b/PROMPT_DATA_SANITIZATION.txt @@ -3,8 +3,8 @@ Padronize o arquivo CSV da seguinte forma, deixando apenas as colunas listadas. Título: CATEGORIA;PONTOS A sua resposta deve ser apenas o CSV com a formatação corrigida, nada mais deve ser incluído na sua resposta, nem mesmo notas sobre a resposta. Se não for possível padronizar o arquivo de entrada de acordo com as instruções fornecidas a resposta deve ser o CSV com o campo de pontuação vazio. -As categorias são: APRESENTAÇÃO, CONFIRMAÇÃO DE E-MAIL, CONFIRMAÇÃO DE TELEFONE, PROTOCOLO, USO DO PORTUGUÊS, PACIÊNCIA E EDUCAÇÃO, DISPONIBILIDADE, CONHECIMENTO TÉCNICO, DIDATISMO -A coluna pontos deve ter apenas os valores 0, 1 ou vazio, se no arquivo de entrada não houver a avaliação da categoria, a coluna de pontos deve ser vazia. +As categorias são: APRESENTAÇÃO, CONFIRMAÇÃO DE E-MAIL, CONFIRMAÇÃO DE TELEFONE, PROTOCOLO, USO DO PORTUGUÊS, PACIÊNCIA E EDUCAÇÃO, DISPONIBILIDADE, ESCLARECIMENTO +A coluna pontos deve ter apenas os valores 0, 1 ou vazio, se no arquivo de entrada não houver a avaliação da categoria (somente neste caso), a coluna de pontos deve ser vazio. Aqui estão alguns exemplos de formatação de como deve ser a sua resposta: Exemplo 01: @@ -55,8 +55,7 @@ PROTOCOLO;1 USO DO PORTUGUÊS;1 PACIÊNCIA E EDUCAÇÃO;1 DISPONIBILIDADE;1 -CONHECIMENTO TÉCNICO;1 -DIDATISMO;1 +ESCLARECIMENTO;1 ``` Exemplo 03: diff --git a/src/groupped_repport_weekly.rs b/src/groupped_repport_weekly.rs index dc13168..bfb5753 100644 --- a/src/groupped_repport_weekly.rs +++ b/src/groupped_repport_weekly.rs @@ -5,7 +5,7 @@ use polars::prelude::*; use reqwest; use std::env; use std::time::Duration; - +use std::path::Path; use csv; use std::fs::metadata; @@ -22,15 +22,15 @@ struct CsvHeader { #[derive(Debug, serde::Deserialize)] struct CsvEvaluation { - APRESENTAÇÃO: u8, + APRESENTACAO: u8, CONFIRMAÇÃO_DE_EMAIL: u8, CONFIRMAÇÃO_DE_TELEFONE: u8, PROTOCOLO: u8, - USO_DO_PORTUGUÊS: u8, - PACIÊNCIA_E_EDUCAÇÃO: u8, + USO_DO_PORTUGUES: u8, + PACIENCIA_E_EDUCACAO: u8, DISPONIBILIDADE: u8, - CONHECIMENTO_TÉCNICO: u8, - DIDATISMO: u8, +// CONHECIMENTO_TÉCNICO: u8, + ESCLARECIMENTO: u8, ID_TALK: String, } @@ -254,47 +254,103 @@ fn main() { eprintln!("📏 Tamanho: {} bytes", meta.len()); } - // Opcional: mostrar as primeiras linhas do CSV - eprintln!("📄 Primeiras 200 caracteres do CSV:\n{}", &ai_response[..200.min(ai_response.len())]); - // + // Opcional: mostrar as primeiras linhas do CSV + eprintln!("📄 Primeiras 200 caracteres do CSV antes de sanitizar:\n{}", &ai_response[..200.min(ai_response.len())]); + // - let mut reader = csv::ReaderBuilder::new() - .has_headers(true) - .delimiter(b';') - .from_reader(ai_response.as_bytes()); + // --- SALVAR CSV SANITIZADO PARA INSPEÇÃO --- + //let sanitized_path = file_path_csv.path().with_extension("sanitized.csv"); + // if let Err(e) = std::fs::write(&sanitized_path, &ai_response) { + // eprintln!("⚠️ Erro ao salvar CSV sanitizado: {}", e); + //} else { + // eprintln!("💾 CSV sanitizado salvo: {:?}", sanitized_path); + // } + // --------------------------------------------- - // ---------- LOG 2: tenta desserializar e conta os registros ---------- - let deserialized = reader.deserialize::().collect::>(); - eprintln!("🧾 Total de linhas lidas (incluindo cabeçalho?): {}", deserialized.len()); - for (i, result) in deserialized.iter().enumerate() { - match result { - Ok(record) => { - eprintln!("✅ Linha {}: CATEGORIA={}, PONTOS={:?}", i, record.CATEGORIA, record.PONTOS); - } - Err(e) => { - eprintln!("❌ Linha {} ERRO: {}", i, e); - } - } - } - // + // --- SALVAR CSV SANITIZADO EM PASTA SEPARADA --- + // Define o diretório base para os arquivos sanitizados + let sanitized_base = Path::new("./evaluations_sanitized"); + + // Obtém o caminho relativo do arquivo original em relação a "./evaluations" + // Exemplo: "./evaluations/2026-02-09/arquivo.csv" -> "2026-02-09/arquivo.csv" + if let Ok(relative_path) = file_path_csv.path().strip_prefix("./evaluations") { + let dest_path = sanitized_base.join(relative_path); + + // Cria o diretório de destino, se necessário + if let Some(parent) = dest_path.parent() { + std::fs::create_dir_all(parent).expect("Falha ao criar diretório para sanitizados"); + } + + // Altera a extensão para .sanitized.csv (ou mantém .csv, como preferir) + let dest_path = dest_path.with_extension("sanitized.csv"); + + // Escreve o arquivo + if let Err(e) = std::fs::write(&dest_path, &ai_response) { + eprintln!("⚠️ Erro ao salvar CSV sanitizado em {:?}: {}", dest_path, e); + } else { + eprintln!("💾 CSV sanitizado salvo em: {:?}", dest_path); + } + } else { + eprintln!("⚠️ Caminho do arquivo não está dentro de ./evaluations: {:?}", file_path_csv.path()); + } - let mut deserialized_iter = reader.deserialize::(); - let mut columns = deserialized_iter - .filter_ok(|value| value.PONTOS.is_some()) - .map_ok(|value| { - let col = - Column::new(value.CATEGORIA.into(), [value.PONTOS.unwrap() as u32]); - col - }) - .filter_map(|value| { - if value.is_ok() { - return Some(value.unwrap()); - } - None - }) - .collect_vec(); + let mut reader = csv::ReaderBuilder::new() + .has_headers(true) + .delimiter(b';') + .from_reader(ai_response.as_bytes()); + + // ---------- LOG 2: tenta desserializar e conta os registros ---------- + let deserialized = reader.deserialize::().collect::>(); + eprintln!("🧾 Total de linhas lidas (incluindo cabeçalho?): {}", deserialized.len()); + + for (i, result) in deserialized.iter().enumerate() { + match result { + Ok(record) => { + eprintln!("✅ Linha {}: CATEGORIA={}, PONTOS={:?}", i, record.CATEGORIA, record.PONTOS); + } + Err(e) => { + eprintln!("❌ Linha {} ERRO: {}", i, e); + } + } + } + // + + //let mut deserialized_iter = reader.deserialize::(); +// let mut columns = deserialized_iter + // .filter_ok(|value| value.PONTOS.is_some()) + // .map_ok(|value| { + // let col = + // Column::new(value.CATEGORIA.into(), [value.PONTOS.unwrap() as u32]); + // col + // }) + // .filter_map(|value| { + // if value.is_ok() { + // return Some(value.unwrap()); + // } + // None + //}) + //.collect_vec(); + + let mut columns = deserialized + .into_iter() // usa os registros já lidos + .filter_ok(|value| { + // Se PONTOS for None, considera como 0 e mantém a linha + true // sempre mantém + }) + .map_ok(|value| { + let pontos = value.PONTOS.unwrap_or(0) as u32; + Column::new(value.CATEGORIA.into(), [pontos]) + }) + //.filter_ok(|value| value.PONTOS.is_some()) + //.map_ok(|value| { + //let pontos = value.PONTOS.unwrap() as u32; + //Column::new(value.CATEGORIA.into(), [pontos]) + // }) + .filter_map(|value| value.ok()) + .collect_vec(); + if columns.len() != 8 { return None;