Guide de démarrage

Créez un pipeline RAG en cinq minutes : importez des documents dans un vecteur store, puis effectuez une recherche dessus.

Prérequis

Prérequis

  • Python 3.12+
  • Docker (pour exécuter Vespa en local)
  • Une clé API Mistral à obtenir sur console.mistral.ai
Installation

Installation

Installez Search Toolkit avec le plugin Vespa en utilisant uv :

uv add "mistralai-search-toolkit[vespa]"
Configurer Vespa

Configurer Vespa

Démarrez une instance Vespa locale avec Docker :

docker run --detach \
  --name vespa \
  --hostname vespa-container \
  --publish 8080:8080 \
  --publish 19071:19071 \
  vespaengine/vespa

Attendez que Vespa soit prêt :

curl --retry 10 --retry-delay 3 --retry-all-errors \
  http://localhost:19071/state/v1/health

Définissez votre clé API Mistral :

export MISTRAL_API_KEY=your-api-key
Définir le schéma

Définir le schéma

Créez une migration pour décrire la structure de vos documents :

mistral-vespa generate-migration \
  --app-dir ./vespa/migrations \
  initial_schema

Complétez le fichier généré :

from mistralai.search.toolkit.plugins.vespa.app.schemas.app import FieldDefinition, SearchMode
from mistralai.search.toolkit.plugins.vespa.migration import VespaMigration, create_default_schema, set_app_name


class InitialSchema(VespaMigration):
    def migrate(self) -> None:
        set_app_name("myquickstart")
        create_default_schema(
            name="quickstart_collection",
            mode=SearchMode.INDEX,
            embedding_dimensions=1024,
            schema_version=1,
            additional_fields=[
                FieldDefinition.TextField(name="title"),
            ],
        )
i
Information

Restrictions sur le nom de l’application : le nom passé à set_app_name() doit contenir uniquement des lettres minuscules (a-z). Les chiffres, les traits de soulignement, les traits d’union et les autres caractères spéciaux ne sont pas autorisés.

Pour obtenir plus de détails sur la gestion et le déploiement d’applications Vespa, consultez Gérer et déployer des applications Vespa.

Déployer à partir des migrations

Déployer à partir des migrations

Déployez le schéma sur votre instance Vespa locale :

mistral-vespa migrate \
  --app-dir ./vespa/migrations \
  --config-server http://localhost:19071 \
  --query-port 8080

Cela construit le package applicatif à partir de vos migrations en mémoire, le déploie, et attend que l’application soit prête.

Ingestion des documents

Ingestion des documents

Créez un pipeline qui charge des fichiers, extrait le texte, segmente en fragments, génère des embeddings et indexe dans Vespa :

import asyncio
from pathlib import Path

from mistralai.client import Mistral
from mistralai.search.toolkit.embedders import MistralEmbedder
from mistralai.search.toolkit.ingestion.extractors import PlainTextExtractor
from mistralai.search.toolkit.ingestion.loaders import FilesystemFileLoader
from mistralai.search.toolkit.ingestion.pipelines import Pipeline
from mistralai.search.toolkit.ingestion.text_splitters import CharacterTextSplitter
from mistralai.search.toolkit.plugins.vespa import VespaClientConfig
from vespa_app import app


async def main():
    mistral_client = Mistral(api_key="your-api-key")

    # Configure Vespa
    config = VespaClientConfig(
        endpoint="http://localhost:8080",
    )
    vector_store = app.get_search_index(config, collection_name="quickstart_collection")

    # Create the pipeline
    pipeline = Pipeline(
        loader=FilesystemFileLoader(),
        extractor=PlainTextExtractor(),
        text_splitter=CharacterTextSplitter(chunk_size=500),
        embedder=MistralEmbedder(client=mistral_client, model_name="mistral-embed"),
        vector_store=vector_store,
    )

    # Ingest documents
    await pipeline.run(
        documents=[Path("doc1.txt"), Path("doc2.txt")],
        collection_name="quickstart_collection",
    )
    print("Documents ingested!")


asyncio.run(main())

Ce pipeline enchaîne cinq étapes :

  1. FilesystemFileLoader lit les octets du fichier depuis le disque.
  2. PlainTextExtractor extrait le contenu texte. Pour les PDF, utilisez à la place MistralOCRExtractor.
  3. CharacterTextSplitter découpe le texte en fragments de 500 caractères.
  4. MistralEmbedder génère un embedding vectoriel pour chaque fragment.
  5. vector_store (Vespa) indexe chaque fragment et stocke l’embedding pour la recherche vectorielle.

Interrogez les documents indexés avec la recherche vectorielle :

import asyncio

from mistralai.client import Mistral
from mistralai.search.toolkit.embedders import MistralEmbedder
from mistralai.search.toolkit.plugins.vespa import VespaClientConfig
from mistralai.search.toolkit.retrieval import QueryEngine
from mistralai.search.toolkit.retrieval.retrievers import VectorRetriever
from vespa_app import app


async def main():
    mistral_client = Mistral(api_key="your-api-key")
    embedder = MistralEmbedder(client=mistral_client, model_name="mistral-embed")

    # Configure Vespa for search
    config = VespaClientConfig(
        endpoint="http://localhost:8080",
    )
    vector_store = app.get_search_index(config, collection_name="quickstart_collection")

    # Build query engine
    query_engine = QueryEngine(
        retriever=[VectorRetriever(client=vector_store, embedder=embedder)],
    )

    # Search
    result = await query_engine.search(
        query="What is RAG?",
        top_k=5,
        include_metadata=True,
        include_content=True,
    )

    for i, r in enumerate(result.results, 1):
        print(f"{i}. [Score: {r.score:.3f}] {r.chunk.content[:200]}...")


asyncio.run(main())
Ingestion de PDF avec OCR

Ingestion de PDF avec OCR

Pour des fichiers PDF, remplacez PlainTextExtractor par MistralOCRExtractor et utilisez MarkdownTextSplitter pour un découpage adapté à la structure du document :

from mistralai.search.toolkit.ingestion.extractors import MistralOCRExtractor
from mistralai.search.toolkit.ingestion.text_splitters import (
    MarkdownTextSplitter,
    MarkdownTextSplitterConfig,
)

pipeline = Pipeline(
    loader=FilesystemFileLoader(),
    extractor=MistralOCRExtractor(client=mistral_client),
    text_splitter=MarkdownTextSplitter(
        MarkdownTextSplitterConfig(chunk_size=5048, chunk_overlap=50)
    ),
    embedder=MistralEmbedder(client=mistral_client),
    vector_store=vector_store,
)