Tier 1: chat quality — fuzzy aliases, reply templates, dedup, rewriter
Foco: matar repetición y adaptar respuestas. Los handlers tenían ~30 strings hardcodeadas (3-7 lugares cada una). Aliases hacían substring exacto. - pg_trgm + GIN indexes en product_aliases / alias_product_mappings. Captura plurales, diminutivos, typos sin reglas. catalogRetrieval re-busca el snapshot con normalized_alias cuando el query original no rinde (vasio→vacio→Vacío). - reply_templates table + replyTemplates.js. 20 keys, 2-3 variantes c/u con DEFAULTS hardcodeados como fallback. pickVariant excluye las usadas en context.recent_replies (FIFO cap 8). Wired en idle/cart/cartHelpers/ shipping/payment/waiting. - failed_searches counter en context. count>=3 escala via humanFallback. Reset en cada add_to_cart exitoso. - storeContext.js: vars derivadas de getStoreConfig (delivery_zones, hours, zonas) listas para inyectar en templates cuando los datos se carguen. - replyRewriter.js: LLM call opcional (REPLY_REWRITER=1) que adapta el template al hilo conversacional. 1.5s timeout, fallback al template puro. Sólo activo en 8 slots semánticamente importantes. - 12 unit tests para replyTemplates (rotation, recency, FIFO, vars). 208 tests totales pasando. Plan completo: ~/.claude/plans/ok-creo-que-tiene-humming-sutton.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
handleWaitingState,
|
||||
} from "./stateHandlers.js";
|
||||
import { getStoreConfig } from "../0-ui/db/settingsRepo.js";
|
||||
import { pushRecent } from "./replyTemplates.js";
|
||||
|
||||
// Feature flag para NLU modular
|
||||
const USE_MODULAR_NLU = process.env.USE_MODULAR_NLU === "true";
|
||||
@@ -60,10 +61,20 @@ export async function runTurnV3({
|
||||
|
||||
// Migrar contexto viejo a nuevo formato de orden
|
||||
const order = migrateOldContext(prev_context);
|
||||
|
||||
|
||||
// Mapear estados viejos a nuevos
|
||||
const normalizedState = normalizeState(prev_state);
|
||||
|
||||
// Recent replies para dedup de templates (FIFO cap 8)
|
||||
const recentReplies = Array.isArray(prev_context?.recent_replies)
|
||||
? prev_context.recent_replies
|
||||
: [];
|
||||
|
||||
// Counter de búsquedas fallidas consecutivas para escalación
|
||||
const failedSearches = (prev_context?.failed_searches && typeof prev_context.failed_searches === "object")
|
||||
? prev_context.failed_searches
|
||||
: { count: 0, last_query: null, last_at: null };
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// NLU (con feature flag para sistema modular)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
@@ -124,13 +135,16 @@ export async function runTurnV3({
|
||||
order,
|
||||
audit,
|
||||
storeConfig,
|
||||
recentReplies,
|
||||
conversation_history: conversation_history || [],
|
||||
failedSearches,
|
||||
};
|
||||
|
||||
// Regla universal: si quiere agregar productos, volver a CART
|
||||
const returnToCart = shouldReturnToCart(normalizedState, nlu, text);
|
||||
if (returnToCart) {
|
||||
const result = await handleCartState({ ...handlerParams, fromIdle: false });
|
||||
return formatResult(result, prev_context);
|
||||
return formatResult(result, prev_context, recentReplies, failedSearches);
|
||||
}
|
||||
|
||||
// Dispatch por estado actual
|
||||
@@ -162,7 +176,7 @@ export async function runTurnV3({
|
||||
result = await handleIdleState(handlerParams);
|
||||
}
|
||||
|
||||
return formatResult(result, prev_context);
|
||||
return formatResult(result, prev_context, recentReplies, failedSearches);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,9 +212,22 @@ function normalizeState(state) {
|
||||
/**
|
||||
* Formatea el resultado para compatibilidad con el sistema existente
|
||||
*/
|
||||
function formatResult(result, prevContext) {
|
||||
function formatResult(result, prevContext, recentReplies = [], failedSearches = { count: 0 }) {
|
||||
const { plan, decision } = result;
|
||||
const order = decision?.order || createEmptyOrder();
|
||||
|
||||
// Mergear template_ids usados por los handlers en recent_replies
|
||||
const idsUsed = Array.isArray(decision?.template_ids_used)
|
||||
? decision.template_ids_used.filter(Boolean)
|
||||
: [];
|
||||
let nextRecent = recentReplies;
|
||||
for (const id of idsUsed) {
|
||||
nextRecent = pushRecent(nextRecent, id);
|
||||
}
|
||||
|
||||
// failed_searches: handlers pueden devolver decision.failed_searches_next.
|
||||
// Si no, mantener el previo.
|
||||
const nextFailedSearches = decision?.failed_searches_next || failedSearches;
|
||||
|
||||
// Construir context_patch para compatibilidad con pipeline
|
||||
const context_patch = {
|
||||
@@ -238,6 +265,11 @@ function formatResult(result, prevContext) {
|
||||
order.is_delivery === false ? "pickup" : null,
|
||||
delivery_address: order.shipping_address ? { text: order.shipping_address } : null,
|
||||
woo_order_id: order.woo_order_id,
|
||||
|
||||
// Dedup de respuestas: ids de templates usados, FIFO cap 8
|
||||
recent_replies: nextRecent,
|
||||
// Counter de búsquedas fallidas para escalación
|
||||
failed_searches: nextFailedSearches,
|
||||
};
|
||||
|
||||
// Construir basket_resolved para UI
|
||||
|
||||
Reference in New Issue
Block a user