大家好,我是Java1234_小锋老师,最近更新《2027版本 LangChain4j 开发Java Agent 智能体 视频教程》专辑,感谢大家支持。

本课程主要介绍和讲解 LangChain4j 简介,阿里云百炼大模型平台接入,Ollama简介以及安装和使用,HelloWorld 实现,日志配置,集成SpringBoot,Ai Service 使用,对话与提示词工程(Prompt),结构化输出,会话记忆,工具调用(Function Calling),嵌入模型 与向量数据库,RAG(检索增强生成),MCP(模型上下文协议),多模态支持
视频教程+课件+源码打包下载:
链接:https://pan.baidu.com/s/1o-zRfndo1HHrS_uFroOiCw?pwd=1234
提取码:0000
LangChain4j 开发Java Agent智能体- 对话与提示词工程(Prompt)
LangChain4j为复杂的提示词工程和对话管理,提供了一套简洁、声明式的解决方案。其核心是通过AI服务(AiServices) 与消息注解,让开发者只需定义Java接口,即可轻松编排与大模型的交互。
💡 核心概念:对话消息与提示词工程
在LangChain4j中,每一次与大模型的交互,都围绕两类核心消息展开:
- SystemMessage (系统消息):为AI设定"角色"和"规则",定义其在整个对话中的身份、行为准则、回复风格,它由开发者预先设定,不随用户输入而改变。
- UserMessage (用户消息):代表用户在当前轮次中提出的具体问题或指令,是对话中的动态输入部分。
🚀 声明式开发:AI服务与消息注解
通过声明一个普通的Java接口,并使用 @SystemMessage 和 @UserMessage 注解,LangChain4j 的 AiServices 会自动生成其实现,并将方法调用转换为与大模型的交互。
1. @SystemMessage:为 AI 设定"人设"
@SystemMessage 注解于接口或方法之上,为后续所有对话奠定行为基础。
- 支持通过
fromResource属性从外部文件加载模板,便于管理和更新提示词。 - 当
@SystemMessage内容修改后,原有的聊天记忆(ChatMemory)会失效,以确保新"身份"不受旧历史影响。
2. @UserMessage:定义用户输入模板
@UserMessage 注解于方法参数上,用于指示哪个参数是用户输入。
- 若方法只有一个参数,则它默认作为用户消息,可省略
@UserMessage注解。 - 当方法有多个参数时,必须用
@UserMessage明确指定哪一个是用户输入。
3. 占位符与变量注入:{``{it}} 和 @V
为了构建动态提示词,可以在消息模板中使用占位符,然后在运行时将方法参数注入其中。
- 当方法只有一个参数时,可以用
{``{it}}作为默认占位符来引用它。 - 对于多个参数,需使用
@V("变量名")注解参数,并在提示词模板中使用{``{变量名}}来进行注入。
此外,LangChain4j 还内置了如 {``{current_date}} 的实用占位符,可直接在消息模板中获取当前日期。
💻 实战演练:构建一个智能客服助手
新建一个新的TravelAssistantService.我们使用openAI方式,流式响应输出
java
package com.java1234.service;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
import reactor.core.publisher.Flux;
/**
* 旅游助手
*/
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT, // 手动指定接入模型 手动装配
streamingChatModel = "openAiStreamingChatModel"
)
public interface TravelAssistantService {
@SystemMessage("你是一位专业的旅游顾问,请用专业、热情的语气回答问题。")
Flux<String> chat(String userMessage);
}
再新建一个MyTravelChatController
java
package com.java1234.controller;
import com.java1234.service.TravelAssistantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/travel")
public class MyTravelChatController {
@Autowired
private TravelAssistantService travelAssistantService;
@RequestMapping(value = "/chat",produces = "text/html;charset=utf-8")
public Flux<String> chat(String question) {
return travelAssistantService.chat(question);
}
}
浏览器请求测试:http://localhost:8080/travel/chat?question=南通有哪些好玩的地方?
我们看下请求日志,role:system 就是系统设定,role:user就是用户问题

大模型进行了专业友好的响应:

我们在看一个 @UserMessage 作为用户提示词模板的实例。
java
package com.java1234.service;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
import reactor.core.publisher.Flux;
/**
* 旅游助手
*/
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT, // 手动指定接入模型 手动装配
streamingChatModel = "openAiStreamingChatModel"
)
public interface TravelAssistantService {
/**
* --- 场景1: 简单问答 ---
* 注意:由于只有一个参数,@UserMessage 注解可以省略,框架会自动将参数视为用户消息
* @param userMessage
* @return
*/
@SystemMessage("你是一位专业的旅游顾问,请用专业、热情的语气回答问题。")
Flux<String> chat(String userMessage);
/**
* --- 场景2: 结构化评价分析 (使用 {{it}} 占位符) ---
* 注解 @UserMessage,并通过 {{it}} 指定用户输入的位置
* @param review
* @return
*/
@UserMessage("分析以下旅游评价的正面或负面情感:\n\n评价:{{it}}")
Flux<String> analyzeSentiment(String review);
}
MyTravelChatController 里在加下analyze方法:
java
package com.java1234.controller;
import com.java1234.service.TravelAssistantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/travel")
public class MyTravelChatController {
@Autowired
private TravelAssistantService travelAssistantService;
@RequestMapping(value = "/chat",produces = "text/html;charset=utf-8")
public Flux<String> chat(String question) {
return travelAssistantService.chat(question);
}
@RequestMapping(value = "/analyze",produces = "text/html;charset=utf-8")
public Flux<String> analyze(String review) {
return travelAssistantService.analyzeSentiment(review);
}
}
浏览器输入测试:http://localhost:8080/travel/analyze?review=黄山真是太美了
浏览器返回:

最后我们在看一个@SystemMessage和@UserMessage,以及@V注解参数使用的示例:
java
package com.java1234.service;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
import reactor.core.publisher.Flux;
/**
* 旅游助手
*/
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT, // 手动指定接入模型 手动装配
streamingChatModel = "openAiStreamingChatModel"
)
public interface TravelAssistantService {
/**
* --- 场景1: 简单问答 ---
* 注意:由于只有一个参数,@UserMessage 注解可以省略,框架会自动将参数视为用户消息
* @param userMessage
* @return
*/
@SystemMessage("你是一位专业的旅游顾问,请用专业、热情的语气回答问题。")
Flux<String> chat(String userMessage);
/**
* --- 场景2: 结构化评价分析 (使用 {{it}} 占位符) ---
* 注解 @UserMessage,并通过 {{it}} 指定用户输入的位置
* @param review
* @return
*/
@UserMessage("分析以下旅游评价的正面或负面情感:\n\n评价:{{it}}")
Flux<String> analyzeSentiment(String review);
/**
* --- 场景3: 动态定制行程单 (使用 @V 注入多参数) ---
* 使用 @UserMessage 明确指定用户输入是问题部分,并用 @V 注入其他变量
* @param destination
* @param days
* @param preference
* @return
*/
@SystemMessage("你是一个旅行社的智能行程规划系统。")
@UserMessage("根据以下目的地:{{destination}},天数:{{days}},偏好:{{preference}},生成一份详细的旅行计划。")
Flux<String> planTrip(@V("destination") String destination, @V("days") int days, @V("preference") String preference);
}
MyTravelChatControllerf类加下plan方法:
java
package com.java1234.controller;
import com.java1234.service.TravelAssistantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/travel")
public class MyTravelChatController {
@Autowired
private TravelAssistantService travelAssistantService;
@RequestMapping(value = "/chat",produces = "text/html;charset=utf-8")
public Flux<String> chat(String question) {
return travelAssistantService.chat(question);
}
@RequestMapping(value = "/analyze",produces = "text/html;charset=utf-8")
public Flux<String> analyze(String review) {
return travelAssistantService.analyzeSentiment(review);
}
@RequestMapping(value = "/plan",produces = "text/html;charset=utf-8")
public Flux<String> plan() {
return travelAssistantService.planTrip("成都", 2, "美食和熊猫");
}
}
最后我们测试下:http://localhost:8080/travel/plan

结构化输出
LangChain4j 的结构化输出功能,能将大语言模型(LLM)生成的 JSON 文本自动转换为 Java 对象,让 Java 开发者能像调用普通方法一样获取结果,无需手动处理 JSON 或使用特定 Prompt。
结构化输出的三种方式
LangChain4j 主要提供三种实现结构化输出的方式,它们在可靠性和使用场景上有所区别。
| 方式 | 可靠性 | 原理与机制 | 适用场景 |
|---|---|---|---|
| JSON Schema | ⭐ 最可靠 | 将 JSON Schema 作为API专用属性传递给支持的模型,约束力最强。 | 追求高精度、高可靠性,并使用了支持的模型(如 OpenAI、Azure OpenAI 等)。 |
| JSON Mode + Prompting | ⭐⭐ 中等 | 强制 LLM 输出 JSON,但仍需在 Prompt 中包含 Schema 说明。 | 模型部分支持 JSON 模式,可靠性要求不是首要时。 |
| Prompting Only | ⭐ 最弱 | 仅通过 Prompt 要求 LLM 输出特定格式,无底层机制保障。 | 兼容性要求高,或作为不支持 JSON Schema 的模型的备选方案。 |
具体示例
第一步:第一数据模型
java
package com.java1234.model;
import dev.langchain4j.model.output.structured.Description;
import java.util.List;
// 使用 Java 16+ 的 Record 特性,编译器会自动生成构造器、equals/hashCode等方法
@Description("按主题推荐的书籍列表")
public record TopicBooks(
@Description("用户指定的书籍主题,例如:Java 并发、Spring Boot") String topic,
@Description("推荐书目列表,每项格式:书名 - 作者,推荐 3~5 本") List<String> books
) {}
第二步:定义AI Service接口
java
package com.java1234.service;
import com.java1234.model.TopicBooks;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;
/**
* 图书助手
*/
@AiService(
wiringMode = AiServiceWiringMode.EXPLICIT, // 手动指定接入模型 手动装配
chatModel = "openAiChatModel" // 指定模型
)
public interface BookAssistantService {
@SystemMessage("你是一位资深图书推荐专家,只推荐真实存在的经典书籍。")
@UserMessage("请根据主题「{{topic}}」推荐相关书籍。")
TopicBooks recommendBooks(@V("topic") String topic);
}
第三步:定义MyBookChatController
java
package com.java1234.controller;
import com.java1234.model.TopicBooks;
import com.java1234.service.BookAssistantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/book")
public class MyBookChatController {
@Autowired
private BookAssistantService bookAssistantService;
@RequestMapping("/recommend")
public String recommend() {
String topic="Java";
TopicBooks topicBooks = bookAssistantService.recommendBooks(topic);
System.out.println(topicBooks);
return "ok";
}
}
我们来测试下,浏览器输入:http://localhost:8080/book/recommend
运行输出:可以看到请求的时候,就约定大模型返回的json格式
2026-06-02T19:34:30.765+08:00 INFO 19464 --- [nio-8080-exec-1] d.l.http.client.log.LoggingHttpClient : HTTP request:
- method: POST
- url: https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
- headers: [Authorization: Beare...bd], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
"model" : "qwen3.6-plus",
"messages" : [ {
"role" : "system",
"content" : "你是一位资深图书推荐专家,只推荐真实存在的经典书籍。"
}, {
"role" : "user",
"content" : "请根据主题「Java」推荐相关书籍。\nYou must answer strictly in the following JSON format: {\n\"topic\": (用户指定的书籍主题,例如:Java 并发、Spring Boot; type: string),\n\"books\": (推荐书目列表,每项格式:书名 - 作者,推荐 3~5 本; type: array of string)\n}"
} ],
"temperature" : 0.7,
"stream" : false
}
2026-06-02T19:34:42.624+08:00 INFO 19464 --- [nio-8080-exec-1] d.l.http.client.log.LoggingHttpClient : HTTP response:
- status code: 200
- headers: [x-request-id: 03172ed5-4967-9959-9927-34f9f3295e55], [date: Tue, 02 Jun 2026 11:34:42 GMT], [x-dashscope-call-gateway: true], [server: istio-envoy], [x-envoy-upstream-service-time: 11146], [transfer-encoding: chunked], [vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers, Accept-Encoding], [content-type: application/json], [req-arrive-time: 1780400071309], [req-cost-time: 11153], [resp-start-time: 1780400082463]
- body: {"choices":[{"message":{"content":"```json\n{\n \"topic\": \"Java\",\n \"books\": [\n \"Java核心技术 卷I - Cay S. Horstmann\",\n \"Effective Java(第3版) - Joshua Bloch\",\n \"Java并发编程实战 - Brian Goetz\",\n \"Spring实战(第5版) - Craig Walls\"\n ]\n}\n```","reasoning_content":"嗯,用户让我根据"Java"主题推荐相关书籍。首先,我需要确定用户的具体需求。他们可能刚开始学Java,或者想深入某个领域,比如并发编程、框架应用等。不过用户只提到了"Java"这个主题,没有更具体的细分,所以我得考虑推荐一些经典且覆盖面广的书籍。\n\n首先想到的是《Java核心技术》,这本书分为卷I和卷II,是学习Java基础的核心教材,适合初学者和有一定基础的读者。作者Cay S. Horstmann很有名,内容全面,应该不错。\n\n接下来,关于设计模式和最佳实践,《Effective Java》是必读的。Joshua Bloch写的这本书,第三版更新到Java 9,涵盖了现代Java的最佳实践,对提升代码质量很有帮助。\n\n然后,并发编程是Java的重要部分,用户可能需要这方面的资料。《Java并发编程实战》由Brian Goetz等人合著,是并发领域的权威书籍,详细讲解了并发原理和实用技巧,虽然有点老,但内容依然适用。\n\n另外,Spring框架在Java开发中非常流行,所以推荐《Spring in Action》。Craig Walls的这本书从基础到高级,适合想学习Spring生态的开发者,尤其是Spring Boot和微服务部分。\n\n最后,考虑是否要加入更深入的书籍,比如JVM相关的,但用户可能更需要实用和广泛认可的。不过四本可能够了,用户要求3-5本。检查一下这些书是否都是经典且真实存在,作者是否正确。确认无误后,按格式整理成JSON。确保书名和作者正确,顺序合理,覆盖基础、最佳实践、并发和框架应用,这样应该能满足大多数Java学习者的需求。","role":"assistant"},"finish_reason":"stop","index":0,"logprobs":null}],"object":"chat.completion","usage":{"prompt_tokens":107,"completion_tokens":433,"total_tokens":540,"completion_tokens_details":{"reasoning_tokens":346,"text_tokens":433},"prompt_tokens_details":{"text_tokens":107}},"created":1780400082,"system_fingerprint":null,"model":"qwen3.6-plus","id":"chatcmpl-03172ed5-4967-9959-9927-34f9f3295e55"}
TopicBooks[topic=Java, books=[Java核心技术 卷I - Cay S. Horstmann, Effective Java(第3版) - Joshua Bloch, Java并发编程实战 - Brian Goetz, Spring实战(第5版) - Craig Walls]]