LangChain4j 提供了靈活的模型參數配置方式,允許你根據不同的 AI 模型(如 OpenAI、GPT-4、Anthropic 等)設置各種參數來控制生成結果。
后面手擼代碼繼續在之前章節的代碼上拓展
一、日志配置(Logging)
在 LangChain4j 中,日志(logging)相關參數主要用于控制是否記錄模型的請求和響應信息,這對于開發調試、監控 API 調用內容以及排查問題非常有用。
將日志級別調整為debug級別,同時配置上langchain日志輸出開關才能有效。
step1:修改LLMConfig類
package com.xxx.demo.config;import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class LLMConfig {@Bean(name = "qwen")public ChatModel chatModelQwen(){return OpenAiChatModel.builder().apiKey(System.getenv("aliqwen-apikey")).modelName("qwen-plus").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").logRequests(true) // 日志界別設置為debug才有效.logResponses(true)// 日志界別設置為debug才有效.build();}}
step2:修改YML文件
server.port=9005spring.application.name=langchain4j-0301-parameters# 只有日志級別調整為debug級別,同時配置以上 langchain 日志輸出開關才有效
logging.level.dev.langchain4j = DEBUG
step3:在控制臺檢查結果
二、監控(Observability)
LangChain4j 的 Observability(可觀測性)配置主要涉及日志記錄、指標監控及鏈路追蹤等方面
step1:先實現ChatModelListener
package com.xxx.demo.listener;import cn.hutool.core.util.IdUtil;
import dev.langchain4j.model.chat.listener.ChatModelErrorContext;
import dev.langchain4j.model.chat.listener.ChatModelListener;
import dev.langchain4j.model.chat.listener.ChatModelRequestContext;
import dev.langchain4j.model.chat.listener.ChatModelResponseContext;
import lombok.extern.slf4j.Slf4j;@Slf4j
public class TestModeLlistener implements ChatModelListener {@Overridepublic void onRequest(ChatModelRequestContext requestContext){// onRequest配置的k:v鍵值對,在onResponse階段可以獲得,上下文傳遞參數好用String uuidValue = IdUtil.simpleUUID();requestContext.attributes().put("TraceID",uuidValue);log.info("請求參數requestContext:{}", requestContext+"\t"+uuidValue);}@Overridepublic void onResponse(ChatModelResponseContext responseContext){Object object = responseContext.attributes().get("TraceID");log.info("返回結果responseContext:{}", object);}@Overridepublic void onError(ChatModelErrorContext errorContext){log.error("請求異常ChatModelErrorContext:{}", errorContext);}
}
step2:再次拓展LLMConfig類
package com.xxx.demo.config;import com.bbchat.demo.listener.TestModeLlistener;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;@Configuration
public class LLMConfig {@Bean(name = "qwen")public ChatModel chatModelQwen(){return OpenAiChatModel.builder().apiKey(System.getenv("aliqwen-apikey")).modelName("qwen-plus").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").logRequests(true) // 日志界別設置為debug才有效.logResponses(true)// 日志界別設置為debug才有效.listeners(List.of(new TestModeLlistener())) //監聽器.build();}}
step3:驗證結果
三、重試機制(Retry Configuration)
在 LangChain4j 中配置重試機制,核心是通過?RetryAssistant(重試助手)或結合底層依賴(如 OkHttp、Resilience4j 等)實現,用于解決 LLM 調用過程中的網絡波動、API 限流、臨時服務不可用等問題。下面代碼簡單做展示一下效果,更加詳細的配置請大家看官網查API。
再次拓展LLMConfig
package com.xxx.demo.config;import com.bbchat.demo.listener.TestModeLlistener;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;@Configuration
public class LLMConfig {@Bean(name = "qwen")public ChatModel chatModelQwen(){return OpenAiChatModel.builder().apiKey(System.getenv("aliqwen-apikey")).modelName("qwen-plus").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").logRequests(true) // 日志界別設置為debug才有效.logResponses(true)// 日志界別設置為debug才有效.listeners(List.of(new TestModeLlistener())) //監聽器.maxRetries(2)// 重試機制共計2次.build();}}
四、超時機制(timeout)
繼續拓展LLMConfig
package com.xxx.demo.config;import com.bbchat.demo.listener.TestModeLlistener;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.time.Duration;
import java.util.List;@Configuration
public class LLMConfig {@Bean(name = "qwen")public ChatModel chatModelQwen(){return OpenAiChatModel.builder().apiKey(System.getenv("aliqwen-apikey")).modelName("qwen-plus").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").logRequests(true) // 日志界別設置為debug才有效.logResponses(true)// 日志界別設置為debug才有效.listeners(List.of(new TestModeLlistener())) //監聽器.maxRetries(2)// 重試機制共計2次.timeout(Duration.ofSeconds(2))//向大模型發送請求,2s沒有響應將中斷請求并提示reqquest time out.build();}}
找一個大模型思考肯定超過2s問題測試一下即可見效果
五、流式輸出(Response Streaming)
流式輸出(StreamingOutput)是一種逐步返回大模型生成結果的技術,生成一點返回一點,允許服務器將響應內容。分批次實時傳輸給客戶端,而不是等待全部內容生成完畢后再一次性返回。
?這種機制能顯著提升用戶體驗,尤其適用于大模型響應較慢的場景(如生成長文本或復雜推理結果)
step1:修改一下我們的pom文件,確認下langchain4j原生maven坐標三件套
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-reactor</artifactId></dependency>
step2:修改下yml文件
server.port=9005spring.application.name=langchain4j-0301-parameters# 只有日志級別調整為debug級別,同時配置以上 langchain 日志輸出開關才有效
logging.level.dev.langchain4j = DEBUG# 設置響應的字符編碼,避免流式返回輸出亂碼
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
step3:ChatAssistant接口
package com.xxx.demo.service;
import reactor.core.publisher.Flux;public interface ChatAssistant {String chat(String prompt);Flux<String> chatFlux(String prompt);
}
step4:重寫LLMConfig
package com.xxx.demo.config;import com.bbchat.demo.listener.TestModeLlistener;
import com.bbchat.demo.service.ChatAssistant;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.service.AiServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;@Configuration
public class LLMConfig {/*普通對話接口*/@Bean(name = "qwen")public ChatModel chatModelQwen(){return OpenAiChatModel.builder().apiKey(System.getenv("aliqwen-apikey")).modelName("qwen-plus").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").logRequests(true) // 日志界別設置為debug才有效.logResponses(true)// 日志界別設置為debug才有效.listeners(List.of(new TestModeLlistener())) //監聽器// .maxRetries(2)// 重試機制共計2次// .timeout(Duration.ofSeconds(2))//向大模型發送請求,2s沒有響應將中斷請求并提示reqquest time out.build();}/*流式對話接口*/@Beanpublic StreamingChatModel streamingChatModel(){return OpenAiStreamingChatModel.builder().apiKey(System.getenv("aliqwen-apikey")).modelName("qwen-plus").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}@Beanpublic ChatAssistant chatAssistant(StreamingChatModel streamingChatModel){return AiServices.create(ChatAssistant.class, streamingChatModel);}}
step5:寫一個新的controller,分別調用一下就能體驗流式輸出了。
接口地址 | 核心邏輯 | 返回類型 | 適用場景 |
---|---|---|---|
| 直接調用? |
| 前端需要流式接收文本(如 SSE) |
| 直接調用? |
| 后端調試(無前端交互) |
| 調用自定義? |
| 高內聚場景(業務邏輯封裝) |
package com.xxx.demo.controller;import com.bbchat.demo.service.ChatAssistant;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;@RestController
@Slf4j
public class StreamingChatModelController {@Resource //直接使用 low-level LLM APIprivate StreamingChatModel streamingChatLanguageModel;@Resource //自己封裝接口使用 high-level LLM APIprivate ChatAssistant chatAssistant;// http://localhost:9005/chatstream/chat?prompt=天津有什么好吃的@GetMapping(value = "/chatstream/chat")public Flux<String> chat(@RequestParam("prompt") String prompt){return Flux.create(stringFluxSink -> {streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler(){@Overridepublic void onPartialResponse(String s){stringFluxSink.next(s);}@Overridepublic void onCompleteResponse(ChatResponse completeResponse){stringFluxSink.complete();}@Overridepublic void onError(Throwable throwable){stringFluxSink.error(throwable);}});});}@GetMapping(value = "/chatstream/chat2")public void chat2(@RequestParam(value = "prompt", defaultValue = "北京有什么好吃") String prompt){System.out.println("---come in chat2");streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler(){@Overridepublic void onPartialResponse(String partialResponse){System.out.println(partialResponse);}@Overridepublic void onCompleteResponse(ChatResponse completeResponse){System.out.println("---response over: "+completeResponse);}@Overridepublic void onError(Throwable throwable){throwable.printStackTrace();}});}@GetMapping(value = "/chatstream/chat3")public Flux<String> chat3(@RequestParam(value = "prompt", defaultValue = "南京有什么好吃") String prompt){System.out.println("---come in chat3");return chatAssistant.chatFlux(prompt);}
}