diff --git a/PROMPT.txt b/PROMPT.txt new file mode 100644 index 0000000..cffca9e --- /dev/null +++ b/PROMPT.txt @@ -0,0 +1,30 @@ +SEGUINDO OS CRITÉRIOS QUE VÃO ESTAR ABAIXO, AVALIE ATENDIMENTOS DE SUPORTE PRESTADOS VIA CHAT. + +01 (APRESENTAÇÃO) - O AGENTE DEVE SE APRESENTAR NO INÍCIO DO ATENDIMENTO. + +02 (CONFIRMAÇÃO DE E-MAIL) - O AGENTE DEVE SOLICITAR OU CONFIRMAR O E-MAIL DO CLIENTE DURANTE A CONVERSA. + +03 (PROTOCOLO) - O AGENTE DEVE INFORMOU O PROTOCOLO DE ATENDIMENTO DURANTE A CONVERSA. + +04 (USO DO PORTUGUÊS) - O AGENTE DEVE UTILIZAR CORRETAMENTE O PORTUGUÊS. LEMBRANDO QUE SOMOS UMA EMPRESA REGIONAL, UTILIZAMOS UMA LINGUAGEM MAIS INFORMAL. NÃO ESTÁ ERRADO SE O AGENTE UTILIZAR 'TU', 'TEU', 'TUA'. + +05 (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', 'VOU VERIFICAR, AGUARDE UM MOMENTO POR GENTILEZA', 'DESCULPE, MAS NÃO TE COMPREENDI', 'PODERIA EXPLICAR DE NOVO?', 'OBRIGADO PELAS INFORMAÇÕES'. + +06 (DISPONIBILIDADE) - O AGENTE DEVE SE COLOCAR À DISPOSIÇÃO DO CLIENTE, DEIXANDO CLARO QUE A EMPRESA ESTÁ SEMPRE DISPONÍVEL PARA AJUDAR +---------- + +As mensagens do chat estão estruturadas no formato JSON com os campos: +- **message**: conteúdo da mensagem +- **sent_at**: horário de envio +- **type**: tipo da mensagem ('IN', 'OUT' ou 'SYSTEM'). As mensagens do agente são sempre do tipo 'OUT'. +- **user_name**: nome do usuário que enviou a mensagem. Não considere respostas do PipeBot como do agente + +O fluxo de mensagens inica-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. + +A sua resposta deve ser uma apenas uma tabela CSV e nada mais, utilizando como separador o caracter ';' com as colunas: CATEGORIA;PONTOS;MOTIVO;EVIDENCIA onde cada linha contém a avaliação de um dos critérios acima. + +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. \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3e59b1c..7642fee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -170,6 +170,9 @@ fn main() -> anyhow::Result<()> { std::fs::create_dir(format!("./evaluations/{formatted_day_before}")).expect("Failed to create directory") } + // Read system prompt + let prompt = std::fs::read_to_string("PROMPT.txt").unwrap(); + let talks_array = get_piperun_chats_on_date(&PIPERUN_API_URL, &client, &access_token, formatted_day_before_at_midnight, formatted_day_before_at_23_59_59); println!("Number of consolidated talks: {}", talks_array.len()); @@ -206,16 +209,14 @@ fn main() -> anyhow::Result<()> { return talk_id_get_result; }) - .skip(0) - .take(15) - .for_each(|result| { - let json = result.unwrap().json::().expect("Failed to deserialize response to JSON"); + .filter_map_ok(|result| { + let json = result.json::().expect("Failed to deserialize response to JSON").to_owned(); let talk_histories = &json["talk_histories"]; let data = &talk_histories["data"]; // Filter chats that have very few messages let talk_lenght = talk_histories.as_array().expect("Wrong message type received from talk histories").len(); - if talk_lenght < MINIMUM_NUMBER_OF_MESSAGES_TO_EVALUATE {return;} + if talk_lenght < MINIMUM_NUMBER_OF_MESSAGES_TO_EVALUATE {return None;} // Filter chats that have less that specified ammount of talks with support agent form the last queue transfer let found = talk_histories.as_array().expect("Wrong message type received from talk histories").into_iter().enumerate().find(|(pos, message_object)|{ @@ -225,10 +226,10 @@ fn main() -> anyhow::Result<()> { }); match found { - None => {return;}, + None => {return None;}, Some(pos) => { let pos_found = pos.0; - if pos_found < MINIMUM_NUMBER_OF_MESSAGES_WITH_AGENT_TO_EVALUATE {return;} + if pos_found < MINIMUM_NUMBER_OF_MESSAGES_WITH_AGENT_TO_EVALUATE {return None;} } }; @@ -238,7 +239,7 @@ fn main() -> anyhow::Result<()> { .filter(|name| name.as_str().unwrap() != "PipeBot".to_string()) .unique() .count(); - if number_of_agents_except_pipebot > 1 {return;} + if number_of_agents_except_pipebot > 1 {return None;} // Filter stop notification active let found_stop_notification = talk_histories.as_array().expect("Wrong message type received from talk histories").into_iter().enumerate().find(|(pos, message_object)|{ @@ -247,11 +248,20 @@ fn main() -> anyhow::Result<()> { found.is_some() }); - if found_stop_notification.is_some() {return;} + if found_stop_notification.is_some() {return None;} // Filter Bot finished chats - if json["agent"]["user"]["name"].as_str().unwrap_or("unknown_user") == "PipeBot" {return;} + if json["agent"]["user"]["name"].as_str().unwrap_or("unknown_user") == "PipeBot" {return None;} + return Some(json); + }) + .skip(0) + .take(15) + .for_each(|result| { + let json = result.unwrap(); + let talk_histories = &json["talk_histories"]; + let data = &talk_histories["data"]; + let talk = talk_histories.as_array().expect("Wrong message type received from talk histories").iter().rev().map(|message_object| { @@ -271,47 +281,13 @@ fn main() -> anyhow::Result<()> { new_json_filtered }).reduce(|acc, e| {format!("{acc}\n{e}")}).expect("Error extracting talk"); -let system = -" -SEGUINDO OS CRITÉRIOS QUE VÃO ESTAR ABAIXO, AVALIE ATENDIMENTOS DE SUPORTE PRESTADOS VIA CHAT. - -01 (APRESENTAÇÃO) - O AGENTE DEVE SE APRESENTAR NO INÍCIO DO ATENDIMENTO. - -02 (CONFIRMAÇÃO DE E-MAIL) - O AGENTE DEVE SOLICITAR OU CONFIRMAR O E-MAIL DO CLIENTE DURANTE A CONVERSA. - -03 (PROTOCOLO) - O AGENTE DEVE INFORMOU O PROTOCOLO DE ATENDIMENTO DURANTE A CONVERSA. - -04 (USO DO PORTUGUÊS) - O AGENTE DEVE UTILIZAR CORRETAMENTE O PORTUGUÊS. LEMBRANDO QUE SOMOS UMA EMPRESA REGIONAL, UTILIZAMOS UMA LINGUAGEM MAIS INFORMAL. NÃO ESTÁ ERRADO SE O AGENTE UTILIZAR 'TU', 'TEU', 'TUA'. - -05 (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', 'VOU VERIFICAR, AGUARDE UM MOMENTO POR GENTILEZA', 'DESCULPE, MAS NÃO TE COMPREENDI', 'PODERIA EXPLICAR DE NOVO?', 'OBRIGADO PELAS INFORMAÇÕES'. - -06 (DISPONIBILIDADE) - O AGENTE DEVE SE COLOCAR À DISPOSIÇÃO DO CLIENTE, DEIXANDO CLARO QUE A EMPRESA ESTÁ SEMPRE DISPONÍVEL PARA AJUDAR ----------- - -As mensagens do chat estão estruturadas no formato JSON com os campos: -- **message**: conteúdo da mensagem -- **sent_at**: horário de envio -- **type**: tipo da mensagem ('IN', 'OUT' ou 'SYSTEM'). As mensagens do agente são sempre do tipo 'OUT'. -- **user_name**: nome do usuário que enviou a mensagem. Não considere respostas do PipeBot como do agente - -O fluxo de mensagens inica-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. - -A sua resposta deve ser uma apenas uma tabela CSV e nada mais, utilizando como separador o caracter ';' com as colunas: CATEGORIA;PONTOS;MOTIVO;EVIDENCIA onde cada linha contém a avaliação de um dos critérios acima. - -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. - -"; - // println!("{system}\n {talk}"); + println!("{prompt}\n {talk}"); let ollama_api_request = client.post(format!("http://{OLLAMA_SANITIZED_IP}:{OLLAMA_PORT}/api/generate")) .body( serde_json::json!({ "model": OLLAMA_AI_MODEL, - "prompt": format!("{system} \n{talk}"), + "prompt": format!("{prompt} \n{talk}"), // "options": serde_json::json!({"temperature": 0.1}), "stream": false, }).to_string() @@ -336,7 +312,7 @@ A seguir estão as mensagens do atendimento, em JSON, avalie e retorne apenas um let talk_id = &json["id"].as_u64().unwrap_or(0); let tracking_number = &json["tracking_number"].as_str().unwrap_or(""); std::fs::write(format!("./evaluations/{}/{} - {} - {}.csv", formatted_day_before, user_name, talk_id, tracking_number), csv_response).expect("Unable to write file"); - std::fs::write(format!("./evaluations/{}/{} - {} - {} - prompt.txt", formatted_day_before, user_name, talk_id, tracking_number), format!("{system} \n{talk}")).expect("Unable to write file"); + std::fs::write(format!("./evaluations/{}/{} - {} - {} - prompt.txt", formatted_day_before, user_name, talk_id, tracking_number), format!("{prompt} \n{talk}")).expect("Unable to write file"); }, Err(error) => {println!("Error {error}");} };