Anleitung

Technische Dokumentation: RAG für Entwickler

8. März 2026
Équipe Ailog

Einen RAG-Assistenten für Ihre technische Dokumentation bereitstellen: API docs, Developer Guides, READMEs und technische Wikis.

Technische Dokumentation: RAG für Entwickler

Technische Dokumentation ist das Rückgrat von Entwicklungsteams. APIs, Integrationsanleitungen, Architektur, Runbooks, ADRs... Diese kritischen Ressourcen sind oft verstreut, schwer zu durchsuchen und schnell veraltet. Ein RAG-Assistent verwandelt diese Dokumentation in einen intelligenten Copiloten, der Entwicklern in Echtzeit Fragen beantwortet und die Zeit reduziert, die für das Suchen nach Informationen verloren geht.

Die Herausforderung der technischen Dokumentation

Symptome einer defekten Dokumentation

Jedes technische Team kennt diese täglichen Frustrationen:

  • "Die Doku ist veraltet, wir wissen nicht mehr, was aktuell ist"
  • "Ich weiß nicht, wo ich anfangen soll, um diese API zu integrieren"
  • "Das README beantwortet meine spezifische Frage nicht"
  • "Neue Entwickler sind beim Onboarding verloren"
  • "Wir haben das irgendwo dokumentiert, aber können es nicht wiederfinden"

Die versteckten Kosten der Informationssuche

Eine Studie von McKinsey zeigt, dass Entwickler durchschnittlich 20% ihrer Zeit damit verbringen, Informationen zu suchen. Für ein Team von 10 Entwicklern mit 70k€/Jahr:

20% × 10 × 70 000€ = 140 000€/Jahr, die in der Suche verloren gehen

Und diese Zahl berücksichtigt nicht die Kosten von Fehlern aufgrund schlecht verstandener oder veralteter Dokumentation.

Spezifitäten der technischen Dokumentation

Type de documentationDéfi principalFréquence d'accès
API ReferenceTrouver le bon endpoint parmi des centainesTrès haute
Guides d'intégrationSuivre les étapes dans le bon ordreHaute
Architecture docsComprendre les dépendances systèmeMoyenne
RunbooksAccéder rapidement aux procédures d'urgenceCritique
ADRs (Architecture Decision Records)Comprendre le "pourquoi" des décisionsMoyenne
Code comments/docstringsTrouver l'usage d'une fonctionTrès haute

Architektur eines RAG für technische Dokumentation

┌─────────────────────────────────────────────────────────────────┐
│                    Sources de Documentation                      │
├─────────────────────────────────────────────────────────────────┤
│  OpenAPI    Markdown     Code        ADRs        Wiki           │
│  Specs      Docs         Comments    Records     Pages          │
│    │           │            │           │           │            │
│    └───────────┼────────────┼───────────┼───────────┘            │
│                ▼                                                 │
│        ┌───────────────┐                                         │
│        │  Doc Parser   │  ← Parseurs spécialisés par format     │
│        └───────┬───────┘                                         │
└────────────────┼────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Pipeline d'Indexation                         │
├─────────────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌────────────────────┐     │
│  │   Chunking   │  │  Metadata    │  │     Embedding      │     │
│  │  Sémantique  │──│  Extraction  │──│    (BGE-M3)        │     │
│  │              │  │              │  │                    │     │
│  └──────────────┘  └──────────────┘  └─────────┬──────────┘     │
│                                                 │                │
│                                                 ▼                │
│                                      ┌──────────────────┐       │
│                                      │  Vector Store    │       │
│                                      │   (Qdrant)       │       │
│                                      └──────────────────┘       │
└─────────────────────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Interfaces d'Accès                            │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────────────┐     │
│  │  Web    │  │  Slack  │  │  IDE    │  │     CLI         │     │
│  │  Chat   │  │  Bot    │  │ Plugin  │  │   (ailog ask)   │     │
│  └─────────┘  └─────────┘  └─────────┘  └─────────────────┘     │
└─────────────────────────────────────────────────────────────────┘

Indexation Multi-Source

Connecteur de Documentation Technique

DEVELOPERpython
from pathlib import Path from typing import List, Dict, Optional import yaml import re from dataclasses import dataclass @dataclass class TechDocument: """Document technique indexable.""" id: str title: str content: str doc_type: str source_path: str metadata: Dict class TechDocIndexer: """Indexeur multi-source pour documentation technique.""" def __init__(self, vector_db, embedding_model): self.vector_db = vector_db self.embedding_model = embedding_model self.parsers = { "openapi": self._parse_openapi, "markdown": self._parse_markdown, "python": self._parse_python_docstrings, "adr": self._parse_adr, } async def index_all_sources(self, config: Dict) -> int: """Indexe toutes les sources de documentation configurées.""" documents = [] # API Documentation (OpenAPI/Swagger) if "openapi" in config: for spec_path in config["openapi"]: api_docs = await self._parse_openapi(spec_path) documents.extend(api_docs) print(f"Indexed {len(api_docs)} endpoints from {spec_path}") # Markdown Documentation if "markdown_dirs" in config: for doc_dir in config["markdown_dirs"]: md_docs = await self._parse_markdown_folder(doc_dir) documents.extend(md_docs) print(f"Indexed {len(md_docs)} markdown files from {doc_dir}") # Code Documentation (Docstrings) if "code_dirs" in config: for code_dir in config["code_dirs"]: code_docs = await self._parse_code_folder(code_dir) documents.extend(code_docs) print(f"Indexed {len(code_docs)} code docs from {code_dir}") # Architecture Decision Records if "adr_dir" in config: adr_docs = await self._parse_adr_folder(config["adr_dir"]) documents.extend(adr_docs) print(f"Indexed {len(adr_docs)} ADRs") # Génération des embeddings et stockage await self._store_documents(documents) return len(documents) async def _parse_openapi(self, spec_path: str) -> List[TechDocument]: """Parse une spécification OpenAPI/Swagger.""" with open(spec_path) as f: spec = yaml.safe_load(f) documents = [] base_url = spec.get("servers", [{}])[0].get("url", "") for path, methods in spec.get("paths", {}).items(): for method, details in methods.items(): if method in ["get", "post", "put", "patch", "delete"]: # Construire le contenu enrichi content = self._format_endpoint_doc( method=method, path=path, details=details, base_url=base_url ) documents.append(TechDocument( id=f"api_{method}_{path.replace('/', '_')}", title=f"{method.upper()} {path}", content=content, doc_type="api_endpoint", source_path=spec_path, metadata={ "method": method.upper(), "path": path, "tags": details.get("tags", []), "deprecated": details.get("deprecated", False), "auth_required": self._requires_auth(details), } )) return documents def _format_endpoint_doc( self, method: str, path: str, details: Dict, base_url: str ) -> str: """Formate la documentation d'un endpoint pour l'indexation.""" sections = [] # En-tête sections.append(f"# {method.upper()} {path}") sections.append(f"URL complète: {base_url}{path}") # Description if details.get("summary"): sections.append(f"\n## Résumé\n{details['summary']}") if details.get("description"): sections.append(f"\n## Description\n{details['description']}") # Paramètres params = details.get("parameters", []) if params: sections.append("\n## Paramètres") for param in params: required = "obligatoire" if param.get("required") else "optionnel" param_type = param.get("schema", {}).get("type", "any") sections.append( f"- **{param['name']}** ({param['in']}, {param_type}, {required}): " f"{param.get('description', 'Non documenté')}" ) # Request Body request_body = details.get("requestBody", {}) if request_body: sections.append("\n## Corps de la requête") content = request_body.get("content", {}) for content_type, schema in content.items(): sections.append(f"Content-Type: {content_type}") if "example" in schema: sections.append(f"Exemple:\n```json\n{schema['example']}\n```") # Réponses responses = details.get("responses", {}) if responses: sections.append("\n## Réponses") for code, response in responses.items(): sections.append(f"- **{code}**: {response.get('description', '')}") return "\n".join(sections) def _requires_auth(self, details: Dict) -> bool: """Vérifie si l'endpoint nécessite une authentification.""" return bool(details.get("security", [])) async def _parse_markdown_folder(self, folder: str) -> List[TechDocument]: """Parse tous les fichiers Markdown d'un dossier.""" documents = [] folder_path = Path(folder) for md_file in folder_path.rglob("*.md"): with open(md_file, encoding="utf-8") as f: content = f.read() # Extraire les métadonnées YAML si présentes metadata = {} if content.startswith("---"): try: _, yaml_content, content = content.split("---", 2) metadata = yaml.safe_load(yaml_content) except: pass # Extraire le titre title_match = re.search(r"^#\s+(.+)$", content, re.MULTILINE) title = title_match.group(1) if title_match else md_file.stem # Déterminer la catégorie depuis le chemin relative_path = md_file.relative_to(folder_path) category = relative_path.parent.name if relative_path.parent.name != "." else "general" documents.append(TechDocument( id=f"doc_{md_file.stem}_{hash(str(md_file))}", title=title, content=content, doc_type="markdown", source_path=str(md_file), metadata={ "category": category, "file_name": md_file.name, **metadata } )) return documents async def _parse_code_folder(self, folder: str) -> List[TechDocument]: """Extrait les docstrings des fichiers Python.""" import ast documents = [] folder_path = Path(folder) for py_file in folder_path.rglob("*.py"): try: with open(py_file, encoding="utf-8") as f: source = f.read() tree = ast.parse(source) for node in ast.walk(tree): # Classes if isinstance(node, ast.ClassDef): docstring = ast.get_docstring(node) if docstring: documents.append(TechDocument( id=f"class_{py_file.stem}_{node.name}", title=f"Classe {node.name}", content=f"# Classe {node.name}\n\n{docstring}", doc_type="code_class", source_path=str(py_file), metadata={ "class_name": node.name, "line_number": node.lineno } )) # Fonctions elif isinstance(node, ast.FunctionDef): docstring = ast.get_docstring(node) if docstring and not node.name.startswith("_"): # Extraire la signature args = [arg.arg for arg in node.args.args] signature = f"{node.name}({', '.join(args)})" documents.append(TechDocument( id=f"func_{py_file.stem}_{node.name}", title=f"Fonction {node.name}", content=f"# {signature}\n\n{docstring}", doc_type="code_function", source_path=str(py_file), metadata={ "function_name": node.name, "signature": signature, "line_number": node.lineno } )) except SyntaxError: continue # Ignorer les fichiers avec erreurs de syntaxe return documents async def _parse_adr_folder(self, folder: str) -> List[TechDocument]: """Parse les Architecture Decision Records.""" documents = [] folder_path = Path(folder) for adr_file in sorted(folder_path.glob("*.md")): with open(adr_file, encoding="utf-8") as f: content = f.read() # Extraire le numéro et le titre de l'ADR adr_match = re.match(r"(\d+)-(.+)\.md", adr_file.name) if adr_match: adr_number = adr_match.group(1) adr_slug = adr_match.group(2) else: adr_number = "0" adr_slug = adr_file.stem # Extraire le statut (Accepted, Deprecated, Superseded, etc.) status_match = re.search(r"Status:\s*(\w+)", content, re.IGNORECASE) status = status_match.group(1) if status_match else "Unknown" # Extraire le titre title_match = re.search(r"^#\s+(.+)$", content, re.MULTILINE) title = title_match.group(1) if title_match else adr_slug.replace("-", " ") documents.append(TechDocument( id=f"adr_{adr_number}_{adr_slug}", title=f"ADR-{adr_number}: {title}", content=content, doc_type="adr", source_path=str(adr_file), metadata={ "adr_number": int(adr_number), "status": status, "slug": adr_slug } )) return documents async def _store_documents(self, documents: List[TechDocument]): """Génère les embeddings et stocke dans la base vectorielle.""" for doc in documents: embedding = await self.embedding_model.embed(doc.content) await self.vector_db.upsert( collection="tech_docs", points=[{ "id": doc.id, "vector": embedding, "payload": { "title": doc.title, "content": doc.content, "doc_type": doc.doc_type, "source_path": doc.source_path, **doc.metadata } }] )

Prompt Système pour Développeurs

DEVELOPERpython
TECH_DOC_SYSTEM_PROMPT = """Tu es un assistant technique expert pour l'équipe de développement. Tu aides les développeurs à trouver rapidement des informations dans la documentation technique. ## Tes compétences - API Reference : endpoints, paramètres, exemples de requêtes - Guides d'intégration : procédures étape par étape - Architecture : composants, dépendances, flux de données - Code : fonctions, classes, patterns d'utilisation - ADRs : contexte et justification des décisions techniques ## Règles de réponse 1. **Précision technique** : Sois exact, cite les versions et paramètres spécifiques 2. **Exemples de code** : Inclus toujours un exemple fonctionnel quand pertinent 3. **Sources** : Cite systématiquement le fichier ou l'endpoint source 4. **Concision** : Va droit au but, les développeurs ont peu de temps 5. **Honnêteté** : Si l'info n'existe pas dans la doc, dis-le clairement ## Format de réponse ### Pour les questions API

Endpoint: METHOD /path Auth: Requis/Non requis

Exemple: curl -X METHOD 'url' -H 'Header: value'

Réponse: {json_example}

📄 Source: openapi.yaml


### Pour les questions d'intégration

Étapes:

  1. Première étape
  2. Deuxième étape

Code: code_example

📄 Source: docs/integration.md


## Documentation disponible
{context}

## Question
{query}
"""

Konkrete Anwendungsfälle

API-Fragen

Entwickler : "Comment créer un utilisateur via l'API ?"

Assistant :

**Endpoint**: POST /api/v1/users
**Auth**: Bearer token requis

**Corps de la requête**:
{
  "email": "[email protected]",
  "name": "John Doe",
  "role": "member"
}

**Exemple curl**:
curl -X POST 'https://api.example.com/api/v1/users' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"email": "[email protected]", "name": "John Doe", "role": "member"}'

**Réponses**:
- 201: Utilisateur créé avec succès
- 400: Données invalides
- 409: Email déjà utilisé

📄 Source: openapi.yaml (POST /api/v1/users)

Debug und Fehler

Entwickler : "Que signifie l'erreur AUTH_TOKEN_EXPIRED ?"

Assistant :

**Erreur AUTH_TOKEN_EXPIRED**

Cette erreur survient quand le token JWT a dépassé sa durée de validité (24h par défaut).

**Solutions**:
1. Rafraîchir le token via POST /api/v1/auth/refresh
2. Redemander un nouveau token via POST /api/v1/auth/login

**Code de gestion**:
async function apiCall(endpoint) {
  try {
    return await fetch(endpoint, { headers: getAuthHeaders() });
  } catch (error) {
    if (error.code === 'AUTH_TOKEN_EXPIRED') {
      await refreshToken();
      return apiCall(endpoint);  // Retry
    }
    throw error;
  }
}

📄 Sources: docs/authentication.md, errors/auth-errors.md

Architektur

Entwickler : "Comment le service de paiement communique avec le service commandes ?"

Assistant :

**Communication Payment Service ↔ Order Service**

Le Payment Service communique avec l'Order Service via un pattern Event-Driven :

┌─────────────┐    payment.completed    ┌─────────────┐
│   Payment   │ ────────────────────→   │    Order    │
│   Service   │                         │   Service   │
└─────────────┘                         └─────────────┘
       │                                       │
       │         RabbitMQ Exchange             │
       └───────────────┬───────────────────────┘
                       │
               payment.events

**Événements émis**:
- payment.initiated : Paiement démarré
- payment.completed : Paiement réussi → déclenche order.confirm
- payment.failed : Échec → déclenche order.cancel

**Pourquoi ce choix ?** (ADR-015)
Découplage des services pour permettre le scaling indépendant et la résilience aux pannes.

📄 Sources: ADR-015, docs/architecture/event-flows.md

IDE-Integration

VS Code Extension

DEVELOPERtypescript
import * as vscode from 'vscode'; interface AilogResponse { answer: string; sources: Array<{ path: string; title: string }>; confidence: number; } export function activate(context: vscode.ExtensionContext) { // Commande principale : poser une question const askCommand = vscode.commands.registerCommand( 'ailog.askDoc', async () => { const question = await vscode.window.showInputBox({ prompt: 'Posez votre question sur la documentation', placeHolder: 'Comment authentifier une requête API ?' }); if (!question) return; // Récupérer le contexte actuel const editor = vscode.window.activeTextEditor; const context = { currentFile: editor?.document.fileName, selectedCode: editor?.document.getText(editor.selection), language: editor?.document.languageId }; // Afficher le panneau de réponse const panel = vscode.window.createWebviewPanel( 'ailogResponse', 'Ailog - Documentation', vscode.ViewColumn.Beside, { enableScripts: true } ); panel.webview.html = getLoadingHtml(); try { const response = await askAilog(question, context); panel.webview.html = formatResponseHtml(response); } catch (error) { panel.webview.html = getErrorHtml(error); } } ); // Commande contextuelle : expliquer le code sélectionné const explainCommand = vscode.commands.registerCommand( 'ailog.explainCode', async () => { const editor = vscode.window.activeTextEditor; if (!editor) return; const selection = editor.selection; const selectedCode = editor.document.getText(selection); if (!selectedCode) { vscode.window.showWarningMessage('Sélectionnez du code à expliquer'); return; } const question = `Explique ce code et son usage dans notre codebase:\n\`\`\`\n${selectedCode}\n\`\`\``; const response = await askAilog(question, { currentFile: editor.document.fileName, language: editor.document.languageId }); // Afficher en hover ou panneau showInlineResponse(editor, selection, response); } ); context.subscriptions.push(askCommand, explainCommand); } async function askAilog( question: string, context: object ): Promise<AilogResponse> { const config = vscode.workspace.getConfiguration('ailog'); const apiKey = config.get<string>('apiKey'); const endpoint = config.get<string>('endpoint') || 'https://api.ailog.fr'; const response = await fetch(`${endpoint}/v1/ask`, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ question, context }) }); if (!response.ok) { throw new Error(`API Error: ${response.status}`); } return response.json(); }

JetBrains Plugin (IntelliJ, PyCharm)

DEVELOPERkotlin
// AilogToolWindowFactory.kt class AilogToolWindowFactory : ToolWindowFactory { override fun createToolWindowContent( project: Project, toolWindow: ToolWindow ) { val panel = AilogPanel(project) val content = ContentFactory.getInstance() .createContent(panel, "Ailog Docs", false) toolWindow.contentManager.addContent(content) } } class AilogPanel(private val project: Project) : JPanel() { private val questionField = JTextField() private val responseArea = JEditorPane("text/html", "") init { layout = BorderLayout() // Barre de question val inputPanel = JPanel(BorderLayout()) inputPanel.add(JLabel("Question: "), BorderLayout.WEST) inputPanel.add(questionField, BorderLayout.CENTER) val askButton = JButton("Ask") askButton.addActionListener { askQuestion() } inputPanel.add(askButton, BorderLayout.EAST) add(inputPanel, BorderLayout.NORTH) add(JScrollPane(responseArea), BorderLayout.CENTER) // Raccourci clavier questionField.addActionListener { askQuestion() } } private fun askQuestion() { val question = questionField.text if (question.isBlank()) return // Contexte du fichier actif val editor = FileEditorManager.getInstance(project).selectedTextEditor val context = mapOf( "file" to editor?.virtualFile?.path, "selection" to editor?.selectionModel?.selectedText ) // Appel asynchrone ApplicationManager.getApplication().executeOnPooledThread { val response = AilogClient.ask(question, context) SwingUtilities.invokeLater { responseArea.text = formatResponse(response) } } } }

Kontinuierliche Synchronisation

Git Webhook für automatische Aktualisierung

DEVELOPERpython
from fastapi import FastAPI, Request, HTTPException import hmac import hashlib app = FastAPI() @app.post("/webhook/github") async def github_webhook(request: Request): """Webhook GitHub pour synchroniser la documentation.""" # Vérifier la signature signature = request.headers.get("X-Hub-Signature-256") body = await request.body() if not verify_signature(body, signature): raise HTTPException(status_code=401, detail="Invalid signature") payload = await request.json() event = request.headers.get("X-GitHub-Event") if event == "push": # Vérifier si des fichiers de doc ont changé changed_files = [] for commit in payload.get("commits", []): changed_files.extend(commit.get("added", [])) changed_files.extend(commit.get("modified", [])) doc_files = [f for f in changed_files if is_doc_file(f)] if doc_files: # Déclencher la réindexation await trigger_reindex(doc_files) return {"status": "reindex_triggered", "files": doc_files} return {"status": "ignored"} def is_doc_file(path: str) -> bool: """Vérifie si le fichier est de la documentation.""" doc_patterns = [ "docs/", "README", ".md", "openapi", "swagger", "adr/" ] return any(pattern in path for pattern in doc_patterns) async def trigger_reindex(files: list): """Déclenche la réindexation des fichiers modifiés.""" indexer = TechDocIndexer(vector_db, embedding_model) for file_path in files: if file_path.endswith(".md"): doc = await indexer._parse_markdown_file(file_path) await indexer._store_documents([doc]) elif "openapi" in file_path or "swagger" in file_path: docs = await indexer._parse_openapi(file_path) await indexer._store_documents(docs) print(f"Reindexed {len(files)} documentation files")

Effektivität messen

Wichtige Kennzahlen

MétriqueObjectifComment mesurer
Temps de résolution< 30 secondesTemps entre question et réponse satisfaisante
Précision des réponses> 90%Feedback utilisateur (pouce haut/bas)
Réduction tickets support interne> 50%Comparaison avant/après déploiement
Adoption> 80% de l'équipeUtilisateurs actifs / équipe totale
Satisfaction développeurs> 4/5Survey NPS trimestriel

Monitoring-Dashboard

DEVELOPERpython
class DocRAGAnalytics: """Analytics pour mesurer l'efficacité du RAG documentation.""" def get_weekly_report(self) -> Dict: """Génère le rapport hebdomadaire.""" return { "total_queries": self.count_queries(days=7), "unique_users": self.count_unique_users(days=7), "avg_response_time_ms": self.avg_response_time(days=7), "satisfaction_rate": self.satisfaction_rate(days=7), "top_queries": self.top_queries(days=7, limit=10), "unanswered_queries": self.unanswered_queries(days=7), "most_used_docs": self.most_cited_sources(days=7, limit=10) }

Ergänzende Ressourcen

FAQ

Oui, les docstrings Python, JSDoc JavaScript, et commentaires de code sont extraits et indexes. Le RAG peut expliquer le fonctionnement d'une fonction, son usage et ses parametres. Pour le code sans documentation, il peut analyser la structure mais avec moins de precision.
Chaque version peut etre indexee avec un tag de version dans les metadonnees. Le chatbot peut filtrer par version ou preciser dans quelle version l'information est valide. Une bonne pratique est de privilegier la version actuelle et archiver les anciennes dans un index separe.
Le RAG base ses reponses sur la documentation existante. Si vos docs contiennent des exemples, il les cite et adapte. Pour des exemples nouveaux, le LLM peut en generer bases sur les patterns documentes, mais il est important de verifier leur exactitude avant utilisation en production.
L'integration webhook Git est la solution ideale : chaque push sur la branche docs declenche une reindexation automatique des fichiers modifies. Pour les specs OpenAPI, la CI/CD peut inclure une etape de sync vers le RAG. La mise a jour est ainsi transparente pour les developpeurs.
Les etudes montrent que les developpeurs passent 20% de leur temps a chercher de l'information. Un RAG bien configure peut reduire ce temps de 50 a 70%. Pour une equipe de 10 developpeurs, cela represente 1 a 2 ETP economises par an, sans compter la reduction des erreurs dues a une mauvaise comprehension de la doc. ---

Intelligente Dokumentation mit Ailog

Machen Sie Ihre technische Dokumentation sofort zugänglich. Ailog bietet:

  • Indexation multi-source : OpenAPI, Markdown, code comments, ADRs
  • Intégration IDE : Extensions VS Code et JetBrains
  • Sync automatique : Webhooks Git pour mise à jour en temps réel
  • Réponses avec code : Snippets et exemples dans les réponses
  • Hébergement France : Conformité RGPD native

Ihre Entwickler verlieren jede Woche Stunden mit der Suche in der Dokumentation. Geben Sie ihnen einen Assistenten, der sofort findet, was sie brauchen.

Essayez Ailog gratuitement | Voir la démo documentation

Tags

RAGdocumentationdéveloppeursAPItechniqueknowledge base

Verwandte Artikel

Ailog Assistant

Ici pour vous aider

Salut ! Pose-moi des questions sur Ailog et comment intégrer votre RAG dans vos projets !