agroplan-ai-cli 1.0.26 → 1.0.27

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli_version": "1.0.26",
3
- "backend_template_version": "1.0.26",
2
+ "cli_version": "1.0.27",
3
+ "backend_template_version": "1.0.27",
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,8 @@
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"
18
19
  ],
19
- "generated_at": "2026-05-09T22:00:00Z"
20
+ "generated_at": "2026-05-09T23:00:00Z"
20
21
  }
@@ -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.27",
4
4
  "description": "CLI global para AgroPlan AI - modo local acelerado",
5
5
  "type": "module",
6
6
  "bin": {