Seguidores

Mostrando postagens com marcador Solução de Problemas. Mostrar todas as postagens
Mostrando postagens com marcador Solução de Problemas. Mostrar todas as postagens

segunda-feira, 9 de junho de 2025

Estudo de Caso T2E6: Solução de Problema com IA e MASP - Correção T2E2

INTRODUÇÃO

Este é o episódio 6 da temporada 2 da série “Estudo de Caso”, com foco na correção de um Bug em aplicativo Web, utilizando Inteligência Artificial (IA).





O objetivo deste Post é mostrar um Prompt para pedir a um Chatbot de IA, uma explicação a respeito de uma correção na configuração onde ocorreu o Bug relatado no Episódio 2 da Temporada 2 da Série Estudo de Caso.

No Episódio 2 o Chatbot de IA Gemini forneceu um Plano de Ação para descobrir a causa do Bug e neste Episódio 2.

Quem corrigiu o bug foi um especialista na tecnologia do app, fazendo ajustes no arquivo de configurações.

O Gemini, neste caso, vai explicar a correção do Bug.

O Prompt terá a estrutura abaixo:
  1. Objetivo 
  2. Contexto 

Prompt

1 Objetivo

Explique os parâmetros de configuração que foram alterados, para corrigir o bug.

A correção foi realizada pelo especialista na tecnologia onde a App Web está implementada, mas eu preciso entender esses parâmetros de configuração e o funcionamento associado.

2 Contexto

O contexto é de um App Web, com FrontEnd React JS e BackEnd Django, com a configuração abaixo:

2.1 Configuração que havia quando ocorreu o Bug

CORS_ALLOWED_ORIGINS = ["http://0.0.0.0:9501", "http://0.0.0.0:2100",
                               "http://localhost:3000", "http://127.0.0.1:5200",
                               "https://dev-app.votenet.net", "http://dev-app.votenet.net",
                               "http://homologacao.votenet.net", "https://homologacao.votenet.net",
                               "https://dev.votenet.net", "http://dev.votenet.net",
                               "https://app.votenet.net", "http://app.votenet.net",
                               "https://api-dev.votenet.net", "https://api.votenet.net"]
# CORS_ALLOWED_ORIGIN_REGEXES = ["http://0.0.0.0:9501", "http://0.0.0.0:2100",
#                                "http://localhost:3000", "http://127.0.0.1:5200",
#                                "https://dev-app.votenet.net", "http://dev-app.votenet.net",
#                                "http://homologacao.votenet.net", "https://homologacao.votenet.net",
#                                "https://dev.votenet.net", "http://dev.votenet.net",
#                                "https://app.votenet.net", "http://app.votenet.net",
#                                "https://api-dev.votenet.net", "https://api.votenet.net"]
# CORS_ALLOW_ALL_ORIGINS = ["*"]
# CORS_ORIGIN_ALLOW_ALL = True
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

2.2 Configuração que ficou após a correção do Bug

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = ["http://0.0.0.0:9501", "http://0.0.0.0:2100",
                               "http://localhost:3000", "http://127.0.0.1:5200",
                               "https://dev-app.votenet.net", "http://dev-app.votenet.net",
                               "http://homologacao.votenet.net", "https://homologacao.votenet.net",
                               "https://dev.votenet.net", "http://dev.votenet.net",
                               "https://app.votenet.net", "http://app.votenet.net",
                               "https://api-dev.votenet.net", "https://api.votenet.net"]

CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS

Resposta do Gemini


Com certeza! A correção do bug foi feita adicionando dois parâmetros essenciais para a comunicação segura e autenticada entre um front-end e um back-end que estão em domínios diferentes: CORS_ALLOW_CREDENTIALS e CSRF_TRUSTED_ORIGINS.

Abaixo explico o que cada um faz e por que a ausência deles provavelmente causou o problema.


Resumo da Correção

Foram adicionados dois parâmetros de configuração ao Django para resolver o bug:

  1. CORS_ALLOW_CREDENTIALS = True: Permite que o navegador do usuário envie "credenciais" (como cookies de sessão ou tokens de autenticação) em requisições para o back-end.
  2. CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS: Informa ao Django que as origens (domínios) do front-end são confiáveis, permitindo que elas realizem operações que alteram dados (como POST, PUT, DELETE).

1. CORS_ALLOW_CREDENTIALS = True

Este parâmetro está diretamente ligado ao CORS (Cross-Origin Resource Sharing), que é um mecanismo de segurança do navegador.

  • O que são "Credenciais"? No contexto de um navegador, "credenciais" não são apenas login e senha. Elas incluem principalmente:

    • Cookies: Usados para gerenciar sessões de usuário.
    • Tokens de Autenticação: Enviados no cabeçalho da requisição (ex: Authorization: Bearer <token>).
    • Certificados TLS de cliente.
  • Por que a alteração foi necessária? Por padrão, mesmo que você liste um domínio em CORS_ALLOWED_ORIGINS, o navegador não envia credenciais em requisições para esse domínio por motivos de segurança.

    O bug provavelmente ocorria quando o seu front-end em React tentava fazer uma chamada à API que necessitava de autenticação (por exemplo, buscar dados do perfil do usuário logado). O navegador, ao ver que a requisição era "cross-origin", bloqueava o envio do cookie de sessão ou do token de autenticação. O back-end Django, ao receber a requisição sem essas credenciais, a tratava como uma chamada anônima e a recusava, gerando um erro.

  • Como a correção funciona? Ao definir CORS_ALLOW_CREDENTIALS = True, o servidor Django passa a incluir o cabeçalho Access-Control-Allow-Credentials: true em suas respostas. Isso é um sinal para o navegador do cliente, dizendo: "Pode confiar em mim. Na próxima vez que fizer uma requisição, pode me enviar os cookies e tokens de autenticação que você tem para este domínio."


2. CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS

Este parâmetro está relacionado a outra camada de segurança do Django: a proteção contra CSRF (Cross-Site Request Forgery).

  • O que é CSRF? É um tipo de ataque onde um site malicioso engana o navegador de um usuário logado para que ele envie uma requisição indesejada para outro site (o seu back-end). O Django possui um sistema de proteção robusto que usa um token (csrfmiddlewaretoken) para validar que as requisições que alteram dados (como POST, PUT, DELETE) vêm de uma fonte confiável (geralmente um formulário renderizado pelo próprio Django).

  • Por que a alteração foi necessária? Em uma arquitetura com front-end e back-end separados (React + Django), o front-end está em um domínio diferente. Para métodos "inseguros" (que alteram dados), a proteção CSRF do Django também verifica o cabeçalho Origin da requisição. Se a origem não estiver em uma lista de domínios confiáveis, o Django bloqueia a requisição com um erro 403 Forbidden.

    O bug provavelmente acontecia em qualquer operação que não fosse uma simples leitura de dados (GET). Por exemplo, ao tentar salvar um formulário, criar um novo item ou deletar algo. O React enviava a requisição POST, mas como a origem (https://app.votenet.net, por exemplo) não era explicitamente confiável para o mecanismo CSRF, o Django a rejeitava.

  • Como a correção funciona? A linha CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS resolve isso de forma elegante. Ela diz ao Django: "Qualquer domínio que já foi autorizado para fazer requisições (pela configuração do CORS) também é considerado confiável para enviar requisições que alteram dados."

    Isso reutiliza a lista já existente de origens permitidas, garantindo que apenas os seus front-ends autorizados possam executar ações de escrita na API.

Conclusão

A correção foi executada pelo desenvolvedor e os detalhes técnicos explicados pelo Gemini.


Demais Episódios da Temporada:


Estudo de Caso T2E5: Solução de Problema com IA e MASP - Correção T2E4

 INTRODUÇÃO

Este é o episódio 5 da temporada 2 da série “Estudo de Caso”, com foco na correção de um Bug em aplicativo Web, utilizando Inteligência Artificial (IA).



O objetivo deste Post é mostrar um Prompt para pedir a um Chatbot de IA, uma correção no código fonte onde ocorreu o Bug relatado no Episódio 4 da Temporada 2 da Série Estudo de Caso.

No Episódio 4 o Chatbot de IA Gemini forneceu um Plano de Ação para descobrir a causa do Bug e neste Episódio 5, o Gemini fará a correção do Bug.

O Prompt terá a estrutura abaixo:
  1. Objetivo
  2. Resultado
  3. Alertas  
  4. Contexto 
Para informações adicionais sobre essa estrutura de Prompt veja este post: Dicas de prompt para Chatbot de IA fazer mudanças em aplicativos

1 Objetivo

O objetivo deste prompt é solicitar ao Chatbot de IA Gemini a implementação de mudanças no código fonte fornecido no item 4 Contexto, escrito em React JS, a fim de corrigir um bug, por meio da implementação da regra abaixo:

"O botão RESUMO deve ser apresentado, incondicionalmente".

2 Resultado

O resultado deve ser um conjunto de mudanças no código fonte React JS, fornecido no item 4 Contexto.

As mudanças devem ficar prontas para execução, sem nenhuma intervenção do desenvolvedor. 

3 Alerta

O código fonte da correção deve ser escrito para a versão 15.6.1 do React JS.

4 Contexto

O contexto é caracterizado pelo código fonte abaixo.

import React from 'react';
import {Link} from 'react-router';
import ajax from '../../ajax-utils';
import { Modal, Button } from 'react-materialize';

let SectionCandidateDateil = React.createClass({
    /* Adicionado pelo AI Assistant  */
    getInitialState: function() {
        return {
          user_id: this.props.user.id,
          elections: [],
          litigation: this.props.user.public_perfil != null && this.props.user.public_perfil.litigation != null ? this.props.user.public_perfil.litigation : null,
          nomeEleicao: ' ',
          idEleicao: 0,
          roleName: this.props.user.public_perfil != null && this.props.user.public_perfil.litigation != null ? this.props.user.public_perfil.litigation.role : null,
          partyName: this.props.user.public_perfil != null && this.props.user.public_perfil.litigation != null ? this.props.user.public_perfil.litigation.party : null,
          statusName: this.props.user.public_perfil != null && this.props.user.public_perfil.litigation != null ? this.props.user.public_perfil.litigation.status : null,
          loading_fetchElection:false,
          loading_fetchLitigation:false,
          loading_getRoleName: false,
          loading_getPartyName: false,
          loading_getStatusName: false
        };
    },
    /* Adicionado pelo AI Assistant  */
    componentDidMount: function() {
    /*
        ajax.fetchElection().then(elections => {
          this.setState({elections});
          if (elections.length > 0) {
              this.updateLitigationDetails(elections[0].id);
          }
        });
    */
        this.setState({loading_fetchElection:true});
        ajax.fetchElection().then(data => {
          let {err, elections} = data;
          if (err) return alert(err);
          if (elections.length > 0 && this.state.litigation != null) {
              this.updateLitigationDetails(elections[0].name, elections[0].id);
          }

          this.setState({
            elections: elections,
            nomeEleicao: elections[0].name,
            idEleicao: elections[0].id,
            loading_fetchElection:false
          });
        });
    },
    showModal () {
        $('#remuseppc').modal('open');
    },
    /* Adicionado pelo AI Assistant  */
    handleElectionChange(event) {
      const selectElement = event.target;
      const electionName = selectElement.options[selectElement.selectedIndex].text;
      const electionId = selectElement.value;
      this.setState({
          nomeEleicao: electionName,
          idEleicao: electionId
      });
      this.updateLitigationDetails(electionName, electionId);
    },
    updateLitigationDetails(electionName, electionId) {
      this.setState({loading_fetchLitigation:true});
      ajax.GetLitigationByElectorAndElection(this.props.user.id, electionId, electionName)
        .then(data => {
            let {err, litigation} = data;
            if (err) return alert(err);
            if (litigation.id !== -1 && litigation.id !== -2){
                this.getRoleName(litigation.role);
                this.getPartyName(litigation.party);
                this.getStatusName(litigation.status);
                this.setState({
                    litigation: litigation
                });
            }else{
                this.setState({
                    litigation: null,
                    roleName: " ",
                    partyName: " ",
                    statusName: " "
                });
                if (litigation.id === -1 ){
                    alert('Os dados do candidato não foram atualizados para a eleição!');
                    console.log('SectionCandidateDateil - updateLitigationDetails: Os dados do candidato não foram atualizados para a eleição!');
                }else{
                    if (litigation.id === -2 ){
                       alert('O status de candidato não foi atualizado para a Pessoa Pública!');
                       console.log('SectionCandidateDateil - updateLitigationDetails: O status de candidato não foi atualizado para a Pessoa Pública!');
                    }
                }
            }
            this.setState({
                loading_fetchLitigation:false
            });
        });
    },
    getRoleName(role_id){
        this.setState({loading_getRoleName:true});
        ajax.getRoleName(role_id).then(data => {
          let {err, role_name} = data;
          if (err) return alert(err);

          this.setState({
            roleName: role_name,
            loading_getRoleName:false
          });
        });
    },
    getPartyName(party_id){
        this.setState({loading_getPartyName:true});
        ajax.getPartyName(party_id).then(data => {
          let {err, party_name} = data;
          if (err) return alert(err);

          this.setState({
            partyName: party_name,
            loading_getPartyName:false
          });
        });
    },
    getStatusName(status_id){
        this.setState({loading_getStatusName:true});
        ajax.getStatusName(status_id).then(data => {
          let {err, status_name} = data;
          if (err) return alert(err);

          this.setState({
            statusName: status_name,
            loading_getStatusName:false
          });
        });
    },

  render() {
    let {user} = this.props;
    let {isLogged} = this.props;
    let shouldShowElectionDropdown = user.public_perfil != null && user.public_perfil.litigation != null &&
                                      ['Candidato', 'Pre-Candidato'].includes(user.public_perfil.litigation.status);
    return (
        <div>
            {
            user.public_perfil ? (              
                <div className="row col s12 m6 l6 xl6"> 

                    <h6 className="user-name">Perfil Público</h6>
                    <div className="row col s12 m6 l6 xl6">  
                    { user.public_perfil.email ? (<p ><i className="icon-mail-alt blue-text darken-2"></i> 
                     <a href={"mailto:" + user.public_perfil.email}> {user.public_perfil.email}</a></p>) : (
                        !isLogged ? (null): 
                            (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>)                        
                     ) }

                    { user.public_perfil.facebook.length > 7 ? (
                        <p ><i className="icon-facebook-squared blue-text darken-2"></i> 
                        <a target="_blank" href={user.public_perfil.facebook}>Facebook</a></p>
                    ) : (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>)}
                    
                    {user.public_perfil.personal.length > 7 ? (
                        <p ><i className=" icon-doc-7 blue-text darken-2"></i> 
                        <a target="_blank" href={user.public_perfil.personal}> Página</a></p>
                    ) : (
                        !isLogged ? (null): 
                        (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>) 
                    )} 
                   
                    
                    {user.public_perfil.video.length > 7 ? (
                        <p ><i className=" icon-videocam-3 blue-text darken-2"></i> 
                        <a target='__black' href={user.public_perfil.video}>Vídeo</a></p>
                        ) : (
                            !isLogged ? (null): 
                            (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>) 
                        )} 
                    
                    
                    {isLogged && user.public_perfil.resumo ? (
                        <p ><strong>Resumo:</strong> 
                            <buttom onClick={this.showModal}
                            className="waves-effect waves-light btn-small btn green-text custom-btn margin-bottom-10">Clique aqui </buttom>
                        </p>
                        ) : (
                            !isLogged ? (null): 
                            (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>) 
                        )} 
                    <div className="col m5 s12 l5 xl5 center">
                                       
                                        <Modal id='remuseppc' 
                                        header='Resumo Perfil Público' 
                                        actions={
                                              <Button waves='light' flat className="btn-small waves-light green darken-2 white-text modal-action modal-close">Fechar</Button>
                                          }
                                        >
                                                         
                                        <h4><strong>{user.name}</strong></h4>
                                        <p>{user.public_perfil.resumo}</p>
                                        </Modal>
                                    </div>
                     </div>
                    {
                        user.public_perfil.litigation ? (
                            <div className="row col s12 m6 l6 xl6">
                                <p >{this.state.nomeEleicao}</p>
                                <p ><strong>Cargo:</strong> {this.state.roleName}</p>
                                <p ><strong>Partido:</strong> {this.state.partyName}</p>
                                <p ><strong>Status:</strong> {this.state.statusName}</p>
                                {
                                    this.state.litigation != null && this.state.litigation.number ? (
                                    <p ><strong>Número:</strong> {this.state.litigation.number}</p>
                                    ) : null
                                }
                            </div>
                        ): null
                    }

{
                user.public_perfil &&
                user.public_perfil.litigation &&
                window.location.href.includes("/profile/" + user.slug) ?
                (<div className="row col s12 m10 l10 xl10">
                  <a className="btn waves-light light-green darken-4 white-text center" 
                     href={`https://wa.me/?text=Para melhorar nossa cidade e a câmara legislativa, seguem informações sobre um candidato a considerar nessas eleições. ${window.location.href}`} 
                     data-action="share/whatsapp/share">Compartilhe aqui seu candidato</a>                        
                </div>) : (null)
              }
                </div> ):null
            }   
            
        </div>

    );
  }
});

export default SectionCandidateDateil;

Resposta do Gemini

Em relação ao seu pedido, o botão RESUMO será exibido incondicionalmente, conforme a regra de negócio que você especificou.


JavaScript
import React from 'react';
import {Link} from 'react-router';
import ajax from '../../ajax-utils';
import { Modal, Button } from 'react-materialize';

let SectionCandidateDateil = React.createClass({
    /* Adicionado pelo AI Assistant  */
    getInitialState: function() {
        return {
          user_id: this.props.user.id,
          elections: [],
          litigation: this.props.user.public_perfil != null && this.props.user.public_perfil.litigation != null ? this.props.user.public_perfil.litigation : null,
          nomeEleicao: ' ',
          idEleicao: 0,
          roleName: this.props.user.public_perfil != null && this.props.user.public_perfil.litigation != null ? this.props.user.public_perfil.litigation.role : null,
          partyName: this.props.user.public_perfil != null && this.props.user.public_perfil.litigation != null ? this.props.user.public_perfil.litigation.party : null,
          statusName: this.props.user.public_perfil != null && this.props.user.public_perfil.litigation != null ? this.props.user.public_perfil.litigation.status : null,
          loading_fetchElection:false,
          loading_fetchLitigation:false,
          loading_getRoleName: false,
          loading_getPartyName: false,
          loading_getStatusName: false
        };
    },
    /* Adicionado pelo AI Assistant  */
    componentDidMount: function() {
    /*
        ajax.fetchElection().then(elections => {
          this.setState({elections});
          if (elections.length > 0) {
              this.updateLitigationDetails(elections[0].id);
          }
        });
    */
        this.setState({loading_fetchElection:true});
        ajax.fetchElection().then(data => {
          let {err, elections} = data;
          if (err) return alert(err);
          if (elections.length > 0 && this.state.litigation != null) {
              this.updateLitigationDetails(elections[0].name, elections[0].id);
          }

          this.setState({
            elections: elections,
            nomeEleicao: elections[0].name,
            idEleicao: elections[0].id,
            loading_fetchElection:false
          });
        });
    },
    showModal () {
        $('#remuseppc').modal('open');
    },
    /* Adicionado pelo AI Assistant  */
    handleElectionChange(event) {
      const selectElement = event.target;
      const electionName = selectElement.options[selectElement.selectedIndex].text;
      const electionId = selectElement.value;
      this.setState({
          nomeEleicao: electionName,
          idEleicao: electionId
      });
      this.updateLitigationDetails(electionName, electionId);
    },
    updateLitigationDetails(electionName, electionId) {
      this.setState({loading_fetchLitigation:true});
      ajax.GetLitigationByElectorAndElection(this.props.user.id, electionId, electionName)
        .then(data => {
            let {err, litigation} = data;
            if (err) return alert(err);
            if (litigation.id !== -1 && litigation.id !== -2){
                this.getRoleName(litigation.role);
                this.getPartyName(litigation.party);
                this.getStatusName(litigation.status);
                this.setState({
                    litigation: litigation
                });
            }else{
                this.setState({
                    litigation: null,
                    roleName: " ",
                    partyName: " ",
                    statusName: " "
                });
                if (litigation.id === -1 ){
                    alert('Os dados do candidato não foram atualizados para a eleição!');
                    console.log('SectionCandidateDateil - updateLitigationDetails: Os dados do candidato não foram atualizados para a eleição!');
                }else{
                    if (litigation.id === -2 ){
                       alert('O status de candidato não foi atualizado para a Pessoa Pública!');
                       console.log('SectionCandidateDateil - updateLitigationDetails: O status de candidato não foi atualizado para a Pessoa Pública!');
                    }
                }
            }
            this.setState({
                loading_fetchLitigation:false
            });
        });
    },
    getRoleName(role_id){
        this.setState({loading_getRoleName:true});
        ajax.getRoleName(role_id).then(data => {
          let {err, role_name} = data;
          if (err) return alert(err);

          this.setState({
            roleName: role_name,
            loading_getRoleName:false
          });
        });
    },
    getPartyName(party_id){
        this.setState({loading_getPartyName:true});
        ajax.getPartyName(party_id).then(data => {
          let {err, party_name} = data;
          if (err) return alert(err);

          this.setState({
            partyName: party_name,
            loading_getPartyName:false
          });
        });
    },
    getStatusName(status_id){
        this.setState({loading_getStatusName:true});
        ajax.getStatusName(status_id).then(data => {
          let {err, status_name} = data;
          if (err) return alert(err);

          this.setState({
            statusName: status_name,
            loading_getStatusName:false
          });
        });
    },

  render() {
    let {user} = this.props;
    let {isLogged} = this.props;
    let shouldShowElectionDropdown = user.public_perfil != null && user.public_perfil.litigation != null &&
                                     ['Candidato', 'Pre-Candidato'].includes(user.public_perfil.litigation.status);
    return (
        <div>
            {
            user.public_perfil ? (         
                <div className="row col s12 m6 l6 xl6"> 

                    <h6 className="user-name">Perfil Público</h6>
                    <div className="row col s12 m6 l6 xl6">  
                    { user.public_perfil.email ? (<p ><i className="icon-mail-alt blue-text darken-2"></i>  
                     <a href={"mailto:" + user.public_perfil.email}> {user.public_perfil.email}</a></p>) : (
                         !isLogged ? (null): 
                             (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>)         
                      ) }

                    { user.public_perfil.facebook.length > 7 ? (
                        <p ><i className="icon-facebook-squared blue-text darken-2"></i>  
                         <a target="_blank" href={user.public_perfil.facebook}>Facebook</a></p>
                    ) : (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>)}
                    
                    {user.public_perfil.personal.length > 7 ? (
                        <p ><i className=" icon-doc-7 blue-text darken-2"></i>  
                         <a target="_blank" href={user.public_perfil.personal}> Página</a></p>
                    ) : (
                        !isLogged ? (null): 
                        (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>) 
                    )} 
                    
                    
                    {user.public_perfil.video.length > 7 ? (
                        <p ><i className=" icon-videocam-3 blue-text darken-2"></i>  
                         <a target='__black' href={user.public_perfil.video}>Vídeo</a></p>
                         ) : (
                             !isLogged ? (null): 
                             (<Link to={'/profile/electoral/edit'} className="red-text text-darken-2" > <p>Favor completar seu perfil</p> </Link>) 
                         )} 
                    
                    
                    {/* Alteração para exibir o botão RESUMO incondicionalmente */}
                    <p ><strong>Resumo:</strong>  
                        <button onClick={this.showModal}
                        className="waves-effect waves-light btn-small btn green-text custom-btn margin-bottom-10">Clique aqui </button>
                    </p>
                    <div className="col m5 s12 l5 xl5 center">
                                            
                                             <Modal id='remuseppc' 
                                             header='Resumo Perfil Público' 
                                             actions={
                                                     <Button waves='light' flat className="btn-small waves-light green darken-2 white-text modal-action modal-close">Fechar</Button>
                                                  }
                                             >
                                                     
                                             <h4><strong>{user.name}</strong></h4>
                                             <p>{user.public_perfil.resumo}</p>
                                             </Modal>
                                         </div>
                     </div>
                    {
                        user.public_perfil.litigation ? (
                            <div className="row col s12 m6 l6 xl6">
                                <p >{this.state.nomeEleicao}</p>
                                <p ><strong>Cargo:</strong> {this.state.roleName}</p>
                                <p ><strong>Partido:</strong> {this.state.partyName}</p>
                                <p ><strong>Status:</strong> {this.state.statusName}</p>
                                {
                                    this.state.litigation != null && this.state.litigation.number ? (
                                    <p ><strong>Número:</strong> {this.state.litigation.number}</p>
                                    ) : null
                                }
                            </div>
                        ): null
                    }

{
                user.public_perfil &&
                user.public_perfil.litigation &&
                window.location.href.includes("/profile/" + user.slug) ?
                (<div className="row col s12 m10 l10 xl10">
                  <a className="btn waves-light light-green darken-4 white-text center" 
                      href={`https://wa.me/?text=Para melhorar nossa cidade e a câmara legislativa, seguem informações sobre um candidato a considerar nessas eleições. ${window.location.href}`} 
                      data-action="share/whatsapp/share">Compartilhe aqui seu candidato</a>                
                </div>) : (null)
              }
                </div> ):null
            }    
            
        </div>

    );
  }
});

export default SectionCandidateDateil;

CONCLUSÃO

O teste da mudança foi realizado com sucesso.


Demais Episódios da Temporada:





terça-feira, 3 de junho de 2025

Dicas de prompt para Chatbot de IA fazer mudanças em aplicativos

INTRODUÇÃO




As dicas de prompt para mudanças em aplicativos servem para garantir que suas especificações sejam bem entendidas e que o Chatbot de IA entregue exatamente o que você precisa.

Para isso, é importante ser claro e detalhado ao escrever seu prompt. 

Estrutura do Prompt

Aqui estão sugestões para cada item:

  1. Objetivo

    • Descreva o que você deseja desenvolver ou corrigir.
    • Explique a finalidade do código (por exemplo, um script para automação de tarefas ou a otimização de um algoritmo já existente).
    • Se houver um problema específico, detalhe-o (por exemplo, "Corrigir erro de timeout em consultas ao banco de dados").
  2. Resultado

    • Defina o que espera como entrega final (novo código funcional, relatório de performance, documentação técnica).
    • Especifique quais critérios indicam que o código está funcionando corretamente (testes unitários, integração com determinado sistema).
    • Caso tenha exemplos ou requisitos de formato, inclua-os (por exemplo, "O código deve seguir os padrões de estilo PEP 8").
    • Identifique aqui a linguagem de programação, se for código novo.
      Certa vez eu não informei que a linguagem era Javascript e o Chatbot de IA criou o código em Python. Nada grave, pois pedi outra vez para Javascript e demorou mais alguns segundos.
  3. Alertas

    • Mencione restrições técnicas (linguagem de programação, compatibilidade com versões específicas).
      Certa vez eu não informei a versão da linguagem e, durante os testes, encontrei vários erros por incompatibilidade com versões de bibliotecas. Neste caso, perdi um tempo razoável, até descobrir a causa raiz dos erros.
    • Informe sobre riscos ou desafios conhecidos (por exemplo, "O código não pode depender de bibliotecas externas que não sejam suportadas pelo ambiente atual").
    • Se o código envolver questões críticas de segurança, destaque isso (por exemplo, "Evitar vulnerabilidades de injeção SQL").
  4. Contexto

    • Explique o cenário onde o código será usado (por exemplo, "Este código será integrado a um sistema legado desenvolvido em Python 2.7").
    • Se houver código existente que servirá de base, forneça detalhes (link, trecho de código, explicação da lógica).
    • Informe quem usará o código e com que nível de conhecimento técnico (por exemplo, "Usuários finais sem experiência em programação precisarão rodar o script").

Um prompt bem estruturado pode fazer toda a diferença na qualidade do código entregue. 

Erros a evitar

Criar um bom prompt exige atenção a detalhes, e alguns erros comuns podem comprometer a clareza e a qualidade do código que você recebe. Aqui estão os principais deslizes que você deve evitar:

  1. Falta de clareza e especificidade

    • Solicitações vagas geram respostas genéricas. Em vez de dizer "Preciso de um script para automatizar tarefas", explique quais tarefasem qual ambientequais entradas e saídas são esperadas.
  2. Ignorar restrições técnicas

    • Esquecer de mencionar a linguagem de programação, versão específica, framework ou ambiente em que o código será executado pode resultar em algo incompatível.
  3. Não definir critérios de sucesso

    • Sem indicar como o código deve ser testado, o desenvolvedor pode não ter um parâmetro claro para garantir que atende às suas expectativas.
  4. Excesso de detalhes irrelevantes

    • Informações desnecessárias podem dificultar a compreensão e desviar o foco do objetivo principal. Foque no essencial para a implementação.
  5. Ignorar questões de segurança

    • Se o código envolve dados sensíveis, autenticação ou acesso a sistemas externos, certifique-se de solicitar que sejam seguidas boas práticas de segurança.
  6. Não fornecer contexto suficiente

    • Muitos problemas ocorrem quando um código precisa interagir com sistemas existentes. Se houver dependências ou integrações, informe isso claramente.
  7. Pressa na entrega sem definir prioridades

    • Se for um projeto complexo, priorize o que é mais importante e o que pode ser ajustado depois. Um prazo apertado sem uma estrutura clara pode comprometer a qualidade.

No próximo tópico será apresentado um exemplo de prompt com base em um projeto específico.

Exemplo de um bom prompt

Aqui está um exemplo de um bom prompt para solicitar um serviço de programação a um Chatbot de IA. Suponha que você precise de um script Python para automatizar o envio de e-mails a partir de uma planilha do Excel.


Objetivo:
Preciso de um script em Python para automatizar o envio de e-mails usando dados de uma planilha Excel. O script deve ler os destinatários, mensagens e anexos da planilha e enviar os e-mails de forma eficiente.

Resultado esperado:

  • Um script funcional em Python.
  • Deve permitir a configuração do remetente via credenciais de autenticação.
  • Necessário suportar anexos no envio de e-mails.
  • O código deve ser bem documentado e conter instruções de uso.

Alertas:

  • O script deve usar a biblioteca smtplib para envio de e-mails.
  • A autenticação deve ser feita via OAuth2 para maior segurança, evitando o uso direto de senha.
  • O código não deve armazenar credenciais sensíveis em texto plano.
  • Certifique-se de que o script funciona em versões do Python 3.8 ou superior.

Contexto:

  • Será utilizado para automatizar envios de e-mails corporativos a partir de uma planilha de pedidos.
  • A planilha contém colunas como "Nome", "E-mail", "Mensagem" e "Arquivo Anexo".
  • O usuário final não tem experiência em programação, então a configuração deve ser simples.

Esse exemplo garante clareza, especificação técnica e medidas de segurança. 

CONCLUSÃO

Espero que estas dicas sejam úteis para o leitor escrever prompt para Chatbots de IA, com foco em geração de código para mudanças em aplicativos. 





quinta-feira, 29 de maio de 2025

Estudo de Caso T2E4: Solução de Problema com IA e MASP - Aplicativo Web

INTRODUÇÃO

Este é o episódio 4 da temporada 2 da série “Estudo de Caso”, com foco na solução de problema em aplicativo Web, utilizando Inteligência Artificial (IA) e o Método de Análise e Solução de Problemas (MASP).




O objetivo deste Post é mostrar um Prompt completo, elaborado com base no MASP, para pedir a um Chatbot de IA, um Plano de Ação para resolver um bug em app Web.



Estudo de Caso

Para este estudo de caso foi criado o Prompt abaixo, que utiliza o MASP para especificar com clareza um problema, a fim de pedir ajuda a um Chatbot de IA na solução.

Objetivo

Solucionar um problema de Tecnologia da Informação (TI), usando o Método de Análise e Solução de Problema (MASP), no contexto abaixo.

Contexto

O contexto é a execução de um aplicativo Web no ambiente de produção, no serviço de infraestrutura na nuvem AWS, no servidor S2.

O usuário relatou o bug com essa mensagem: "Desapareceu o botão RESUMO das pessoas públicas…".

1 PERSONAGEM

1.1 Qual o papel do Chatbot de IA? Profissional da área de TI especialista em desenvolvimento de aplicativo Web usando o container de desenvolvimento Docker, com Frontend React e Backend Django, usando o serviço de infraestrutura na nuvem AWS.

2 IDENTIFICAÇÃO DO PROBLEMA

Definir claramente o problema (diferença entre o que é e o que deveria ser). Utilizar 5W1H.

2.1 [What?]  - O que aconteceu diferente do esperado?

O usuário fez uma consulta e não apareceu o resultado esperado. 

O resultado esperado era um botão com o rótulo "RESUMO".

O botão esperado não apareceu. 

2.2 [Who?]   - Quem iniciou a execução? O usuário

2.3 [When?]  - Evento que iniciou a execução? Ícone "Pessoa Pública e Candidatos".

2.4 [Where?] - Onde a execução parou? Parou  após o usuário clicar no nome de um Candidato.

2.5 [Why?]   - Por que a execução parou? Não sei, mas o fato é que o botão "RESUMO" deveria aparecer após o usuário clicar no nome do Candidato. 

2.6 [How?]   - Como é o procedimento, passo a passo, para reproduzir o erro? 

Passo 1: Clicar no Ícone "Pessoa Pública e Candidatos".

Passo 2: Clicar no nome de um Candidato.

3 OBSERVAÇÃO

Investigar as características específicas do problema com uma visão ampla e sob vários pontos de vista. Utilizar Diagrama de Ishikawa ou Causa e Efeito.

3.1 [Pessoas?] Como este item influencia no problema? 

3.1.1 Falta de treinamento adequado da equipe? Não.

3.1.2 Problemas de comunicação entre membros da equipe? Não.

3.1.3 Ausência de revisões? Não

3.2 [Recurso?] Como este item influencia no problema?

3.2.1 Computador desatualizado? Não

3.2.2 Aplicativo desatualizado ou instável? Não

3.2.3 Configurações do ambiente incorretas ou inconsistentes? Não.

3.2.4 Configuração incorreta das variáveis de ambiente? Não.

3.2.5 Incompatibilidades no ambiente, ou seja, diferenças entre os ambientes onde o processo é executado? Não. 

3.2.6 Condições de rede, ou seja, problemas de conectividade que afetam a execução do processo? Não.

3.3 [Método?] Como este item influencia no problema?

3.3.1 Práticas de execução inadequadas? Não.

3.3.2 Falta de documentação do Procedimento Operacional Padrão? Não.

3.3.3 Processo de verificação deficiente, com testes insuficientes ou mal planejados? Sim.

3.4 [Entrada?] Como este item influencia no problema?

3.4.1 Informações de terceiros com falhas ou incompatíveis? Não

3.4.2 Informações de terceiros instáveis ou desatualizadas? Não

3.4.3 Dependências de Informações de terceiros inadequadas ou desatualizadas? Não.

3.5 [Saída?] Como este item influencia no problema?

3.5.1 Resultado incompleto, impreciso ou inválido? Não

3.5.2 Resultado indisponível no local esperado? Sim

3.5.3 Resultado rejeitado por quem vai utilizá-lo? Não

3.6 [Medição?] Como este item influencia no problema?

3.6.1 Falta de monitoramento adequado do desempenho? Não

3.6.2 Falta de métricas claras e consistentes para avaliar a qualidade do processo? Não

3.6.3 Ausência de ferramentas de análise para detectar problemas no processo? Não

4 ANÁLISE

Descobrir as causas fundamentais. Utilizar "5 Porquês".

4.1 Para cada problema encontrado no item 3 OBSERVAÇÃO, perguntar 5 vezes o porquê do problema ou defeito ter ocorrido, a fim de descobrir a sua real causa, ou seja, a causa raiz.

4.1.1 Método: Processo de verificação deficiente, com testes insuficientes ou mal planejados? Por quê? Efeito colateral de mudanças.

4.1.2 Saída: Resultado indisponível no local esperado? Por quê? Efeito colateral de mudanças.

COMANDO PARA O CHATBOT DE IA

Crie um Plano de Ação para resolver o problema especificado acima e, se possível, ensine como executar as ações.

O Plano de Ação deve ter 5 etapas: Iniciação, Planejamento, Execução, Monitoramento e Controle e Encerramento.

Cada etapa do Plano de Ação deve ter 3 grupos de tarefas executáveis: Entrada, Passos, Saída.

Cada Entrada deve especificar, além de dados e recursos disponíveis, as pré-condições, que são os requisitos para iniciar a execução dos passos.

Cada Saída deve especificar, além de dados e recursos disponibilizados, as pós-condições, que são os requisitos para considerar a execução encerrada com sucesso.

Resposta do Chatbot de IA Google Gemini

Link da conversa com o Chatbot de IA Gemini: https://g.co/gemini/share/79de9650c287

Plano de Ação para Solução do Bug "Botão RESUMO ausente"

Com base na sua análise usando o MASP, fica claro que o problema de o botão "RESUMO" ter desaparecido está relacionado a efeitos colaterais de mudanças que não foram cobertos por um processo de verificação robusto, resultando em um resultado indisponível no local esperado.

Como seu especialista em desenvolvimento web com React, Django, Docker e AWS, vou criar um plano de ação detalhado para resolver e prevenir esse tipo de ocorrência.


1. Iniciação

Esta etapa visa formalizar o problema, definir os objetivos e montar a equipe inicial para a resolução.

  • Entrada:

    • Dados e Recursos Disponíveis: Relato do usuário ("Desapareceu o botão RESUMO das pessoas públicas…"), análise 5W1H e Diagrama de Ishikawa (Causa e Efeito) já realizados.
    • Pré-condições: Confirmação do bug pelo usuário e priorização como incidente a ser resolvido.
  • Passos:

    1. Revisar e Validar o Problema: Confirmar que a descrição do problema (botão "RESUMO" ausente) e a reprodutibilidade (Passo 1: Clicar em "Pessoa Pública e Candidatos" -> Passo 2: Clicar no nome de um Candidato) estão corretas.
    2. Comunicar às Partes Interessadas: Notificar a equipe de desenvolvimento e stakeholders sobre o incidente, seu impacto e a prioridade.
    3. Definir o Objetivo Imediato: Restaurar a funcionalidade do botão "RESUMO" no ambiente de produção.
    4. Definir Objetivo de Longo Prazo: Implementar melhorias no processo de verificação e testes para evitar recorrências.
    5. Designar Responsáveis: Atribuir um líder técnico e membros da equipe para trabalhar na solução.
  • Saída:

    • Dados e Recursos Disponibilizados: Problema formalizado e validado, comunicação inicial realizada, objetivos definidos (curto e longo prazo), e equipe inicial designada.
    • Pós-condições: Entendimento comum do problema e alinhamento sobre a necessidade de ação.

2. Planejamento

Nesta etapa, detalharemos as ações necessárias para resolver o problema, incluindo a análise da causa raiz mais profunda, a definição das soluções e a criação de um plano de testes.

  • Entrada:

    • Dados e Recursos Disponíveis: Problema validado, objetivos definidos. Acesso ao código-fonte (React, Django), ambiente AWS, logs do S2, ferramentas de monitoramento.
    • Pré-condições: Acesso a todos os sistemas e informações relevantes para a investigação.
  • Passos:

    1. Análise Aprofundada da Causa Raiz (5 Porquês):
      • Problema: Botão "RESUMO" não aparece.
      • Por quê? (Causa 1) Efeito colateral de mudanças.
      • Por quê? (Causa 2) Uma alteração recente no código (Frontend React ou Backend Django) ou na infraestrutura (AWS) impactou a renderização/disponibilidade do botão.
      • Por quê? (Causa 3) A alteração não foi devidamente testada em cenários de regressão que incluíam a funcionalidade do botão "RESUMO".
      • Por quê? (Causa 4) O processo de testes existente é deficiente, com cobertura insuficiente para detectar efeitos colaterais em áreas não diretamente relacionadas à mudança principal.
      • Por quê? (Causa 5) Faltam testes automatizados ou um processo de revisão de código mais rigoroso para identificar possíveis impactos em componentes críticos como este botão.
    2. Identificar a Mudança Causa: Rastrear as últimas alterações no código (commits no Git) ou na configuração da infraestrutura (CloudFormation, Terraform) implantadas antes do aparecimento do bug.
    3. Propor Soluções para o Problema Imediato:
      • Opção A (Reverter): Se a mudança for recente e a regressão for clara, considerar reverter a última implantação para uma versão estável anterior.
      • Opção B (Hotfix): Desenvolver um patch rápido para corrigir especificamente a exibição do botão "RESUMO". Isso pode envolver ajustar o componente React, a API do Django ou a forma como os dados são consumidos.
    4. Propor Soluções para Prevenção (Longo Prazo):
      • Implementar mais testes de regressão automatizados para o Frontend (ex: Cypress, Jest).
      • Reforçar testes de integração no Backend (ex: Pytest com Django Test Client).
      • Melhorar o processo de Code Review para incluir verificação de impacto em funcionalidades interdependentes.
      • Considerar a adoção de testes de ponta a ponta (E2E) em ambiente de homologação.
    5. Definir Plano de Testes: Elaborar um plano de testes detalhado para a solução imediata, incluindo testes unitários, de integração e, principalmente, de regressão para o cenário reportado.
    6. Criar um Cronograma: Definir prazos realistas para cada etapa do plano de ação.
  • Saída:

    • Dados e Recursos Disponibilizados: Causa raiz identificada (falta de testes abrangentes), lista de possíveis soluções (imediata e de longo prazo), plano de testes detalhado e cronograma.
    • Pós-condições: Equipe com clareza sobre o que precisa ser feito e como será validado.

3. Execução

Nesta etapa, o plano de ação é colocado em prática.

  • Entrada:

    • Dados e Recursos Disponíveis: Plano de soluções definido, plano de testes, acesso aos ambientes de desenvolvimento, homologação e produção.
    • Pré-condições: Ferramentas de desenvolvimento (IDE, Docker, Git), ambiente de desenvolvimento configurado.
  • Passos:

    1. Desenvolver a Solução Imediata (Hotfix ou Reversão):
      • Se Hotfix: Implementar a correção no código (React ou Django) para garantir que o botão "RESUMO" seja renderizado corretamente e receba os dados esperados.
      • Se Reversão: Executar o rollback para a versão anterior do deploy.
    2. Testar em Ambiente de Desenvolvimento: Executar os testes unitários e de integração no ambiente local.
    3. Deploy em Ambiente de Homologação: Realizar o deploy da correção no ambiente de homologação (staging).
    4. Executar Testes em Homologação: Executar o plano de testes definido, incluindo o cenário de regressão do bug e outros cenários críticos, com a equipe de QA e/ou usuários-chave.
    5. Preparar para Deploy em Produção: Se os testes em homologação forem bem-sucedidos, preparar o pacote de deploy para produção.
  • Saída:

    • Dados e Recursos Disponibilizados: Código corrigido ou ambiente revertido, resultados dos testes em desenvolvimento e homologação, pacote de deploy para produção.
    • Pós-condições: Solução validada em ambiente de homologação e pronta para implantação em produção.

4. Monitoramento e Controle

Esta etapa garante que a solução implementada funcione como esperado e que quaisquer problemas emergentes sejam tratados.

  • Entrada:

    • Dados e Recursos Disponíveis: Solução implantada em produção ou pronta para implantação, ferramentas de monitoramento (CloudWatch, logs do servidor S2, New Relic/Datadog se aplicável).
    • Pré-condições: Acesso aos sistemas de monitoramento e logs de produção.
  • Passos:

    1. Deploy em Produção: Realizar o deploy da correção (hotfix ou reversão) no servidor S2, no ambiente AWS.
    2. Monitoramento Imediato Pós-Deploy: Acompanhar de perto os logs do servidor S2 e as métricas de performance e erro.
    3. Validação em Produção: Solicitar ao usuário ou a um membro da equipe que reproduza o cenário original do bug para confirmar que o botão "RESUMO" agora aparece corretamente.
    4. Monitoramento Contínuo: Manter o monitoramento por um período após o deploy para garantir que não surjam novos efeitos colaterais.
    5. Coletar Feedback do Usuário: Entrar em contato com o usuário que reportou o bug para confirmar a resolução.
    6. Documentar a Solução: Registrar as etapas da correção, a causa raiz e as observações durante o monitoramento.
  • Saída:

    • Dados e Recursos Disponibilizados: Bug resolvido em produção, evidências de monitoramento, feedback positivo do usuário, documentação da solução.
    • Pós-condições: Funcionalidade restaurada e ambiente estável.

5. Encerramento

Nesta etapa final, o foco é em formalizar a resolução, aprender com o problema e implementar melhorias para evitar futuras ocorrências.

  • Entrada:

    • Dados e Recursos Disponíveis: Problema resolvido, documentação da solução, análise da causa raiz.
    • Pré-condições: Confirmação da estabilidade do ambiente após a correção.
  • Passos:

    1. Revisão Pós-Incidente (Post-Mortem): Realizar uma reunião com a equipe para discutir:
      • O que aconteceu?
      • Como foi resolvido?
      • O que aprendemos?
      • O que poderíamos ter feito melhor?
      • Discutir as soluções de longo prazo (testes automatizados, melhorias no Code Review, etc.).
    2. Implementar Melhorias de Processo (Longo Prazo):
      • Priorizar e iniciar o desenvolvimento de testes de regressão automatizados para a funcionalidade afetada e outras áreas críticas.
      • Atualizar a documentação de procedimentos de deploy e testes.
      • Discutir a inclusão de mais cenários de teste em revisões de código.
    3. Comunicação Final: Informar todas as partes interessadas sobre a resolução do incidente e as lições aprendidas.
    4. Arquivar Documentação: Armazenar todos os registros relacionados ao incidente para referência futura e aprendizado.
  • Saída:

    • Dados e Recursos Disponibilizados: Relatório de post-mortem, plano de melhorias de processo, comunicação final, documentação arquivada.
    • Pós-condições: Problema totalmente resolvido, lições aprendidas e ações preventivas em andamento para fortalecer o processo de desenvolvimento e implantação.

Espero que este plano de ação detalhado ajude sua equipe a resolver o problema e a fortalecer seus processos de TI. Lembre-se que a colaboração entre as equipes de desenvolvimento, QA e operações é crucial para o sucesso.

Você gostaria de aprofundar em alguma das etapas ou discutir as ferramentas específicas para implementar os testes automatizados, por exemplo?

CONCLUSÃO

A utilização do MASP, com suas 42 perguntas, foi importante para o desenvolvedor relembrar, coletar e registrar no Prompt fatos e dados importantes para solicitar ajuda ao Chatbot de IA.

Assim que o plano de ação for executado com sucesso, a solução do problema será relatada no próximo tópico.

Solução do problema

O desenvolvedor está executando o plano de ação para solução do problema.


Demais Episódios da Temporada:

Entendendo a Metáfora "Home Office é Moto Boy"

UCEM T-7.II Capítulo 6 AS DÁDIVAS DO REINO - II. A lei do Reino - Caso de uso da IA para estudar

  Seção anterior Escolher outra seção do capítulo 6 Escolher outro capítulo Próxima seção Acesso ao material de estudo de UCEM Você pode esc...