Intelligence Artificielle angularforall.com

- Claude API d'Anthropic : guide de démarrage

ClaudeAnthropicApiLlmClaude-ApiTool-UseStreaming-SseSystem-PromptTypescriptSdkIa-GenerativePrompt-EngineeringNode-Js
Claude API d'Anthropic : guide de démarrage

Intégrez l'API Claude d'Anthropic dans vos applications : messages, system prompt, streaming temps réel et tool use avec le SDK officiel TypeScript.

Installation et configuration

npm install @anthropic-ai/sdk

# TypeScript (recommandé)
npm install typescript @types/node
// anthropic.ts — Client configuré
import Anthropic from '@anthropic-ai/sdk';

// La clé API est lue depuis la variable d'environnement ANTHROPIC_API_KEY
// Ne jamais hardcoder la clé dans le code source
const client = new Anthropic({
    apiKey: process.env.ANTHROPIC_API_KEY,
    // Optionnel : timeout et retry
    timeout: 30_000,      // 30 secondes
    maxRetries: 2,        // Retry automatique sur erreurs réseau
});

export default client;
Sécurité : Récupère ta clé sur console.anthropic.com. Stocke-la dans .env (jamais dans le code). Pour les apps Angular, l'API doit être appelée côté serveur (Node.js/Express) — ne jamais exposer la clé côté client.

Messages et structure de réponse

import client from './anthropic.js';

// Appel simple
const message = await client.messages.create({
    model: 'claude-opus-4-6',
    max_tokens: 1024,
    messages: [
        { role: 'user', content: 'Explique les closures JavaScript en 3 points avec des exemples.' }
    ]
});

// Accès au texte de la réponse
const text = message.content[0].type === 'text' ? message.content[0].text : '';
console.log(text);
// Structure complète de la réponse Message
interface Message {
    id: string;           // 'msg_01XFDUDYJgAACzvnptvVoYEL'
    type: 'message';
    role: 'assistant';
    content: ContentBlock[];  // Tableau de blocs de contenu
    model: string;        // 'claude-opus-4-6'
    stop_reason: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use';
    stop_sequence: string | null;
    usage: {
        input_tokens: number;   // Tokens dans le prompt
        output_tokens: number;  // Tokens générés
    };
}

// Content blocks typés
type ContentBlock =
    | { type: 'text'; text: string }
    | { type: 'tool_use'; id: string; name: string; input: Record<string, unknown> };

// Extraction sécurisée du texte
function extractText(message: Anthropic.Message): string {
    return message.content
        .filter(block => block.type === 'text')
        .map(block => (block as Anthropic.TextBlock).text)
        .join('\n');
}

System Prompt et conversations multi-tours

// Conversation multi-tours avec gestion de l'historique
class ConversationManager {
    private history: Anthropic.MessageParam[] = [];
    private systemPrompt: string;

    constructor(systemPrompt: string) {
        this.systemPrompt = systemPrompt;
    }

    async chat(userMessage: string): Promise<string> {
        // Ajouter le message utilisateur à l'historique
        this.history.push({ role: 'user', content: userMessage });

        const response = await client.messages.create({
            model: 'claude-sonnet-4-6',
            max_tokens: 2048,
            system: this.systemPrompt, // System prompt en dehors de messages[]
            messages: this.history,    // Historique complet à chaque requête
        });

        const assistantMessage = extractText(response);

        // Ajouter la réponse à l'historique pour le prochain tour
        this.history.push({ role: 'assistant', content: assistantMessage });

        // Truncation de l'historique si trop long (fenêtre de contexte)
        if (this.history.length > 20) {
            // Garder les 2 premiers (contexte initial) et les 18 derniers
            this.history = [...this.history.slice(0, 2), ...this.history.slice(-18)];
        }

        return assistantMessage;
    }

    clearHistory() { this.history = []; }
}

// Utilisation
const bot = new ConversationManager(`
    Tu es un expert Angular qui répond en français.
    Fournis toujours des exemples TypeScript avec commentaires.
    Sois précis, concis, et pratique.
`);

console.log(await bot.chat('Comment créer un service Angular injectable ?'));
console.log(await bot.chat('Et comment l\´injecter dans un composant ?'));

Streaming avec SSE

Le streaming envoie les tokens au fur et à mesure, réduisant la latence perçue de 80% sur les longues réponses.

// Streaming avec SDK helper
const stream = await client.messages.stream({
    model: 'claude-opus-4-6',
    max_tokens: 2048,
    messages: [{ role: 'user', content: 'Explique le SSR Angular en détail.' }],
});

// Écouter les tokens en temps réel
stream.on('text', (text) => {
    process.stdout.write(text); // Affichage progressif
});

// Message final avec usage complet
const finalMessage = await stream.finalMessage();
console.log('\n\nUsage:', finalMessage.usage);
// Endpoint Express avec Server-Sent Events (SSE)
import express from 'express';
import client from './anthropic.js';

app.post('/api/chat/stream', async (req, res) => {
    const { messages, system } = req.body;

    // Configuration SSE
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    res.setHeader('Access-Control-Allow-Origin', '*');

    try {
        const stream = await client.messages.stream({
            model: 'claude-sonnet-4-6',
            max_tokens: 2048,
            system,
            messages,
        });

        for await (const event of stream) {
            if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
                // Envoyer chaque token au client
                res.write(`data: ${JSON.stringify({ text: event.delta.text })}\n\n`);
            }
        }

        const final = await stream.finalMessage();
        res.write(`data: ${JSON.stringify({ done: true, usage: final.usage })}\n\n`);
        res.end();

    } catch (error) {
        res.write(`data: ${JSON.stringify({ error: 'Erreur de streaming' })}\n\n`);
        res.end();
    }
});

Tool Use — cycle complet

Le Tool Use (function calling) permet à Claude d'appeler des fonctions définies par le développeur. Le cycle complet requiert plusieurs échanges : Claude demande l'outil → vous l'exécutez → vous renvoyez le résultat.

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

const client = new Anthropic();

// Définition des outils disponibles
const tools: Anthropic.Tool[] = [
    {
        name: 'get_weather',
        description: 'Obtenir la météo actuelle pour une ville',
        input_schema: {
            type: 'object',
            properties: {
                city: { type: 'string', description: 'Nom de la ville' },
                unit: { type: 'string', enum: ['celsius', 'fahrenheit'], description: 'Unité de température' }
            },
            required: ['city']
        }
    },
    {
        name: 'search_web',
        description: 'Rechercher des informations sur le web',
        input_schema: {
            type: 'object',
            properties: {
                query: { type: 'string', description: 'Requête de recherche' }
            },
            required: ['query']
        }
    }
];

// Fonctions d'implémentation
async function executeToolCall(name: string, input: Record<string, unknown>): Promise<string> {
    switch (name) {
        case 'get_weather':
            // Appel API météo réel
            const weather = await fetchWeatherApi(input.city as string);
            return JSON.stringify(weather);
        case 'search_web':
            const results = await webSearch(input.query as string);
            return JSON.stringify(results);
        default:
            return JSON.stringify({ error: `Outil inconnu: ${name}` });
    }
}

// Cycle complet tool use
async function chatWithTools(userMessage: string): Promise<string> {
    const messages: Anthropic.MessageParam[] = [
        { role: 'user', content: userMessage }
    ];

    // Boucle : continuer tant que Claude appelle des outils
    while (true) {
        const response = await client.messages.create({
            model: 'claude-opus-4-6',
            max_tokens: 4096,
            tools,
            messages,
        });

        // Claude a terminé (pas d'appel d'outil)
        if (response.stop_reason === 'end_turn') {
            return extractText(response);
        }

        // Claude demande un ou plusieurs outils
        if (response.stop_reason === 'tool_use') {
            // Ajouter la réponse de Claude à l'historique
            messages.push({ role: 'assistant', content: response.content });

            // Exécuter TOUS les outils demandés (Claude peut en demander plusieurs)
            const toolResults: Anthropic.ToolResultBlockParam[] = [];

            for (const block of response.content) {
                if (block.type === 'tool_use') {
                    const result = await executeToolCall(block.name, block.input);
                    toolResults.push({
                        type: 'tool_result',
                        tool_use_id: block.id,
                        content: result,
                    });
                }
            }

            // Ajouter les résultats à l'historique pour le prochain tour
            messages.push({ role: 'user', content: toolResults });
        }
    }
}

// Usage
const answer = await chatWithTools('Quel temps fait-il à Paris aujourd\'hui ?');
console.log(answer);

Vision — analyse d'images

Claude supporte l'analyse d'images (Vision) via des blocs image dans les messages. Supporte JPG, PNG, GIF, WebP et les URLs.

import * as fs from 'fs';

// Vision avec image en base64
const imageData = fs.readFileSync('./screenshot.png').toString('base64');

const response = await client.messages.create({
    model: 'claude-opus-4-6',
    max_tokens: 1024,
    messages: [{
        role: 'user',
        content: [
            {
                type: 'image',
                source: {
                    type: 'base64',
                    media_type: 'image/png',
                    data: imageData,
                }
            },
            {
                type: 'text',
                text: 'Analyse cette capture d\'écran d\'interface. Quels problèmes UX vois-tu ?'
            }
        ]
    }]
});

// Vision avec URL (image publique)
const responseUrl = await client.messages.create({
    model: 'claude-opus-4-6',
    max_tokens: 1024,
    messages: [{
        role: 'user',
        content: [
            { type: 'image', source: { type: 'url', url: 'https://example.com/diagram.png' } },
            { type: 'text', text: 'Explique ce diagramme d\'architecture.' }
        ]
    }]
});

Modèles et paramètres

Modèle Points forts Contexte Usage recommandé
claude-opus-4-6 Raisonnement avancé, code complexe 200k tokens Analyse, architecture, debug difficile
claude-sonnet-4-6 Équilibre perf/vitesse/coût 200k tokens Chatbots, génération de contenu
claude-haiku-4-5-20251001 Ultra rapide, faible coût 200k tokens Classification, extraction, fort volume
// Paramètres importants de messages.create()
await client.messages.create({
    model: 'claude-sonnet-4-6',
    max_tokens: 2048,          // Tokens maximum à générer (obligatoire)
    temperature: 0.7,          // 0 = déterministe, 1 = créatif (défaut: 1)
    top_p: 0.9,                // Nucleus sampling (alternatif à temperature)
    top_k: 40,                 // Limite le vocabulaire (moins courant)
    stop_sequences: ['', 'FIN'], // Arrêter la génération sur ces séquences
    system: 'Tu es un expert Angular.',
    messages: [...],
    metadata: {
        user_id: 'user_123'    // Traçabilité (non envoyé au modèle)
    }
});

Service Angular avec Claude API

En production Angular, l'appel à l'API Claude doit passer par un backend Node.js. Voici l'architecture complète :

// === BACKEND : api.service.ts (Node.js/Express) ===
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

app.post('/api/ai/chat', async (req, res) => {
    const { messages, system } = req.body;
    const response = await client.messages.create({
        model: 'claude-sonnet-4-6',
        max_tokens: 2048,
        system,
        messages,
    });
    res.json({ text: extractText(response), usage: response.usage });
});

// === FRONTEND Angular : claude.service.ts ===
import { Injectable, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';

interface ChatMessage {
    role: 'user' | 'assistant';
    content: string;
}

@Injectable({ providedIn: 'root' })
export class ClaudeService {
    private http = inject(HttpClient);

    readonly isLoading = signal(false);
    readonly history = signal<ChatMessage[]>([]);
    readonly error = signal<string | null>(null);

    async sendMessage(userMessage: string, systemPrompt?: string): Promise<string> {
        this.isLoading.set(true);
        this.error.set(null);

        // Ajouter le message utilisateur à l'historique
        this.history.update(h => [...h, { role: 'user', content: userMessage }]);

        try {
            const response = await this.http.post<{ text: string }>('/api/ai/chat', {
                messages: this.history(),
                system: systemPrompt ?? 'Tu es un assistant Angular expert.'
            }).toPromise();

            const assistantText = response!.text;

            // Ajouter la réponse à l'historique
            this.history.update(h => [...h, { role: 'assistant', content: assistantText }]);

            return assistantText;
        } catch (err) {
            const msg = 'Erreur de communication avec l\'assistant IA';
            this.error.set(msg);
            throw new Error(msg);
        } finally {
            this.isLoading.set(false);
        }
    }

    clearHistory() { this.history.set([]); }
}

Gestion d'erreurs et rate limits

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

// Le SDK gère les retries automatiquement, mais voici la gestion manuelle
async function callWithRetry(fn: () => Promise<Anthropic.Message>, maxRetries = 3): Promise<Anthropic.Message> {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
        try {
            return await fn();
        } catch (error) {
            if (error instanceof Anthropic.RateLimitError) {
                // 429 — attendre avant de réessayer
                const waitMs = Math.pow(2, attempt) * 1000; // Backoff exponentiel
                console.warn(`Rate limit atteint, attente ${waitMs}ms...`);
                await new Promise(resolve => setTimeout(resolve, waitMs));
            } else if (error instanceof Anthropic.APIError) {
                // Erreurs non-retryables
                if (error.status === 401) throw new Error('Clé API invalide');
                if (error.status === 400) throw new Error(`Requête invalide: ${error.message}`);
                if (attempt === maxRetries) throw error;
            } else {
                throw error;
            }
        }
    }
    throw new Error('Nombre maximum de tentatives atteint');
}

// Gestion des types d'erreurs Anthropic
try {
    const response = await client.messages.create({ ... });
} catch (error) {
    if (error instanceof Anthropic.AuthenticationError) {
        console.error('401: Clé API invalide ou expirée');
    } else if (error instanceof Anthropic.RateLimitError) {
        console.error('429: Quota dépassé — attendre ou upgrader');
    } else if (error instanceof Anthropic.APIConnectionError) {
        console.error('Erreur réseau — vérifier la connexion');
    } else if (error instanceof Anthropic.APIError) {
        console.error(`Erreur API ${error.status}: ${error.message}`);
    }
}

Conclusion

L'API Claude via le SDK @anthropic-ai/sdk couvre tous les cas d'usage d'une application IA moderne. Points clés à retenir :

  • client.messages.create() — appel simple et conversations multi-tours
  • messages.stream() — streaming SSE pour l'UX temps réel
  • Tool Use : cycle demander → exécuter → renvoyer pour connecter Claude à des APIs externes
  • Vision : blocs image (base64 ou URL) pour l'analyse d'images
  • Ne jamais exposer la clé API côté client Angular — toujours via un backend Node.js
  • Gérer les RateLimitError avec backoff exponentiel

Partager