agroplan-ai-cli 1.0.25 → 1.0.26

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.25",
3
- "backend_template_version": "1.0.25",
2
+ "cli_version": "1.0.26",
3
+ "backend_template_version": "1.0.26",
4
4
  "zarc_index_version": "2025-2026-fast-index-v2",
5
5
  "price_index_version": "2025-05-reference-v1",
6
6
  "features": [
@@ -13,7 +13,8 @@
13
13
  "price_provider_index_fallback",
14
14
  "price_display_only",
15
15
  "price_unit_normalization",
16
- "market_profit_estimate"
16
+ "market_profit_estimate",
17
+ "market_profit_validation"
17
18
  ],
18
- "generated_at": "2026-05-09T21:00:00Z"
19
+ "generated_at": "2026-05-09T22:00:00Z"
19
20
  }
@@ -523,6 +523,63 @@ def get_precos_comparar(
523
523
  except Exception as e:
524
524
  raise HTTPException(status_code=500, detail=str(e))
525
525
 
526
+ @app.get("/debug/lucro-mercado")
527
+ def get_debug_lucro_mercado(
528
+ uf: Optional[str] = Query(None, description="Unidade Federativa (ex: SP, PR)"),
529
+ municipio: Optional[str] = Query(None, description="Município"),
530
+ safra: str = Query("2025/2026", description="Safra ZARC")
531
+ ):
532
+ """Diagnóstico detalhado do lucro de mercado para validação"""
533
+ try:
534
+ from core.loader import carregar_dados
535
+ from core.planner import gerar_plano_inteligente
536
+ from core.price_adapter import aplicar_precos_no_plano
537
+ from core.market_profit_validator import gerar_diagnostico_lucro_mercado
538
+
539
+ # Carregar dados
540
+ culturas, talhoes, regras = carregar_dados()
541
+
542
+ # Gerar plano inteligente
543
+ plano_inteligente = gerar_plano_inteligente(culturas, talhoes, regras)
544
+
545
+ # Mapear para formato esperado pelo price_adapter
546
+ plano = []
547
+ for item in plano_inteligente:
548
+ plano.append({
549
+ 'talhao': item['talhao'],
550
+ 'area': item['area'],
551
+ 'solo': item['solo'],
552
+ 'clima': item['clima'],
553
+ 'relevo': item['relevo'],
554
+ 'agua': item['agua'],
555
+ 'cultura': item['cultura_recomendada'], # Mapear cultura_recomendada -> cultura
556
+ 'lucro_estimado': item['lucro_estimado'],
557
+ 'risco': item['risco'],
558
+ 'nota': item['nota'],
559
+ 'tempo': item['tempo']
560
+ })
561
+
562
+ resultado = {"plano": plano}
563
+
564
+ # Aplicar preços e normalização
565
+ resultado = aplicar_precos_no_plano(resultado, uf=uf)
566
+
567
+ # Gerar diagnóstico
568
+ diagnostico = gerar_diagnostico_lucro_mercado(resultado["plano"], uf=uf)
569
+
570
+ # Adicionar resumo de validação
571
+ validacao = resultado.get("validacao_lucro_mercado", {})
572
+
573
+ return {
574
+ "diagnostico": diagnostico,
575
+ "validacao_resumo": validacao,
576
+ "municipio": municipio,
577
+ "safra": safra
578
+ }
579
+
580
+ except Exception as e:
581
+ raise HTTPException(status_code=500, detail=str(e))
582
+
526
583
  @app.get("/dashboard")
527
584
  def get_dashboard(
528
585
  lat: Optional[float] = None,
@@ -0,0 +1,266 @@
1
+ """
2
+ Validador de Lucro de Mercado
3
+
4
+ Classifica confiabilidade dos valores de lucro de mercado comparando com lucro do sistema.
5
+ Detecta distorções e fornece diagnóstico para validação antes de ativar recálculo automático.
6
+ """
7
+
8
+ from typing import Dict, List, Optional
9
+
10
+
11
+ def calcular_diferenca_lucro(lucro_sistema: float, lucro_mercado: float) -> Dict:
12
+ """
13
+ Calcula diferença entre lucro do sistema e lucro de mercado.
14
+
15
+ Args:
16
+ lucro_sistema: Lucro calculado com base interna
17
+ lucro_mercado: Lucro calculado com preços de mercado normalizados
18
+
19
+ Returns:
20
+ Dict com diferença absoluta, percentual e direção
21
+ """
22
+ diferenca_absoluta = lucro_mercado - lucro_sistema
23
+
24
+ # Calcular percentual baseado no valor absoluto do lucro sistema
25
+ # para evitar divisão por zero e lidar com valores negativos
26
+ if lucro_sistema != 0:
27
+ diferenca_percentual = (diferenca_absoluta / abs(lucro_sistema)) * 100
28
+ else:
29
+ diferenca_percentual = 0 if lucro_mercado == 0 else float('inf')
30
+
31
+ # Determinar direção
32
+ if abs(diferenca_percentual) < 5:
33
+ direcao = "igual"
34
+ elif diferenca_absoluta > 0:
35
+ direcao = "maior"
36
+ else:
37
+ direcao = "menor"
38
+
39
+ return {
40
+ "diferenca_absoluta": round(diferenca_absoluta, 2),
41
+ "diferenca_percentual": round(diferenca_percentual, 2),
42
+ "direcao": direcao
43
+ }
44
+
45
+
46
+ def classificar_confiabilidade_lucro(item: Dict) -> Dict:
47
+ """
48
+ Classifica confiabilidade do lucro de mercado para um item do plano.
49
+
50
+ Args:
51
+ item: Item do plano com dados de lucro
52
+
53
+ Returns:
54
+ Dict com confiabilidade (alta/media/baixa) e motivos
55
+ """
56
+ motivos = []
57
+
58
+ # Verificar se tem preço normalizado
59
+ preco_norm = item.get("preco_normalizado", {})
60
+ if not preco_norm.get("normalizado"):
61
+ return {
62
+ "confiabilidade": "baixa",
63
+ "motivos": ["Preço não normalizado ou não disponível"]
64
+ }
65
+
66
+ # Verificar se tem produtividade e custo
67
+ produtividade = item.get("produtividade", 0)
68
+ custo = item.get("custo", 0)
69
+
70
+ if produtividade <= 0:
71
+ motivos.append("Produtividade não disponível ou inválida")
72
+
73
+ if custo <= 0:
74
+ motivos.append("Custo não disponível ou inválido")
75
+
76
+ if motivos:
77
+ return {
78
+ "confiabilidade": "baixa",
79
+ "motivos": motivos
80
+ }
81
+
82
+ # Verificar se tem lucro de mercado calculado
83
+ lucro_mercado = item.get("lucro_mercado_estimado")
84
+ if lucro_mercado is None:
85
+ return {
86
+ "confiabilidade": "baixa",
87
+ "motivos": ["Lucro de mercado não calculado"]
88
+ }
89
+
90
+ # Calcular diferença com lucro do sistema
91
+ lucro_sistema = item.get("lucro_estimado", 0)
92
+ diferenca = calcular_diferenca_lucro(lucro_sistema, lucro_mercado)
93
+ diferenca_percentual = abs(diferenca["diferenca_percentual"])
94
+
95
+ # Classificar baseado na diferença percentual
96
+ if diferenca_percentual > 100:
97
+ confiabilidade = "baixa"
98
+ motivos.append(f"Diferença muito alta ({diferenca_percentual:.1f}%) entre lucro sistema e mercado")
99
+ elif diferenca_percentual > 50:
100
+ confiabilidade = "media"
101
+ motivos.append(f"Diferença moderada ({diferenca_percentual:.1f}%) entre lucro sistema e mercado")
102
+ else:
103
+ confiabilidade = "alta"
104
+ motivos.append(f"Diferença aceitável ({diferenca_percentual:.1f}%) entre lucro sistema e mercado")
105
+
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")
118
+
119
+ return {
120
+ "confiabilidade": confiabilidade,
121
+ "motivos": motivos,
122
+ "diferenca": diferenca
123
+ }
124
+
125
+
126
+ def validar_plano_lucro_mercado(resultado: Dict) -> Dict:
127
+ """
128
+ Valida lucro de mercado para todo o plano e adiciona classificação de confiabilidade.
129
+
130
+ Args:
131
+ resultado: Resultado do AG ou cenário com plano
132
+
133
+ Returns:
134
+ Resultado enriquecido com validação de lucro de mercado
135
+ """
136
+ if "plano" not in resultado:
137
+ return resultado
138
+
139
+ # Contadores
140
+ alta_confiabilidade = 0
141
+ media_confiabilidade = 0
142
+ baixa_confiabilidade = 0
143
+ alertas = []
144
+
145
+ # Validar cada item do plano
146
+ for item in resultado["plano"]:
147
+ # Classificar confiabilidade
148
+ validacao = classificar_confiabilidade_lucro(item)
149
+ item["validacao_lucro_mercado"] = validacao
150
+
151
+ # Contabilizar
152
+ conf = validacao["confiabilidade"]
153
+ if conf == "alta":
154
+ alta_confiabilidade += 1
155
+ elif conf == "media":
156
+ media_confiabilidade += 1
157
+ else:
158
+ baixa_confiabilidade += 1
159
+ # Adicionar alerta para baixa confiabilidade
160
+ cultura = item.get("cultura", "desconhecida")
161
+ talhao = item.get("talhao", "?")
162
+ alertas.append(f"Talhão {talhao} ({cultura}): {', '.join(validacao['motivos'])}")
163
+
164
+ # Adicionar resumo de validação
165
+ total = len(resultado["plano"])
166
+ percentual_alta = (alta_confiabilidade / total * 100) if total > 0 else 0
167
+ percentual_baixa = (baixa_confiabilidade / total * 100) if total > 0 else 0
168
+
169
+ resultado["validacao_lucro_mercado"] = {
170
+ "ativo": True,
171
+ "total_itens": total,
172
+ "itens_alta_confiabilidade": alta_confiabilidade,
173
+ "itens_media_confiabilidade": media_confiabilidade,
174
+ "itens_baixa_confiabilidade": baixa_confiabilidade,
175
+ "percentual_alta_confiabilidade": round(percentual_alta, 1),
176
+ "percentual_baixa_confiabilidade": round(percentual_baixa, 1),
177
+ "alertas": alertas[:5], # Limitar a 5 alertas principais
178
+ "total_alertas": len(alertas),
179
+ "recomendacao": _gerar_recomendacao(percentual_alta, percentual_baixa)
180
+ }
181
+
182
+ return resultado
183
+
184
+
185
+ def _gerar_recomendacao(percentual_alta: float, percentual_baixa: float) -> str:
186
+ """
187
+ Gera recomendação baseada nos percentuais de confiabilidade.
188
+
189
+ Args:
190
+ percentual_alta: Percentual de itens com alta confiabilidade
191
+ percentual_baixa: Percentual de itens com baixa confiabilidade
192
+
193
+ Returns:
194
+ String com recomendação
195
+ """
196
+ if percentual_alta >= 70:
197
+ return "Lucro de mercado apresenta boa confiabilidade. Considere validação detalhada antes de ativar PRICE_APPLY_TO_PROFIT."
198
+ elif percentual_baixa >= 50:
199
+ return "Muitos itens com baixa confiabilidade. Valide preços, produtividades e custos antes de usar lucro de mercado."
200
+ else:
201
+ return "Confiabilidade mista. Revise itens com baixa confiabilidade e valide dados de mercado."
202
+
203
+
204
+ def gerar_diagnostico_lucro_mercado(plano: List[Dict], uf: Optional[str] = None) -> Dict:
205
+ """
206
+ Gera diagnóstico detalhado do lucro de mercado para análise.
207
+
208
+ Args:
209
+ plano: Lista de itens do plano
210
+ uf: Unidade Federativa (opcional)
211
+
212
+ Returns:
213
+ Dict com diagnóstico por cultura
214
+ """
215
+ diagnostico = {
216
+ "uf": uf,
217
+ "total_culturas": 0,
218
+ "culturas": {}
219
+ }
220
+
221
+ # Agrupar por cultura
222
+ culturas_map = {}
223
+ for item in plano:
224
+ cultura = item.get("cultura", "desconhecida")
225
+
226
+ if cultura not in culturas_map:
227
+ culturas_map[cultura] = []
228
+
229
+ culturas_map[cultura].append(item)
230
+
231
+ # Gerar diagnóstico por cultura
232
+ for cultura, itens in culturas_map.items():
233
+ # Calcular médias
234
+ lucro_sistema_total = sum(i.get("lucro_estimado", 0) for i in itens)
235
+ lucro_mercado_total = sum(i.get("lucro_mercado_estimado", 0) for i in itens if i.get("lucro_mercado_estimado") is not None)
236
+
237
+ lucro_sistema_medio = lucro_sistema_total / len(itens) if itens else 0
238
+ lucro_mercado_medio = lucro_mercado_total / len(itens) if itens else 0
239
+
240
+ # Calcular diferença
241
+ diferenca = calcular_diferenca_lucro(lucro_sistema_medio, lucro_mercado_medio)
242
+
243
+ # Obter confiabilidade do primeiro item (representativo)
244
+ validacao = classificar_confiabilidade_lucro(itens[0]) if itens else {"confiabilidade": "baixa", "motivos": []}
245
+
246
+ # Obter preço normalizado
247
+ preco_norm = itens[0].get("preco_normalizado", {}) if itens else {}
248
+ preco_real = itens[0].get("preco_real", {}) if itens else {}
249
+
250
+ diagnostico["culturas"][cultura] = {
251
+ "total_talhoes": len(itens),
252
+ "lucro_sistema_medio": round(lucro_sistema_medio, 2),
253
+ "lucro_mercado_medio": round(lucro_mercado_medio, 2),
254
+ "diferenca": diferenca,
255
+ "confiabilidade": validacao["confiabilidade"],
256
+ "motivos": validacao["motivos"],
257
+ "preco_original": preco_real.get("preco"),
258
+ "unidade_original": preco_real.get("unidade"),
259
+ "preco_por_tonelada": preco_norm.get("preco_por_tonelada"),
260
+ "normalizado": preco_norm.get("normalizado", False),
261
+ "fallback": preco_real.get("fallback", False)
262
+ }
263
+
264
+ diagnostico["total_culturas"] = len(culturas_map)
265
+
266
+ return diagnostico
@@ -1,6 +1,6 @@
1
1
  """
2
2
  Adaptador de Preços Agrícolas
3
- Integra preços reais no planejamento com normalização de unidades
3
+ Integra preços reais no planejamento com normalização de unidades e validação
4
4
  """
5
5
 
6
6
  import os
@@ -12,6 +12,7 @@ from core.price_normalizer import (
12
12
  calcular_lucro_com_preco_normalizado,
13
13
  obter_estatisticas_normalizacao
14
14
  )
15
+ from core.market_profit_validator import validar_plano_lucro_mercado
15
16
 
16
17
  # Configuração
17
18
  PRICE_APPLY_TO_PROFIT = os.getenv("PRICE_APPLY_TO_PROFIT", "false").lower() == "true"
@@ -171,6 +172,9 @@ def aplicar_precos_no_plano(
171
172
  }
172
173
  }
173
174
 
175
+ # Validar lucro de mercado e adicionar classificação de confiabilidade
176
+ resultado = validar_plano_lucro_mercado(resultado)
177
+
174
178
  return resultado
175
179
 
176
180
 
@@ -169,6 +169,11 @@ def gerar_relatorio_completo(culturas, talhoes, regras, objetivo='equilibrado',
169
169
  secao_precos = gerar_secao_precos_relatorio(resultado_temp["plano"], uf, formato)
170
170
  conteudo += "\n\n" + secao_precos
171
171
 
172
+ # Adicionar seção de validação de lucro de mercado
173
+ if resultado_temp.get("validacao_lucro_mercado", {}).get("ativo"):
174
+ secao_validacao = gerar_secao_validacao_lucro_mercado(resultado_temp, formato)
175
+ conteudo += "\n\n" + secao_validacao
176
+
172
177
  # Salva arquivo
173
178
  os.makedirs('reports', exist_ok=True)
174
179
  timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
@@ -249,6 +254,193 @@ def gerar_secao_climatica(contexto_climatico, formato='md'):
249
254
  return secao
250
255
 
251
256
 
257
+ def gerar_secao_validacao_lucro_mercado(resultado, formato='md'):
258
+ """
259
+ Gera seção de validação de lucro de mercado para o relatório
260
+
261
+ Args:
262
+ resultado: Resultado com validacao_lucro_mercado
263
+ formato: 'md' ou 'txt'
264
+
265
+ Returns:
266
+ String com seção formatada
267
+ """
268
+ validacao = resultado.get("validacao_lucro_mercado", {})
269
+
270
+ if not validacao.get("ativo"):
271
+ return ""
272
+
273
+ if formato == "md":
274
+ secao = "## 🔍 Validação do Lucro de Mercado\n\n"
275
+
276
+ secao += "### Resumo de Confiabilidade\n\n"
277
+
278
+ total = validacao.get("total_itens", 0)
279
+ alta = validacao.get("itens_alta_confiabilidade", 0)
280
+ media = validacao.get("itens_media_confiabilidade", 0)
281
+ baixa = validacao.get("itens_baixa_confiabilidade", 0)
282
+
283
+ perc_alta = validacao.get("percentual_alta_confiabilidade", 0)
284
+ perc_baixa = validacao.get("percentual_baixa_confiabilidade", 0)
285
+
286
+ secao += f"- **Total de itens analisados**: {total}\n"
287
+ secao += f"- **Alta confiabilidade**: {alta} ({perc_alta:.1f}%) 🟢\n"
288
+ secao += f"- **Média confiabilidade**: {media} ({100 - perc_alta - perc_baixa:.1f}%) 🟡\n"
289
+ secao += f"- **Baixa confiabilidade**: {baixa} ({perc_baixa:.1f}%) 🔴\n\n"
290
+
291
+ # Recomendação
292
+ recomendacao = validacao.get("recomendacao", "")
293
+ if recomendacao:
294
+ secao += f"**Recomendação**: {recomendacao}\n\n"
295
+
296
+ # Alertas
297
+ alertas = validacao.get("alertas", [])
298
+ if alertas:
299
+ secao += "### ⚠️ Alertas\n\n"
300
+ for alerta in alertas:
301
+ secao += f"- {alerta}\n"
302
+ secao += "\n"
303
+
304
+ total_alertas = validacao.get("total_alertas", 0)
305
+ if total_alertas > len(alertas):
306
+ secao += f"*Mostrando {len(alertas)} de {total_alertas} alertas*\n\n"
307
+
308
+ # Detalhes por item
309
+ secao += "### Detalhes por Talhão\n\n"
310
+ secao += "| Talhão | Cultura | Lucro Sistema | Lucro Mercado | Diferença % | Confiabilidade |\n"
311
+ secao += "|--------|---------|---------------|---------------|-------------|----------------|\n"
312
+
313
+ for item in resultado["plano"]:
314
+ validacao_item = item.get("validacao_lucro_mercado", {})
315
+ if not validacao_item:
316
+ continue
317
+
318
+ talhao = item.get("talhao", "N/A")
319
+ cultura = item.get("cultura", "").upper()
320
+ lucro_sistema = item.get("lucro_estimado", 0)
321
+ lucro_mercado = item.get("lucro_mercado_estimado", 0)
322
+
323
+ lucro_sistema_fmt = f"R$ {lucro_sistema:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
324
+
325
+ if lucro_mercado is not None:
326
+ lucro_mercado_fmt = f"R$ {lucro_mercado:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
327
+ diferenca = validacao_item.get("diferenca", {})
328
+ diferenca_perc = diferenca.get("diferenca_percentual", 0)
329
+ diferenca_fmt = f"{diferenca_perc:.1f}%"
330
+ else:
331
+ lucro_mercado_fmt = "N/A"
332
+ diferenca_fmt = "N/A"
333
+
334
+ confiabilidade = validacao_item.get("confiabilidade", "baixa")
335
+ conf_emoji = "🟢" if confiabilidade == "alta" else "🟡" if confiabilidade == "media" else "🔴"
336
+ conf_label = confiabilidade.title()
337
+
338
+ secao += f"| {talhao} | {cultura} | {lucro_sistema_fmt} | {lucro_mercado_fmt} | {diferenca_fmt} | {conf_emoji} {conf_label} |\n"
339
+
340
+ secao += "\n"
341
+
342
+ # Explicação
343
+ secao += "### 📊 Sobre a Classificação de Confiabilidade\n\n"
344
+ secao += "A confiabilidade do lucro de mercado é classificada com base em:\n\n"
345
+ 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"
348
+
349
+ secao += "### ⚠️ Aviso Importante\n\n"
350
+ secao += "**O lucro de mercado ainda é experimental e não substitui o lucro principal do sistema.**\n\n"
351
+ secao += "Os valores são exibidos apenas como comparação para validação. Diferenças altas indicam "
352
+ secao += "necessidade de validação de produtividade, custos ou unidade comercial.\n\n"
353
+ secao += "**Status atual**: `PRICE_APPLY_TO_PROFIT=false` (lucro de mercado não afeta otimização)\n"
354
+
355
+ else: # txt
356
+ secao = "VALIDAÇÃO DO LUCRO DE MERCADO\n\n"
357
+
358
+ secao += "Resumo de Confiabilidade:\n\n"
359
+
360
+ total = validacao.get("total_itens", 0)
361
+ alta = validacao.get("itens_alta_confiabilidade", 0)
362
+ media = validacao.get("itens_media_confiabilidade", 0)
363
+ baixa = validacao.get("itens_baixa_confiabilidade", 0)
364
+
365
+ perc_alta = validacao.get("percentual_alta_confiabilidade", 0)
366
+ perc_baixa = validacao.get("percentual_baixa_confiabilidade", 0)
367
+
368
+ secao += f" Total de itens analisados: {total}\n"
369
+ secao += f" Alta confiabilidade: {alta} ({perc_alta:.1f}%)\n"
370
+ secao += f" Média confiabilidade: {media} ({100 - perc_alta - perc_baixa:.1f}%)\n"
371
+ secao += f" Baixa confiabilidade: {baixa} ({perc_baixa:.1f}%)\n\n"
372
+
373
+ # Recomendação
374
+ recomendacao = validacao.get("recomendacao", "")
375
+ if recomendacao:
376
+ secao += f"Recomendação: {recomendacao}\n\n"
377
+
378
+ # Alertas
379
+ alertas = validacao.get("alertas", [])
380
+ if alertas:
381
+ secao += "Alertas:\n\n"
382
+ for alerta in alertas:
383
+ secao += f" - {alerta}\n"
384
+ secao += "\n"
385
+
386
+ total_alertas = validacao.get("total_alertas", 0)
387
+ if total_alertas > len(alertas):
388
+ secao += f" (Mostrando {len(alertas)} de {total_alertas} alertas)\n\n"
389
+
390
+ # Detalhes por item
391
+ secao += "Detalhes por Talhão:\n\n"
392
+
393
+ for item in resultado["plano"]:
394
+ validacao_item = item.get("validacao_lucro_mercado", {})
395
+ if not validacao_item:
396
+ continue
397
+
398
+ talhao = item.get("talhao", "N/A")
399
+ cultura = item.get("cultura", "").upper()
400
+ lucro_sistema = item.get("lucro_estimado", 0)
401
+ lucro_mercado = item.get("lucro_mercado_estimado", 0)
402
+
403
+ lucro_sistema_fmt = f"R$ {lucro_sistema:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
404
+
405
+ secao += f" Talhão {talhao} ({cultura}):\n"
406
+ secao += f" Lucro Sistema: {lucro_sistema_fmt}\n"
407
+
408
+ if lucro_mercado is not None:
409
+ lucro_mercado_fmt = f"R$ {lucro_mercado:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
410
+ diferenca = validacao_item.get("diferenca", {})
411
+ diferenca_perc = diferenca.get("diferenca_percentual", 0)
412
+ secao += f" Lucro Mercado: {lucro_mercado_fmt}\n"
413
+ secao += f" Diferença: {diferenca_perc:.1f}%\n"
414
+ else:
415
+ secao += f" Lucro Mercado: N/A\n"
416
+
417
+ confiabilidade = validacao_item.get("confiabilidade", "baixa")
418
+ secao += f" Confiabilidade: {confiabilidade.title()}\n"
419
+
420
+ motivos = validacao_item.get("motivos", [])
421
+ if motivos:
422
+ secao += f" Motivos: {', '.join(motivos)}\n"
423
+
424
+ secao += "\n"
425
+
426
+ # Explicação
427
+ secao += "Sobre a Classificação de Confiabilidade:\n\n"
428
+ secao += "A confiabilidade do lucro de mercado é classificada com base em:\n\n"
429
+ secao += " - Alta: Diferença < 50% entre lucro sistema e mercado\n"
430
+ secao += " - Média: Diferença 50-100%, uso de fallback, ou lucro negativo\n"
431
+ secao += " - Baixa: Diferença > 100%, dados incompletos, ou preço não disponível\n\n"
432
+
433
+ secao += "Aviso Importante:\n\n"
434
+ secao += "O lucro de mercado ainda é experimental e não substitui o lucro principal\n"
435
+ secao += "do sistema. Os valores são exibidos apenas como comparação para validação.\n"
436
+ secao += "Diferenças altas indicam necessidade de validação de produtividade, custos\n"
437
+ secao += "ou unidade comercial.\n\n"
438
+ secao += "Status atual: PRICE_APPLY_TO_PROFIT=false (lucro de mercado não afeta\n"
439
+ secao += "otimização)\n"
440
+
441
+ return secao
442
+
443
+
252
444
  def gerar_relatorio_markdown(culturas, talhoes, regras, objetivo, cenarios, resultado_ag, validacao, estabilidade):
253
445
  """Gera relatório em formato Markdown"""
254
446
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroplan-ai-cli",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "CLI global para AgroPlan AI - modo local acelerado",
5
5
  "type": "module",
6
6
  "bin": {