RAGatouille Documentation

repository·main·Indexed 24 days ago

https://github.com/answerdotai/ragatouille

A library designed to simplify the use and training of state-of-the-art late-interaction retrieval models, specifically ColBERT, for RAG pipelines. It provides high-level interfaces like RAGPretrainedModel for indexing and searching, and RAGTrainer for fine-tuning models. RAGatouille supports creating compressed indices for stateless deployments and integrates with the official ColBERT implementation, Vespa, Intel's FastRAG, and LlamaIndex.

Tokens
7.6K
Snippets
17
Records
39
Agent score
86%

What's inside RAGatouille

  1. Understand RAGatouille's core components and philosophy

    main

    RAGatouille is designed to democratize the use of late-interaction retrievers like ColBERT. The library follows a philosophy of providing strong, parameterizable defaults while ensuring all core components are reusable and stand-alone.

    Key high-level interfaces include:

    • RAGPretrainedModel: The primary interface for leveraging ColBERT in pipelines.
    • RAGTrainer: The primary interface for training and fine-tuning models.

    Core components like TrainingDataProcessor and negative miners (e.g., SimpleMiner) are designed to be used independently of the main classes if needed.

  2. Understand Late-Interaction (ColBERT) vs. Dense Retrieval

    main

    RAGatouille focuses on Late-Interaction retrieval (e.g., ColBERT), which uses a bag-of-embeddings approach. This differs from traditional methods in the following ways:

    MethodProsCons
    BM25 (Keyword)Fast, consistent, no trainingRequires exact matches, no semantic understanding
    Cross-EncodersVery strong performanceMajor scalability issues (must compare query to every document)
    Dense RetrievalFast, semantic understandingStruggles with contrastive info, fine-tuning is difficult, often generalizes poorly
    Late-Interaction (ColBERT)Semantic keyword matching, high generalization, scalableRequires specialized indexing

    Why Late-Interaction works: Instead of compressing a whole document into a single vector (which limits information capacity), ColBERT breaks documents into a 'bag' of contextualized units of information. This allows the model to capture meaning at the token level, making it more robust to different phrasing and improving generalization to unseen data.

  3. Install RAGatouille via pip

    main

    You can install RAGatouille using pip.

    Requirements & Notes:

    • Python 3.9, 3.10, or 3.11 is required.
    • Windows is not supported. Use WSL2 (WSL1 is not recommended).
    • If running within a script, ensure the code is wrapped in an if __name__ == "__main__": block.
    pip install ragatouille
  4. Use reusable components like TrainingDataProcessor and SimpleMiner

    main

    RAGatouille is built with modularity in mind. You can use specific components outside of the main RAGPretrainedModel or RAGTrainer classes:

    • Data Processing: Use TrainingDataProcessor to streamline the processing and exporting of training triplets.
    • Negative Mining: Use SimpleMiner (currently for dense retrieval) or implement your own custom negative miner to integrate into the pipeline.
    • Document Chunking: The library leverages LlamaIndex for document chunking tasks.
  5. Fine-tune a ColBERT model with RAGTrainer

    main

    To fine-tune an existing ColBERT model, instantiate RAGTrainer with a pretrained_model_name that points to a ColBERT instance. If pretrained_model_name points to a different transformer, it will initialize a new ColBERT model from those weights.

    Use prepare_training_data() to process your pairs and corpus, then call train() to start the process.

    from ragatouille import RAGTrainer
    from ragatouille.utils import get_wikipedia_page
    
    pairs = [
        ("What is the meaning of life ?", "The meaning of life is 42"),
        ("What is Neural Search?", "Neural Search is a terms referring to a family of ..."),
        # You need many more pairs to train!
        ...
    ]
    
    my_full_corpus = [get_wikipedia_page("Hayao_Miyazaki"), get_wikipedia_page("Studio_Ghibli")]
    
    trainer = RAGTrainer(model_name = "MyFineTunedColBERT",
            pretrained_model_name = "colbert-ir/colbertv2.0") # In this example, we run fine-tuning
    
    # This step handles all the data processing
    trainer.prepare_training_data(raw_data=pairs,
                                    data_out_path="./data/",
                                    all_documents=my_full_corpus)
    
    trainer.train(batch_size=32) # Train with the default hyperparams
  6. Integrate RAGatouille indices into your project

    main
    RAGatouille allows you to build ColBERT native indices that are persisted on disk in a compressed format. For production deployments, you can integrate these indices directly into your project and query them without needing a stateful database cluster, enabling stateless deployments (e.g., via Kubernetes).
  7. Integrate RAGatouille with llama-hub loaders

    main

    You can use any loader from llama-hub to ingest data into RAGatouille. The general workflow involves using a llama-hub loader to retrieve documents, extracting the text content from those documents into a list, and then passing that list to RAGPretrainedModel.index().

    from ragatouille import RAGPretrainedModel
    from llama_index import download_loader
    
    # 1. Initialize RAGatouille
    RAG = RAGPretrainedModel.from_pretrained("colbert-ir/colbertv2.0")
    
    # 2. Load data using llama-hub
    PubmedReader = download_loader("PubmedReader")
    loader = PubmedReader()
    documents = loader.load_data(search_query="your query")
    
    # 3. Extract text and index
    list_documents = [document.text for document in documents]
    RAG.index(
        collection=list_documents,
        index_name="my_index",
        max_document_length=180,
        split_documents=True,
    )
  8. Fine-tune ColBERT(v2) using synthetic data with Instructor

    main

    You can fine-tune ColBERT models without manual annotations by using the instructor library and an LLM (like OpenAI) to generate synthetic [query, relevant_passage] pairs. This process involves:

    1. Extracting structured queries: Use instructor.patch(OpenAI(...)) and a Pydantic model to force the LLM to return specific query types (e.g., hypothetical questions or search queries) based on document chunks.
    2. Creating training pairs: Map the generated queries to their corresponding document chunks.
    3. Preparing training data: Use RAGTrainer.prepare_training_data to combine these pairs with your full corpus to automatically mine hard negatives.

    This approach allows for domain adaptation without the cost of human labeling.

    import instructor
    from openai import OpenAI
    from pydantic import BaseModel, Field
    from typing import List
    from ragatouille import RAGTrainer
    
    # 1. Setup Instructor with OpenAI
    client = instructor.patch(OpenAI(api_key=os.environ["OPENAI_API_KEY"]))
    
    # 2. Define schema for synthetic query generation
    class QueryForPassage(BaseModel):
        hypothetical_questions: List[str] = Field(
            default_factory=list,
            description="A wide variety of hypothetical questions that this document could answer.",
        )
        hypothetical_queries: List[str] = Field(
            default_factory=list,
            description="A wide variety of hypothetical queries that this document would be relevant to.",
        )
    
    # 3. Generate queries using the LLM
    # (Assuming 'relevant_documents' is a list of text chunks)
    candidate_queries = []
    for doc in relevant_documents:
        candidate = client.chat.completions.create(
            model="gpt-4-1106-preview",
            response_model=QueryForPassage,
            messages=[
                {"role": "system", "content": "You are an expert AI..."},
                {"role": "user", "content": doc},
            ],
        )
        candidate_queries.append(candidate)
    
    # 4. Format pairs for RAGatouille
    pairs = []
    for candidates, doc in zip(candidate_queries, relevant_documents):
        candidates_dict = candidates.model_dump()
        # Combine different query types into the training pairs
        queries = candidates_dict['hypothetical_questions'] + candidates_dict['hypothetical_queries']
        for q in queries:
            pairs.append([q, doc])
    
    # 5. Prepare training data with hard negative mining
    trainer = RAGTrainer(model_name="MyModel", pretrained_model_name="colbert-ir/colbertv2.0")
    trainer.prepare_training_data(
        raw_data=pairs,
        all_documents=documents,
        num_new_negatives=10,
        mine_hard_negatives=True,
    )
  9. Load a pretrained ColBERT model with RAGPretrainedModel

    main

    To start using RAGatouille, load a pretrained ColBERT model using the RAGPretrainedModel.from_pretrained() method. This initializes the model and its configuration, making it ready for indexing or searching.

    Note: Indexing is currently not supported on Google Colab and Windows 10.

    from ragatouille import RAGPretrainedModel
    
    RAG = RAGPretrainedModel.from_pretrained("colbert-ir/colbertv2.0")
  10. Retrieve documents using RAG.search

    main

    To query an existing index, use RAGPretrainedModel.from_index("path_to_your_index"). This is the preferred method as it loads the full model configuration saved with the index.

    RAG.search() supports:

    • Single queries (returns a list of dictionaries).
    • Multiple queries (returns a list of lists of dictionaries).
    • Setting the k parameter to specify the number of results (defaults to 10).

    Result Format: Each result dictionary contains:

    • content: The document text.
    • score: The retrieval score.
    • rank: The rank of the result.
    • document_id: The ID of the document.
    • document_metadata (optional): A dictionary of metadata if provided during indexing.
  11. Create a ColBERT index with RAGPretrainedModel

    main

    You can create an index by loading a pretrained model or your own trained model using RAGPretrainedModel.from_pretrained().

    When calling .index(), you can provide:

    • index_name: The name of the index.
    • collection: A list of documents to index.
    • document_ids (optional): A list of IDs corresponding to the documents.
    • document_metadatas (optional): A list of dictionaries containing metadata for each document.
    from ragatouille import RAGPretrainedModel
    from ragatouille.utils import get_wikipedia_page
    
    RAG = RAGPretrainedModel.from_pretrained("colbert-ir/colbertv2.0")
    my_documents = [get_wikipedia_page("Hayao_Miyazaki"), get_wikipedia_page("Studio_Ghibli")]
    index_path = RAG.index(index_name="my_index", collection=my_documents)
    
    # Indexing with IDs and Metadata
    document_ids = ["miyazaki", "ghibli"]
    document_metadatas = [
        {"entity": "person", "source": "wikipedia"},
        {"entity": "organisation", "source": "wikipedia"},
    ]
    index_path = RAG.index(
        index_name="my_index_with_ids_and_metadata",
        collection=my_documents,
        document_ids=document_ids,
        document_metadatas=document_metadatas,
    )
  12. Train a ColBERT model

    main

    Execute the training process using the train method. ColBERT training continues until it reaches maxsteps or completes one full epoch (it does not use an explicit epochs parameter).

    Key Arguments:

    • batch_size: Training batch size.
    • nbits: Number of bits used for index compression (e.g., 4).
    • maxsteps: Maximum number of training steps (hard stop).
    • use_ib_negatives: Boolean to use in-batch negatives for loss calculation.
    • dim: Embedding dimensions (default 128).
    • learning_rate: Learning rate (e.g., 5e-6).
    • doc_maxlen: Maximum document length (e.g., 256).
    • use_relu: Boolean to enable/disable ReLU (default False).
    • warmup_steps: Number of warmup steps (e.g., `