Docs / Créer une application RAG

Créer une application RAG avec aqoon

La génération augmentée par récupération (RAG) est un modèle dans lequel vous récupérez du contenu pertinent depuis une base de connaissances, puis le transmettez comme contexte à un modèle de langage. Le résultat est une réponse IA fondée sur vos propres documents plutôt que sur des données d'entraînement générales.

aqoon est idéal pour la couche de récupération : il stocke, traite et indexe vos documents, et expose une API de recherche unique qui effectue une recherche hybride sémantique et par mots-clés. Votre application appelle le point d'accès de recherche, récupère les meilleurs résultats et les inclut dans le prompt que vous envoyez au LLM.

Prérequis

  • An aqoon account with at least one collection containing documents
  • Une clé API limitée avec accès accordé à la collection cible — voir Clés API et limites de débit
  • Python 3.8+ (les exemples ci-dessous utilisent la bibliothèque standard plus deux paquets)
  • Une clé API Anthropic ou OpenAI pour l'étape LLM

Étape 1 : Installer les dépendances

pip install requests anthropic

Si vous préférez OpenAI, remplacez anthropic par openai — le modèle est identique.

Étape 2 : Rechercher dans aqoon le contexte pertinent

Appelez le point d'accès de recherche avec votre requête. aqoon renvoie des résultats classés — chaque résultat contient le texte du passage (content), le titre du document (title) et un score de similarité.

import requests

AQOON_URL = "https://your-instance"
AQOON_API_KEY = "aqn_your_api_key"

def search_aqoon(query: str, limit: int = 5) -> list[dict]:
    """Search aqoon and return a list of result chunks."""
    response = requests.post(
        f"{AQOON_URL}/api/search/",
        headers={
            "Authorization": f"Bearer {AQOON_API_KEY}",
            "Content-Type": "application/json",
        },
        json={"query": query, "limit": limit},
        timeout=10,
    )
    response.raise_for_status()
    return response.json().get("results", [])

Chaque résultat de la liste ressemble à ceci :

{
  "content": "Retrieval-Augmented Generation (RAG) combines...",
  "title": "AI Architecture Patterns.pdf",
  "document_id": "a1b2c3d4-...",
  "collection_name": "Research",
  "score": 0.91
}

Étape 3 : Construire le prompt avec le contexte récupéré

Formatez les résultats de recherche comme contexte et insérez-les dans un prompt pour le LLM.

def build_prompt(query: str, chunks: list[dict]) -> str:
    """Combine retrieved chunks into a prompt."""
    if not chunks:
        return (
            f"The user asked: {query}\n\n"
            "No relevant documents were found in the knowledge base. "
            "Answer based on your general knowledge and note the absence of specific context."
        )

    context_parts = []
    for i, chunk in enumerate(chunks, start=1):
        context_parts.append(
            f"[Source {i}: {chunk['title']}]\n{chunk['content']}"
        )
    context = "\n\n---\n\n".join(context_parts)

    return (
        f"Use the following excerpts from the knowledge base to answer the question. "
        f"Cite sources by number where relevant.\n\n"
        f"{context}\n\n"
        f"Question: {query}"
    )

Étape 4 : Envoyer au LLM

Transmettez le prompt à Claude en utilisant le SDK Anthropic :

import anthropic

ANTHROPIC_API_KEY = "sk-ant-your-key"

def ask_claude(prompt: str) -> str:
    """Send a prompt to Claude and return the response text."""
    client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}],
    )
    return message.content[0].text

Étape 5 : Exemple complet fonctionnel

Le tout assemblé :

import requests
import anthropic

AQOON_URL = "https://your-instance"
AQOON_API_KEY = "aqn_your_api_key"
ANTHROPIC_API_KEY = "sk-ant-your-key"


def search_aqoon(query: str, limit: int = 5) -> list[dict]:
    response = requests.post(
        f"{AQOON_URL}/api/search/",
        headers={
            "Authorization": f"Bearer {AQOON_API_KEY}",
            "Content-Type": "application/json",
        },
        json={"query": query, "limit": limit},
        timeout=10,
    )
    response.raise_for_status()
    return response.json().get("results", [])


def build_prompt(query: str, chunks: list[dict]) -> str:
    if not chunks:
        return (
            f"The user asked: {query}\n\n"
            "No relevant documents were found. Answer based on general knowledge."
        )
    context_parts = [
        f"[Source {i}: {c['title']}]\n{c['content']}"
        for i, c in enumerate(chunks, start=1)
    ]
    context = "\n\n---\n\n".join(context_parts)
    return (
        f"Use the following knowledge base excerpts to answer the question. "
        f"Cite sources by number.\n\n{context}\n\nQuestion: {query}"
    )


def rag_query(query: str) -> str:
    chunks = search_aqoon(query, limit=5)
    prompt = build_prompt(query, chunks)
    client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}],
    )
    return message.content[0].text


if __name__ == "__main__":
    answer = rag_query("What is our policy on data retention?")
    print(answer)

Conseils

  • Choisissez la bonne portée. Utilisez une clé API limitée et n'accordez que les collections pertinentes pour votre application. Cela empêche le LLM de recevoir du contexte provenant de documents non liés.
  • Ajustez la limite de résultats. Plus de résultats donnent plus de contexte au modèle mais augmentent l'utilisation de tokens et le coût. Cinq à dix résultats est un bon point de départ ; ajustez en fonction de la longueur typique de vos documents et de la spécificité des requêtes.
  • Gérez l'absence de résultats avec élégance. La fonction build_prompt ci-dessus montre une approche : indiquer au modèle qu'aucun contexte n'a été trouvé et le laisser répondre à partir de ses connaissances générales ou refuser de répondre, selon votre cas d'utilisation.
  • Filtrez par collection. Si vos requêtes sont toujours limitées à un domaine, passez "collection": "votre-slug-de-collection" dans le corps de la requête de recherche pour limiter les résultats à cette collection et améliorer la pertinence.
  • Mettez en cache les requêtes fréquentes. Les résultats de recherche aqoon sont déterministes pour un ensemble de documents donné. Envisagez de mettre en cache les résultats des requêtes courantes dans Redis pour réduire les appels API et la latence.

Bonus : Exemple JavaScript / Node.js

import Anthropic from "@anthropic-ai/sdk";

const AQOON_URL = "https://your-instance";
const AQOON_API_KEY = "aqn_your_api_key";
const ANTHROPIC_API_KEY = "sk-ant-your-key";

async function searchAqoon(query, limit = 5) {
  const response = await fetch(`${AQOON_URL}/api/search/`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${AQOON_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, limit }),
  });
  if (!response.ok) {
    throw new Error(`aqoon search failed: ${response.status}`);
  }
  const data = await response.json();
  return data.results ?? [];
}

function buildPrompt(query, chunks) {
  if (chunks.length === 0) {
    return `The user asked: ${query}\n\nNo relevant documents found.`;
  }
  const context = chunks
    .map((c, i) => `[Source ${i + 1}: ${c.title}]\n${c.content}`)
    .join("\n\n---\n\n");
  return (
    `Use the following knowledge base excerpts to answer the question. ` +
    `Cite sources by number.\n\n${context}\n\nQuestion: ${query}`
  );
}

async function ragQuery(query) {
  const chunks = await searchAqoon(query);
  const prompt = buildPrompt(query, chunks);
  const client = new Anthropic({ apiKey: ANTHROPIC_API_KEY });
  const message = await client.messages.create({
    model: "claude-opus-4-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: prompt }],
  });
  return message.content[0].text;
}

const answer = await ragQuery("What is our refund policy?");
console.log(answer);