Spring AI 2.0 源码解析(一):一次 ChatClient 调用到底经历了什么?
Spring AI 2.0 æºç è§£æï¼ä¸ï¼ï¼ä¸æ¬¡ ChatClient è°ç¨å°åºç»åäºä»ä¹ï¼
ä¸åªå«ç ¤ççç« 2026-08-18 15 é 读8åéå è·ä¸ªç¤ºä¾
ä»ç¶ä»é¢çæç« éçè¿å è¡ä»£ç å¼å§ï¼
String content = chatClient.prompt()
.user("ä½ å¥½")
.call()
.content();
4 è¡ä»£ç ï¼å°±è½å®æä¸æ¬¡å大模åç对è¯ã
ä»ç»ä¸çï¼é®é¢å°±åºæ¥äºï¼
ChatClientæ¯è°å建çï¼prompt()è¿åäºä»ä¹ï¼user("ä½ å¥½")ä»ä¹æ¶ååæUserMessageï¼call()å°åºææ²¡æè°ç¨æ¨¡åï¼- Advisor å¨åªä¸ªä½ç½®ä»å ¥ï¼
- æåçåç¬¦ä¸²åæ¯ææ ·ä»
ChatResponseéååºæ¥çï¼
å®åï¼è¿æ®µä»£ç å¹¶éä» ChatClient ç´æ¥è·³å° OpenAiChatModelãä¸é´è¿æä¸æ®µé»è¾ï¼æ¯å¦å建请æ±è§æ ¼ãç»è£
Promptãæå»º Advisor Chainï¼åç±é¾å°¾ç ChatModelCallAdvisor è°ç¨ ChatModelã
è¿æä¸ä¸ªå¾å®¹ææ··æ·çå°æ¹ï¼
call() å¹¶ä¸ä¼ç´æ¥å起模å请æ±ï¼çæ£è§¦å请æ±è°ç¨çæ¯ content()ã
è¿ä¹æ¯æ¬ç« è¦éç¹è®²è§£ç主è¦é¾è·¯ã
çæ¬åºçº¿
| é¡¹ç® | çæ¬ |
|---|---|
| Spring AI | 2.0.0 |
| Commit | ef502da |
| Spring Boot | 4.1.0 |
| Java | 17 |
| 模åå®ç° | OpenAiChatModel |
åç»æç« å¦ææ²¡æç¹å«è¯´æï¼é½æ¯æ²¿ç¨è¿å¥åºçº¿ï¼ç¡®ä¿ä¸æ··å ¥ä¸åçæ¬çæºç ã
æå°ç¤ºä¾
å å¼å ¥ OpenAI Starterã
Spring AI ççæ¬æä»¬äº¤ç» BOM 管çï¼
<properties>
<java.version>17</java.version>
<spring-ai.version>2.0.0</spring-ai.version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
2.0.0 ç OpenAI Chat é
ç½®ä¸ï¼æ¨¡å屿§ç´æ¥æ¾å¨ spring.ai.openai.chat.modelï¼ä¸é´æ²¡æ optionsï¼
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
# ä¸å¡ä¸ä¸è¬éç¨ä¾¿å®ä¸æ´å
·å¤æ§ä»·æ¯ç模å
model: gpt-4.1-mini
ç¶ååä¸ä¸ªæå° Controllerï¼
@RestController
@RequestMapping("/ai")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping
public String chat(
@RequestParam(defaultValue = "ä½ å¥½") String message) {
return this.chatClient.prompt()
.user(message)
.call()
.content();
}
}
è¿é注å
¥çæ¯ ChatClient.Builderï¼ä¸æ¯ ChatClientã
è¿ä¸ªåºå«å¾éè¦ã
ChatClient å°åºæ¯è°å建ç
å¼å
¥ spring-ai-starter-model-openai åï¼Starter 伿 OpenAI 模åå®ç°ãChatClient å对åºçèªå¨é
ç½®ä¸èµ·å¸¦è¿æ¥ã
å¯å¨é¶æ®µä¸»è¦åç两件äºï¼
OpenAiChatAutoConfigurationæ ¹æ®é ç½®å建OpenAiChatModelï¼ChatClientAutoConfiguration使ç¨è¿ä¸ªChatModelå建ChatClient.Builderã
大è´çé¾è·¯å ³ç³»å¯ä»¥å çä¸é¢çæµç¨å¾ï¼
flowchart TD
A[spring-ai-starter-model-openai] --> B[OpenAiChatAutoConfiguration]
B --> C[OpenAiChatModel ä½ä¸º ChatModel Bean]
C --> D[ChatClientAutoConfiguration]
D --> E[rototype ChatClient.Builder]
E --> F[ä¸å¡ä»£ç è°ç¨ build]
F --> G[DefaultChatClient]
ç»§ç»æ¥çè¿ä¸¤ä¸ªé
置类ï¼OpenAiChatAutoConfiguration çæ ¸å¿ä»£ç å¹¶ä¸å¤æï¼
@Bean
@ConditionalOnMissingBean
public OpenAiChatModel openAiChatModel(
OpenAiCommonProperties commonProperties,
OpenAiChatProperties chatProperties,
ToolCallingManager toolCallingManager,
ObjectProvider<ObservationRegistry> observationRegistry,
...) {
var chatModel = OpenAiChatModel.builder()
.openAiClient(openAIClient)
.openAiClientAsync(openAIClientAsync)
.options(chatProperties.toOptions())
.toolCallingManager(toolCallingManager)
.observationRegistry(
observationRegistry.getIfUnique(
() -> ObservationRegistry.NOOP))
.build();
return chatModel;
}
åç ChatClientAutoConfigurationï¼
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
ChatClient.Builder chatClientBuilder(
ChatClientBuilderProperties properties,
ChatClientBuilderConfigurer configurer,
ChatModel chatModel,
ObjectProvider<ObservationRegistry> observationRegistry,
...) {
ChatClient.Builder builder = ChatClient.builder(
chatModel,
observationRegistry.getIfUnique(
() -> ObservationRegistry.NOOP),
...);
return configurer.configure(builder);
}
è¿éæä¸¤ä¸ªç»èéè¦å ³æ³¨ã
第ä¸ï¼Spring AI èªå¨é
ç½®çæ¯ ChatClient.Builderï¼æç»ç ChatClient ç±ä¸å¡ä»£ç è°ç¨ build() å建ã
第äºï¼è¿ä¸ª Builder æ¯ prototypeãæ¯æ¬¡ä» Spring 容å¨è·åå®ï¼é½ä¼å¾å°ä¸ä¸ªæ°ç Builderï¼ä¸å¡ä»£ç å¯ä»¥è®¾ç½®ä¸åç default systemãdefault advisor å default optionsï¼äºä¸å½±åã
ç»§ç»è¿å
¥ DefaultChatClientBuilder#build()ï¼
@Override
public ChatClient build() {
return new DefaultChatClient(this.defaultRequest);
}
å°è¿éï¼çæ£å·¥ä½çå®ç°ç±» DefaultChatClient æåºç°ã
Spring å¨è¿é没æç´æ¥æä¾ä¸ä¸ªå
¨å± ChatClientï¼èæ¯æä¾å¯å®å¶ç Builderãè¿æ ·åç¡®å®æ¹ä¾¿äºä¸åä¸å¡åºæ¯å»è®¾ç½®åèªçé»è®¤é
ç½®ï¼ä½åæ ·å¸¦æ¥æ°çé®é¢ï¼ææ¥ Bean å建é®é¢æ¶ï¼åå¤äºä¸å±è·³è½¬ãè¿èªç¶æ¯æå©æå¼ï¼ä¹æ¯ Spring å®¶æäº§åçä¸è´¯ç¹ç¹ã
prompt() åªæ¯å¤å¶ä¸ä»½è¯·æ±è§æ ¼
ç°å¨åå°ä¸å¡ä»£ç ï¼
chatClient.prompt()
DefaultChatClient#prompt() çå®ç°åªæä¸è¡ï¼
@Override
public ChatClientRequestSpec prompt() {
return new DefaultChatClientRequestSpec(
this.defaultChatClientRequest);
}
宿¢æ²¡æå建ç½ç»è¯·æ±ï¼ä¹æ²¡æè°ç¨æ¨¡åã
åªæ¯æ ¹æ® ChatClient ä¿åçé»è®¤é
ç½®ï¼å¤å¶åºä¸ä»½æ°ç DefaultChatClientRequestSpecãBuilder ä¸è®¾ç½®çé»è®¤ system textãmessagesãoptionsãadvisorsãtools å advisor paramsï¼é½ä¼æä¸ºè¿æ¬¡è¯·æ±çèµ·ç¹ã
æä»¥åä¸ä¸ª ChatClient å°±è½å®ç°éå¤ä½¿ç¨ï¼èæ¯æ¬¡ prompt() 齿ç¬ç«ç请æ±ç¶æã
user() è¿æ²¡æå建 UserMessage
ä¸ä¸æ¥ï¼
.user("ä½ å¥½")
è¿ä¸æ¥ä¹æ¯æ³è±¡ä¸ç®åãæºç åªæ¯æææ¬æåå¨ RequestSpec ä¸ï¼
@Override
public ChatClientRequestSpec user(String text) {
Assert.hasText(text, "text cannot be null or empty");
this.userText = text;
return this;
}
æ¤æ¶ä»ç¶æ²¡æ UserMessageã
çæ£çæ¶æ¯ç»è£
åçå¨ DefaultChatClientUtils#toChatClientRequest()ãå®ä¼æé¡ºåºå¤ç system textãå·²æ messages å user textï¼åå建 Promptï¼
Builder promptBuilder = Prompt.builder()
.messages(processedMessages)
.chatOptions(processedChatOptions);
return ChatClientRequest.builder()
.prompt(promptBuilder.build())
.context(new ConcurrentHashMap<>(
inputRequest.getAdvisorParams()))
.build();
æç»è¿å ¥ Advisor Chain çå¯¹è±¡ä¸æ¯é¶æ£çå符串ï¼èæ¯ï¼
public record ChatClientRequest(
Prompt prompt,
Map<String, Object> context) {
}
Prompt ä¿ååç»æ¨¡åç messages å optionsï¼context ä¿å Advisor å¨è°ç¨é¾ä¸å
±äº«çæ°æ®ã
两è ä¸å¯æ··ä¸ºä¸è°ã
模åä¾åºåæç»å
³å¿çæ¯ Promptï¼Advisor è¿éè¦æè¾¹è¿ä»½ context æ¥ä¼ é MemoryãStructured OutputãTool Calling çæ§å¶ä¿¡æ¯ã
call() 为ä»ä¹æ²¡æè°ç¨æ¨¡å
æ¥ççæå®¹æè¯¯å¤ç䏿¥ï¼
.call()
DefaultChatClientRequestSpec#call() çæºç å¦ä¸ï¼
@Override
public CallResponseSpec call() {
BaseAdvisorChain advisorChain = buildAdvisorChain();
return new DefaultCallResponseSpec(
DefaultChatClientUtils.toChatClientRequest(this),
advisorChain,
this.observationRegistry,
this.chatClientObservationConvention);
}
å®åªåäºä¸¤ä»¶äºï¼
- æå»º Advisor Chainï¼
- æå½å RequestSpec 转æ
ChatClientRequestã
ç¶åè¿å DefaultCallResponseSpecã
没æ chatModel.call(...)ï¼ä¹æ²¡æç½ç»è¯·æ±ã
æä»¥è¿æ®µä»£ç æ§è¡å®ï¼æ¨¡åè¿æ²¡ææ¶å°ä»»ä½å 容ï¼
CallResponseSpec responseSpec = chatClient.prompt()
.user("ä½ å¥½")
.call();
åªæç»§ç»è°ç¨ content()ãchatResponse()ãchatClientResponse() æ entity()ï¼åæ¥è¯·æ±æä¼çæ£å¼å§ã
å½åä¸ç¡®å®å®¹æè®©äººäº§ç误解ã
ä»å®é
è¡ä¸ºæ¥çï¼call() æ´åæ¯â忢å°åæ¥è°ç¨æ¨¡å¼å¹¶æé ResponseSpecâï¼çæ£è§¦å模åè°ç¨çæ¯åç»çç»æ¢æ¹æ³ã
Advisor Chain çç»è£
ç»§ç»å¾ä¸çï¼call() å
è¿å
¥ buildAdvisorChain()ï¼
private BaseAdvisorChain buildAdvisorChain() {
autoRegisterToolCallingAdvisor();
validateSingleToolAdvisor();
List<Advisor> chain = new ArrayList<>(this.advisors);
chain.add(ChatModelCallAdvisor.builder()
.chatModel(this.chatModel)
.build());
chain.add(ChatModelStreamAdvisor.builder()
.chatModel(this.chatModel)
.build());
return DefaultAroundAdvisorChain
.builder(this.observationRegistry)
.observationConvention(
this.advisorObservationConvention)
.pushAll(chain)
.build();
}
ä¸å¡é ç½®ç Advisor ä¼å è¿å ¥å表ï¼Spring AI åæä¸¤ä¸ªæ¨¡åè°ç¨ Advisor æ¾å°é¾å°¾ï¼
ChatModelCallAdvisorå¤ç忥è°ç¨ï¼ChatModelStreamAdvisorå¤çæµå¼è°ç¨ã
2.0.0 è¿ä¼é»è®¤æ³¨å ToolCallingAdvisorãå³ä½¿å½åè¯·æ±æ²¡æéæ Toolï¼å®ä¹ä¼ä¿çå¨é¾ä¸ï¼ä»¥ä¾¿å
¶ä» Advisor å¨è¿è¡æ¶å¨æå å
¥å·¥å
·ã
æä»¬å è®°ä½ä¸ç¹ï¼
ChatModelCallAdvisor æ¯åæ¥ Advisor Chain éå¾ ChatModel çæåä¸ç«ã
content() æçæ£è§¦åè°ç¨
ç»§ç»æ§è¡ï¼
.content()
DefaultCallResponseSpec#content() ä¼è°ç¨ doGetObservableChatClientResponse()ï¼
@Override
public @Nullable String content() {
ChatResponse chatResponse =
doGetObservableChatClientResponse(this.request)
.chatResponse();
return getContentFromChatResponse(chatResponse);
}
å¨ Observation å è£ å é¨ï¼çæ£çå ¥å£æ¯ï¼
var response = advisorChain.nextCall(chatClientRequest);
DefaultAroundAdvisorChain#nextCall() æ¯æ¬¡ä»éåä¸ååºä¸ä¸ª Advisorï¼åè°ç¨å®ç adviseCall()ï¼
var advisor = this.callAdvisors.pop();
return observation.observe(() -> {
var response = advisor.adviseCall(
chatClientRequest, this);
observationContext.setChatClientResponse(response);
return response;
});
æ®é Advisor å¨èªå·±ç adviseCall() ä¸ç»§ç»è°ç¨ chain.nextCall(request)ï¼è¯·æ±å°±ä¼éå±ååä¼ éï¼å½ååºè¿åæ¶ï¼å沿åè·¯åå忥ã
è½çåºæ¥ï¼è¿å°±æ¯ä¸ªå ¸åç around chainã
é¾å°¾ææ ·è°ç¨ ChatModel
å½è¯·æ±èµ°å° ChatModelCallAdvisorï¼æç¬¬ä¸æ¬¡çå°çæ£ç模åè°ç¨ï¼
@Override
public ChatClientResponse adviseCall(
ChatClientRequest chatClientRequest,
CallAdvisorChain callAdvisorChain) {
ChatClientRequest formattedRequest =
augmentWithFormatInstructions(chatClientRequest);
ChatResponse chatResponse =
this.chatModel.call(formattedRequest.prompt());
return ChatClientResponse.builder()
.chatResponse(chatResponse)
.context(Map.copyOf(formattedRequest.context()))
.build();
}
è¿é宿äºä¸¤ä¸ªè¾¹ç转æ¢ï¼
- ä»
ChatClientRequestä¸ååºPromptï¼äº¤ç»ChatModelï¼ - æ
ChatModelè¿åçChatResponseå Advisor context éæ°å è£ æChatClientResponseã
ChatModel æ¯ä¾åºåæ å
³çæ¥å£ï¼
public interface ChatModel
extends Model<Prompt, ChatResponse>,
StreamingChatModel {
@Override
ChatResponse call(Prompt prompt);
}
ä¸å±åªä¾èµ ChatModelãæ¢æ AnthropicãOllama æå
¶ä»æ¨¡åå®ç°æ¶ï¼ChatClient å Advisor Chain ä¸éè¦è·çä¾åºå SDK ä¸èµ·æ¹ã
è¿å±æ½è±¡å°±è§£å³äºé离ä¾åºåå·®å¼çé®é¢ï¼é常巧å¦ã
åæ¶ä¹ä»£ä»·ä¹å¾ææ¾ââææ¥å°é¾ã彿æ¥ä¸æ¬¡å¯¹è¯è¯·æ±æ¶ï¼è¦ä» ChatClient åç©¿è¿ Advisor Chainï¼æè½çå°çæ£ç Provider å®ç°ã
OpenAiChatModel ææ ·è¿å ¥ SDK
å½åç¤ºä¾æ³¨å
¥ç ChatModel å®ç°æ¯ OpenAiChatModelã
å®çåæ¥å ¥å£æ¯ï¼
@Override
public ChatResponse call(Prompt prompt) {
Prompt requestPrompt = buildRequestPrompt(prompt);
verifyPromptChatOptions(requestPrompt);
return this.internalCall(requestPrompt, null);
}
internalCall() å
æ Spring AI ç Prompt 转æ OpenAI Java SDK ç ChatCompletionCreateParamsï¼ç¶åå起请æ±ï¼
ChatCompletionCreateParams request =
createRequest(prompt, false);
ChatCompletion chatCompletion =
this.openAiClient.chat()
.completions()
.create(request);
ä¾åºåè¿åç»æåï¼OpenAiChatModel 伿 choices 转æ Spring AI ç Generationï¼åç»è£
æç»ä¸ç ChatResponseã
å°è¿éï¼ä¸æ¬¡å®æ´ç忥è°ç¨é¾å°±å®æäºã
䏿¬¡è°ç¨ç宿´æ¶åº
让æä»¬ç»åæ¶åºå¾ï¼åæ¥åé¡¾è¿æ¬¡çè°ç¨é¾è·¯ï¼
sequenceDiagram
participant U as "ä¸å¡ä»£ç "
participant C as "DefaultChatClient"
participant A as "Advisor Chain"
participant M as "ChatModelCallAdvisor"
participant P as "OpenAiChatModel / SDK"
U->>C: prompt().user("ä½ å¥½").call()
C-->>U: DefaultCallResponseSpec
U->>C: content()
C->>A: nextCall(ChatClientRequest)
A->>A: 便¬¡æ§è¡å·²æ³¨å Advisor
A->>M: adviseCall(request)
M->>P: chatModel.call(prompt)
P->>P: createRequest() å¹¶è°ç¨ OpenAI SDK
P-->>M: ChatResponse
M-->>A: ChatClientResponse
A-->>C: ChatClientResponse
C-->>U: æå output.text
æ³¨ææ¶åºå¾çå两æ¥ï¼
prompt().user(...).call() è¿å DefaultCallResponseSpecï¼å° content() æè®©è¯·æ±è¿å
¥ Advisor Chainã
åé¢åæéè¯ãTool Calling åç»æåè¾åºæ¶ï¼å¾å®¹æææ§è¡ä½ç½®å¤æéï¼å æ¤è¿ä¸åè¦åéç¹è®°å¿ã
æåç String ä»åªéæ¥
Advisor Chain è¿å ChatClientResponse åï¼content() ååºéé¢ç ChatResponseï¼å沿çä¸é¢è¿æ¡è·¯å¾æ¿å°ææ¬ï¼
private static @Nullable String getContentFromChatResponse(
@Nullable ChatResponse chatResponse) {
return Optional.ofNullable(chatResponse)
.map(ChatResponse::getResult)
.map(Generation::getOutput)
.map(AbstractMessage::getText)
.orElse(null);
}
宿´è·¯å¾æ¯ï¼
ChatClientResponse
-> ChatResponse
-> Generation
-> AssistantMessage
-> text
æä»¥ content() åªæ¯ä¸ä¸ªæ¹ä¾¿è°ç¨è
使ç¨çææ¬æåæ¹æ³ã
妿ä¸å¡éè¦ token usageãfinish reasonãresponse metadata æ Advisor contextï¼å°±ä¸è¦è¿æ©æååºåæä¸ä¸ªå符串ï¼åºè¯¥ä½¿ç¨ chatResponse() æ chatClientResponse()ã
建议æè¿äºæç¹
妿è§å¾å çæç« æç¹æ¨¡ç³ï¼å¯ä»¥ç´æ¥æææä¾ç github ä»åº clone 䏿¥ï¼æå¼é¡¹ç®åæä¸é¢ç顺åºä¾æ¬¡ææç¹ï¼
| é¡ºåº | æç¹ä½ç½® | çä»ä¹ |
|---|---|---|
| 1 | ChatClientAutoConfiguration#chatClientBuilder | Builder ææ ·æ¿å° ChatModel |
| 2 | DefaultChatClient#prompt | é»è®¤è¯·æ±ææ ·å¤å¶ |
| 3 | DefaultChatClientRequestSpec#user | user text ä¿åå¨åªé |
| 4 | DefaultChatClientRequestSpec#call | Request å Advisor Chain ææ ·åå¤ |
| 5 | DefaultCallResponseSpec#content | çæ£æ§è¡ä»åªéå¼å§ |
| 6 | DefaultAroundAdvisorChain#nextCall | å½åæ§è¡çæ¯åªä¸ª Advisor |
| 7 | ChatModelCallAdvisor#adviseCall | ChatClientRequest ææ ·è¿å ¥ ChatModel |
| 8 | OpenAiChatModel#call | Provider å±ææ ·å¤ç Prompt |
| 9 | OpenAiChatModel#internalCall | OpenAI SDK 请æ±å¨åªéååº |
è°è¯æ¶ä¿æè§å¯ï¼éç¹çè¿ä¸ä¸ªå¯¹è±¡ï¼
DefaultChatClientRequestSpecï¼è¿æ²¡ææç»ç»è£ ç请æ±åæ°ï¼ChatClientRequestï¼å·²ç»å å«Prompt + contextï¼ChatClientResponseï¼å·²ç»å å«ChatResponse + contextã
çä¼¼è¿å ä¸ªé½æ¯è¯·æ±åååºï¼å®é 䏿å¨ç屿¬¡å®å ¨ä¸åã
åå°å¼å¤´
ç°å¨åå头æ¥çè¿ 4 è¡ä»£ç ï¼
String content = chatClient.prompt()
.user("ä½ å¥½")
.call()
.content();
å¯ä»¥æå®å±å¼æä¸é¢è¿æ¡ä¸»é¾ï¼
ChatClient.Builder
-> DefaultChatClient
-> DefaultChatClientRequestSpec
-> ChatClientRequest(Prompt, context)
-> DefaultAroundAdvisorChain
-> ChatModelCallAdvisor
-> ChatModel
-> OpenAiChatModel
-> OpenAI Java SDK
-> ChatResponse
-> content
ChatClientè´è´£æä¾æµå¼ API åä¿åé»è®¤é ç½®ï¼- RequestSpec è´è´£æ¶éæ¬æ¬¡è¯·æ±åæ°ï¼
- Advisor Chain è´è´£ç»ç»è°ç¨ååçå¢å¼ºé»è¾ï¼
ChatModelé离ä¾åºåå·®å¼ï¼OpenAiChatModelæç»å®æ Spring AI 对象å OpenAI SDK 对象ä¹é´ç转æ¢ã
æ¯ä¸å±é½æèªå·±çèè´£ã
æè¿æ¡ä¸»é¾çæ¸ åï¼åé¢ç Chat MemoryãTool CallingãStructured Output å RAG æè½æ´å®¹æçè§£ã
ä¸ä¸ç¯æä»¬æ¥æè§£ Starter çèªå¨é
ç½®ï¼ä¸ä¸ª spring-ai-starter-model-openai ä¾èµï¼å°åºå Spring 容å¨éæ¾äºåªäº Beanã
ç¸å ³æºç
- Spring AI v2.0.0 æºç
- ChatClientAutoConfiguration.java
- DefaultChatClient.java
- DefaultAroundAdvisorChain.java
- ChatModelCallAdvisor.java
- OpenAiChatModel.java
Aitishiku.com