chore: extract filtering into filter_map instead of for_each

This commit is contained in:
Jelson Stoelben Rodrigues
2025-08-16 09:45:03 -03:00
parent 82c7c151e9
commit 9d017f7b80
2 changed files with 53 additions and 47 deletions

30
PROMPT.txt Normal file
View File

@@ -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.

View File

@@ -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::<serde_json::Value>().expect("Failed to deserialize response to JSON");
.filter_map_ok(|result| {
let json = result.json::<serde_json::Value>().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}");}
};