agroplan-ai-cli 1.0.26 → 1.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,6 +20,10 @@ DATA_MODE=hybrid
20
20
  WEATHER_PROVIDER=open-meteo
21
21
  PROVIDER_CACHE_TTL=3600
22
22
 
23
+ # Debug Configuration
24
+ # Set to true to expose detailed error tracebacks (development only)
25
+ DEBUG_ERRORS=false
26
+
23
27
  # Cache Administration (optional)
24
28
  # If set, /cache/limpar endpoint requires X-Cache-Token header
25
29
  # Leave empty to disable protection (not recommended for production)
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli_version": "1.0.26",
3
- "backend_template_version": "1.0.26",
2
+ "cli_version": "1.0.28",
3
+ "backend_template_version": "1.0.28",
4
4
  "zarc_index_version": "2025-2026-fast-index-v2",
5
5
  "price_index_version": "2025-05-reference-v1",
6
6
  "features": [
@@ -14,7 +14,9 @@
14
14
  "price_display_only",
15
15
  "price_unit_normalization",
16
16
  "market_profit_estimate",
17
- "market_profit_validation"
17
+ "market_profit_validation",
18
+ "market_profit_confidence_refinement",
19
+ "market_profit_comparative_evaluation"
18
20
  ],
19
- "generated_at": "2026-05-09T22:00:00Z"
21
+ "generated_at": "2026-05-09T23:30:00Z"
20
22
  }
@@ -28,6 +28,7 @@ CORS_ORIGINS = os.getenv("CORS_ORIGINS", "http://localhost:3000,http://127.0.0.1
28
28
  DATA_MODE = os.getenv("DATA_MODE", "hybrid") # simulated, real, hybrid
29
29
  WEATHER_PROVIDER = os.getenv("WEATHER_PROVIDER", "open-meteo")
30
30
  PROVIDER_CACHE_TTL = int(os.getenv("PROVIDER_CACHE_TTL", "3600"))
31
+ DEBUG_ERRORS = os.getenv("DEBUG_ERRORS", "false").lower() == "true"
31
32
 
32
33
  # Cache em memória para resultados pesados
33
34
  _resultados_cache = {}
@@ -580,6 +581,69 @@ def get_debug_lucro_mercado(
580
581
  except Exception as e:
581
582
  raise HTTPException(status_code=500, detail=str(e))
582
583
 
584
+ @app.get("/comparar/lucro-mercado")
585
+ def comparar_lucro_mercado(
586
+ objetivo: str = Query("equilibrado", description="Objetivo de otimização"),
587
+ seed: int = Query(42, description="Seed para reprodutibilidade"),
588
+ geracoes: int = Query(100, description="Número de gerações do AG"),
589
+ populacao: int = Query(50, description="Tamanho da população"),
590
+ lat: Optional[float] = Query(None, description="Latitude"),
591
+ lon: Optional[float] = Query(None, description="Longitude"),
592
+ days: int = Query(30, description="Dias para análise climática"),
593
+ uf: Optional[str] = Query(None, description="Unidade Federativa (ex: SP, PR)"),
594
+ municipio: Optional[str] = Query(None, description="Município"),
595
+ safra: str = Query("2025/2026", description="Safra ZARC")
596
+ ):
597
+ """
598
+ Avalia o plano do sistema usando lucro de mercado para comparação.
599
+
600
+ NÃO gera um plano otimizado por mercado. Apenas:
601
+ 1. Gera plano normal com AG usando lucro do sistema
602
+ 2. Avalia esse mesmo plano com lucro de mercado normalizado
603
+ 3. Compara os dois valores de lucro
604
+
605
+ Retorna:
606
+ - modo: "avaliacao_comparativa"
607
+ - plano_sistema: Plano otimizado pelo AG normal
608
+ - avaliacao_mercado: Avaliação do mesmo plano com lucro de mercado
609
+ - comparacao: Diferenças e validação
610
+ """
611
+ try:
612
+ from core.market_profit_comparator import comparar_plano_sistema_com_avaliacao_mercado
613
+
614
+ culturas, talhoes, regras = get_dados()
615
+
616
+ resultado = comparar_plano_sistema_com_avaliacao_mercado(
617
+ culturas=culturas,
618
+ talhoes=talhoes,
619
+ regras=regras,
620
+ uf=uf,
621
+ municipio=municipio,
622
+ safra=safra,
623
+ objetivo=objetivo,
624
+ seed=seed,
625
+ geracoes=geracoes,
626
+ populacao=populacao
627
+ )
628
+
629
+ return converter_tipos_python(resultado)
630
+
631
+ except Exception as e:
632
+ import traceback
633
+ if DEBUG_ERRORS:
634
+ raise HTTPException(
635
+ status_code=500,
636
+ detail={
637
+ "error": str(e),
638
+ "traceback": traceback.format_exc()
639
+ }
640
+ )
641
+ else:
642
+ raise HTTPException(
643
+ status_code=500,
644
+ detail="Erro ao gerar avaliação comparativa de lucro de mercado."
645
+ )
646
+
583
647
  @app.get("/dashboard")
584
648
  def get_dashboard(
585
649
  lat: Optional[float] = None,
@@ -0,0 +1,171 @@
1
+ """
2
+ Avaliação Comparativa: Plano Sistema vs Avaliação com Lucro de Mercado
3
+
4
+ Avalia o plano otimizado pelo sistema usando lucro de mercado normalizado.
5
+ NÃO gera um novo plano otimizado por mercado - apenas avalia o plano atual.
6
+
7
+ Bloqueia uso automático se houver itens críticos ou baixa confiabilidade.
8
+ """
9
+
10
+ from typing import Dict, List, Optional
11
+ from core.planner import gerar_plano_genetico
12
+ from core.price_adapter import aplicar_precos_no_plano
13
+ from core.market_profit_validator import validar_plano_lucro_mercado
14
+
15
+
16
+ def comparar_plano_sistema_com_avaliacao_mercado(
17
+ culturas: List[Dict],
18
+ talhoes: List[Dict],
19
+ regras: List[Dict],
20
+ uf: Optional[str] = None,
21
+ municipio: Optional[str] = None,
22
+ safra: str = "2025/2026",
23
+ objetivo: str = "equilibrado",
24
+ seed: int = 42,
25
+ geracoes: int = 100,
26
+ populacao: int = 50
27
+ ) -> Dict:
28
+ """
29
+ Avalia o plano do sistema usando lucro de mercado para comparação.
30
+
31
+ NÃO gera um plano otimizado por mercado. Apenas:
32
+ 1. Gera plano normal com AG usando lucro do sistema
33
+ 2. Avalia esse mesmo plano com lucro de mercado normalizado
34
+ 3. Compara os dois valores de lucro
35
+
36
+ Args:
37
+ culturas: Lista de culturas disponíveis
38
+ talhoes: Lista de talhões
39
+ regras: Regras de compatibilidade
40
+ uf: Unidade Federativa
41
+ municipio: Município
42
+ safra: Safra agrícola
43
+ objetivo: Objetivo de otimização
44
+ seed: Seed para reprodutibilidade
45
+ geracoes: Número de gerações do AG
46
+ populacao: Tamanho da população
47
+
48
+ Returns:
49
+ Dict com modo, plano_sistema, avaliacao_mercado e comparacao
50
+ """
51
+
52
+ # 1. Gerar plano principal normalmente
53
+ resultado = gerar_plano_genetico(
54
+ culturas=culturas,
55
+ talhoes=talhoes,
56
+ regras=regras,
57
+ objetivo=objetivo,
58
+ seed=seed,
59
+ geracoes=geracoes,
60
+ populacao=populacao
61
+ )
62
+
63
+ # 2. Aplicar ZARC, se houver UF
64
+ if uf:
65
+ try:
66
+ from core.zarc_adapter import enriquecer_plano_com_zarc
67
+ resultado = enriquecer_plano_com_zarc(
68
+ resultado,
69
+ uf=uf,
70
+ municipio=municipio,
71
+ safra=safra
72
+ )
73
+ except Exception as zarc_error:
74
+ resultado["zarc_error"] = str(zarc_error)
75
+
76
+ # 3. Aplicar preços
77
+ # IMPORTANTE: aplicar_precos_no_plano só aceita uf por enquanto
78
+ resultado = aplicar_precos_no_plano(resultado, uf=uf)
79
+
80
+ # 4. Validar lucro de mercado
81
+ resultado = validar_plano_lucro_mercado(resultado)
82
+
83
+ plano = resultado.get("plano", [])
84
+
85
+ lucro_sistema_total = float(resultado.get("lucro_total", 0) or 0)
86
+
87
+ lucro_mercado_total = 0.0
88
+ itens_mercado = []
89
+
90
+ for item in plano:
91
+ lucro_mercado = item.get("lucro_mercado_estimado")
92
+
93
+ if lucro_mercado is not None:
94
+ try:
95
+ lucro_mercado_float = float(lucro_mercado)
96
+ lucro_mercado_total += lucro_mercado_float
97
+ except Exception:
98
+ lucro_mercado_float = None
99
+ else:
100
+ lucro_mercado_float = None
101
+
102
+ itens_mercado.append({
103
+ "talhao": item.get("talhao"),
104
+ "cultura": item.get("cultura"),
105
+ "lucro_sistema": item.get("lucro_estimado"),
106
+ "lucro_mercado_estimado": lucro_mercado_float,
107
+ "preco_real": item.get("preco_real"),
108
+ "preco_normalizado": item.get("preco_normalizado"),
109
+ "validacao_lucro_mercado": item.get("validacao_lucro_mercado")
110
+ })
111
+
112
+ diferenca_absoluta = lucro_mercado_total - lucro_sistema_total
113
+
114
+ if lucro_sistema_total != 0:
115
+ diferenca_percentual = (diferenca_absoluta / abs(lucro_sistema_total)) * 100
116
+ else:
117
+ diferenca_percentual = 0
118
+
119
+ validacao = resultado.get("validacao_lucro_mercado", {}) or {}
120
+
121
+ total = len(plano)
122
+ alta = int(validacao.get("itens_alta_confiabilidade", 0) or 0)
123
+ media = int(validacao.get("itens_media_confiabilidade", 0) or 0)
124
+ baixa = int(validacao.get("itens_baixa_confiabilidade", 0) or 0)
125
+ criticos = int(validacao.get("itens_criticos", 0) or 0)
126
+
127
+ percentual_alta = (alta / total * 100) if total else 0
128
+
129
+ pode_usar_mercado = (
130
+ criticos == 0
131
+ and baixa == 0
132
+ and percentual_alta >= 70
133
+ )
134
+
135
+ motivo_bloqueio = None
136
+ if not pode_usar_mercado:
137
+ motivos = []
138
+ if criticos > 0:
139
+ motivos.append(f"{criticos} item(ns) crítico(s)")
140
+ if baixa > 0:
141
+ motivos.append(f"{baixa} item(ns) de baixa confiabilidade")
142
+ if percentual_alta < 70:
143
+ motivos.append(f"apenas {percentual_alta:.1f}% dos itens têm alta confiabilidade")
144
+
145
+ motivo_bloqueio = (
146
+ "O lucro de mercado não deve ser usado como recomendação principal: "
147
+ + "; ".join(motivos)
148
+ )
149
+
150
+ return {
151
+ "modo": "avaliacao_comparativa",
152
+ "descricao": "Avalia o plano principal usando lucro de mercado normalizado, sem substituir a recomendação oficial.",
153
+ "plano_sistema": resultado,
154
+ "avaliacao_mercado": {
155
+ "lucro_mercado_total": lucro_mercado_total,
156
+ "itens": itens_mercado
157
+ },
158
+ "comparacao": {
159
+ "lucro_sistema_total": lucro_sistema_total,
160
+ "lucro_mercado_total": lucro_mercado_total,
161
+ "diferenca_absoluta": diferenca_absoluta,
162
+ "diferenca_percentual": diferenca_percentual,
163
+ "itens_alta_confiabilidade": alta,
164
+ "itens_media_confiabilidade": media,
165
+ "itens_baixa_confiabilidade": baixa,
166
+ "itens_criticos": criticos,
167
+ "percentual_alta_confiabilidade": percentual_alta,
168
+ "pode_usar_mercado": pode_usar_mercado,
169
+ "motivo_bloqueio": motivo_bloqueio
170
+ }
171
+ }
@@ -47,20 +47,27 @@ def classificar_confiabilidade_lucro(item: Dict) -> Dict:
47
47
  """
48
48
  Classifica confiabilidade do lucro de mercado para um item do plano.
49
49
 
50
+ Critérios refinados (Fase 9.5):
51
+ - Baixa: diferença >= 150%, dados incompletos, lucro invertido, fallback com diferença >= 100%
52
+ - Média: diferença 50-150%, fallback, lucro negativo
53
+ - Alta: diferença < 50%, sem fallback, dados completos
54
+
50
55
  Args:
51
56
  item: Item do plano com dados de lucro
52
57
 
53
58
  Returns:
54
- Dict com confiabilidade (alta/media/baixa) e motivos
59
+ Dict com confiabilidade (alta/media/baixa), motivos e flag crítico
55
60
  """
56
61
  motivos = []
62
+ critico = False
57
63
 
58
64
  # Verificar se tem preço normalizado
59
65
  preco_norm = item.get("preco_normalizado", {})
60
66
  if not preco_norm.get("normalizado"):
61
67
  return {
62
68
  "confiabilidade": "baixa",
63
- "motivos": ["Preço não normalizado ou não disponível"]
69
+ "motivos": ["Preço não normalizado ou não disponível"],
70
+ "critico": True
64
71
  }
65
72
 
66
73
  # Verificar se tem produtividade e custo
@@ -76,7 +83,8 @@ def classificar_confiabilidade_lucro(item: Dict) -> Dict:
76
83
  if motivos:
77
84
  return {
78
85
  "confiabilidade": "baixa",
79
- "motivos": motivos
86
+ "motivos": motivos,
87
+ "critico": True
80
88
  }
81
89
 
82
90
  # Verificar se tem lucro de mercado calculado
@@ -84,7 +92,8 @@ def classificar_confiabilidade_lucro(item: Dict) -> Dict:
84
92
  if lucro_mercado is None:
85
93
  return {
86
94
  "confiabilidade": "baixa",
87
- "motivos": ["Lucro de mercado não calculado"]
95
+ "motivos": ["Lucro de mercado não calculado"],
96
+ "critico": True
88
97
  }
89
98
 
90
99
  # Calcular diferença com lucro do sistema
@@ -92,34 +101,61 @@ def classificar_confiabilidade_lucro(item: Dict) -> Dict:
92
101
  diferenca = calcular_diferenca_lucro(lucro_sistema, lucro_mercado)
93
102
  diferenca_percentual = abs(diferenca["diferenca_percentual"])
94
103
 
95
- # Classificar baseado na diferença percentual
96
- if diferenca_percentual > 100:
104
+ # Verificar se é fallback
105
+ preco_real = item.get("preco_real", {})
106
+ is_fallback = preco_real.get("fallback", False)
107
+
108
+ # Verificar inversão de sinal (lucro vira prejuízo ou vice-versa)
109
+ lucro_invertido = (lucro_sistema > 0 and lucro_mercado < 0) or (lucro_sistema < 0 and lucro_mercado > 0)
110
+
111
+ # CRITÉRIOS REFINADOS (Fase 9.5)
112
+
113
+ # Baixa confiabilidade (crítico)
114
+ if diferenca_percentual >= 150:
115
+ confiabilidade = "baixa"
116
+ motivos.append(f"Diferença extrema ({diferenca_percentual:.1f}%) entre lucro sistema e mercado")
117
+ critico = True
118
+ elif lucro_invertido and lucro_sistema > 0:
119
+ confiabilidade = "baixa"
120
+ motivos.append("Lucro do sistema é positivo mas lucro de mercado indica prejuízo")
121
+ critico = True
122
+ elif is_fallback and diferenca_percentual >= 100:
123
+ confiabilidade = "baixa"
124
+ motivos.append(f"Preço de fallback com diferença muito alta ({diferenca_percentual:.1f}%)")
125
+ critico = True
126
+ elif diferenca_percentual >= 100:
127
+ # Diferença > 100% mesmo sem fallback é baixa confiabilidade
97
128
  confiabilidade = "baixa"
98
129
  motivos.append(f"Diferença muito alta ({diferenca_percentual:.1f}%) entre lucro sistema e mercado")
99
- elif diferenca_percentual > 50:
130
+ critico = False # Não crítico se não for fallback
131
+
132
+ # Média confiabilidade
133
+ elif diferenca_percentual >= 50:
100
134
  confiabilidade = "media"
101
135
  motivos.append(f"Diferença moderada ({diferenca_percentual:.1f}%) entre lucro sistema e mercado")
136
+ elif is_fallback:
137
+ confiabilidade = "media"
138
+ motivos.append("Preço de referência (fallback) - pode não refletir mercado local")
139
+ elif lucro_mercado < 0:
140
+ confiabilidade = "media"
141
+ motivos.append("Lucro de mercado indica prejuízo - requer validação")
142
+
143
+ # Alta confiabilidade
102
144
  else:
103
145
  confiabilidade = "alta"
104
146
  motivos.append(f"Diferença aceitável ({diferenca_percentual:.1f}%) entre lucro sistema e mercado")
147
+ if not is_fallback:
148
+ motivos.append("Preço real disponível (não fallback)")
105
149
 
106
- # Verificar se lucro de mercado é negativo (prejuízo)
107
- if lucro_mercado < 0:
108
- if confiabilidade == "alta":
109
- confiabilidade = "media"
110
- motivos.append("Lucro de mercado indica prejuízo - requer validação de preço/produtividade")
111
-
112
- # Verificar se é fallback
113
- preco_real = item.get("preco_real", {})
114
- if preco_real.get("fallback"):
115
- if confiabilidade == "alta":
116
- confiabilidade = "media"
117
- motivos.append("Preço usando fallback (referência) - pode não refletir mercado local")
150
+ # Adicionar informação sobre normalização bem-sucedida
151
+ if preco_norm.get("normalizado") and confiabilidade != "baixa":
152
+ motivos.append("Unidade comercial normalizada com sucesso")
118
153
 
119
154
  return {
120
155
  "confiabilidade": confiabilidade,
121
156
  "motivos": motivos,
122
- "diferenca": diferenca
157
+ "diferenca": diferenca,
158
+ "critico": critico
123
159
  }
124
160
 
125
161
 
@@ -140,6 +176,7 @@ def validar_plano_lucro_mercado(resultado: Dict) -> Dict:
140
176
  alta_confiabilidade = 0
141
177
  media_confiabilidade = 0
142
178
  baixa_confiabilidade = 0
179
+ itens_criticos = 0
143
180
  alertas = []
144
181
 
145
182
  # Validar cada item do plano
@@ -160,11 +197,16 @@ def validar_plano_lucro_mercado(resultado: Dict) -> Dict:
160
197
  cultura = item.get("cultura", "desconhecida")
161
198
  talhao = item.get("talhao", "?")
162
199
  alertas.append(f"Talhão {talhao} ({cultura}): {', '.join(validacao['motivos'])}")
200
+
201
+ # Contabilizar itens críticos
202
+ if validacao.get("critico", False):
203
+ itens_criticos += 1
163
204
 
164
205
  # Adicionar resumo de validação
165
206
  total = len(resultado["plano"])
166
207
  percentual_alta = (alta_confiabilidade / total * 100) if total > 0 else 0
167
208
  percentual_baixa = (baixa_confiabilidade / total * 100) if total > 0 else 0
209
+ percentual_critico = (itens_criticos / total * 100) if total > 0 else 0
168
210
 
169
211
  resultado["validacao_lucro_mercado"] = {
170
212
  "ativo": True,
@@ -172,31 +214,38 @@ def validar_plano_lucro_mercado(resultado: Dict) -> Dict:
172
214
  "itens_alta_confiabilidade": alta_confiabilidade,
173
215
  "itens_media_confiabilidade": media_confiabilidade,
174
216
  "itens_baixa_confiabilidade": baixa_confiabilidade,
217
+ "itens_criticos": itens_criticos,
175
218
  "percentual_alta_confiabilidade": round(percentual_alta, 1),
176
219
  "percentual_baixa_confiabilidade": round(percentual_baixa, 1),
220
+ "percentual_critico": round(percentual_critico, 1),
177
221
  "alertas": alertas[:5], # Limitar a 5 alertas principais
178
222
  "total_alertas": len(alertas),
179
- "recomendacao": _gerar_recomendacao(percentual_alta, percentual_baixa)
223
+ "recomendacao": _gerar_recomendacao(percentual_alta, percentual_baixa, percentual_critico)
180
224
  }
181
225
 
182
226
  return resultado
183
227
 
184
228
 
185
- def _gerar_recomendacao(percentual_alta: float, percentual_baixa: float) -> str:
229
+ def _gerar_recomendacao(percentual_alta: float, percentual_baixa: float, percentual_critico: float = 0) -> str:
186
230
  """
187
231
  Gera recomendação baseada nos percentuais de confiabilidade.
188
232
 
189
233
  Args:
190
234
  percentual_alta: Percentual de itens com alta confiabilidade
191
235
  percentual_baixa: Percentual de itens com baixa confiabilidade
236
+ percentual_critico: Percentual de itens críticos
192
237
 
193
238
  Returns:
194
239
  String com recomendação
195
240
  """
196
- if percentual_alta >= 70:
241
+ if percentual_critico >= 30:
242
+ return "Há valores críticos no lucro de mercado. Eles não devem ser usados para otimização sem validação manual."
243
+ elif percentual_alta >= 70:
197
244
  return "Lucro de mercado apresenta boa confiabilidade. Considere validação detalhada antes de ativar PRICE_APPLY_TO_PROFIT."
198
245
  elif percentual_baixa >= 50:
199
246
  return "Muitos itens com baixa confiabilidade. Valide preços, produtividades e custos antes de usar lucro de mercado."
247
+ elif percentual_critico > 0:
248
+ return "Alguns valores críticos detectados. Revise itens com baixa confiabilidade antes de usar lucro de mercado."
200
249
  else:
201
250
  return "Confiabilidade mista. Revise itens com baixa confiabilidade e valide dados de mercado."
202
251
 
@@ -279,14 +279,31 @@ def gerar_secao_validacao_lucro_mercado(resultado, formato='md'):
279
279
  alta = validacao.get("itens_alta_confiabilidade", 0)
280
280
  media = validacao.get("itens_media_confiabilidade", 0)
281
281
  baixa = validacao.get("itens_baixa_confiabilidade", 0)
282
+ criticos = validacao.get("itens_criticos", 0)
282
283
 
283
284
  perc_alta = validacao.get("percentual_alta_confiabilidade", 0)
284
285
  perc_baixa = validacao.get("percentual_baixa_confiabilidade", 0)
286
+ perc_critico = validacao.get("percentual_critico", 0)
285
287
 
286
288
  secao += f"- **Total de itens analisados**: {total}\n"
287
289
  secao += f"- **Alta confiabilidade**: {alta} ({perc_alta:.1f}%) 🟢\n"
288
290
  secao += f"- **Média confiabilidade**: {media} ({100 - perc_alta - perc_baixa:.1f}%) 🟡\n"
289
- secao += f"- **Baixa confiabilidade**: {baixa} ({perc_baixa:.1f}%) 🔴\n\n"
291
+ secao += f"- **Baixa confiabilidade**: {baixa} ({perc_baixa:.1f}%) 🔴\n"
292
+
293
+ if criticos > 0:
294
+ secao += f"- **⚠️ Itens críticos**: {criticos} ({perc_critico:.1f}%)\n"
295
+
296
+ secao += "\n"
297
+
298
+ # Aviso de itens críticos
299
+ if criticos > 0:
300
+ secao += "### ⚠️ ATENÇÃO: Valores Críticos Detectados\n\n"
301
+ secao += f"**{criticos} item(ns) apresenta(m) valores críticos** que indicam possível desalinhamento entre:\n"
302
+ secao += "- Preço de mercado vs preço interno\n"
303
+ secao += "- Produtividade estimada vs real\n"
304
+ secao += "- Custos operacionais\n"
305
+ secao += "- Unidade comercial\n\n"
306
+ secao += "**Estes valores não devem ser usados para otimização sem validação manual.**\n\n"
290
307
 
291
308
  # Recomendação
292
309
  recomendacao = validacao.get("recomendacao", "")
@@ -332,8 +349,14 @@ def gerar_secao_validacao_lucro_mercado(resultado, formato='md'):
332
349
  diferenca_fmt = "N/A"
333
350
 
334
351
  confiabilidade = validacao_item.get("confiabilidade", "baixa")
335
- conf_emoji = "🟢" if confiabilidade == "alta" else "🟡" if confiabilidade == "media" else "🔴"
336
- conf_label = confiabilidade.title()
352
+ critico = validacao_item.get("critico", False)
353
+
354
+ if critico:
355
+ conf_emoji = "⚠️🔴"
356
+ conf_label = "**CRÍTICO**"
357
+ else:
358
+ conf_emoji = "🟢" if confiabilidade == "alta" else "🟡" if confiabilidade == "media" else "🔴"
359
+ conf_label = confiabilidade.title()
337
360
 
338
361
  secao += f"| {talhao} | {cultura} | {lucro_sistema_fmt} | {lucro_mercado_fmt} | {diferenca_fmt} | {conf_emoji} {conf_label} |\n"
339
362
 
@@ -343,8 +366,9 @@ def gerar_secao_validacao_lucro_mercado(resultado, formato='md'):
343
366
  secao += "### 📊 Sobre a Classificação de Confiabilidade\n\n"
344
367
  secao += "A confiabilidade do lucro de mercado é classificada com base em:\n\n"
345
368
  secao += "- **Alta (🟢)**: Diferença < 50% entre lucro sistema e mercado, preço normalizado disponível\n"
346
- secao += "- **Média (🟡)**: Diferença 50-100%, uso de fallback, ou lucro negativo\n"
347
- secao += "- **Baixa (🔴)**: Diferença > 100%, dados incompletos, ou preço não disponível\n\n"
369
+ secao += "- **Média (🟡)**: Diferença 50-150%, uso de fallback, ou lucro negativo\n"
370
+ secao += "- **Baixa (🔴)**: Diferença > 150%, dados incompletos, ou preço não disponível\n"
371
+ secao += "- **⚠️ CRÍTICO**: Diferença extrema (>150%), lucro invertido, ou fallback com diferença >100%\n\n"
348
372
 
349
373
  secao += "### ⚠️ Aviso Importante\n\n"
350
374
  secao += "**O lucro de mercado ainda é experimental e não substitui o lucro principal do sistema.**\n\n"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroplan-ai-cli",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "CLI global para AgroPlan AI - modo local acelerado",
5
5
  "type": "module",
6
6
  "bin": {