Claude Platform Docs
CLI, SDK e librerieSDK client

SDK Java

Installa e configura l'SDK Java di Anthropic con pattern builder e supporto asincrono

L'SDK Java di Anthropic fornisce un accesso comodo alla Claude API da applicazioni scritte in Java. Utilizza il "builder pattern" (pattern builder) per creare le richieste e supporta operazioni sia sincrone che asincrone.

Installazione

implementation("com.anthropic:anthropic-java:2.58.0")

Requisiti

Questa libreria richiede Java 8 o versioni successive.

Avvio rapido

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;

// Configura usando le proprietà di sistema `anthropic.apiKey`, `anthropic.authToken` e `anthropic.baseUrl`
// Oppure configura usando le variabili d'ambiente `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` e `ANTHROPIC_BASE_URL`
AnthropicClient client = AnthropicOkHttpClient.fromEnv();

MessageCreateParams params = MessageCreateParams.builder()
  .maxTokens(1024L)
  .addUserMessage("Hello, Claude")
  .model(Model.CLAUDE_OPUS_5)
  .build();

Message message = client.messages().create(params);

Configurazione del client

Configurazione della chiave API

Configura il client utilizzando proprietà di sistema o variabili d'ambiente:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;

// Configura usando le proprietà di sistema `anthropic.apiKey`, `anthropic.authToken` e `anthropic.baseUrl`
// Oppure configura usando le variabili d'ambiente `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` e `ANTHROPIC_BASE_URL`
AnthropicClient client = AnthropicOkHttpClient.fromEnv();

Oppure configuralo manualmente:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;

AnthropicClient client = AnthropicOkHttpClient.builder()
  .apiKey("my-anthropic-api-key")
  .build();

Oppure usa una combinazione di entrambi gli approcci:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;

AnthropicClient client = AnthropicOkHttpClient.builder()
  // Configura tramite proprietà di sistema o variabili d'ambiente
  .fromEnv()
  .apiKey("my-anthropic-api-key")
  .build();

Per le opzioni di autenticazione, inclusa la Workload Identity Federation, consulta Autenticazione. Se la tua chiave API è una chiave personale o di account di servizio con accesso a più workspace, imposta l'ID del workspace nell'header di richiesta anthropic-workspace-id; Seleziona un workspace mostra l'opzione per singola richiesta per questo SDK.

Opzioni di configurazione

SetterProprietà di sistemaVariabile d'ambienteObbligatorioValore predefinito
apiKeyanthropic.apiKeyANTHROPIC_API_KEYfalse-
authTokenanthropic.authTokenANTHROPIC_AUTH_TOKENfalse-
baseUrlanthropic.baseUrlANTHROPIC_BASE_URLtrue"https://api.anthropic.com"

Le proprietà di sistema hanno la precedenza sulle variabili d'ambiente.

Modifica della configurazione

Per utilizzare temporaneamente una configurazione del client modificata riutilizzando gli stessi pool di connessioni e di thread, chiama withOptions() su qualsiasi client o servizio:

import com.anthropic.client.AnthropicClient;

AnthropicClient clientWithOptions = client.withOptions(optionsBuilder -> {
  optionsBuilder.baseUrl("https://example.com");
  optionsBuilder.maxRetries(42);
});

Il metodo withOptions() non influisce sul client o servizio originale.

Utilizzo asincrono

Il client predefinito è sincrono. Per passare all'esecuzione asincrona, chiama il metodo async():

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;

AnthropicClient client = AnthropicOkHttpClient.fromEnv();

MessageCreateParams params = MessageCreateParams.builder()
  .maxTokens(1024L)
  .addUserMessage("Hello, Claude")
  .model(Model.CLAUDE_OPUS_5)
  .build();

CompletableFuture<Message> message = client.async().messages().create(params);

Oppure crea un client asincrono fin dall'inizio:

import com.anthropic.client.AnthropicClientAsync;
import com.anthropic.client.okhttp.AnthropicOkHttpClientAsync;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;

AnthropicClientAsync client = AnthropicOkHttpClientAsync.fromEnv();

MessageCreateParams params = MessageCreateParams.builder()
  .maxTokens(1024L)
  .addUserMessage("Hello, Claude")
  .model(Model.CLAUDE_OPUS_5)
  .build();

CompletableFuture<Message> message = client.messages().create(params);

Il client asincrono supporta le stesse opzioni di quello sincrono, tranne per il fatto che la maggior parte dei metodi restituisce dei CompletableFuture.

Streaming

L'SDK definisce metodi che restituiscono stream di "chunk" (frammenti) di risposta, in cui ogni chunk può essere elaborato singolarmente non appena arriva, invece di attendere la risposta completa.

Streaming sincrono

Questi metodi di streaming restituiscono StreamResponse per i client sincroni:

import com.anthropic.core.http.StreamResponse;
import com.anthropic.models.messages.RawMessageStreamEvent;

try (StreamResponse<RawMessageStreamEvent> streamResponse = client.messages().createStreaming(params)) {
    streamResponse.stream().forEach(chunk -> {
        IO.println(chunk);
    });
    IO.println("No more chunks!");
}

Streaming asincrono

Per i client asincroni, il metodo restituisce AsyncStreamResponse:

import com.anthropic.core.http.AsyncStreamResponse;
import com.anthropic.models.messages.RawMessageStreamEvent;

client.async().messages().createStreaming(params).subscribe(chunk -> {
    IO.println(chunk);
});

// Se devi gestire errori o il completamento dello stream
client.async().messages().createStreaming(params).subscribe(new AsyncStreamResponse.Handler<>() {
    @Override
    public void onNext(RawMessageStreamEvent chunk) {
        IO.println(chunk);
    }

    @Override
    public void onComplete(Optional<Throwable> error) {
        if (error.isPresent()) {
            IO.println("Something went wrong!");
            throw new RuntimeException(error.get());
        } else {
            IO.println("No more chunks!");
        }
    }
});

// Oppure usa i future
client.async().messages().createStreaming(params)
    .subscribe(chunk -> {
        IO.println(chunk);
    })
    .onCompleteFuture()
    .whenComplete((unused, error) -> {
        if (error != null) {
            IO.println("Something went wrong!");
            throw new RuntimeException(error);
        } else {
            IO.println("No more chunks!");
        }
    });

Lo streaming asincrono utilizza un Executor dedicato per client, basato su un cached thread pool, per eseguire lo streaming senza bloccare il thread corrente. Per usare un Executor diverso:

Executor executor = Executors.newFixedThreadPool(4);
client.async().messages().createStreaming(params).subscribe(
    chunk -> IO.println(chunk), executor
);

Oppure configura il client globalmente utilizzando il metodo streamHandlerExecutor:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;

AnthropicClient client = AnthropicOkHttpClient.builder()
  .fromEnv()
  .streamHandlerExecutor(Executors.newFixedThreadPool(4))
  .build();

Streaming con accumulatore di messaggi

Un MessageAccumulator può registrare lo stream di eventi nella risposta man mano che vengono elaborati e accumulare un oggetto Message simile a quello che sarebbe stato restituito dall'API non in streaming.

Per una risposta sincrona, aggiungi una chiamata Stream.peek() alla pipeline dello stream per accumulare ogni evento:

import com.anthropic.core.http.StreamResponse;
import com.anthropic.helpers.MessageAccumulator;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.RawMessageStreamEvent;

MessageAccumulator messageAccumulator = MessageAccumulator.create();

try (StreamResponse<RawMessageStreamEvent> streamResponse =
         client.messages().createStreaming(createParams)) {
    streamResponse.stream()
            .peek(messageAccumulator::accumulate)
            .flatMap(event -> event.contentBlockDelta().stream())
            .flatMap(deltaEvent -> deltaEvent.delta().text().stream())
            .forEach(textDelta -> IO.print(textDelta.text()));
}

Message message = messageAccumulator.message();

Per una risposta asincrona, aggiungi il MessageAccumulator alla chiamata subscribe():

import com.anthropic.helpers.MessageAccumulator;
import com.anthropic.models.messages.Message;

MessageAccumulator messageAccumulator = MessageAccumulator.create();

client.async().messages()
        .createStreaming(createParams)
        .subscribe(event -> messageAccumulator.accumulate(event).contentBlockDelta().stream()
                .flatMap(deltaEvent -> deltaEvent.delta().text().stream())
                .forEach(textDelta -> IO.print(textDelta.text())))
        .onCompleteFuture()
        .join();

Message message = messageAccumulator.message();

È disponibile anche un BetaMessageAccumulator per l'accumulo di un oggetto BetaMessage. Si utilizza nello stesso modo del MessageAccumulator.

Output strutturati

Per la documentazione completa sugli output strutturati, inclusi esempi Java, consulta Output strutturati.

Uso degli strumenti

L'uso degli strumenti con Claude ("tool use") ti consente di integrare strumenti e funzioni esterne direttamente nelle risposte del modello di IA. Invece di produrre testo semplice, il modello può generare istruzioni (con parametri) per chiamare uno strumento o una funzione quando appropriato. Tu definisci gli schemi JSON per gli strumenti e il modello utilizza gli schemi per determinare quando e come usare questi strumenti.

La funzionalità di uso degli strumenti supporta una modalità "strict" che garantisce che l'output JSON del modello di IA sia conforme allo schema JSON fornito nei parametri di input.

L'SDK può derivare automaticamente uno strumento e i suoi parametri dalla struttura di una classe Java arbitraria: il nome della classe (convertito in snake case) fornisce il nome dello strumento e i campi della classe definiscono i parametri dello strumento.

Definizione degli strumenti con annotazioni

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;

enum Unit {
  CELSIUS,
  FAHRENHEIT;

  public String toString() {
    return switch (this) {
      case CELSIUS -> "C";
      case FAHRENHEIT -> "F";
    };
  }

  public double fromKelvin(double temperatureK) {
    return switch (this) {
      case CELSIUS -> temperatureK - 273.15;
      case FAHRENHEIT -> (temperatureK - 273.15) * 1.8 + 32.0;
    };
  }
}

@JsonClassDescription("Get the weather in a given location")
static class GetWeather {

  @JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
  public String location;

  @JsonPropertyDescription("The unit of temperature")
  public Unit unit;

  public Weather execute() {
    double temperatureK = switch (location) {
      case "San Francisco, CA" -> 300.0;
      case "New York, NY" -> 310.0;
      case "Dallas, TX" -> 305.0;
      default -> 295;
    };
    return new Weather(String.format("%.0f%s", unit.fromKelvin(temperatureK), unit));
  }
}

static class Weather {

  public String temperature;

  public Weather(String temperature) {
    this.temperature = temperature;
  }
}

Chiamata degli strumenti

Una volta definite le classi degli strumenti, aggiungile ai parametri del messaggio utilizzando MessageCreateParams.Builder.addTool(Class<T>) e poi chiamale se richiesto nella risposta del modello di IA. BetaToolUseBlock.input(Class<T>) può essere utilizzato per convertire i parametri di uno strumento in formato JSON in un'istanza della classe che definisce lo strumento.

Dopo aver chiamato lo strumento, usa BetaToolResultBlockParam.Builder.contentAsJson(Object) per restituire il risultato dello strumento al modello di IA:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.messages.*;
import com.anthropic.models.messages.Model;

AnthropicClient client = AnthropicOkHttpClient.fromEnv();

MessageCreateParams.Builder createParamsBuilder = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_5)
        .maxTokens(2048)
        .addTool(GetWeather.class)
        .addUserMessage("What's the temperature in New York?");

client.beta().messages().create(createParamsBuilder.build()).content().stream()
        .flatMap(contentBlock -> contentBlock.toolUse().stream())
        .forEach(toolUseBlock -> createParamsBuilder
              // Aggiungi un messaggio che indica che è stato richiesto l'uso degli strumenti.
              .addAssistantMessageOfBetaContentBlockParams(
                      List.of(BetaContentBlockParam.ofToolUse(BetaToolUseBlockParam.builder()
                              .name(toolUseBlock.name())
                              .id(toolUseBlock.id())
                              .input(toolUseBlock._input())
                              .build())))
              // Aggiungi un messaggio con il risultato dell'uso degli strumenti richiesto.
              .addUserMessageOfBetaContentBlockParams(
                      List.of(BetaContentBlockParam.ofToolResult(BetaToolResultBlockParam.builder()
                              .toolUseId(toolUseBlock.id())
                              .contentAsJson(callTool(toolUseBlock))
                              .build()))));

client.beta().messages().create(createParamsBuilder.build()).content().stream()
        .flatMap(contentBlock -> contentBlock.text().stream())
        .forEach(textBlock -> IO.println(textBlock.text()));

private static Object callTool(BetaToolUseBlock toolUseBlock) {
  if (!"get_weather".equals(toolUseBlock.name())) {
    throw new IllegalArgumentException("Unknown tool: " + toolUseBlock.name());
  }

  GetWeather tool = toolUseBlock.input(GetWeather.class);
  return tool != null ? tool.execute() : new Weather("unknown");
}

Conversione dei nomi degli strumenti

I nomi degli strumenti sono derivati dai nomi delle classi degli strumenti in camel case (ad esempio, GetWeather) e convertiti in snake case (ad esempio, get_weather). I confini tra le parole iniziano dove il carattere corrente non è il primo carattere, è maiuscolo e il carattere precedente è minuscolo oppure il carattere successivo è minuscolo. Ad esempio, MyJSONParser diventa my_json_parser e ParseJSON diventa parse_json. Questa conversione può essere sovrascritta utilizzando l'annotazione @JsonTypeName.

Validazione locale dello schema JSON degli strumenti

Puoi eseguire una validazione locale per verificare che lo schema JSON derivato dalla tua classe di strumento rispetti le restrizioni di Anthropic. La validazione locale è abilitata per impostazione predefinita, ma può essere disabilitata:

MessageCreateParams.Builder createParamsBuilder = MessageCreateParams.builder()
  .model(Model.CLAUDE_OPUS_5)
  .maxTokens(2048)
  .addTool(GetWeather.class, JsonSchemaLocalValidation.NO)
  .addUserMessage("What's the temperature in New York?");

Annotazione delle classi degli strumenti

Puoi usare le annotazioni per aggiungere ulteriori informazioni sugli strumenti agli schemi JSON:

  • @JsonClassDescription - Aggiunge una descrizione a una classe di strumento che specifica quando e come usare quello strumento.
  • @JsonTypeName - Imposta il nome dello strumento su un valore diverso dal nome semplice della classe convertito in snake case.
  • @JsonPropertyDescription - Aggiunge una descrizione dettagliata a un parametro dello strumento.
  • @JsonIgnore - Esclude un campo public o un metodo getter dallo schema JSON generato per i parametri di uno strumento.
  • @JsonProperty - Include un campo o un metodo getter non public nello schema JSON generato per i parametri di uno strumento.

Batch di messaggi

L'SDK fornisce supporto per l'elaborazione batch sotto il namespace client.messages().batches(). Consulta Paginazione per sapere come elencare e paginare i batch.

Caricamento di file

L'SDK definisce metodi che accettano file tramite la classe MultipartField:

import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;

FileUploadParams params = FileUploadParams.builder()
  .file(
    MultipartField.<InputStream>builder()
      .value(Files.newInputStream(Paths.get("/path/to/file.pdf")))
      .contentType("application/pdf")
      .build()
  )
  .build();

FileMetadata fileMetadata = client.files().upload(params);

Oppure da un InputStream:

import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;

FileUploadParams params = FileUploadParams.builder()
  .file(
    MultipartField.<InputStream>builder()
      .value(URI.create("https://example.com/path/to/file").toURL().openStream())
      .filename("document.pdf")
      .contentType("application/pdf")
      .build()
  )
  .build();

FileMetadata fileMetadata = client.files().upload(params);

Oppure da byte in memoria:

import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;

FileUploadParams params = FileUploadParams.builder()
  .file(
    MultipartField.<InputStream>builder()
      .value(new ByteArrayInputStream("content".getBytes()))
      .filename("document.txt")
      .contentType("text/plain")
      .build()
  )
  .build();

FileMetadata fileMetadata = client.files().upload(params);

Risposte binarie

L'SDK definisce metodi che restituiscono risposte binarie per le risposte API che non vengono necessariamente interpretate come JSON:

import com.anthropic.core.http.HttpResponse;

HttpResponse response = client.files().download("file_abc123");

Per salvare il contenuto della risposta in un file:

import com.anthropic.core.http.HttpResponse;

try (HttpResponse response = client.files().download(params)) {
    Files.copy(
        response.body(),
        Paths.get(path),
        StandardCopyOption.REPLACE_EXISTING
    );
} catch (Exception e) {
    IO.println("Something went wrong!");
    throw new RuntimeException(e);
}

Oppure trasferisci il contenuto della risposta a qualsiasi OutputStream:

import com.anthropic.core.http.HttpResponse;

try (HttpResponse response = client.files().download(params)) {
    response.body().transferTo(Files.newOutputStream(Paths.get(path)));
} catch (Exception e) {
    IO.println("Something went wrong!");
    throw new RuntimeException(e);
}

Gestione degli errori

L'SDK lancia tipi di eccezioni unchecked personalizzate:

  • AnthropicServiceException - Classe base per gli errori HTTP.
  • AnthropicIoException - Errori di rete I/O.
  • AnthropicRetryableException - Errore generico che indica un fallimento che potrebbe essere ritentato.
  • AnthropicInvalidDataException - Impossibilità di interpretare dati analizzati con successo (ad esempio, quando si accede a una proprietà che dovrebbe essere obbligatoria, ma che l'API ha inaspettatamente omesso).
  • AnthropicException - Classe base per tutte le eccezioni.

Mappatura dei codici di stato

StatoEccezione
400BadRequestException
401UnauthorizedException
403PermissionDeniedException
404NotFoundException
422UnprocessableEntityException
429RateLimitException
5xxInternalServerException
altriUnexpectedStatusCodeException

SseException viene lanciata per gli errori riscontrati durante lo streaming SSE dopo una risposta HTTP iniziale riuscita.

import com.anthropic.errors.*;

try {
    Message message = client.messages().create(params);
} catch (RateLimitException e) {
    IO.println("Rate limited, retry after: " + e.headers());
} catch (UnauthorizedException e) {
    IO.println("Invalid API key");
} catch (AnthropicServiceException e) {
    IO.println("API error: " + e.statusCode());
} catch (AnthropicIoException e) {
    IO.println("Network error: " + e.getMessage());
}

ID delle richieste

Quando utilizzi le risposte raw, puoi accedere all'header di risposta request-id utilizzando il metodo requestId():

import com.anthropic.core.http.HttpResponseFor;
import com.anthropic.models.messages.Message;

HttpResponseFor<Message> message = client.messages().withRawResponse().create(params);

Optional<String> requestId = message.requestId();

Questo può essere utilizzato per registrare rapidamente le richieste non riuscite e segnalarle ad Anthropic. Per ulteriori informazioni sul debug delle richieste, consulta ID della richiesta.

Tentativi ripetuti

L'SDK ritenta automaticamente 2 volte per impostazione predefinita, con un breve backoff esponenziale tra le richieste.

Vengono ritentati solo i seguenti tipi di errore:

  • Errori di connessione (ad esempio, a causa di un problema di connettività di rete)
  • 408 Request Timeout
  • 409 Conflict
  • 429 Rate Limit
  • 5xx Internal

L'API può anche istruire esplicitamente l'SDK a ritentare o non ritentare una richiesta.

Per impostare un numero personalizzato di tentativi, configura il client utilizzando il metodo maxRetries:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;

AnthropicClient client = AnthropicOkHttpClient.builder().fromEnv().maxRetries(4).build();

Timeout

Le richieste vanno in timeout dopo 10 minuti per impostazione predefinita.

Tuttavia, per i metodi che accettano maxTokens, se specifichi un valore maxTokens elevato e stai usando lo streaming, il timeout predefinito verrà calcolato dinamicamente utilizzando questa formula:

Duration.ofSeconds(
    Math.min(
        60 * 60, // 1 hour max
        Math.max(
            10 * 60, // 10 minute minimum
            60 * 60 * maxTokens / 128_000
        )
    )
)

Questo si traduce in un timeout fino a 60 minuti, scalato in base al parametro maxTokens, a meno che non venga sovrascritto.

Per le richieste non in streaming, il timeout dinamico scala da un minimo di 30 secondi fino a un massimo di 10 minuti in base a maxTokens.

Per impostare un timeout personalizzato per singola richiesta:

import com.anthropic.models.messages.Message;

Message message = client
  .messages()
  .create(params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build());

Oppure configura il valore predefinito per tutte le chiamate ai metodi a livello di client:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;

AnthropicClient client = AnthropicOkHttpClient.builder()
  .fromEnv()
  .timeout(Duration.ofSeconds(30))
  .build();

Richieste lunghe

Evita di impostare un valore maxTokens elevato senza usare lo streaming. Alcune reti potrebbero interrompere le connessioni inattive dopo un certo periodo di tempo, il che può causare il fallimento della richiesta o un timeout senza ricevere una risposta da Anthropic. L'SDK invia periodicamente ping all'API per mantenere attiva la connessione e ridurre l'impatto di queste reti.

L'SDK lancia un errore se si prevede che una richiesta non in streaming richieda più di 10 minuti. L'uso di un metodo di streaming o la sovrascrittura del timeout a livello di client o di richiesta disabilita l'errore.

Paginazione

L'SDK fornisce modi comodi per accedere ai risultati paginati, una pagina alla volta oppure elemento per elemento attraverso tutte le pagine.

Paginazione automatica

Per iterare su tutti i risultati di tutte le pagine, usa il metodo autoPager(), che recupera automaticamente altre pagine secondo necessità.

import com.anthropic.models.messages.batches.BatchListPage;
import com.anthropic.models.messages.batches.MessageBatch;

BatchListPage page = client.messages().batches().list();

// Elabora come Iterable
for (MessageBatch batch : page.autoPager()) {
    IO.println(batch);
}

// Elabora come Stream
page.autoPager()
    .stream()
    .limit(50)
    .forEach(batch -> IO.println(batch));

Quando si utilizza il client asincrono, il metodo restituisce un AsyncStreamResponse:

import com.anthropic.core.http.AsyncStreamResponse;
import com.anthropic.models.messages.batches.BatchListPageAsync;
import com.anthropic.models.messages.batches.MessageBatch;

CompletableFuture<BatchListPageAsync> pageFuture = client.async().messages().batches().list();

pageFuture.thenAccept(page -> page.autoPager().subscribe(batch -> {
    IO.println(batch);
}));

// Se devi gestire errori o il completamento dello stream
pageFuture.thenAccept(page -> page.autoPager().subscribe(new AsyncStreamResponse.Handler<>() {
    @Override
    public void onNext(MessageBatch batch) {
        IO.println(batch);
    }

    @Override
    public void onComplete(Optional<Throwable> error) {
        if (error.isPresent()) {
            IO.println("Something went wrong!");
            throw new RuntimeException(error.get());
        } else {
            IO.println("No more!");
        }
    }
}));

// Oppure usa i future
pageFuture.thenAccept(page -> page.autoPager()
    .subscribe(batch -> {
        IO.println(batch);
    })
    .onCompleteFuture()
    .whenComplete((unused, error) -> {
        if (error != null) {
            IO.println("Something went wrong!");
            throw new RuntimeException(error);
        } else {
            IO.println("No more!");
        }
    }));

Paginazione manuale

Per accedere ai singoli elementi della pagina e richiedere manualmente la pagina successiva:

import com.anthropic.models.messages.batches.BatchListPage;
import com.anthropic.models.messages.batches.MessageBatch;

BatchListPage page = client.messages().batches().list();
while (true) {
    for (MessageBatch batch : page.items()) {
        IO.println(batch);
    }

    if (!page.hasNextPage()) {
        break;
    }

    page = page.nextPage();
}

Sistema dei tipi

Immutabilità e builder

Ogni classe nell'SDK ha un builder associato per costruirla. Ogni classe è immutabile una volta costruita. Se la classe ha un builder associato, allora dispone di un metodo toBuilder(), che può essere utilizzato per riconvertirla in un builder per creare una copia modificata.

MessageCreateParams params = MessageCreateParams.builder()
  .maxTokens(1024L)
  .addUserMessage("Hello, Claude")
  .model(Model.CLAUDE_OPUS_5)
  .build();

// Crea una copia modificata usando toBuilder()
MessageCreateParams modified = params.toBuilder().maxTokens(2048L).build();

Poiché ogni classe è immutabile, la modifica del builder non influisce mai sulle istanze di classe già costruite.

Richieste e risposte

Per inviare una richiesta alla Claude API, costruisci un'istanza di una classe Params e passala al metodo del client corrispondente. Quando la risposta viene ricevuta, viene deserializzata in un'istanza di una classe Java.

Ad esempio, client.messages().create(...) deve essere chiamato con un'istanza di MessageCreateParams e restituisce un'istanza di Message.

Parametri non documentati

Per impostare parametri non documentati, chiama i metodi putAdditionalHeader, putAdditionalQueryParam o putAdditionalBodyProperty su qualsiasi classe Params:

import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;

MessageCreateParams params = MessageCreateParams.builder()
  .putAdditionalHeader("Secret-Header", "42")
  .putAdditionalQueryParam("secret_query_param", "42")
  .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
  .build();

È possibile accedervi successivamente sull'oggetto costruito utilizzando i metodi _additionalHeaders(), _additionalQueryParams() e _additionalBodyProperties().

Per impostare parametri non documentati su header, parametri di query o classi del body annidati:

import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Metadata;

MessageCreateParams params = MessageCreateParams.builder()
  .metadata(
    Metadata.builder().putAdditionalProperty("secretProperty", JsonValue.from("42")).build()
  )
  .build();

È possibile accedere a queste proprietà successivamente sull'oggetto annidato costruito utilizzando il metodo _additionalProperties().

Per impostare un parametro o una proprietà documentata su un valore non documentato o non ancora supportato, passa un oggetto JsonValue al suo setter:

import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;

MessageCreateParams params = MessageCreateParams.builder()
  .maxTokens(JsonValue.from(3.14))
  .addUserMessage("Hello, Claude")
  .model(Model.CLAUDE_OPUS_5)
  .build();

Creazione di JsonValue

Il modo più diretto per creare un JsonValue è utilizzare il suo metodo from(...):

import com.anthropic.core.JsonValue;

// Crea valori JSON primitivi
JsonValue nullValue = JsonValue.from(null);

JsonValue booleanValue = JsonValue.from(true);

JsonValue numberValue = JsonValue.from(42);

JsonValue stringValue = JsonValue.from("Hello World!");

// Crea un valore array JSON equivalente a `["Hello", "World"]`
JsonValue arrayValue = JsonValue.from(List.of("Hello", "World"));

// Crea un valore oggetto JSON equivalente a `{ "a": 1, "b": 2 }`
JsonValue objectValue = JsonValue.from(Map.of("a", 1, "b", 2));

// Crea un JSON annidato arbitrariamente equivalente a:
// { "a": [1, 2], "b": [3, 4] }
JsonValue complexValue = JsonValue.from(Map.of("a", List.of(1, 2), "b", List.of(3, 4)));

Omissione forzata dei parametri obbligatori

Normalmente il metodo build di una classe Builder lancia IllegalStateException se un parametro o una proprietà obbligatoria non è impostata. Per omettere forzatamente un parametro o una proprietà obbligatoria, passa JsonMissing:

import com.anthropic.core.JsonMissing;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;

MessageCreateParams params = MessageCreateParams.builder()
  .addUserMessage("Hello, world")
  .model(Model.CLAUDE_OPUS_5)
  .maxTokens(JsonMissing.of())
  .build();

Proprietà della risposta

Per accedere alle proprietà non documentate della risposta, chiama il metodo _additionalProperties():

import com.anthropic.core.JsonValue;

Map<String, JsonValue> additionalProperties = client
  .messages()
  .create(params)
  ._additionalProperties();

JsonValue secretPropertyValue = additionalProperties.get("secretProperty");

String result = secretPropertyValue.accept(new JsonValue.Visitor<>() {
    @Override
    public String visitNull() {
        return "It's null!";
    }

    @Override
    public String visitBoolean(boolean value) {
        return "It's a boolean!";
    }

    @Override
    public String visitNumber(Number value) {
        return "It's a number!";
    }

    // Altri metodi includono `visitMissing`, `visitString`, `visitArray` e `visitObject`
    // L'implementazione predefinita di ogni metodo non implementato delega a `visitDefault`,
    // che per impostazione predefinita lancia un'eccezione, ma può anche essere sovrascritto
});

Per accedere al valore JSON raw di una proprietà, chiama il suo metodo con prefisso _:

import com.anthropic.core.JsonField;
import com.anthropic.models.messages.StopReason;

JsonField<StopReason> stopReason = client.messages().create(params)._stopReason();

if (stopReason.isMissing()) {
  // La proprietà è assente dalla risposta JSON
} else if (stopReason.isNull()) {
  // La proprietà è stata impostata al valore letterale null
} else {
  // Verifica se il valore è stato fornito come stringa
  // Altri metodi includono `asNumber()`, `asBoolean()`, ecc.
  Optional<String> jsonString = stopReason.asString();

  // Prova a deserializzare in un tipo personalizzato
  MyClass myObject = stopReason.asUnknown().orElseThrow().convert(MyClass.class);
}

Validazione della risposta

Per impostazione predefinita, l'SDK non lancia un'eccezione quando l'API restituisce una risposta che non corrisponde al tipo previsto. Lancia AnthropicInvalidDataException solo se accedi direttamente alla proprietà.

Per verificare in anticipo che la risposta sia completamente ben tipizzata, chiama validate():

import com.anthropic.models.messages.Message;

Message message = client.messages().create(params).validate();

Oppure configura per singola richiesta:

import com.anthropic.models.messages.Message;

Message message = client
  .messages()
  .create(params, RequestOptions.builder().responseValidation(true).build());

Oppure configura il valore predefinito per tutte le chiamate ai metodi a livello di client:

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;

AnthropicClient client = AnthropicOkHttpClient.builder()
  .fromEnv()
  .responseValidation(true)
  .build();

Personalizzazione del client HTTP

Configurazione del proxy

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import java.net.Proxy;

AnthropicClient client = AnthropicOkHttpClient.builder()
  .fromEnv()
  .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("https://example.com", 8080)))
  .build();

Configurazione HTTPS / SSL

import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;

AnthropicClient client = AnthropicOkHttpClient.builder()
  .fromEnv()
  .sslSocketFactory(yourSSLSocketFactory)
  .trustManager(yourTrustManager)
  .hostnameVerifier(yourHostnameVerifier)
  .build();

Client HTTP personalizzato

L'SDK è composto da tre artefatti:

  • anthropic-java-core - Contiene la logica principale dell'SDK, non dipende da OkHttp. Espone AnthropicClient, AnthropicClientAsync e le relative classi di implementazione, tutte in grado di funzionare con qualsiasi client HTTP.
  • anthropic-java-client-okhttp - Dipende da OkHttp. Espone AnthropicOkHttpClient e AnthropicOkHttpClientAsync.
  • anthropic-java - Dipende da ed espone le API sia di anthropic-java-core che di anthropic-java-client-okhttp. Non ha una logica propria.

Questa struttura consente di sostituire il client HTTP predefinito dell'SDK senza includere dipendenze non necessarie.

OkHttpClient personalizzato

Per usare un OkHttpClient personalizzato:

  1. Sostituisci la dipendenza anthropic-java con anthropic-java-core.
  2. Copia la classe OkHttpClient di anthropic-java-client-okhttp nel tuo codice e personalizzala.
  3. Costruisci AnthropicClientImpl o AnthropicClientAsyncImpl utilizzando il tuo client personalizzato.

Client HTTP completamente personalizzato

Per usare un client HTTP completamente personalizzato:

  1. Sostituisci la dipendenza anthropic-java con anthropic-java-core.
  2. Scrivi una classe che implementi l'interfaccia HttpClient.
  3. Costruisci AnthropicClientImpl o AnthropicClientAsyncImpl utilizzando la tua nuova classe client.

Integrazioni con le piattaforme

L'SDK Java supporta le seguenti piattaforme tramite dipendenze separate che forniscono implementazioni Backend specifiche per piattaforma:

  • Agent Platform: com.anthropic:anthropic-java-vertex: Usa VertexBackend.fromEnv() o VertexBackend.builder().
  • Bedrock: com.anthropic:anthropic-java-bedrock: Usa BedrockMantleBackend.fromEnv() o BedrockMantleBackend.builder() per l'endpoint Bedrock della Messages API, oppure BedrockBackend.fromEnv() / BedrockBackend.builder() (percorso bedrock-runtime).
  • Claude Platform on AWS: com.anthropic:anthropic-java-aws: Usa AwsBackend.fromEnv() (legge ANTHROPIC_AWS_WORKSPACE_ID e la catena predefinita di regione/credenziali AWS) o AwsBackend.builder(). Disponibile in beta.
  • Foundry: com.anthropic:anthropic-java-foundry: Usa FoundryBackend.fromEnv() o FoundryBackend.builder().

Usa BedrockMantleBackend per i nuovi progetti; BedrockBackend rimane disponibile per le applicazioni esistenti che utilizzano l'API InvokeModel di Bedrock.

Ogni implementazione Backend viene passata al client con .backend() su AnthropicOkHttpClient.builder(). Ogni backend cloud include le classi dell'SDK della rispettiva piattaforma cloud come dipendenze transitive.

Utilizzo avanzato

Accesso alla risposta raw

Per accedere agli header HTTP, ai codici di stato e al body raw della risposta, anteponi withRawResponse() a qualsiasi chiamata di metodo HTTP:

import com.anthropic.core.http.Headers;
import com.anthropic.core.http.HttpResponseFor;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;

MessageCreateParams params = MessageCreateParams.builder()
  .maxTokens(1024L)
  .addUserMessage("Hello, Claude")
  .model(Model.CLAUDE_OPUS_5)
  .build();

HttpResponseFor<Message> message = client.messages().withRawResponse().create(params);

int statusCode = message.statusCode();

Headers headers = message.headers();

Puoi comunque deserializzare la risposta in un'istanza di una classe Java se necessario:

import com.anthropic.models.messages.Message;

Message parsedMessage = message.parse();

Logging

L'SDK utilizza l'interceptor di logging standard di OkHttp.

Abilita il logging impostando la variabile d'ambiente ANTHROPIC_LOG su info:

export ANTHROPIC_LOG=info

Oppure su debug per un logging più dettagliato:

export ANTHROPIC_LOG=debug

Funzionalità API non documentate

L'SDK è tipizzato per un utilizzo comodo dell'API documentata. Tuttavia, supporta anche l'utilizzo di parti dell'API non documentate o non ancora supportate.

Parametri di richiesta non documentati

Per impostare parametri di richiesta non documentati, usa i metodi putAdditionalHeader, putAdditionalQueryParam o putAdditionalBodyProperty come descritto in Parametri non documentati.

Proprietà di risposta non documentate

Per accedere alle proprietà di risposta non documentate, usa il metodo _additionalProperties() come descritto in Proprietà della risposta.

Valori enum nuovi o non ancora rilasciati

Le classi simili a enum nell'SDK, come Model e AnthropicBeta, non sono tipi enum Java chiusi. Ognuna fornisce un metodo factory of(String) che accetta qualsiasi stringa, così puoi usare valori che non sono ancora stati aggiunti all'SDK, come un modello o un header beta rilasciato dopo la tua versione dell'SDK:

import com.anthropic.models.beta.AnthropicBeta;
import com.anthropic.models.messages.Model;

Model model = Model.of("some-new-model");
AnthropicBeta beta = AnthropicBeta.of("some-new-beta-2026-01-01");

I metodi builder che accettano questi tipi spesso forniscono anche un overload String che chiama of(...) per te:

import com.anthropic.models.messages.MessageCreateParams;

MessageCreateParams params = MessageCreateParams.builder()
  .model("some-new-model") // same as .model(Model.of("some-new-model"))
  .maxTokens(1024L)
  .addUserMessage("Hello, Claude")
  .build();

Preferisci le costanti ben tipizzate (ad esempio, Model.CLAUDE_OPUS_5) in modo da ottenere il completamento automatico e gli avvisi di deprecazione. Gli overload String e of(...) servono principalmente per impostare il campo su un valore non documentato o non ancora supportato in attesa di una versione dell'SDK che lo includa.

Funzionalità beta

Le funzionalità beta sono disponibili prima del rilascio generale per raccogliere feedback anticipati e testare nuove funzionalità. Puoi verificare la disponibilità di tutte le capacità e gli strumenti di Claude nella panoramica su come costruire con Claude.

Puoi accedere alla maggior parte delle funzionalità API beta tramite il metodo beta() sul client. Per abilitare una particolare funzionalità beta, aggiungi l'header beta appropriato con .addBeta() durante la costruzione dei parametri del messaggio.

Ad esempio, per abilitare la modifica del contesto:

import com.anthropic.models.beta.AnthropicBeta;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.MessageCreateParams;

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    BetaMessage message = client.beta().messages().create(
        MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_5)
            .maxTokens(1024L)
            .addBeta(AnthropicBeta.CONTEXT_MANAGEMENT_2025_06_27)
            .addUserMessage("Hello, Claude")
            .build());
}

Domande frequenti

Versionamento semantico

Questo pacchetto segue generalmente le convenzioni SemVer, anche se alcune modifiche non retrocompatibili potrebbero essere rilasciate come versioni minori:

  1. Modifiche agli elementi interni della libreria che sono tecnicamente pubblici ma non destinati o documentati per l'uso esterno.
  2. Modifiche che non si prevede abbiano un impatto sulla grande maggioranza degli utenti nella pratica.

Risorse aggiuntive

Was this page helpful?