Búsqueda web con DuckDuckGo, sin API key

Primera tool del agente. No depende del modelo, así que se puede usar y
verificar de punta a punta antes de que exista el modelo propio.

Sobre el endpoint: la API oficial de DuckDuckGo ("Instant Answer") no sirve
para esto — responde definiciones y fichas de entidades, y para una consulta
normal devuelve 200 con el cuerpo vacío. Los resultados web reales solo están
en el endpoint lite, que no es una API con contrato: el HTML puede cambiar y
hay límite de tasa. Por eso el backend está detrás de una interfaz y queda
implementado también SearxNG autoalojado, que es un cambio de una línea de
config cuando haga falta.

Decisiones:

- Presupuesto de contexto explícito: con seq_len 2048 los resultados compiten
  con la memoria recuperada y el turno del usuario, así que se devuelven cinco
  recortados en vez de diez completos.
- Las redirecciones /l/?uddg= se desenvuelven al destino real: guardarlas
  ensuciaría la memoria episódica, donde dos búsquedas a la misma página
  parecerían páginas distintas.
- Caché con TTL, que es de comportamiento y no de rendimiento: en una casa las
  mismas preguntas se repiten muchas veces por día.
- Sin resultados devuelve "Sin resultados." en vez de vacío, para que el modelo
  pueda decir que no encontró nada en lugar de inventar.

Se quitan Brave y Tavily de .env.example: ya no hay credenciales que gestionar
ni un tercero al que informarle qué busca la familia.

23 tests nuevos (71 en total), contra una respuesta real guardada como fixture:
el parser es lo que se rompe cuando cambia el markup, y tiene que fallar en la
suite y no en producción. Ningún test sale a internet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 23:15:13 -03:00
parent 90ac2a6582
commit 179062a5d4
12 changed files with 1369 additions and 8 deletions
+2 -1
View File
@@ -262,7 +262,8 @@ Con tope de pasos, timeouts y fallback explícito a "no entendí" — preferible
- **Canales** (`enlace/channels/`): cada front-end es un adaptador que aporta la identidad fuerte y
normaliza la entrada. CLI primero; después HTTP API, bot de mensajería, agente conversacional de Home
Assistant, y voz. Agregar un canal no toca el runtime.
- **Tools** (`enlace/agent/tools/`), en orden: **búsqueda web** (SearxNG autoalojado, o Brave/Tavily) →
- **Tools** (`enlace/agent/tools/`), en orden: **búsqueda web** (DuckDuckGo, sin API key ni cuenta;
SearxNG autoalojado como reemplazo si el endpoint falla) →
Home Assistant (API REST, con sincronización a `devices.db`) → calendario (CalDAV) → dispositivos
adicionales. Cada tool: esquema JSON + permiso + fuente de enums + implementación + tests.