Changer le thème

Base de connaissances IA en 20 minutes ? Workers AI + Vectorize : tutoriel RAG pas à pas (code complet)

Easton editorial illustration: edge-node constellation

Vous voulez un service client intelligent pour votre entreprise, vous parcourez les tutos RAG, et soit c’est théorique à perdre le fil, soit on vous demande de louer un GPU et monter un environnement. Configurer LangChain et une base vectorielle peut facilement prendre deux jours — sans garantie que ça tourne.

Puis j’ai découvert la suite d’outils IA de Cloudflare : Workers AI + Vectorize + D1, entièrement managés, avec une généreuse offre gratuite. En les utilisant pour une app de Q&R sur mes notes, du zéro au fonctionnel en moins de 20 minutes, une centaine de lignes de code.

Cet article vous guide pas à pas sur le flux complet :

  • Comprendre ce qu’est vraiment le RAG (sans jargon inutile)
  • Construire une app de Q&R sur base de connaissances fonctionnelle (code complet)
  • Optimiser pour un retrieval plus précis et des coûts plus bas
  • Déployer et l’utiliser en production

Il vous faut un peu de JavaScript, un compte Cloudflare (gratuit), et suivre les étapes.

C’est quoi le RAG ? Comprendre le principe en 5 minutes

Le RAG, expliqué avec une analogie d’examen

Une image simple. À l’examen, en contrôle sans documents, vous ne répondez qu’avec ce que vous avez en tête — si vous oubliez, vous inventez. Avec documents autorisés, vous pouvez consulter vos sources : la réponse est bien plus fiable.

Le RAG (Retrieval-Augmented Generation), c’est donner ce droit de consultation à l’IA.

Un LLM classique, c’est l’examen sans documents : il ne répond qu’avec ce qu’il a vu à l’entraînement. Problèmes :

  • les données d’entraînement vieillissent, pas les dernières actus
  • il n’a jamais vu vos docs internes
  • il oublie des détails et invente (on appelle ça des « hallucinations »)

Le RAG : d’abord chercher dans votre base de connaissances, puis donner ces extraits à l’IA pour qu’elle réponde. Réponses plus fiables et à jour.

Les trois étapes clés du RAG

Étape 1
Texte → vecteurs
Transformer le savoir en représentation numérique
Étape 2
Retrieval sémantique
Trouver les passages les plus pertinents
Étape 3
Génération LLM
Répondre à partir du contenu récupéré

Le flux tient en trois étapes :

Étape 1 : transformer le savoir en vecteurs
Vous avez des documents. Le RAG convertit chaque passage en une suite de nombres (vecteur ou embedding) qui encode le sens.
« Les chats sont mignons » et « les chatons sont adorables » utilisent des mots différents, mais le sens est proche : leurs vecteurs seront voisins. Ces vecteurs vont dans une base comme Vectorize.

Étape 2 : à la question, trouver les passages pertinents
Quand l’utilisateur demande « comment éduquer un chat », la question devient aussi un vecteur ; on cherche dans la base les entrées les plus « proches » — sémantiquement les plus pertinentes.
C’est la recherche par similarité : quelques millisecondes pour trouver les 3-5 meilleurs résultats parmi des dizaines de milliers.

Étape 3 : alimenter le LLM avec le contenu récupéré
On assemble un prompt pour l’IA :

以下是相关资料:
[检索到的内容1]
[检索到的内容2]
...
用户问题:怎么训练猫咪?
请基于上述资料回答。

Avec ces « références », l’IA produit une réponse précise et étayée.

Pourquoi la suite Cloudflare ?

Beaucoup de stacks RAG existent — LangChain, LlamaIndex, etc. — mais configurer l’environnement, choisir la base vectorielle et gérer le GPU, c’est lourd.

Workers AI
10+ modèles open source intégrés
Appel API direct
Vectorize
Base vectorielle managée
Sans installation ni maintenance
D1
Base SQLite
Stockage du texte brut

Les avantages de Cloudflare :

Workers AI — une quinzaine de modèles open source (Llama 3, Claude, etc.), API directe, pas de GPU à louer. La couche gratuite offre un quota Neurons quotidien, suffisant pour un projet perso.

Vectorize — base vectorielle managée, pas besoin de Milvus ou Pinecone. Créer un index, insérer des vecteurs, recherche par similarité : quelques lignes de code.

D1 — SQLite Cloudflare pour le texte brut. Vectorize ne stocke que les vecteurs ; le contenu textuel vient de D1.

Tout managé — le plus agréable : pas de serveurs, scaling ou backups à gérer, concentrez-vous sur le code. Le réseau edge Cloudflare accélère l’accès mondial.

"En 2025, Cloudflare a lancé AutoRAG : uploadez des docs sur R2, découpage, vectorisation, retrieval et génération sont automatiques"

- Mises à jour produit Cloudflare

Cloudflare a aussi sorti AutoRAG en 2025 pour simplifier encore — upload sur R2, le reste est automatique. Ici on monte le flux à la main pour comprendre les mécanismes sous-jacents.

Assez de théorie, passons à la pratique.

Mise en pratique — votre première application RAG

On construit une app de Q&R sur notes : l’utilisateur ajoute des notes, pose des questions, le système retrouve le contenu pertinent et répond.

Initialisation du projet et préparation de l’environnement

Installez Wrangler (CLI Cloudflare) :

npm install -g wrangler
wrangler login  # 登录你的 Cloudflare 账号

Créez le projet :

npm create cloudflare@latest rag-notes-app
# 选择 "Hello World" worker
# 选择 TypeScript
cd rag-notes-app

Installez Hono (routeur plus pratique que l’API Workers native) :

npm install hono

Créez la base D1 et l’index Vectorize :

# 创建 D1 数据库存原始笔记
wrangler d1 create notes-db
# 创建 Vectorize 索引(768 维,配合 bge-base-en-v1.5 模型)
wrangler vectorize create notes-index --dimensions=768 --metric=cosine

Configurez wrangler.jsonc (ou wrangler.toml) :

{
  "name": "rag-notes-app",
  "main": "src/index.ts",
  "compatibility_date": "2024-01-01",
  "node_compat": true,
  // AI 绑定
  "ai": {
    "binding": "AI"
  },
  // D1 数据库绑定
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "notes-db",
      "database_id": "你的数据库ID"  // 从上面创建命令的输出复制
    }
  ],
  // Vectorize 索引绑定
  "vectorize": [
    {
      "binding": "VECTORIZE",
      "index_name": "notes-index"
    }
  ],
  // Workflow 绑定(处理异步向量化任务)
  "workflows": [
    {
      "binding": "RAG_WORKFLOW",
      "name": "rag-workflow",
      "class_name": "RAGWorkflow"
    }
  ]
}

Initialisez les tables :

-- schema.sql
CREATE TABLE IF NOT EXISTS notes (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  text TEXT NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Exécutez :

wrangler d1 execute notes-db --file=./schema.sql

Implémenter l’ingestion dans la base de connaissances

C’est le cœur du RAG — transformer les notes en vecteurs stockés.

Créez src/workflow.ts (Workflow pour les tâches asynchrones) :

import { WorkflowEntrypoint, WorkflowStep } from 'cloudflare:workers';
type Env = {
  AI: Ai;
  DB: D1Database;
  VECTORIZE: VectorizeIndex;
};
type Params = {
  noteId: number;
  text: string;
};
export class RAGWorkflow extends WorkflowEntrypoint<Env, Params> {
  async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
    const { noteId, text } = event.payload;
    // 步骤1:确认 D1 记录已创建(由主路由完成)
    // 步骤2:生成向量
    const embeddings = await step.do('generate embeddings', async () => {
      const response = await this.env.AI.run(
        '@cf/baai/bge-base-en-v1.5',  // 768维的 Embedding 模型
        { text: [text] }
      );
      return response.data[0];  // 返回向量数组
    });
    // 步骤3:插入 Vectorize
    await step.do('insert vector', async () => {
      await this.env.VECTORIZE.insert([
        {
          id: noteId.toString(),
          values: embeddings,
          metadata: { text }  // 存一份文本方便调试
        }
      ]);
    });
  }
}

Route principale src/index.ts (ajout de notes) :

import { Hono } from 'hono';
import { RAGWorkflow } from './workflow';
type Bindings = {
  AI: Ai;
  DB: D1Database;
  VECTORIZE: VectorizeIndex;
  RAG_WORKFLOW: Workflow;
};
const app = new Hono<{ Bindings: Bindings }>();
// 添加笔记
app.post('/notes', async (c) => {
  const { text } = await c.req.json<{ text: string }>();
  if (!text?.trim()) {
    return c.json({ error: 'Text is required' }, 400);
  }
  // 插入 D1
  const result = await c.env.DB.prepare(
    'INSERT INTO notes (text) VALUES (?) RETURNING id'
  ).bind(text).first<{ id: number }>();
  if (!result) {
    return c.json({ error: 'Failed to create note' }, 500);
  }
  // 触发 Workflow 异步生成向量
  await c.env.RAG_WORKFLOW.create({
    params: { noteId: result.id, text }
  });
  return c.json({
    id: result.id,
    message: 'Note created, vectorization in progress'
  });
});
export default app;
export { RAGWorkflow };

Quand l’utilisateur envoie un POST pour ajouter une note :

  1. le texte est immédiatement stocké dans D1
  2. le Workflow génère les vecteurs et les insère dans Vectorize en arrière-plan
  3. même si la vectorisation prend quelques secondes, la requête utilisateur n’est pas bloquée

Implémenter la Q&R intelligente

Les notes sont stockées, passons aux requêtes.

Ajoutez dans src/index.ts :

// 查询问答
app.get('/', async (c) => {
  const query = c.req.query('q');
  if (!query) {
    return c.json({ error: 'Query parameter "q" is required' }, 400);
  }
  // 第一步:把问题转成向量
  const queryEmbedding = await c.env.AI.run(
    '@cf/baai/bge-base-en-v1.5',
    { text: [query] }
  );
  // 第二步:在 Vectorize 里找最相似的 3 条笔记
  const matches = await c.env.VECTORIZE.query(
    queryEmbedding.data[0],
    { topK: 3, returnMetadata: true }
  );
  if (matches.count === 0) {
    return c.json({ answer: '没有找到相关笔记' });
  }
  // 第三步:从 D1 获取完整文本(如果需要)
  const noteIds = matches.matches.map(m => m.id);
  const notes = await c.env.DB.prepare(
    `SELECT text FROM notes WHERE id IN (${noteIds.map(() => '?').join(',')})`
  ).bind(...noteIds).all();
  // 第四步:构造 Prompt,调用 LLM 生成答案
  const context = notes.results.map((n: any) => n.text).join('\n\n---\n\n');
  const prompt = `以下是相关的笔记内容:
${context}
用户问题:${query}
请基于上述笔记内容回答用户问题。如果笔记中没有相关信息,请说明。`;
  const aiResponse = await c.env.AI.run(
    '@cf/meta/llama-3-8b-instruct',  // 或者用 claude-3-5-sonnet-latest
    {
      messages: [
        { role: 'system', content: '你是一个智能笔记助手' },
        { role: 'user', content: prompt }
      ]
    }
  );
  return c.json({
    answer: aiResponse.response,
    sources: matches.matches.map(m => ({
      id: m.id,
      score: m.score,
      text: m.metadata?.text
    }))
  });
});

Testez :

# 本地运行
wrangler dev
# 添加笔记
curl -X POST http://localhost:8787/notes \
  -H "Content-Type: application/json" \
  -d '{"text": "Cloudflare Workers AI 支持 Llama 3 和 Claude 模型"}'
curl -X POST http://localhost:8787/notes \
  -H "Content-Type: application/json" \
  -d '{"text": "Vectorize 使用余弦相似度进行向量检索"}'
# 等几秒让 Workflow 完成向量化
# 提问
curl "http://localhost:8787/?q=Workers%20AI%20有哪些模型"

Si tout va bien, vous recevrez une réponse basée sur le contenu de vos notes.

Suppression et mise à jour

Lors d’une suppression, effacez D1 et Vectorize :

app.delete('/notes/:id', async (c) => {
  const id = c.req.param('id');
  // 从 D1 删除
  await c.env.DB.prepare('DELETE FROM notes WHERE id = ?').bind(id).run();
  // 从 Vectorize 删除
  await c.env.VECTORIZE.deleteByIds([id]);
  return c.json({ message: 'Note deleted' });
});

Pour une mise à jour, le plus simple : supprimer puis recréer (re-générer le vecteur).

Code complet : exemple officiel Cloudflare.

Optimisations avancées — un RAG plus intelligent

Les bases tournent ; pour un usage réel, quelques détails valent le coup.

Stratégie de découpage de texte

Aujourd’hui chaque note entière est une unité. Pour une note longue (doc technique), deux problèmes :

  1. la similarité globale du document peut être faible alors qu’un paragraphe est pertinent
  2. un prompt trop long dépasse la fenêtre de contexte du LLM

Mieux vaut découper en chunks, chacun vectorisé séparément.

Méthode simple :

function splitText(text: string, chunkSize: number = 500, overlap: number = 50): string[] {
  const chunks: string[] = [];
  let start = 0;
  while (start < text.length) {
    const end = Math.min(start + chunkSize, text.length);
    chunks.push(text.slice(start, end));
    start = end - overlap;  // 重叠一点,避免句子被切断
  }
  return chunks;
}

Plus fin : découpage par paragraphe ou sémantique (ex. RecursiveCharacterTextSplitter de LangChain) ; pour la plupart des cas, taille fixe + overlap suffit.

Modifiez le Workflow pour un ID unique par chunk :

const chunks = splitText(text);
for (let i = 0; i < chunks.length; i++) {
  const chunkId = `${noteId}-${i}`;
  const embeddings = await this.env.AI.run('@cf/baai/bge-base-en-v1.5', {
    text: [chunks[i]]
  });
  await this.env.VECTORIZE.insert([{
    id: chunkId,
    values: embeddings.data[0],
    metadata: { noteId, chunkIndex: i, text: chunks[i] }
  }]);
}

Améliorer la précision du retrieval

Ajuster topK et le seuil de similarité

Par défaut top 3 peut être trop ou pas assez. Essayez 5, en filtrant les scores trop bas :

const matches = await c.env.VECTORIZE.query(queryEmbedding.data[0], {
  topK: 5,
  returnMetadata: true
});
// 只保留相似度 > 0.7 的结果
const relevantMatches = matches.matches.filter(m => m.score > 0.7);

Score entre 0 et 1 (similarité cosinus) ; au-dessus de 0,7, généralement pertinent.

Optimiser le prompt

Ne vous contentez pas de coller le contenu récupéré — dites à l’IA comment l’utiliser :

const prompt = `你是一个智能笔记助手。以下是从笔记库中检索到的相关内容(按相关性排序):
${context}
请严格基于上述内容回答用户问题。如果内容不足以回答问题,明确说明"笔记中没有找到相关信息",不要编造答案。
用户问题:${query}`;

Points clés :

  • préciser que ce sont des extraits récupérés
  • exiger une réponse strictement basée sur ce contenu
  • autoriser à dire « je ne sais pas »

Cela réduit les hallucinations.

Maîtrise des coûts et rate limiting

La couche gratuite Workers AI a un quota Neurons quotidien (valeur variable — voir la page Pricing).

Suivi de consommation : Dashboard Cloudflare → Workers AI. Les modèles d’embedding coûtent peu ; la génération LLM coûte plus.

Stratégie de dégradation :

  1. limiter la fréquence par utilisateur (compteur KV ou Durable Objects)
  2. au-delà du quota, modèle plus petit ou résultats en cache
  3. pour les requêtes non critiques, renvoyer le texte récupéré sans appeler le LLM
// 简单限流示例
const userKey = c.req.header('X-User-ID') || 'anonymous';
const requestCount = await c.env.KV.get(`rate:${userKey}`) || 0;
if (requestCount > 100) {
  return c.json({ error: 'Rate limit exceeded' }, 429);
}
await c.env.KV.put(`rate:${userKey}`, requestCount + 1, { expirationTtl: 86400 });

Passer à un modèle plus puissant

Llama 3 8B est déjà solide ; pour une meilleure compréhension, essayez Claude :

// 需要先在 Dashboard 绑定 Anthropic API key
const aiResponse = await c.env.AI.run('claude-3-5-sonnet-latest', {
  messages: [
    { role: 'system', content: '你是一个智能笔记助手' },
    { role: 'user', content: prompt }
  ]
});

Claude est meilleur en qualité, mais consomme plus de Neurons. Adaptez au besoin.

Mon retour d’expérience :

  • Q&R simples : Llama 3 suffit
  • Raisonnement, synthèse : Claude nettement meilleur
  • Budget serré : tester d’abord avec Llama, upgrader une fois le besoin confirmé

Déploiement et cas d’usage réels

Processus de déploiement

Tests locaux OK, déploiement en une commande :

wrangler deploy

Cloudflare s’occupe de :

  • packager votre code
  • déployer sur le réseau edge mondial
  • générer un domaine .workers.dev

Sortie typique :

Published rag-notes-app
  https://rag-notes-app.your-account.workers.dev

Voilà l’URL de votre API.

Domaine personnalisé (optionnel) :
Si votre domaine est sur Cloudflare :

wrangler domains add api.yourdomain.com

Ou via Dashboard → Workers & Pages → votre Worker → Settings → Domains.

Variables d’environnement et Secrets :
Pour une clé Anthropic ou autre secret :

wrangler secret put ANTHROPIC_API_KEY
# 输入你的 key

Dans le code :

const apiKey = c.env.ANTHROPIC_API_KEY;

Scénarios d’application réels

Cette architecture RAG couvre beaucoup de cas :

1. Q&R base de connaissances entreprise

Scénario : des centaines de pages de manuel, docs techniques, FAQ — difficile pour les nouveaux de trouver l’info.
Approche :

  • uploader tous les docs, découper par chapitre dans Vectorize
  • interface web simple ou bot WeChat entreprise
  • l’employé demande « quelle est la procédure de remboursement ? », le système retrouve et répond
    Bénéfice : disponible 24h/24, bien plus rapide que feuilleter des PDF.

2. Service client intelligent

Scénario : site e-commerce, infos produits et politiques SAV, mêmes questions en boucle.
Approche :

  • stocker FAQ, descriptions, politique de retour
  • le RAG répond en premier
  • escalade humaine si insuffisant
    Résultat : un développeur a réduit la charge support de plus de 60 %.

3. Assistant notes personnelles

Scénario : des années de notes Notion ou Obsidian, retrouver un détail précis.
Approche :

  • export périodique des notes via API vers le système RAG
  • demander « c’était quoi déjà ce truc TypeScript que j’avais noté ? »
  • retrieval des passages pertinents
    J’utilise un outil similaire : gain d’efficacité net pour retrouver l’info.

4. Outil « Chat with PDF »

Scénario : l’utilisateur uploade un PDF (article, contrat, rapport) et veut extraire des infos rapidement.
Approche (voir le cas de Rohit Patil) :

  • upload PDF sur R2
  • Worker extrait le texte, découpe, vectorise
  • l’utilisateur demande « quelles sont les clauses de paiement de ce contrat ? »
    Très utile en juridique et conseil.
24h/24
Base de connaissances entreprise en ligne
60%+
Réduction charge service client
10x
Gain d’efficacité retrieval notes personnelles
Source: Données de cas d’usage réels

Dépannage des problèmes courants

Problème 1 : dimensions de vecteurs incohérentes

Erreur : dimension mismatch: expected 768, got 512
Cause : la dimension de l’index Vectorize (768) ne correspond pas à celle du modèle.
Solution : aligner index et modèle. bge-base-en-v1.5 = 768 dimensions.

Problème 2 : D1 et Vectorize désynchronisés

Symptôme : un ID de note retourné n’existe pas dans D1.
Cause : suppression D1 sans Vectorize, ou Workflow en échec.
Solution : transaction ou Workflow garantissant la cohérence des deux côtés.

Problème 3 : timeout Workflow

Erreur : workflow execution timeout
Cause : vectorisation d’un gros volume dépasse la limite de temps.
Solution : plusieurs tâches Workflow ou traitement par lots.

// 分批处理
const batchSize = 10;
for (let i = 0; i < chunks.length; i += batchSize) {
  const batch = chunks.slice(i, i + batchSize);
  await c.env.RAG_WORKFLOW.create({
    params: { noteId, chunks: batch, offset: i }
  });
}

Conclusion

Récapitulons :

  • Principe RAG : retrieval augmenté, « examen avec documents » — chercher d’abord, répondre ensuite
  • App fonctionnelle : système de Q&R sur notes, de l’environnement au code
  • Optimisations : découpage, tuning du retrieval, maîtrise des coûts
  • Cas réels : base entreprise, service client, assistant perso, chat PDF

Le grand avantage Cloudflare : barrière basse. Pas de GPU, pas de base à installer, pas d’ops — l’offre gratuite suffit pour un projet perso. En production, les plans payants restent souvent moins chers qu’un stack self-hosted.

Prochaines étapes :

  1. Agir tout de suite : clonez l’exemple officiel, wrangler dev, effet en 5 minutes
  2. Brancher vos données : importez notes, docs, FAQ, testez la qualité du retrieval
  3. Ajouter une interface : chat React/Vue simple, ou déploiement Cloudflare Pages
  4. Aller plus loin : RAG multimodal (images, tableaux), GraphRAG (graphe de connaissances)

Le RAG est l’une des architectures IA les plus utiles aujourd’hui. La suite Cloudflare règle beaucoup de problèmes concrets.

Des questions ? Discord Cloudflare ou le forum Community — communauté très active.

FAQ

Quelle est la différence entre le RAG et un moteur de recherche classique ?
Moteur de recherche classique :
• retourne des liens vers des documents via la correspondance de mots-clés

RAG :
• comprend le sens, récupère le contenu pertinent et génère une réponse en langage naturel
• sait que « les chats sont mignons » et « les chatons sont adorables » veulent dire la même chose
• la recherche classique ne matche que les mots-clés identiques
• le RAG donne directement la réponse, sans faire feuilleter plusieurs documents
Quelle taille de base de connaissances la version gratuite de Cloudflare peut-elle supporter ?
Capacité gratuite :
• D1 gratuit : 10 Go de stockage
• Vectorize gratuit : 5 millions de vecteurs (environ 5 Go de texte)

Largement suffisant pour les projets personnels et les bases de connaissances PME.

Pour plus de capacité, passez à une offre payante ou répartissez sur plusieurs index.
Comment améliorer la précision du retrieval RAG ?
Points d'optimisation clés :

1) Découpage raisonnable :
• chunk size 500-1000 caractères
• overlap 50-100 caractères

2) Ajustement des paramètres :
• topK (généralement 3-5)
• seuil de similarité (>0,7)

3) Optimiser le prompt pour que l'IA réponde clairement à partir du contenu récupéré

4) Utiliser un meilleur modèle d'embedding (ex. text-embedding-3 d'OpenAI)

5) Mettre en cache les réponses aux questions fréquentes (FAQ)
Comment maîtriser les coûts d'une application RAG ?
Stratégies de maîtrise des coûts :

1) Utiliser la couche gratuite Workers AI (quota Neurons quotidien fixe)

2) Limiter la fréquence des requêtes utilisateur (compteur KV)

3) Mettre en cache les réponses aux questions fréquentes

4) Llama 3 pour les Q&R simples, Claude pour les tâches complexes

5) Surveiller la consommation quotidienne ; près de la limite, ne renvoyer que les résultats de retrieval sans appeler le LLM
Quels cas d'usage concrets pour le RAG ?
Quatre scénarios typiques :

1) Q&R base de connaissances entreprise (manuel employé, docs techniques, politiques)

2) Service client intelligent (infos produits, SAV, FAQ)

3) Assistant notes personnelles (retrouver rapidement des années de notes)

4) Outil Q&R documentaire (upload PDF/Word, extraction intelligente)

Tout scénario où il faut répondre à partir de connaissances existantes convient.

14 min de lecture · Publié le: 1 déc. 2025 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog