feat: one-shot audio entity extraction y pre-fill de formularios

- AiBot: NLU ahora extrae entities (finca, valor, etc.) del audio
- BotRouter: guarda __nlu_entities en metadata antes de enrutar
- NormalBot: handleCollectAndPost pre-rellena campos desde entities
  y capAskNext salta los ya completados
- NormalBot: handleCollectForEach registra ítem directo si entities
  trae finca+valor coincidente, sin iterar todo el listado
- BotRouter: mid-flow audio va a NormalBot directamente (no NLU)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-05 21:48:48 -05:00
co-authored by Claude Sonnet 4.6
parent d0c1b1d3be
commit 54a93adea9
3 changed files with 125 additions and 9 deletions
+8 -3
View File
@@ -364,13 +364,16 @@ PROMPT;
}
if ($json['action'] === 'route' && isset($json['key'])) {
// Validate key exists in flows
$allFlows = $config['flows'] ?? [];
foreach ($config['per_type'] ?? [] as $pt) {
$allFlows = array_merge($allFlows, $pt['flows'] ?? []);
}
if (isset($allFlows[$json['key']])) {
return ['action' => 'route', 'key' => $json['key']];
return [
'action' => 'route',
'key' => $json['key'],
'entities' => is_array($json['entities'] ?? null) ? $json['entities'] : [],
];
}
}
@@ -442,7 +445,9 @@ PROMPT;
. "Opciones disponibles:\n" . implode("\n", $lines) . "\n\n"
. "Analiza el mensaje y determina si se refiere claramente a una opción.\n\n"
. "Si sí → responde SOLO este JSON (sin markdown):\n"
. "{\"action\":\"route\",\"key\":\"<key_exacto>\"}\n\n"
. "{\"action\":\"route\",\"key\":\"<key_exacto>\",\"entities\":{\"campo\":\"valor\",...}}\n\n"
. "En 'entities' incluye los datos que el usuario ya mencionó (nombre de finca, fecha, valor numérico, etc.).\n"
. "Si no mencionó datos extra, omite entities o usa {}.\n\n"
. "Si no está claro o no hay opción correspondiente → responde SOLO:\n"
. "{\"action\":\"chat\",\"text\":\"<respuesta corta en español, máx 2 oraciones>\"}\n\n"
. "No inventes keys. Usa exactamente los keys de la lista.";