Esta página documenta el mecanismo tal como está implementado en
src/lib/search-interpret.ts, src/lib/search-interpret-shared.ts, src/lib/search-location-shared.ts, src/lib/search-conversation-shared.ts y src/lib/search-openai.ts. Los endpoints son POST /api/search/interpret y POST /api/search/interpret-image; sus contratos están en docs/openapi-auth.yaml.Regla: qué + dónde
Una consulta es explícita cuando tiene qué (servicio o problema) y dónde (zona). Quién, cuándo y cómo no se preguntan.- Qué está resuelto cuando la interpretación es de confianza alta (reglas locales u OpenAI; basta una categoría), cuando el usuario eligió un servicio en la tarjeta o cuando lo clasificó una foto. Un
keyword_fallback(confianza baja) no cuenta: se pregunta. - Dónde está resuelto si viene de cualquier fuente (ver más abajo).
missing: null si ambos están resueltos, o "que" / "donde". Si faltan los dos, primero se pregunta el qué. question trae el texto de la pregunta:
Máximo 2 preguntas
MAX_SEARCH_QUESTIONS es 2 y nunca se pregunta dos veces lo mismo. El servidor no guarda la conversación: el cliente reenvía en cada turno el objeto turn con lo acumulado.
Cuando se agotan las 2 preguntas,
missing vuelve null y se va a resultados con lo que haya. Los consumidores que no envían turn reciben lo mismo que antes y pueden ignorar los campos nuevos.
Cuándo se llama a OpenAI
- Si las reglas locales no resuelven el qué (como antes).
- Si las reglas sí lo resolvieron pero no hay dónde en la respuesta ni en el campo del buscador, y el perfil no tiene ciudad: se llama para extraer el dónde del texto.
- Si el dónde ya viene del campo, no hay llamada extra.
- Si el perfil tiene ciudad, tampoco se llama. La tarjeta se abre con esa ciudad y ese barrio.
- Una respuesta escrita al dónde, sin ciudad, se separa en ciudad y barrio con OpenAI cuando hay
OPENAI_API_KEY.
OPENAI_API_KEY, se degrada a reglas y fallback; nunca hay un 500 por la IA. Sin la clave, el dónde sale de la respuesta o del campo. Si solo está el perfil, la tarjeta pregunta con la ciudad y el barrio ya escritos.
De dónde sale el dónde
Se elige una sola fuente, sin mezclar ciudad de una con barrio de otra. Prioridad:answer: lo que el usuario respondió o editó en la tarjeta.query: el lugar que OpenAI extrajo del texto de la consulta, ya separado en ciudad y barrio.field: el campo “Ubicación” del buscador (texto libre).profile: la ciudad y el barrio guardados en el perfil. No cierran la búsqueda: rellenan la tarjeta para confirmarlos o cambiarlos. Se leen en el servidor con la sesión, no viajan desde el cliente.
location con source, city, neighborhood y text.
Ciudad filtra, barrio ordena
GET /api/camellos recibe la ubicación según lo que se sepa:
- Con ciudad conocida (
city): filtra por ciudad. Si además llegabarrio, los camellos de ese barrio salen primero dentro del orden elegido y el resto va después (incluido quien no tiene barrio). El barrio nunca excluye. El orden por barrio aplica con paginación porpage; la respuesta incluyebarrioTotal. - Texto libre sin ciudad (
ubicacion): comportamiento clásico, la ciudad o el barrio contienen el texto.
- Si en el barrio no hay nadie (
barrioTotales 0), se muestran los de la ciudad con este aviso, que nombra el servicio, el barrio y la ciudad: “No encontramos plomeros en Chapinero, pero encontramos plomeros en Bogotá.” - Si con la ubicación la primera página queda vacía, el servidor repite la consulta sin ella y marca
locationWidened: "pais". El aviso es: “No encontramos plomeros en Chapinero; te mostramos los de todo el país.” Sin barrio ni ciudad dice “en esa zona”.
locationNoticeText. /explorar muestra la ubicación aplicada como una píldora que se puede quitar.
Búsqueda por imagen
POST /api/search/interpret-image recibe un multipart/form-data con image y, opcional, ubicacion. Una foto del problema se clasifica contra el catálogo con un modelo de visión y entra al mismo flujo: con el qué resuelto, si hay dónde se va a resultados y si no se pregunta el dónde. Si la IA no logra clasificarla, la tarjeta pregunta el qué.
- Límites: los de las demás imágenes subidas, JPEG, PNG o WebP de hasta 2 MB (
src/lib/upload-image-shared.ts). Los errores son “Solo se permiten JPEG, PNG o WebP” y “El archivo supera el tamano maximo (2 MB)”. - Privacidad: la foto se procesa en memoria; no se escribe en disco ni se guarda.
- Modelo:
OPENAI_VISION_MODEL; si falta,OPENAI_MODEL, y si tampoco está,gpt-6-luna. - Disponibilidad: el botón de cámara solo se muestra con
OPENAI_API_KEYconfigurada. Es público, también sin sesión. - Rate limit: cupo propio por IP de 5 solicitudes por minuto, más bajo que el del texto porque una foto cuesta más.