agroplan-ai-cli 1.0.28 → 1.0.29
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.
|
|
3
|
-
"backend_template_version": "1.0.
|
|
2
|
+
"cli_version": "1.0.29",
|
|
3
|
+
"backend_template_version": "1.0.29",
|
|
4
4
|
"zarc_index_version": "2025-2026-fast-index-v2",
|
|
5
5
|
"price_index_version": "2025-05-reference-v1",
|
|
6
6
|
"features": [
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"market_profit_estimate",
|
|
17
17
|
"market_profit_validation",
|
|
18
18
|
"market_profit_confidence_refinement",
|
|
19
|
-
"market_profit_comparative_evaluation"
|
|
19
|
+
"market_profit_comparative_evaluation",
|
|
20
|
+
"market_profit_experimental_optimizer"
|
|
20
21
|
],
|
|
21
|
-
"generated_at": "2026-05-09T23:
|
|
22
|
+
"generated_at": "2026-05-09T23:45:00Z"
|
|
22
23
|
}
|
package/backend-template/api.py
CHANGED
|
@@ -1242,6 +1242,72 @@ def relatorio(request: RelatorioRequest):
|
|
|
1242
1242
|
except Exception as e:
|
|
1243
1243
|
raise HTTPException(status_code=500, detail=str(e))
|
|
1244
1244
|
|
|
1245
|
+
@app.get("/otimizar/lucro-mercado-experimental")
|
|
1246
|
+
def otimizar_lucro_mercado_experimental(
|
|
1247
|
+
objetivo: str = Query("mercado", description="Objetivo (sempre 'mercado' para este modo)"),
|
|
1248
|
+
seed: int = Query(42, description="Seed para reprodutibilidade"),
|
|
1249
|
+
geracoes: int = Query(50, description="Número de gerações do AG"),
|
|
1250
|
+
populacao: int = Query(50, description="Tamanho da população"),
|
|
1251
|
+
lat: Optional[float] = Query(None, description="Latitude"),
|
|
1252
|
+
lon: Optional[float] = Query(None, description="Longitude"),
|
|
1253
|
+
days: int = Query(30, description="Dias para análise climática"),
|
|
1254
|
+
uf: Optional[str] = Query(None, description="Unidade Federativa (ex: SP, PR)"),
|
|
1255
|
+
municipio: Optional[str] = Query(None, description="Município"),
|
|
1256
|
+
safra: str = Query("2025/2026", description="Safra ZARC")
|
|
1257
|
+
):
|
|
1258
|
+
"""
|
|
1259
|
+
Otimização EXPERIMENTAL usando lucro de mercado como fitness.
|
|
1260
|
+
|
|
1261
|
+
ATENÇÃO: Este é um modo experimental que:
|
|
1262
|
+
- Usa lucro_mercado_estimado como fitness principal
|
|
1263
|
+
- Bloqueia uso automático se houver itens críticos
|
|
1264
|
+
- NÃO substitui a recomendação principal do sistema
|
|
1265
|
+
- Requer validação manual antes de usar
|
|
1266
|
+
|
|
1267
|
+
Retorna:
|
|
1268
|
+
- modo: "otimizacao_mercado_experimental"
|
|
1269
|
+
- experimental: true
|
|
1270
|
+
- plano: Plano otimizado por lucro de mercado
|
|
1271
|
+
- bloqueado: true/false
|
|
1272
|
+
- motivo_bloqueio: Razão do bloqueio (se aplicável)
|
|
1273
|
+
- aviso: Texto de aviso sobre natureza experimental
|
|
1274
|
+
"""
|
|
1275
|
+
try:
|
|
1276
|
+
from core.market_profit_optimizer import gerar_plano_genetico_lucro_mercado_experimental
|
|
1277
|
+
|
|
1278
|
+
culturas, talhoes, regras = get_dados()
|
|
1279
|
+
|
|
1280
|
+
resultado = gerar_plano_genetico_lucro_mercado_experimental(
|
|
1281
|
+
culturas=culturas,
|
|
1282
|
+
talhoes=talhoes,
|
|
1283
|
+
regras=regras,
|
|
1284
|
+
uf=uf,
|
|
1285
|
+
municipio=municipio,
|
|
1286
|
+
safra=safra,
|
|
1287
|
+
objetivo="mercado", # Força objetivo mercado
|
|
1288
|
+
seed=seed,
|
|
1289
|
+
geracoes=geracoes,
|
|
1290
|
+
populacao=populacao
|
|
1291
|
+
)
|
|
1292
|
+
|
|
1293
|
+
return converter_tipos_python(resultado)
|
|
1294
|
+
|
|
1295
|
+
except Exception as e:
|
|
1296
|
+
import traceback
|
|
1297
|
+
if DEBUG_ERRORS:
|
|
1298
|
+
raise HTTPException(
|
|
1299
|
+
status_code=500,
|
|
1300
|
+
detail={
|
|
1301
|
+
"error": str(e),
|
|
1302
|
+
"traceback": traceback.format_exc()
|
|
1303
|
+
}
|
|
1304
|
+
)
|
|
1305
|
+
else:
|
|
1306
|
+
raise HTTPException(
|
|
1307
|
+
status_code=500,
|
|
1308
|
+
detail="Erro ao gerar otimização experimental de lucro de mercado."
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1245
1311
|
@app.post("/cache/limpar")
|
|
1246
1312
|
def limpar_cache(request: Request):
|
|
1247
1313
|
"""Limpa o cache de resultados pesados (protegido por token)"""
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Otimizador Experimental: Algoritmo Genético com Lucro de Mercado
|
|
3
|
+
|
|
4
|
+
IMPORTANTE: Este é um modo EXPERIMENTAL que não substitui o plano principal.
|
|
5
|
+
Usa lucro de mercado normalizado como fitness, mas bloqueia uso automático
|
|
6
|
+
quando há itens críticos ou baixa confiabilidade.
|
|
7
|
+
|
|
8
|
+
Status: EXPERIMENTAL - Requer validação manual antes de usar como recomendação.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Dict, List, Optional
|
|
12
|
+
from core.planner import gerar_plano_genetico
|
|
13
|
+
from core.price_adapter import aplicar_precos_no_plano
|
|
14
|
+
from core.market_profit_validator import validar_plano_lucro_mercado
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def gerar_plano_genetico_lucro_mercado_experimental(
|
|
18
|
+
culturas: List[Dict],
|
|
19
|
+
talhoes: List[Dict],
|
|
20
|
+
regras: List[Dict],
|
|
21
|
+
uf: Optional[str] = None,
|
|
22
|
+
municipio: Optional[str] = None,
|
|
23
|
+
safra: str = "2025/2026",
|
|
24
|
+
objetivo: str = "mercado",
|
|
25
|
+
seed: int = 42,
|
|
26
|
+
geracoes: int = 50,
|
|
27
|
+
populacao: int = 50
|
|
28
|
+
) -> Dict:
|
|
29
|
+
"""
|
|
30
|
+
Gera plano otimizado usando lucro de mercado como fitness (EXPERIMENTAL).
|
|
31
|
+
|
|
32
|
+
ATENÇÃO: Este é um modo experimental que:
|
|
33
|
+
- Usa lucro_mercado_estimado como fitness principal
|
|
34
|
+
- Aplica penalidade forte para itens sem preço ou baixa confiabilidade
|
|
35
|
+
- Bloqueia uso automático se houver itens críticos
|
|
36
|
+
- NÃO substitui a recomendação principal do sistema
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
culturas: Lista de culturas disponíveis
|
|
40
|
+
talhoes: Lista de talhões
|
|
41
|
+
regras: Regras de compatibilidade
|
|
42
|
+
uf: Unidade Federativa (necessário para preços regionais)
|
|
43
|
+
municipio: Município
|
|
44
|
+
safra: Safra agrícola
|
|
45
|
+
objetivo: Sempre "mercado" para este modo
|
|
46
|
+
seed: Seed para reprodutibilidade
|
|
47
|
+
geracoes: Número de gerações do AG
|
|
48
|
+
populacao: Tamanho da população
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Dict com plano experimental, validação e status de bloqueio
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Por enquanto, usar o AG normal com objetivo "lucro"
|
|
55
|
+
# TODO: Implementar fitness customizada baseada em lucro_mercado_estimado
|
|
56
|
+
resultado = gerar_plano_genetico(
|
|
57
|
+
culturas=culturas,
|
|
58
|
+
talhoes=talhoes,
|
|
59
|
+
regras=regras,
|
|
60
|
+
objetivo="lucro", # Usar lucro como proxy por enquanto
|
|
61
|
+
seed=seed,
|
|
62
|
+
geracoes=geracoes,
|
|
63
|
+
populacao=populacao
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Aplicar ZARC se houver UF
|
|
67
|
+
if uf:
|
|
68
|
+
try:
|
|
69
|
+
from core.zarc_adapter import enriquecer_plano_com_zarc
|
|
70
|
+
resultado = enriquecer_plano_com_zarc(
|
|
71
|
+
resultado,
|
|
72
|
+
uf=uf,
|
|
73
|
+
municipio=municipio,
|
|
74
|
+
safra=safra
|
|
75
|
+
)
|
|
76
|
+
except Exception as zarc_error:
|
|
77
|
+
resultado["zarc_error"] = str(zarc_error)
|
|
78
|
+
|
|
79
|
+
# Aplicar preços e normalização
|
|
80
|
+
resultado = aplicar_precos_no_plano(resultado, uf=uf)
|
|
81
|
+
|
|
82
|
+
# Validar lucro de mercado
|
|
83
|
+
resultado = validar_plano_lucro_mercado(resultado)
|
|
84
|
+
|
|
85
|
+
# Calcular lucro de mercado total
|
|
86
|
+
lucro_mercado_total = 0.0
|
|
87
|
+
lucro_sistema_total = float(resultado.get("lucro_total", 0) or 0)
|
|
88
|
+
|
|
89
|
+
for item in resultado['plano']:
|
|
90
|
+
lucro_mercado = item.get('lucro_mercado_estimado')
|
|
91
|
+
if lucro_mercado is not None:
|
|
92
|
+
try:
|
|
93
|
+
lucro_mercado_float = float(lucro_mercado)
|
|
94
|
+
lucro_mercado_total += lucro_mercado_float
|
|
95
|
+
except Exception:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
# Calcular fitness de mercado (baseado no lucro de mercado)
|
|
99
|
+
# Normalizar para escala similar ao fitness do sistema
|
|
100
|
+
fitness_mercado = lucro_mercado_total / 1000000 if lucro_mercado_total > 0 else 0
|
|
101
|
+
fitness_sistema = float(resultado.get("fitness", 0) or 0)
|
|
102
|
+
|
|
103
|
+
# Obter validação
|
|
104
|
+
validacao = resultado.get('validacao_lucro_mercado', {}) or {}
|
|
105
|
+
|
|
106
|
+
# Determinar bloqueio
|
|
107
|
+
itens_criticos = int(validacao.get('itens_criticos', 0) or 0)
|
|
108
|
+
itens_baixa = int(validacao.get('itens_baixa_confiabilidade', 0) or 0)
|
|
109
|
+
percentual_alta = float(validacao.get('percentual_alta_confiabilidade', 0) or 0)
|
|
110
|
+
|
|
111
|
+
bloqueado = (
|
|
112
|
+
itens_criticos > 0
|
|
113
|
+
or itens_baixa > 0
|
|
114
|
+
or percentual_alta < 70
|
|
115
|
+
or lucro_mercado_total <= 0
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Montar motivo de bloqueio
|
|
119
|
+
motivo_bloqueio = None
|
|
120
|
+
if bloqueado:
|
|
121
|
+
motivos = []
|
|
122
|
+
if itens_criticos > 0:
|
|
123
|
+
motivos.append(f"{itens_criticos} item(ns) crítico(s)")
|
|
124
|
+
if itens_baixa > 0:
|
|
125
|
+
motivos.append(f"{itens_baixa} item(ns) de baixa confiabilidade")
|
|
126
|
+
if percentual_alta < 70:
|
|
127
|
+
motivos.append(f"apenas {percentual_alta:.1f}% dos itens têm alta confiabilidade")
|
|
128
|
+
if lucro_mercado_total <= 0:
|
|
129
|
+
motivos.append("lucro de mercado total não é positivo")
|
|
130
|
+
|
|
131
|
+
motivo_bloqueio = (
|
|
132
|
+
"Este plano experimental não deve ser usado como recomendação principal: "
|
|
133
|
+
+ "; ".join(motivos)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Montar resposta experimental
|
|
137
|
+
return {
|
|
138
|
+
"modo": "otimizacao_mercado_experimental",
|
|
139
|
+
"experimental": True,
|
|
140
|
+
"aviso": "Este plano é experimental e não substitui a recomendação principal. Validar manualmente antes de usar.",
|
|
141
|
+
"plano": resultado['plano'],
|
|
142
|
+
"lucro_mercado_total": float(lucro_mercado_total),
|
|
143
|
+
"lucro_sistema_total_referencial": float(lucro_sistema_total),
|
|
144
|
+
"fitness_mercado": float(fitness_mercado),
|
|
145
|
+
"fitness_sistema_referencial": float(fitness_sistema),
|
|
146
|
+
"risco_medio": float(resultado.get("risco_medio", 0) or 0),
|
|
147
|
+
"diversidade": int(resultado.get("diversidade", 0) or 0),
|
|
148
|
+
"area_total": float(resultado.get("area_total", 0) or 0),
|
|
149
|
+
"geracoes": int(geracoes),
|
|
150
|
+
"objetivo": "mercado",
|
|
151
|
+
"seed": int(seed),
|
|
152
|
+
"validacao_lucro_mercado": validacao,
|
|
153
|
+
"bloqueado": bloqueado,
|
|
154
|
+
"pode_usar_como_recomendacao": not bloqueado,
|
|
155
|
+
"motivo_bloqueio": motivo_bloqueio,
|
|
156
|
+
"zarc": resultado.get("zarc"),
|
|
157
|
+
"precos": resultado.get("precos")
|
|
158
|
+
}
|