> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gradium.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Keenable

> Utiliser la recherche web en temps réel de Keenable avec les agents vocaux IA Gradium

Keenable fournit une recherche web et un accès au contenu conçus pour les agents IA. Dans un agent vocal Gradium, Keenable joue le rôle de couche web en direct : l'utilisateur pose une question à voix haute, l'agent la transforme en requête de recherche, Keenable renvoie des résultats web à jour en mode temps réel, et Gradium énonce la réponse pendant que les sources s'affichent à l'écran en streaming.

<CardGroup cols={2}>
  <Card title="Keenable" icon="arrow-up-right-from-square" href="https://docs.keenable.ai/">
    Découvrez l'API de recherche, la CLI et le serveur MCP de Keenable.
  </Card>

  <Card title="Démo de recherche web vocale" icon="github" href="https://github.com/gradium-ai/gradbot/tree/main/demos/web_voice_search">
    Découvrez une démo Gradbot qui répond à des questions orales grâce à la recherche web en direct.
  </Card>
</CardGroup>

## Recherche vocale instantanée

Un agent de recherche vocale construit avec Gradium et Keenable suit cette boucle :

1. L'utilisateur pose une question à l'oral, par exemple « Qu'a annoncé la Fed aujourd'hui ? »
2. La transcription (STT) de Gradium transcrit la demande en temps réel.
3. L'agent appelle un outil `web_search` avec une requête ciblée, silencieusement et en tout premier.
4. Keenable renvoie les meilleurs résultats avec leurs titres, leurs sources et leurs extraits.
5. L'agent répond en une à trois phrases courtes, en citant la source de façon naturelle.
6. La synthèse vocale (TTS) de Gradium renvoie la réponse à l'utilisateur en streaming.

```
mic → Gradium STT → LLM (tool-capable) → Keenable realtime search → Gradium TTS → speaker
```

Comme le mode temps réel de Keenable renvoie des résultats en bien moins d'une seconde, la recherche tient dans une pause naturelle de la conversation. L'appelant entend une réponse, pas un compte rendu de progression.

## Câblage de base

La démo expose la recherche sous la forme d'un unique outil de l'agent vocal. Gradium gère la session STT et TTS en direct ; l'agent décide quand appeler `web_search`.

### Définition de l'outil de recherche

```python theme={null}
gradbot.ToolDef(
    name="web_search",
    description=(
        "Search the live web for current or factual information. Use this "
        "for any question about recent events, facts, prices, people, "
        "products, news, or anything you are unsure about. Returns the top "
        "results with title, source site, and a snippet."
    ),
    parameters_json=json.dumps({
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": (
                    "The search query as a clear natural-language "
                    "description of what to find."
                ),
            }
        },
        "required": ["query"],
    }),
)
```

### Appeler l'API Search de Keenable

Lorsque le modèle appelle l'outil, le handler lance une recherche Keenable en temps réel. L'API accepte une requête POST avec un en-tête `X-API-Key` et renvoie une liste de résultats.

```python theme={null}
import httpx

KEENABLE_API_URL = "https://api.keenable.ai/v1/search"

async def keenable_search(query: str, limit: int = 5) -> list[dict]:
    payload = {"query": query, "mode": "realtime"}
    headers = {
        "X-API-Key": os.environ["KEENABLE_API_KEY"],
        "Content-Type": "application/json",
    }

    async with httpx.AsyncClient() as client:
        r = await client.post(
            KEENABLE_API_URL, json=payload, headers=headers, timeout=30.0
        )
        r.raise_for_status()
        data = r.json()

    rows = []
    for item in data.get("results") or []:
        rows.append({
            "title": item.get("title") or "Untitled",
            "url": item.get("url"),
            "snippet": item.get("snippet") or item.get("description") or "",
        })
        if len(rows) >= limit:
            break
    return rows
```

Chaque résultat comprend un `title`, une `url` et une `description`, ainsi qu'un `snippet` étendu et un horodatage `published_at` lorsqu'ils sont disponibles. Le corps de la requête accepte aussi des filtres optionnels, comme `site` pour limiter les résultats à un seul domaine, et `published_after` ou `published_before` pour borner les résultats par date de publication.

### Renvoyer les résultats à l'agent

Le handler de l'outil renvoie à l'agent un résumé textuel compact, accompagné d'instructions sur la façon d'énoncer la réponse. Un payload réduit permet de garder rapide l'étape LLM du tour de parole.

```python theme={null}
async def handle_tool_call(handle, input_handle, websocket):
    if handle.name != "web_search":
        await handle.send_error(f"Unknown tool: {handle.name}")
        return

    query = (handle.args.get("query") or "").strip()
    results = await keenable_search(query, limit=5)

    summary = "\n".join(
        f"- {r['title']} ({r['url']}): {r['snippet'][:300]}" for r in results
    )
    await handle.send(json.dumps({
        "success": True,
        "query": query,
        "results_summary": summary,
        "message": (
            "Answer the caller's question in 1-3 short spoken sentences "
            "using these results. Do NOT search again for this."
        ),
    }))
```

### Configuration de la session de l'agent

La configuration de session branche l'outil sur une session vocale Gradium en direct.

```python theme={null}
def make_config(msg: dict) -> gradbot.SessionConfig:
    return gradbot.SessionConfig(
        voice_id="_6Aslh2DxfmnRLmP",
        language=gradbot.LANGUAGES["en"],
        instructions=SYSTEM_PROMPT,
        tools=[WEB_SEARCH_TOOL],
        assistant_speaks_first=True,
        silence_timeout_s=0.0,
    )

await gradbot.websocket.handle_session(
    websocket,
    config=cfg,
    on_start=make_config,
    on_tool_call=handle_tool_call,
)
```

## Garder la boucle vocale rapide

C'est la latence qui rend la recherche vocale magique ou inutilisable. La démo applique quelques règles qui méritent d'être reprises :

* **Rechercher en silence.** Demandez à l'agent, dans le prompt, d'appeler `web_search` immédiatement, sans formule de remplissage comme « je vais vérifier ». Les premiers mots prononcés doivent être la réponse elle-même.
* **Une recherche par question.** Demandez à l'agent de se fier à ses premiers résultats au lieu de reformuler la requête pour vérifier. Une seconde recherche double l'attente.
* **Limiter et tronquer les résultats.** Cinq résultats avec des extraits réduits à environ 300 caractères donnent au LLM de quoi répondre sans alourdir le prompt.
* **Préchauffer le chemin de recherche.** Lancez une requête Keenable jetable lorsque l'utilisateur démarre une session, quelques secondes avant la première vraie question, afin que le DNS et l'origine de recherche soient déjà chauds.
* **Gérer les échecs avec la voix de l'agent.** Si une recherche échoue, renvoyez un message d'outil demandant à l'agent de dire qu'il ne parvient pas à accéder au web pour le moment, plutôt que de laisser le modèle deviner.

## Authentification

Obtenez une clé API depuis la [console Keenable](https://keenable.ai/console). Les clés commencent par `keen_` et sont transmises dans l'en-tête `X-API-Key`. Définissez-la à côté de votre clé Gradium :

| Variable | Rôle |
| - | - |
| `KEENABLE_API_KEY` | Clé de recherche Keenable (`keen_...`) |
| `GRADIUM_API_KEY` | Voix Gradium (STT et TTS) |

## Pourquoi utiliser Keenable avec Gradium ?

* **Des réponses dans une pause de conversation** : le mode temps réel est assez rapide pour que l'appelant entende la réponse sans blanc ni formule de remplissage.
* **Conçu pour les agents** : les résultats sont renvoyés sous forme de JSON structuré et propre, avec titres, extraits et horodatages, prêts à être résumés et énoncés.
* **Des réponses orales ancrées dans les sources** : l'agent répond à partir de résultats web en direct plutôt que de la mémoire obsolète du modèle, et peut citer la source de façon naturelle.
* **Des relances ciblées** : les appelants peuvent affiner une question au fil de la conversation, et des filtres comme `site` et `published_after` gardent chaque nouvelle recherche précise.

## Démo

La [démo de recherche web vocale](https://github.com/gradium-ai/gradbot/tree/main/demos/web_voice_search) illustre le schéma complet : une application FastAPI qui exécute une session Gradbot, expose `web_search` comme unique outil de l'agent, envoie en streaming des cartes de sources au navigateur à mesure que les résultats arrivent, et mesure la latence de chaque tour pour le STT, le LLM, la recherche et le TTS. Servez-vous-en comme référence pour construire votre propre expérience de recherche vocale instantanée.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.