mozak.tech Engenharia de IA Corporativa 71%

Parte IV — Gestão de Projetos e Produto com Suporte de IA

4.4 — Estimativas com Intervalos de Confiança e Análise de Incerteza (Estimativas)

Objetivo da Aula

Ao concluir esta aula, você será capaz de:

Calcular intervalo de confiança com t-distribution para estimativas de prazo

Implementar análise de incerteza com fatores de ajuste

Usar LLM para calibrar estimativas com analogia de projetos anteriores

Completar os 2 TODOs: analisarIncerteza e calcularIntervaloConfianca

Aplicar o cone de incerteza em fases diferentes do projeto

Por que isso importa

“Vai levar 2 semanas” não é uma estimativa — é um número sem contexto de incerteza. O mesmo projeto estimado no início tem incerteza de ±400%; no meio, ±25%. Intervalo de confiança transforma estimativas em probabilidades: “Com 80% de confiança, entregamos entre 4 e 7 semanas.”

LLMs não estimam bem (não têm sua velocidade de time). Mas são excelentes em identificar fatores de risco que inflam a incerteza — novidades tecnológicas, tamanho da codebase, integrações externas.

Conceitos Fundamentais

Cone de Incerteza

Fase de conceito:   x0.25 a x4.0  (±300%)

Fase de definição:  x0.5  a x2.0  (±100%)

Fase de design:     x0.67 a x1.5  (±50%)

Fase de código:     x0.8  a x1.25 (±25%)

Beta/UAT:           x0.9  a x1.1  (±10%)

Na fase de conceito, “2 semanas” pode ser de 3 dias a 8 semanas. Comunicar isso ao stakeholder é mais honesto do que a falsa precisão de “2 semanas”.

TODO 1: analisarIncerteza(estimativa, fatores)

def analisar_incerteza(estimativa_dias: float, fatores: dict) -> dict:

    """

    fatores = {

      'novidade_tecnologica': 0.0-1.0,

      'tamanho_codebase': 0.0-1.0,

      'integracoes_externas': int,

      'requisitos_claros': 0.0-1.0,

      'fase_projeto': str,

    }

    """

    CONE = {

        'conceito': (0.25, 4.0),

        'definicao': (0.5, 2.0),

        'design': (0.67, 1.5),

        'codigo': (0.8, 1.25),

        'beta': (0.9, 1.1),

    }

    

    min_mult, max_mult = CONE.get(fatores.get('fase_projeto', 'definicao'), (0.5, 2.0))

    

    # Ajustar multiplicadores por fatores de risco

    risco_adicional = (

        fatores.get('novidade_tecnologica', 0) * 0.5 +

        fatores.get('tamanho_codebase', 0) * 0.3 +

        fatores.get('integracoes_externas', 0) * 0.1 * max_mult +

        (1 - fatores.get('requisitos_claros', 0.5)) * 0.4

    )

    

    max_ajustado = max_mult + risco_adicional

    

    return {

        'estimativa_base': estimativa_dias,

        'minimo': round(estimativa_dias * min_mult, 1),

        'maximo': round(estimativa_dias * max_ajustado, 1),

        'mais_provavel': round(estimativa_dias * ((min_mult + max_ajustado) / 2 * 0.7 + 0.3), 1),

        'fase': fatores.get('fase_projeto', 'definicao'),

    }

TODO 2: calcularIntervaloConfianca(amostras)

import math



def calcular_intervalo_confianca(amostras: list[float], nivel_confianca: float = 0.80) -> dict:

    """Intervalo de confiança com t-distribution para amostras pequenas."""

    n = len(amostras)

    if n < 2:

        return {'erro': 'Precisa de pelo menos 2 amostras'}

    

    media = sum(amostras) / n

    variancia = sum((x - media) ** 2 for x in amostras) / (n - 1)  # variância amostral

    desvio = math.sqrt(variancia)

    erro_padrao = desvio / math.sqrt(n)

    

    # Valores críticos da t-distribution para graus de liberdade = n-1

    # (valores aproximados para nível 80%)

    t_critico_80 = {1: 3.08, 2: 1.89, 3: 1.64, 4: 1.53, 5: 1.48,

                    10: 1.37, 20: 1.33, 30: 1.31, 999: 1.28}

    df = n - 1

    t = next((t_critico_80[k] for k in sorted(t_critico_80.keys()) if k >= df), 1.28)

    

    margem = t * erro_padrao

    

    return {

        'media': round(media, 1),

        'desvio_padrao': round(desvio, 1),

        'ic_inferior': round(max(0, media - margem), 1),

        'ic_superior': round(media + margem, 1),

        'nivel_confianca': nivel_confianca,

        'n_amostras': n,

    }

Aprofundamento Técnico

Calibração com Analogia de Projetos

async def calibrar_por_analogia(descricao: str, historico: list[dict], client) -> dict:

    historico_str = '\n'.join(

        f'- {p["titulo"]}: estimado {p["estimado"]}d, real {p["real"]}d ({round(p["real"]/p["estimado"]*100-100, 0):+.0f}%)'

        for p in historico

    )

    

    response = await client.messages.create(

        model='claude-haiku-4-5-20251001',

        max_tokens=300,

        messages=[{

            'role': 'user',

            'content': f'''Dado este histórico de projetos e a nova tarefa, estime e identifique o fator de risco principal:



Histórico:

{historico_str}



Nova tarefa: {descricao}



Retorne JSON: {{"estimativa_dias": X, "fator_principal": "...", "similar_a": "..."}}''',

        }],

    )

    texto = response.content[0].text

    i, f = texto.index('{'), texto.rindex('}') + 1

    return json.loads(texto[i:f])

Times que usam analogia calibrada com LLM reduzem o erro médio de estimativa em ~30% segundo estudos de software estimation.

Exemplos Anotados

Exemplo 1: Análise de Incerteza

estimativa = 10  # dias estimados



fatores = {

    'novidade_tecnologica': 0.8,   # tecnologia nova para o time

    'tamanho_codebase': 0.6,       # codebase grande e pouco documentada

    'integracoes_externas': 2,     # 2 APIs externas

    'requisitos_claros': 0.3,      # requisitos vagos

    'fase_projeto': 'definicao',

}



resultado = analisar_incerteza(10, fatores)

# → minimo: 5.0 dias

# → maximo: 24.0 dias   ← risco alto inflou muito

# → mais_provavel: 12.3 dias

Exemplo 2: Intervalo de Confiança com Histórico

# Duração real de sprints similares (em dias)

amostras = [12, 15, 14, 18, 11, 16]



ic = calcular_intervalo_confianca(amostras, nivel_confianca=0.80)

# → media: 14.3 dias

# → ic_inferior: 11.9 dias

# → ic_superior: 16.8 dias

# Comunicação: "Com 80% de confiança, entregamos entre 12 e 17 dias"

Padrões e Armadilhas

Padrões

Padrão 1: Sempre comunicar intervalo, nunca ponto único

f"Estimativa: {ic['ic_inferior']}-{ic['ic_superior']} dias (80% confiança)"

# Não: "Estimativa: 14 dias"

Padrão 2: Cone de incerteza decresce com progresso

# Atualizar fase conforme o projeto avança

fatores['fase_projeto'] = 'codigo'  # de 'definicao' para 'codigo' → IC menor

Armadilhas

⚠️ Armadilha 1: IC com n<5 amostras é muito largo Com 3 amostras, o IC 80% pode ser tão amplo quanto o cone de incerteza da fase de conceito. Precisa de pelo menos 8-10 amostras históricas para IC significativo.

⚠️ Armadilha 2: Usar variância populacional em vez de amostral

# ERRADO: divide por n (variância populacional)

variancia = sum((x - media)**2 for x in amostras) / n



# CORRETO: divide por n-1 (variância amostral de Bessel)

variancia = sum((x - media)**2 for x in amostras) / (n - 1)
⚗ Laboratório prático — mozak.tech
Se não for realizar o laboratório, pule para o próximo capítulo.

Ponte para o Lab

TODO 1 — analisar_incerteza(estimativa, fatores): aplique o cone de incerteza por fase e ajuste por fatores de risco.

TODO 2 — calcular_intervalo_confianca(amostras): implemente com variância amostral (n-1) e valores t críticos aproximados.

Agora você está pronto para o lab.