Spring AI の ChatClient は、LLM(大規模言語モデル)を Spring Boot から呼ぶための API です。 prompt() で問い合わせを組み立て、call() か stream() で送り、content()(文字列)や entity()(Java のクラス)で受け取ります。

String answer = chatClient.prompt()
        .user("Spring AIとは何ですか")
        .call()
        .content();

2026年10月時点の最新の安定版は Spring AI 2.0.1 です。2.0 では対応する Spring Boot が 4.0・4.1 になり、OpenAI との通信やツール呼び出しの仕組みが 1.x から変わりました。

やりたいこと 書き方 戻り値
文字列で受け取る .call().content() String
少しずつ受け取る(ストリーミング) .stream().content() Flux<String>
Java のクラスで受け取る(構造化出力) .call().entity(Summary.class) Summary
トークン数も見る .call().responseEntity(Summary.class) ResponseEntity<ChatResponse, Summary>
アプリのメソッドを呼ばせる(ツール呼び出し) .tools(new WeatherTools()) ―

確認環境:Spring AI 2.0.1(GitHub のタグ v2.0.1 のソースとリファレンス)、Spring Boot 4.1.1、Java 17 以上。2.0.1 の OpenAI 連携は openai-java 4.49.0 を使う構成です。OpenAI に送られる JSON は、Spring AI のソースから組み立て、OpenAI の OpenAPI 仕様(openai-openapi、info.version 2.3.0)に jsonschema 4.26.0 で照らして、形が仕様どおりであることを確かめました(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Spring AI入門|ChatClientの使い方とコード全文(2.0対応)」

Spring AI 2.0 で変わったこと(1.x との違い)

Spring AI は、1.x と 2.0 で書き方が変わった部分があります。2.0 では次の点が変わりました。 1.1.8 と 2.0.1 のソースとアップグレードノートで確かめた内容です。

項目 1.1.8 2.0.1
対応する Spring Boot 3.x 4.0.x・4.1.x
OpenAI との通信 Spring AI 独自のクライアント OpenAI 公式の openai-java(2.0.0-M5 から)
base-url の既定値 https://api.openai.com(パス /v1/chat/completions を足す) https://api.openai.com/v1(/v1 まで含む)
既定のモデル gpt-4o-mini gpt-5-mini
temperature の既定値 0.7 なし(指定しなければ送らない)
ツール呼び出しの繰り返し 各 ChatModel の中で実行 ChatClient の ToolCallingAdvisor が実行
Function の Bean を toolNames() で指定 使えた 削除。ToolCallback の Bean にする
モデル設定のプロパティ spring.ai.openai.chat.options.model spring.ai.openai.chat.model(.options を外す)
ツールの strict モード(OpenAI) ― 2.0.1 で既定が false

特に base-url の意味が変わった点に注意します。Ollama などの OpenAI 互換サーバーにつなぐときは、http://localhost:11434/v1 のように /v1 まで書きます(2.0.1 の OpenAiSetup は、指定された base-url をそのまま使います)。

1. 依存関係(pom.xml・build.gradle.kts)

spring-ai-starter-model-openai と、版をそろえる BOM(spring-ai-bom)を入れます。 ストリーミング(stream())を使う場合、リファレンスには「Servlet のアプリでも Reactive のスタックを入れる(例:spring-boot-starter-webflux)」とあります。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>4.1.1</version>
    <relativePath/>
  </parent>

  <groupId>com.example</groupId>
  <artifactId>ai-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <properties>
    <java.version>21</java.version>
    <spring-ai.version>2.0.1</spring-ai.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webmvc</artifactId>
    </dependency>
    <!-- stream() を使うとき -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-model-openai</artifactId>
    </dependency>
  </dependencies>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-bom</artifactId>
        <version>${spring-ai.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

Gradle(Kotlin DSL)の場合です。

plugins {
    java
    id("org.springframework.boot") version "4.1.1"
    id("io.spring.dependency-management") version "1.1.7"
}

java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }

repositories { mavenCentral() }

dependencies {
    implementation(platform("org.springframework.ai:spring-ai-bom:2.0.1"))
    implementation("org.springframework.boot:spring-boot-starter-webmvc")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("org.springframework.ai:spring-ai-starter-model-openai")
}

Spring Boot 4 の starter の名前(spring-boot-starter-web → spring-boot-starter-webmvc など)は、関連記事「Spring Boot starter一覧|用途別の選び方とBoot 4の新旧対応表」で扱っています。

2. application.yml

API キーは環境変数から読みます。 OPENAI_API_KEY という名前の環境変数があれば、プロパティを書かなくても使われます(2.0.1 の OpenAiSetup の処理)。

spring:
  application:
    name: ai-demo
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      # OpenAI 互換サーバー(Ollama など)につなぐときは /v1 まで書く
      # base-url: http://localhost:11434/v1
      timeout: 60s          # 既定 60 秒
      max-retries: 3        # 既定 3 回
      chat:
        model: gpt-5-mini   # 既定値も gpt-5-mini
        # max-completion-tokens: 2000
    chat:
      client:
        tool-calling:
          enabled: true     # 既定 true。ToolCallingAdvisor を自動で入れる
    tools:
      limits:
        max-calls-per-tool-default: 40   # 1回の問い合わせで同じツールを呼べる回数(既定 40)
        max-total-tool-calls: 150        # ツール呼び出しの合計(既定 150)

logging:
  level:
    # SimpleLoggerAdvisor の request / response を出す
    org.springframework.ai.chat.client.advisor: DEBUG

よく使うプロパティです(リファレンスとソースの既定値)。

プロパティ 既定値 内容
spring.ai.openai.api-key なし(環境変数 OPENAI_API_KEY) API キー
spring.ai.openai.base-url https://api.openai.com/v1 接続先。環境変数 OPENAI_BASE_URL も読む
spring.ai.openai.timeout 60 秒 リクエストのタイムアウト
spring.ai.openai.max-retries 3 openai-java のリトライ回数
spring.ai.openai.chat.model gpt-5-mini モデル名
spring.ai.openai.chat.temperature なし gpt-5 系では使えない(リファレンスの注記)
spring.ai.openai.chat.max-tokens なし 推論モデル以外の出力上限
spring.ai.openai.chat.max-completion-tokens なし 推論モデルの出力上限。max-tokens と同時には使えない
spring.ai.model.chat openai none にするとチャットの自動設定を止める

gpt-5・gpt-5-mini・gpt-5-nano では temperature を指定するとエラーになると、リファレンスに書かれています。1.x 向けの設定例にある temperature: 0.7 を gpt-5 系で使うときは、この行を外します。

Spring Boot の設定ファイル全般は、関連記事「Spring Boot application.properties設定一覧」で扱っています。

3. ChatClient を Bean にする

自動設定で ChatClient.Builder が用意されるので、それに共通の設定を足して ChatClient を作ります。 Builder は prototype スコープ(注入するたびに新しいもの)なので、用途の違う ChatClient を複数作れます。

package com.example.aidemo;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AiConfig {

    @Bean
    ChatClient chatClient(ChatClient.Builder builder) {
        return builder
                .defaultSystem("あなたはJavaとSpringに詳しいアシスタントです。日本語で、短く答えます。")
                .defaultAdvisors(new SimpleLoggerAdvisor())   // request / response をDEBUGで出す
                .build();
    }
}
Builder のメソッド 内容
defaultSystem(...) すべての問い合わせに付けるシステムメッセージ
defaultUser(...) 既定のユーザーメッセージ
defaultOptions(...) モデル名などの既定オプション
defaultTools(...) すべての問い合わせで使えるツール
defaultAdvisors(...) ログ・会話履歴などの Advisor(前後に処理を挟む部品)

4. コード全文:Controller・構造化出力・ツール

構造化出力の受け皿(record)

package com.example.aidemo;

import java.util.List;

/** 記事の要約。LLM の返す JSON をこの形に変換する */
public record Summary(String title, List<String> keywords, int level) {
}

ツール(@Tool)

package com.example.aidemo;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

@Component
public class WeatherTools {

    @Tool(description = "指定した都市の現在の天気を返す")
    public String getWeather(@ToolParam(description = "都市名(例:東京)") String city) {
        // 本来は天気APIやDBを呼ぶ。ここでは固定値
        return city + "は晴れ、気温22度";
    }
}

Controller

package com.example.aidemo;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.ResponseEntity;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.Map;

@RestController
@RequestMapping("/api")
public class ChatController {

    private final ChatClient chatClient;
    private final WeatherTools weatherTools;

    public ChatController(ChatClient chatClient, WeatherTools weatherTools) {
        this.chatClient = chatClient;
        this.weatherTools = weatherTools;
    }

    // ① 文字列で受け取る
    @GetMapping("/chat")
    public String chat(@RequestParam String q) {
        return chatClient.prompt()
                .user(q)
                .call()
                .content();
    }

    // ② ストリーミング(Server-Sent Events で少しずつ返す)
    @GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream(@RequestParam String q) {
        return chatClient.prompt()
                .user(q)
                .stream()
                .content();
    }

    // ③ テンプレートと変数
    @GetMapping("/explain")
    public String explain(@RequestParam String topic) {
        return chatClient.prompt()
                .user(u -> u.text("{topic} を初心者向けに3行で説明して").param("topic", topic))
                .call()
                .content();
    }

    // ④ 構造化出力:Summary に変換して返す
    @PostMapping("/summary")
    public Summary summary(@RequestBody Map<String, String> body) {
        return chatClient.prompt()
                .user("次の文章を要約して: " + body.get("text"))
                .call()
                .entity(Summary.class);
    }

    // ④' 構造化出力+トークン数
    @PostMapping("/summary/usage")
    public Map<String, Object> summaryWithUsage(@RequestBody Map<String, String> body) {
        ResponseEntity<ChatResponse, Summary> result = chatClient.prompt()
                .user("次の文章を要約して: " + body.get("text"))
                .call()
                .responseEntity(Summary.class);
        return Map.of(
                "summary", result.entity(),
                "totalTokens", result.response().getMetadata().getUsage().getTotalTokens());
    }

    // ⑤ ツール呼び出し:必要ならモデルが getWeather を呼ぶ
    @GetMapping("/weather")
    public String weather(@RequestParam String q) {
        return chatClient.prompt()
                .user(q)
                .tools(weatherTools)
                .call()
                .content();
    }
}
package com.example.aidemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AiDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(AiDemoApplication.class, args);
    }
}

呼び出し例です。

export OPENAI_API_KEY=sk-...
./mvnw spring-boot:run

curl "http://localhost:8080/api/chat?q=Spring%20AI%E3%81%A8%E3%81%AF"
curl -N "http://localhost:8080/api/chat/stream?q=Java%E3%81%AE%E6%AD%B4%E5%8F%B2"
curl -X POST http://localhost:8080/api/summary \
     -H "Content-Type: application/json" -d '{"text":"Spring AIはSpringでLLMを扱うためのライブラリで..."}'
curl "http://localhost:8080/api/weather?q=%E6%9D%B1%E4%BA%AC%E3%81%AE%E5%A4%A9%E6%B0%97%E3%81%AF"

call() を呼んだ時点ではまだ送られず、content() や entity() を呼んだときに送られます(リファレンスの説明)。

5. 実際に送られる HTTP リクエスト

2.0.1 の OpenAI 連携は、Chat Completions API(POST {base-url}/chat/completions)を呼びます。 OpenAiChatModel のソースでは、値が設定された項目だけを JSON に入れています。

POST https://api.openai.com/v1/chat/completions
Authorization: Bearer sk-...
Content-Type: application/json
User-Agent: spring-ai-openai

User-Agent は、ソースでは spring-ai-openai が固定で入ります(リファレンスには spring-ai と書かれています)。互換サーバーを作る側は、この値で Spring AI からのリクエストを見分けられます。

①の content() で送られる JSON

{
  "messages": [
    {"role": "system", "content": "あなたはJavaとSpringに詳しいアシスタントです。日本語で、短く答えます。"},
    {"role": "user", "content": "Spring AIとは何ですか"}
  ],
  "model": "gpt-5-mini"
}

temperature を指定しなければ、temperature は送られません。 2.0.1 の OpenAiChatOptions に既定値が無いためです。

②の stream() で増える項目

{
  "messages": [{"role": "user", "content": "Javaの歴史"}],
  "model": "gpt-5-mini",
  "stream": true,
  "stream_options": {"include_usage": true}
}

ストリーミングでは、ソースで include_usage: true が既定で付きます。最後のチャンクでトークン数を受け取るためです。

6. 構造化出力(entity)の仕組み:2つの送り方

entity(Summary.class) には、プロンプトで頼む方法と、API の機能で縛る方法の2つがあります。

書き方 送り方 強さ
.entity(Summary.class) ユーザーメッセージの末尾に、JSON Schema 付きの指示文を足す お願いするだけ。崩れることがある
.entity(Summary.class, s -> s.useProviderStructuredOutput()) response_format に JSON Schema を入れる(OpenAI の Structured Outputs) API 側で形を守らせる
.entity(Summary.class, s -> s.validateSchema()) 返ってきた JSON をスキーマで検証し、違えば指摘を足して再送(既定で最大3回) 崩れても直させる
上の2つを両方 s -> s.useProviderStructuredOutput().validateSchema() 一番強い

既定の entity():指示文がユーザーメッセージに足される

リファレンスの図では「システムの文脈に足す」と説明されていますが、2.0.1 のソース(ChatModelCallAdvisor)では、ユーザーメッセージの末尾に改行を挟んで足しています。 足される文は BeanOutputConverter のソースにある次の英文です。

{
  "messages": [
    {"role": "user", "content": "次の文章を要約して: ...\nYour response should be in JSON format.\nDo not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.\nDo not include markdown code blocks in your response.\nRemove the ```json markdown from the output.\nHere is the JSON Schema instance your output must adhere to:\n```{\n  \"$schema\" : \"https://json-schema.org/draft/2020-12/schema\",\n  \"type\" : \"object\",\n  \"properties\" : {\n    \"title\" : {\"type\" : \"string\"},\n    \"keywords\" : {\"type\" : \"array\", \"items\" : {\"type\" : \"string\"}},\n    \"level\" : {\"type\" : \"integer\"}\n  },\n  \"required\" : [ \"title\", \"keywords\", \"level\" ],\n  \"additionalProperties\" : false\n}```\n"}
  ],
  "model": "gpt-5-mini"
}

スキーマは record から自動で作られ、すべての項目が required、additionalProperties は false になります(Spring AI のテストが期待している形)。省略してよい項目は @Nullable か @ToolParam(required = false) を付けると required から外れます。

useProviderStructuredOutput():response_format で送る

{
  "messages": [{"role": "user", "content": "次の文章を要約して: ..."}],
  "model": "gpt-5-mini",
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "json_schema",
      "strict": true,
      "schema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {
          "title": {"type": "string"},
          "keywords": {"type": "array", "items": {"type": "string"}},
          "level": {"type": "integer"}
        },
        "required": ["title", "keywords", "level"],
        "additionalProperties": false
      }
    }
  }
}

ソースでは、name は "json_schema" 固定、strict は指定がなければ true です。このときは指示文はユーザーメッセージに足されません。

entity() は call() 専用です。 stream() では使えないと、リファレンスに明記されています。ストリーミングで受けた文字列をまとめてから変換する場合は、BeanOutputConverter を自分で使います。

7. ツール呼び出し(@Tool)の流れと JSON

ツール呼び出しは、モデルが「このメソッドをこの引数で呼んで」と返し、アプリが実行して結果を返す、という往復です。 2.0 では ChatClient が自動で入れる ToolCallingAdvisor が、この往復を繰り返します。

順番 誰が 中身
1 アプリ → モデル 質問と、使えるツールの一覧(tools)
2 モデル → アプリ finish_reason: "tool_calls" と、呼ぶツール名・引数
3 アプリ ToolCallingManager が getWeather("東京") を実行
4 アプリ → モデル 1 の会話+ 2 の呼び出し+ 3 の結果(role: "tool")
5 モデル → アプリ ツール呼び出しの無い最終回答。content() が返す

1回目:tools が付く

@Tool のメソッド名がツール名、description が説明、引数から JSON Schema が作られます。

{
  "messages": [{"role": "user", "content": "東京の天気は?"}],
  "model": "gpt-5-mini",
  "tools": [{
    "type": "function",
    "function": {
      "name": "getWeather",
      "description": "指定した都市の現在の天気を返す",
      "parameters": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "properties": {"city": {"type": "string", "description": "都市名(例:東京)"}},
        "required": ["city"],
        "additionalProperties": false
      },
      "strict": false
    }
  }]
}

2.0.1 では、ツールの strict の既定が false です。 アップグレードノートによると、省略できる引数を持つツールが OpenAI の strict モードで 400 エラーになっていたためです。OpenAiChatOptions.builder().strict(true) で戻せます。

モデルの応答:tool_calls

OpenAI の仕様では、ツールを呼ぶときの応答は次の形です。arguments は JSON の文字列 です。

{
  "id": "chatcmpl-abc123",
  "object": "chat.completion",
  "created": 1791870000,
  "model": "gpt-5-mini",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": null,
      "refusal": null,
      "tool_calls": [{
        "id": "call_001",
        "type": "function",
        "function": {"name": "getWeather", "arguments": "{\"city\":\"東京\"}"}
      }]
    },
    "logprobs": null,
    "finish_reason": "tool_calls"
  }],
  "usage": {"prompt_tokens": 80, "completion_tokens": 20, "total_tokens": 100}
}

2回目:ツールの結果を role: tool で返す

会話の履歴に、モデルの呼び出し要求とツールの結果を足して、もう一度送ります(形は OpenAI の仕様に沿った例です)。ツールの戻り値は JSON に変換されて content に入ります。String を返すと、ソースの DefaultToolCallResultConverter により "..." と引用符付きの JSON 文字列になり、void のメソッドは "Done" になります。

{
  "messages": [
    {"role": "user", "content": "東京の天気は?"},
    {"role": "assistant", "tool_calls": [{"id": "call_001", "type": "function",
      "function": {"name": "getWeather", "arguments": "{\"city\":\"東京\"}"}}]},
    {"role": "tool", "tool_call_id": "call_001", "content": "\"東京は晴れ、気温22度\""}
  ],
  "model": "gpt-5-mini",
  "tools": [ "(1回目と同じ)" ]
}

tool_call_id は、応答の tool_calls[].id と同じ値にします。OpenAI の仕様で tool_call_id は必須で、tool_call_id の無い role: "tool" のメッセージは、仕様のスキーマに照らすと不正と判定されました。互換サーバーを自作するときや、ログを読むときの目安になります。

5章・6章・7章の JSON(content()・stream()・entity() の2通り・tools 付き・2回目の送信・モデルの応答)は、OpenAI の OpenAPI 仕様(info.version 2.3.0)の CreateChatCompletionRequest・CreateChatCompletionResponse に照らして、いずれも仕様どおりと判定されました("tools": [ "(1回目と同じ)" ] の省略部分は除く)。

ツールの注意点

項目 内容(リファレンス・アップグレードノート)
ツールの例外 RuntimeException はメッセージをモデルに返す。spring.ai.tools.throw-exception-on-error=true で呼び出し元に投げる
呼び出し回数の上限 1ツール40回・合計150回(2.0.1 の既定)。超えると ToolCallLimitExceededException
使えない型 Optional・CompletableFuture・Mono/Flux・Function などは引数・戻り値にできない
既定のツール defaultTools(...) は全リクエストで共有される。削除などの危険な操作は呼び出しごとに渡す
登録していないツール 2.0.1 から、リクエストに付けたツールだけが実行される(Bean の自動解決は既定で無効)

よくある質問

Q. Ollama やローカルの LLM でも使えますか。 A. OpenAI 互換の API を持つサーバーなら、spring.ai.openai.base-url を http://localhost:11434/v1 のように /v1 まで書いて向けられます。専用の spring-ai-starter-model-ollama もあります。Ollama 自体は、関連記事「Ollamaの使い方|ローカルLLMのコマンド一覧とAPIの呼び方」で扱っています。

Q. 会話の履歴を覚えさせるには? A. MessageChatMemoryAdvisor と MessageWindowChatMemory(既定で直近20件)を使います。2.0 では、呼び出しごとに .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, id)) で会話 ID を渡す必要があり、無いと IllegalArgumentException になるとリファレンスにあります。

Q. ChatModel を直接呼んでもツールは動きますか。 A. 2.0 では、ChatModel.call() はツール呼び出しの要求をそのまま返すだけで、実行しません。自動で実行させるなら ChatClient を使います。

Q. Python の OpenAI SDK と比べて何が違いますか。 A. 送る JSON(Chat Completions)は同じ仕様です。Spring AI は、Java のクラスからスキーマを作る・ツールの往復を自動で回す・Spring の設定や監視(Micrometer)とつながる、といった部分を受け持ちます。Python 側は、関連記事「OpenAI API Python入門|Responses APIのコピペ集」で扱っています。

まとめ

  • 最新の安定版は Spring AI 2.0.1。Spring Boot 4.0・4.1 に対応
  • 依存は spring-ai-starter-model-openai + spring-ai-bom。ストリーミングは webflux も入れる
  • base-url は /v1 まで、既定モデルは gpt-5-mini、temperature は既定で送らない
  • entity() は既定でユーザーメッセージに JSON Schema の指示文を足す。強く縛るなら useProviderStructuredOutput()
  • ツールは @Tool + .tools(...)。往復は ToolCallingAdvisor が回し、2.0.1 では strict の既定が false

参考・出典

確認日はいずれも 2026年10月13日です。

  • Spring AI Reference「Chat Client API」:https://docs.spring.io/spring-ai/reference/api/chatclient.html
  • Spring AI Reference「Structured Output」:https://docs.spring.io/spring-ai/reference/api/structured-output.html
  • Spring AI Reference「Tool Calling」:https://docs.spring.io/spring-ai/reference/api/tools.html
  • Spring AI Reference「OpenAI Chat」:https://docs.spring.io/spring-ai/reference/api/chat/openai-chat.html
  • Spring AI Reference「Upgrade Notes」:https://docs.spring.io/spring-ai/reference/upgrade-notes.html
  • Spring AI ソース(タグ v2.0.1):https://github.com/spring-projects/spring-ai/tree/v2.0.1
  • OpenAI API 仕様(openai-openapi):https://github.com/openai/openai-openapi

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Spring AI入門|ChatClientの使い方とコード全文(2.0対応)」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?