Navigation dans les documents

search et retrieve classent les fragments par pertinence sur l'ensemble de l'index. La navigation dans les documents fait l'inverse : elle reste dans un seul document source et renvoie ses fragments dans l'ordre de lecture. Utilisez-la pour lire ce qui précède et ce qui suit un résultat, pour lire une section, ou pour trouver un terme exact dans un document dont vous savez déjà qu'il est pertinent.

Un Mistral Search Index expose quatre méthodes de navigation dans les documents :

MéthodeRenvoie
get_chunkUn fragment, à partir de son identifiant
navigateLes fragments situés immédiatement avant ou après une position
readLes fragments compris dans une plage de caractères
grepLes fragments d'un même document qui contiennent une expression exacte ou des termes
Pourquoi la navigation dans les documents est importante

Pourquoi la navigation dans les documents est importante

La récupération renvoie des fragments, pas des réponses. Un fragment est un morceau de document, découpé selon une taille fixe. Les preuves nécessaires pour répondre à une question sont souvent dispersées autour de lui :

  • La réponse se poursuit au-delà du résultat. Un fragment introduit une procédure, et les étapes se trouvent dans le fragment suivant.
  • Le résultat est privé de son contexte. Une ligne de tableau correspond à la requête, mais son en-tête, son unité ou le paragraphe qui la qualifie se trouve deux fragments plus haut.
  • Le terme clé se trouve ailleurs dans le document. Le résultat mentionne une clause, mais la définition de cette clause se trouve plus loin, et une nouvelle recherche globale classe d'autres documents en premier.

Relancer une recherche avec une requête reformulée est une solution médiocre : elle classe à nouveau tout l'index, renvoie des fragments que l'appelant a déjà vus et ne peut pas exprimer « le paragraphe qui suit celui-ci ». La navigation dans les documents répond à ces besoins directement et à moindre coût, car chaque appel lit une partie connue d'un document connu.

Recherche agentique s'appuie sur cette approche. Un agent exécute une boucle. Il interroge l'index pour trouver des sources prometteuses, puis navigue à l'intérieur de celles-ci : il ouvre un résultat, lit ce qui l'entoure, ou effectue un grep pour un terme. Il relance la recherche avec exclude_ids pour ignorer ce qu'il a déjà lu, jusqu'à avoir suffisamment de preuves. Les outils de Recherche agentique open, navigate, read et grep sont construits à partir des méthodes présentées sur cette page.

Positions dans un document

Positions dans un document

Ces méthodes localisent les fragments à partir de leur document source et de leur position dans ce document. Chaque fragment d'un SearchResult comporte les champs suivants :

  • source_id : le document auquel il appartient.
  • start_offset et end_offset : son intervalle de caractères dans ce document, semi-ouvert ([start, end)).
  • id : son propre identifiant, pour get_chunk.

Les quatre méthodes renvoient une liste d'objets SearchResult dans l'ordre de lecture (start_offset croissant). Elles ne classent pas les résultats, donc score vaut toujours 0.0. Elles ne prennent en compte qu'un seul type de fragment, ChunkType.CONTENT par défaut. Pour lire plutôt des résumés ou d'autres types de fragments, définissez content_type. Si vous passez un source_id inconnu, la méthode lève une erreur SourceNotFoundError. Atteindre le début ou la fin d'un document renvoie une liste vide.

get_chunk

get_chunk

get_chunk récupère un fragment d'après son id et le renvoie avec sa position, ou None si aucun fragment n'a cet identifiant. Utilisez-la lorsque vous ne disposez que de l'identifiant d'un résultat, par exemple parce qu'un agent a renvoyé cet identifiant dans un appel d'outil :

hit = await index.get_chunk(chunk_id)
if hit is not None:
    print(hit.chunk.source_id, hit.chunk.start_offset, hit.chunk.end_offset)

navigate renvoie les top_k fragments situés immédiatement après une position (NavigationDirection.NEXT) ou immédiatement avant celle-ci (NavigationDirection.PREVIOUS) :

  • NEXT renvoie les premiers fragments qui commencent à end_offset ou après.
  • PREVIOUS renvoie les derniers fragments qui se terminent à start_offset ou avant.

Pour lire le contexte autour d'un résultat, combinez les deux directions :

from mistralai.search.toolkit.search import NavigationDirection

chunk = results[0].chunk

before = await index.navigate(
    chunk.source_id, chunk.start_offset, chunk.end_offset,
    NavigationDirection.PREVIOUS, top_k=2,
)
after = await index.navigate(
    chunk.source_id, chunk.start_offset, chunk.end_offset,
    NavigationDirection.NEXT, top_k=2,
)

context = "\n".join(r.chunk.content for r in [*before, results[0], *after])

top_k vaut 1 par défaut. Pour poursuivre la lecture du document, appelez à nouveau navigate à partir du dernier fragment reçu.

read

read

read renvoie les fragments entièrement contenus dans une plage de caractères d'un document, dans la limite de top_k (20 par défaut). Pour lire depuis le début ou jusqu'à la fin du document, passez None pour la borne correspondante :

# The first chunks of the document, for example its title and introduction
intro = await index.read(source_id, None, 2000, top_k=5)

# A known section
section = await index.read(source_id, 12_000, 18_000)

read n'inclut pas les fragments qui chevauchent une borne. Pour inclure les fragments situés aux extrémités, élargissez la plage ou utilisez navigate.

grep

grep

grep trouve les fragments d'un document qui contiennent un texte exact, sans classement vectoriel ni par mots-clés. Utilisez-la lorsque vous connaissez déjà le terme dont vous avez besoin, comme un numéro de clause, un nom de produit ou un code d'erreur. Elle renvoie jusqu'à top_k correspondances (5 par défaut), dans l'ordre de lecture :

from mistralai.search.toolkit.search import GrepMode

matches = await index.grep(source_id, "termination for convenience")
matches = await index.grep(source_id, "notice period days", mode=GrepMode.TERM)
modeCorrespond aux fragments qui contiennent
GrepMode.PHRASE (par défaut)Le motif tel qu'écrit, en un seul morceau de texte
GrepMode.TERMTous les termes du motif, séparés par des espaces, dans n'importe quel ordre

La correspondance ne tient pas compte de la casse et fonctionne sur les sous-chaînes. Par exemple, "cat" correspond à "Category". Une expression doit respecter exactement l'espacement du texte. Un motif vide ne renvoie aucun fragment.

Exemple : d'un résultat à la réponse

Exemple : d'un résultat à la réponse

La fonction suivante trouve le meilleur résultat pour une question, lit deux fragments de chaque côté de celui-ci, et effectue un grep dans le même document pour un terme dont l'appelant a besoin. Elle renvoie les passages dans l'ordre de lecture, prêts à être transmis à un modèle :

from mistralai.search.toolkit.mistral_search_index.retriever import (
    KeywordRetriever,
    NearestNeighbourRetriever,
    RRFRetriever,
)
from mistralai.search.toolkit.search import NavigationDirection


async def gather_evidence(index, question: str, term: str) -> list[str]:
    hits = await index.retrieve(
        RRFRetriever(
            top_k=1,
            retrievers=[
                KeywordRetriever(query=question, top_k=10),
                NearestNeighbourRetriever(query=question, top_k=10),
            ],
        )
    )
    if not hits:
        return []
    hit = hits[0].chunk

    before = await index.navigate(
        hit.source_id, hit.start_offset, hit.end_offset, NavigationDirection.PREVIOUS, top_k=2
    )
    after = await index.navigate(
        hit.source_id, hit.start_offset, hit.end_offset, NavigationDirection.NEXT, top_k=2
    )
    matches = await index.grep(hit.source_id, term)

    chunks = {r.chunk.id: r.chunk for r in [*before, hits[0], *after, *matches]}
    return [c.content for c in sorted(chunks.values(), key=lambda c: c.start_offset)]

Un agent suit les mêmes étapes, un appel d'outil à la fois, et décide à chaque étape s'il a suffisamment de preuves. Pour donner ces étapes à un agent sous forme d'outils, voir Recherche agentique.

Voir aussi

Voir aussi