06-03 LangChain4J高层次API使用
✅LangChain4J高层次API使用
LangChain4J为了方便开发者使用,提供了高层次API,但是想要使用的话,需要增加依赖:
1 | <dependency> |
只有加了这个以来,我们才能用到他提供的@AIService这个注解。
LangChain4J的高层次API主要以dev.langchain4j.service.AiServices和dev.langchain4j.service.spring.AiService作为核心的。前面是个接口,后面是个注解。
对话
有了高层次API之后,如果想要实现模型对话的功能就非常简单了。
我们先定义一个接口,并且使用@AIService注解声明他,然后定义一个简单的chat方法,入参出参都是String类型:
1 |
|
这个@AiService会把我们定义的LangChainAiService暴露成一个Spring的Bean。我们在需要和LLM对话的地方调用这个chat方法即可:
1 |
|
就这么简单!!!!
其实,通过@AiService定义一个bean之后,这里面的chat方法会自动调用dev.langchain4j.model.chat.ChatModel#chat(dev.langchain4j.model.chat.request.ChatRequest)来实现对话。

并且,这里并不要求LangChainAiService中的方法一定也要叫chat,我改成hollis666,他一样可以调用到:

流式输出
如果是流失输出的话,实现起来也简单,只需要把方法的返回值声明称Flux
1 |
|
但是,这么执行会报错:
1 | dev.langchain4j.service.IllegalConfigurationException: Please import langchain4j-reactor module if you wish to use Flux<String> as a method return type at dev.langchain4j.service.IllegalConfigurationException.illegalConfiguration(IllegalConfigurationException.java:14) ~[langchain4j-1.8.0.jar:na] at dev.langchain4j.service.output.PojoOutputParser.validateJsonStructure(PojoOutputParser.java:139) ~[langchain4j-1.8.0.jar:na] at dev.langchain4j.service.output.PojoOutputParser.formatInstructions(PojoOutputParser.java:57) ~[langchain4j-1.8.0.jar:na] at dev.langchain4j.service.output.ServiceOutputParser.outputFormatInstructions(ServiceOutputParser.java:108) ~[langchain4j-1.8.0.jar:na] at dev.langchain4j.service.DefaultAiServices$1.appendOutputFormatInstructions(DefaultAiServices.java:502) ~[langchain4j-1.8.0.jar:na] at dev.langchain4j.service.DefaultAiServices$1.invoke(DefaultAiServices.java:301) ~[langchain4j-1.8.0.jar:na] at dev.langchain4j.service.DefaultAiServices$1.invoke(DefaultAiServices.java:236) ~[langchain4j-1.8.0.jar:na] at jdk.proxy2/jdk.proxy2.$Proxy70.chatStream(Unknown Source) ~[na:na] |
还需要增加对langchain4j-reactor的依赖才能实现流失输出。所以增加依赖:
1 | <dependency> |
这样,当我们调用chatStream这个方法的时候,就能进行流式输出。
默认参数
可以通过langchain4j提供的一些内置注解,在方法上增加一些默认的信息,比如系统提示词,用户提示词、
用户提示词也可以用模板形式,但是要记得,langchain4j中的模板需要用{{}} 来表示变量。
1 |
|
当然,提示词模板我们之前还介绍用配置文件的方式,langchain4j也支持:
1 |
|
结构化输出
1 |
|
1 |
|
Book[title=Effective Java, author=Joshua Bloch, description=A must-read for Java developers, offering best practices and expert advice on how to design robust, efficient, and maintainable code., price=54.99]
提示词方式实现:

但是,提示词方式,不支持List

List不支持:https://github.com/langchain4j/langchain4j/issues/3264
对话记忆
我们再来用高层次API来实现一下对话记忆,直接讲那种带对话id,可以做记忆隔离的实现。
首先,我们需要定义一个方法,支持对话记忆:
1 | package org.example.cpuai.service; |
这里面用到了一个@MemoryId 注解,用来标注一个memoryId,我们就是根据这个memoryId的不同来隔离不同的对话记忆的。接着,在我们的controller里面,需要初始化一下LangChainMemoryAiService,注意,这里不能直接autowire,如果想把LangChainMemoryAiService像之前我们定义的AIService一样直接注入的话会报错:In order to use @MemoryId, please configure the ChatMemoryProvider on the 'org.example.cpuai.service.LangChainAiService'. 。报错提醒的很清楚,就是如果使用了 @MemoryId,就需要提供一个ChatMemoryProvider,那么如何提供这个ChatMemoryProvider呢?
可以用LangChain4J中提供的AiServices这个类:
1 | langChainMemoryAiService = AiServices.builder(LangChainMemoryAiService.class) .chatModel(chatModel) .streamingChatModel(streamingChatModel) .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10)) .build(); |
这里我们就用AiServices来构造了一个LangChainMemoryAiService,并且给他配置了一个MessageWindowChatMemory。
或者直接声明一个ChatMemoryProvider的bean,别的都不用干,正常autowire这个LangChainMemoryAiService我发现也是可以的。
但是需要注意的是,如果想要让记忆持续保存的话,这个langChainMemoryAiService需要用同一个,比如以下这种方式,是我最开始使用的方式,发现怎么搞都无法记忆:
1 |
|
原因是因为,以上代码,每次memoryChat被访问的时候,都是新建了一个LangChainMemoryAiService,而这里面的chatMemoryProvider也是新的,他们之前就不可能保存之前的记忆了。
所以,想要带有记忆,就需要针对langChainMemoryAiService只初始化一次,比如:
1 |
|
这样就可以了,第一次我和他对话”我叫Hollis”,并且指定memoryId为1234

以下是控制台打印出来的日志:
1 | HTTP request:- method: POST- url: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions- headers: [Authorization: Beare...25], [User-Agent: langchain4j-openai], [Content-Type: application/json]- body: { "model" : "deepseek-v3", "messages" : [ { "role" : "user", "content" : "我叫Hollis" } ], "stream" : false} |
1 | HTTP response:- status code: 200- headers: [vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers, Accept-Encoding], [x-request-id: f9bd2e05-7de1-40b2-8143-9681bfed3bc6], [x-dashscope-call-gateway: true], [content-type: application/json], [content-length: 639], [req-cost-time: 2399], [req-arrive-time: 1762409853391], [resp-start-time: 1762409855791], [x-envoy-upstream-service-time: 2399], [set-cookie: acw_tc=f9bd2e05-7de1-40b2-8143-9681bfed3bc6cc5102414763b6920e134353257978b0;path=/;HttpOnly;Max-Age=1800], [date: Thu, 06 Nov 2025 06:17:35 GMT], [server: istio-envoy]- body: {"choices":[{"message":{"content":"你好,Hollis!很高兴认识你~ 😊 你的名字很有英伦风格呢!有什么我可以帮你解答的吗?无论是生活琐事、兴趣爱好,还是学习工作上的问题,都可以随时告诉我哦~ (比如:名字的由来?需要起英文名建议?或者想聊聊其他话题?)","role":"assistant"},"finish_reason":"stop","index":0,"logprobs":null}],"object":"chat.completion","usage":{"prompt_tokens":6,"completion_tokens":64,"total_tokens":70},"created":1762409856,"system_fingerprint":null,"model":"deepseek-v3","id":"chatcmpl-f9bd2e05-7de1-40b2-8143-9681bfed3bc6"} |
第2次我问他”我叫什么”,并且同样指定memoryId为1234

发送的请求内容为:
1 | HTTP request:- method: POST- url: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions- headers: [Authorization: Beare...25], [User-Agent: langchain4j-openai], [Content-Type: application/json]- body: { "model" : "deepseek-v3", "messages" : [ { "role" : "user", "content" : "我叫Hollis" }, { "role" : "assistant", "content" : "你好,Hollis!很高兴认识你~ 😊 你的名字很有英伦风格呢!有什么我可以帮你解答的吗?无论是生活琐事、兴趣爱好,还是学习工作上的问题,都可以随时告诉我哦~ (比如:名字的由来?需要起英文名建议?或者想聊聊其他话题?)" }, { "role" : "user", "content" : "我叫什么" } ], "stream" : false} |
得到的响应为:
1 | HTTP response:- status code: 200- headers: [vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers, Accept-Encoding], [x-request-id: ccb4ece5-a821-49d5-aed4-2b11d748e66c], [x-dashscope-call-gateway: true], [content-type: application/json], [content-length: 725], [req-cost-time: 3844], [req-arrive-time: 1762409868494], [resp-start-time: 1762409872339], [x-envoy-upstream-service-time: 3843], [set-cookie: acw_tc=ccb4ece5-a821-49d5-aed4-2b11d748e66c38aa04a2af51da65fda0f37c8434ec2f;path=/;HttpOnly;Max-Age=1800], [date: Thu, 06 Nov 2025 06:17:52 GMT], [server: istio-envoy]- body: {"choices":[{"message":{"content":"哈哈,你刚刚告诉我你叫 **Hollis** 呀~ 😉 \n(难道是考验我的记忆力?放心,虽然AI没有真正的“记忆”,但在当前对话中,我会牢牢记住你的名字的!✨) \n\n需要我帮你做点什么吗,Hollis?比如: \n- 分析这个名字的含义或起源? \n- 设计一个酷炫的签名? \n- 或者…悄悄提醒你下次别忘? 😄","role":"assistant"},"finish_reason":"stop","index":0,"logprobs":null}],"object":"chat.completion","usage":{"prompt_tokens":75,"completion_tokens":92,"total_tokens":167},"created":1762409872,"system_fingerprint":null,"model":"deepseek-v3","id":"chatcmpl-ccb4ece5-a821-49d5-aed4-2b11d748e66c"} |
如果我换一个memoryId:

发送的请求:
1 | HTTP request:- method: POST- url: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions- headers: [Authorization: Beare...25], [User-Agent: langchain4j-openai], [Content-Type: application/json]- body: { "model" : "deepseek-v3", "messages" : [ { "role" : "user", "content" : "我叫什么" } ], "stream" : false} |
得到的响应:
1 | HTTP response:- status code: 200- headers: [vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers, Accept-Encoding], [x-request-id: e8e7cfcd-d72b-49e6-9bba-9d16ba48dfc1], [x-dashscope-call-gateway: true], [content-type: application/json], [content-length: 657], [req-cost-time: 2746], [req-arrive-time: 1762409890637], [resp-start-time: 1762409893383], [x-envoy-upstream-service-time: 2744], [set-cookie: acw_tc=e8e7cfcd-d72b-49e6-9bba-9d16ba48dfc145930277f0a09705bb02deda343411f8;path=/;HttpOnly;Max-Age=1800], [date: Thu, 06 Nov 2025 06:18:13 GMT], [server: istio-envoy]- body: {"choices":[{"message":{"content":"你还没有告诉我你的名字呢!😊 你可以告诉我你的名字,我会记住并和你打招呼哦!或者,你可以给自己取个有趣的昵称,比如“小星星”或“冒险家”——由你决定! \n\n等你告诉我后,我会说:“你好,[你的名字]!今天有什么想聊聊的吗?”✨","role":"assistant"},"finish_reason":"stop","index":0,"logprobs":null}],"object":"chat.completion","usage":{"prompt_tokens":5,"completion_tokens":68,"total_tokens":73},"created":1762409893,"system_fingerprint":null,"model":"deepseek-v3","id":"chatcmpl-e8e7cfcd-d72b-49e6-9bba-9d16ba48dfc1"} |
通过以上的对话日志我们可以发现,记忆其实是内存维护的,只不过在memoryId相同的情况下,后续的对话中,langchain4j会把之前的对话内容(提问+响应)也加到上下文中一起提交给LLM。
工具调用
之前我们介绍用等层次API做工具调用,大家都发现了,代码很复杂,写起来太麻烦了,而高层次api用起来就很简单了。
只需要用以下方式即可,
1 |
|
我们还是用了AiServices构造了一个LangChainAiService,并且指定工具列表。然后调用chat进行对话。

可以看到,他自动的调用了我给的工具,就像spring ai一样,不需要再自己写一堆调用的方法了。
看一下控制台里面的输出内容:
1 | HTTP request:- method: POST- url: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions- headers: [Authorization: Beare...25], [User-Agent: langchain4j-openai], [Content-Type: application/json]- body: { "model" : "deepseek-v3", "messages" : [ { "role" : "user", "content" : "2025年11月11日,杭州的气温怎样?" } ], "stream" : false, "tools" : [ { "type" : "function", "function" : { "name" : "getTemperatureByCityAndDate", "description" : "Get temperature by city and date", "parameters" : { "type" : "object", "properties" : { "city" : { "type" : "string", "description" : "city for get Temperature" }, "date" : { "type" : "string", "description" : "date for get Temperature" } }, "required" : [ "city", "date" ] } } } ]} |
可以发现这里面共有2次对话:
向LLM发起对话,指定工具
LLM返回,告知调用哪个工具,参数是什么
工具调用
向LL发起对话,告知工具返回结果
LL返回,告知最终结果
以上流程,和我们自己实现的流程是一样的。也就是其实是langchain4j帮我们把这个流程都给封装好了。
按需加载工具
有的时候,如果工具太多,一次性给到LLM可能会占用上下文,所以也可以按需加载,以下是langchain4j给出的一个官方示例:
1 | ToolProvider toolProvider = (toolProviderRequest) -> { |
AiServices支持配置一个toolProvider,这个toolProvider可以定义条件,按照指定条件组装不同的工具。以上就是仅在用户消息包含”booking”一词时添加 get_booking_details 工具。
