Skip to main content
La búsqueda del inicio interpreta lo que escribe la persona y decide si ya puede mostrar resultados o si debe preguntar algo. Pregunta solo lo que falta y nunca encierra al usuario: “Ver resultados igual” siempre está visible.
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).
La respuesta trae 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.
El timeout es de 8 segundos. Si OpenAI falla o no hay 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:
  1. answer: lo que el usuario respondió o editó en la tarjeta.
  2. query: el lugar que OpenAI extrajo del texto de la consulta, ya separado en ciudad y barrio.
  3. field: el campo “Ubicación” del buscador (texto libre).
  4. 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.
El resultado trae 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 llega barrio, 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 por page; la respuesta incluye barrioTotal.
  • Texto libre sin ciudad (ubicacion): comportamiento clásico, la ciudad o el barrio contienen el texto.
Una búsqueda con qué resuelto nunca termina vacía por la ubicación:
  • Si en el barrio no hay nadie (barrioTotal es 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”.
Si no se conoce el servicio, los avisos usan “profesionales”. Los textos salen de 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_KEY configurada. 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.