ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Java开发者AI入门实战:基于LangChain4j构建智能对话应用

2026/8/8 8:28:51 拓冰建站 浏览量
Java开发者AI入门实战:基于LangChain4j构建智能对话应用

1. 项目概述:从Java到AI的“破冰”之旅

作为一名在Java生态里摸爬滚打了十多年的老码农,我最近明显感觉到,身边讨论AI应用落地的声音越来越多了。从各种大模型API的开放,到像LangChain这样的框架出现,都在传递一个信号:AI能力正在从研究实验室走向工程化、产品化。对于我们这些习惯了Spring Boot、MyBatis、微服务架构的Java开发者来说,这既是机遇也是挑战。机遇在于,我们可以用自己熟悉的工具栈,将强大的AI能力快速集成到现有的企业级应用中;挑战在于,AI开发似乎有一套全新的“语言”和范式,上手门槛不低。

于是,我决定动手实践,目标很明确:用最熟悉的Java,写一个最简单的AI对话程序。我选择了LangChain4j作为切入点,它是Java生态中对标Python LangChain的框架,旨在让Java开发者也能轻松构建基于大语言模型的应用。这个“入门”项目,就是一次从零开始的探索记录。如果你也在寻找一条从传统Java后端开发平滑过渡到AI应用开发的路径,那么这篇记录或许能给你一些直接的参考。整个过程,我会聚焦于如何用最少的配置、最清晰的步骤,让一个Java程序“开口说话”。

2. 环境准备与核心依赖解析

动手之前,我们先得把“战场”布置好。对于一个Java项目,这通常意味着确定技术栈、引入依赖、配置环境变量。这次的项目核心是LangChain4j,但它本身只是一个“粘合剂”,真正提供AI大脑的是背后的大语言模型。

2.1 项目骨架与依赖选型

我选择使用Spring Boot 3.x来快速搭建项目骨架,这能帮我们省去大量基础配置的麻烦。在pom.xml中,我们需要引入几个核心依赖:

<dependencies> <!-- Spring Boot Web (可选,用于构建API接口) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- LangChain4j 核心 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>0.31.0</version> <!-- 请使用最新稳定版 --> </dependency> <!-- LangChain4j 与 OpenAI 模型集成 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>0.31.0</version> </dependency> <!-- 其他可选工具,如用于文档加载的Apache PDFBox --> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.29</version> </dependency> </dependencies>

为什么是这些依赖?

  • langchain4j:提供了抽象层和核心组件,如模型、工具、记忆、链等概念的统一接口。
  • langchain4j-open-ai:这是一个“适配器”模块。LangChain4j支持多种模型提供商(如OpenAI、Azure OpenAI、Ollama本地模型等)。我们选择OpenAI是因为其API稳定、文档丰富,最适合入门。如果你想用其他模型,只需替换成对应的模块,如langchain4j-ollama
  • spring-boot-starter-web:非必须,但如果你想快速把对话能力封装成一个HTTP API,它会非常方便。

注意:版本兼容性。LangChain4j和其子模块的版本必须严格一致,否则可能会引发奇怪的类找不到错误。建议在 官方GitHub仓库 查看最新推荐版本。

2.2 模型API密钥配置

使用云端大模型(如OpenAI)的核心前提是获得API密钥。你需要注册相应的平台账号并创建密钥。切记,API密钥是高度敏感信息,绝不能硬编码在代码中或提交到版本库。

最安全便捷的方式是使用环境变量。在项目的application.yml(或application.properties)中配置:

# application.yml openai: api-key: ${OPENAI_API_KEY:} # 从环境变量OPENAI_API_KEY读取,如果为空则默认为空字符串

然后在你的系统环境变量、IDE运行配置或服务器上设置名为OPENAI_API_KEY的变量,值为你的真实密钥。

实操心得:密钥管理分级对于企业级项目,建议采用更专业的密钥管理服务(如HashiCorp Vault、AWS Secrets Manager)。但在开发和测试阶段,使用环境变量是平衡安全与便利性的最佳实践。我习惯在~/.bash_profile~/.zshrc中设置,这样所有本地项目都能共享。

2.3 基础代码结构搭建

创建一个简单的Spring Boot应用入口:

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Langchain4jDemoApplication { public static void main(String[] args) { SpringApplication.run(Langchain4jDemoApplication.class, args); } }

接下来,我们将创建一个配置类来初始化LangChain4j的核心组件——AiServices。这是LangChain4j一个非常强大的特性,它允许你通过声明式接口来定义AI服务的行为。

3. 核心概念与第一个对话程序实现

环境就绪后,我们正式进入编码环节。LangChain4j的设计哲学是“约定优于配置”,它通过几个核心抽象,让我们能以极简的方式组织AI交互逻辑。

3.1 理解LangChain4j的核心抽象

在写代码前,花几分钟理解这几个概念,能让你事半功倍:

  1. Model(模型):AI的大脑,例如OpenAI的GPT-4、GPT-3.5-Turbo。在LangChain4j中,通过ChatLanguageModel接口来交互。
  2. ChatMemory(聊天记忆):决定对话是否有上下文。没有记忆,每次问答都是独立的;有记忆,AI能记住之前的对话历史,实现多轮对话。
  3. Tool(工具):赋予AI调用外部能力的手段。比如,你可以定义一个查询天气的Tool,AI在对话中就能主动使用它来获取实时信息。
  4. AiServices:这是LangChain4j的“灵魂”。它允许你定义一个Java接口,然后动态生成这个接口的实现类。你只需要在接口方法上添加注解,描述你想让AI做什么,剩下的复杂交互(组装提示词、调用模型、解析响应)都由框架完成。

我们的第一个目标,是实现一个无记忆的、单次问答的对话程序。这是最简单的形态。

3.2 声明式AI服务接口

我们创建一个接口ConversationAIService

import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.V; import dev.langchain4j.service.spring.AiService; // 使用@AiService注解,Spring会自动创建它的Bean @AiService public interface ConversationAIService { // @SystemMessage 定义AI的“系统角色”或基础设定。这段提示词会隐式地影响AI的所有回复。 @SystemMessage("你是一个乐于助人且知识渊博的Java技术专家,用中文回答所有问题。") // @UserMessage 定义用户输入。这里的{question}是一个占位符,会被方法参数替换。 // @V 注解用于标注方法参数,并可以给它起别名(这里别名为“question”),在提示词中引用。 String chat(@UserMessage("{{question}}") @V("question") String userQuestion); }

代码解读与技巧:

  • @SystemMessage:这是塑造AI“人格”的关键。你可以在这里设定回复风格(如“简洁明了”、“幽默风趣”)、知识范围(如“专注于后端开发”)或行为准则(如“如果不知道,就如实承认”)。好的系统提示词能极大提升回复质量。
  • @UserMessage中的{{question}}:这是Mustache模板语法。运行时,{{question}}会被@V("question")标注的参数值替换。这比简单的字符串拼接更清晰、安全。
  • 接口方法返回String:表示我们期望AI返回一个文本字符串。

3.3 配置与注入模型

现在我们需要告诉LangChain4j,使用哪个模型来为这个AiService提供能力。创建一个配置类AiConfig

import dev.langchain4j.model.openai.OpenAiChatModel; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.time.Duration; @Configuration public class AiConfig { @Value("${openai.api-key}") private String openAiApiKey; @Bean public OpenAiChatModel openAiChatModel() { // 使用建造者模式创建OpenAI模型实例 return OpenAiChatModel.builder() .apiKey(openAiApiKey) // 注入密钥 .modelName("gpt-3.5-turbo") // 指定模型,gpt-3.5-turbo性价比高,适合入门 .temperature(0.7) // 创造性参数,0.0最确定,1.0最随机。0.7是一个平衡值。 .timeout(Duration.ofSeconds(60)) // 设置超时时间 .logRequests(true) // 开启请求日志,调试时非常有用 .logResponses(true) .build(); } }

关键参数解析:

  • modelName:对于OpenAI,常用的是gpt-3.5-turbo(快、便宜)和gpt-4(更强、更贵)。入门从gpt-3.5-turbo开始完全足够。
  • temperature:这是控制AI输出随机性的核心参数。
    • 接近0.0(如0.1):输出非常确定、一致,适合事实性问答、代码生成。
    • 接近1.0(如0.9):输出创造性、多样性高,适合写故事、头脑风暴。
    • 0.7是一个常用默认值,在准确性和创造性之间取得平衡。你可以根据场景调整。
  • timeout:网络请求超时。大模型响应有时较慢,设置一个合理的超时(如60秒)避免线程长时间阻塞。
  • logRequests/responses强烈建议在开发阶段开启。这会在控制台打印出实际发送给AI的提示词和收到的回复,是调试提示词工程的最重要工具。

3.4 编写控制器进行测试

最后,我们创建一个简单的REST控制器,来测试我们的AI对话服务:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ConversationController { // Spring会自动注入我们通过@AiService定义的接口实现 @Autowired private ConversationAIService conversationAIService; @GetMapping("/chat") public String chat(@RequestParam String question) { long startTime = System.currentTimeMillis(); String answer = conversationAIService.chat(question); long cost = System.currentTimeMillis() - startTime; System.out.printf("问题:%s | 耗时:%dms%n", question, cost); return answer; } }

3.5 运行与验证

启动Spring Boot应用。打开浏览器或使用curl、Postman等工具进行测试:

GET http://localhost:8080/chat?question=请用Java写一个Hello World程序

你应该会收到一个格式良好的JavaHelloWorld示例代码。同时,在应用控制台,你会看到类似如下的日志,这就是我们开启的logRequestslogResponses的效果:

Request to OpenAI { "model": "gpt-3.5-turbo", "messages": [ {"role": "system", "content": "你是一个乐于助人且知识渊博的Java技术专家,用中文回答所有问题。"}, {"role": "user", "content": "请用Java写一个Hello World程序"} ], "temperature": 0.7, ... } Response from OpenAI { "id": "chatcmpl-...", "choices": [ { "message": { "role": "assistant", "content": "以下是Java语言的Hello World程序示例:\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n System.out.println(\"Hello, World!\");\n }\n}\n```\n\n// 解释..." } } ], ... }

恭喜!至此,你的第一个Java AI对话程序已经跑通了。它虽然简单,但完整展示了LangChain4j最核心、最优雅的使用方式:通过声明式接口,将复杂的AI交互抽象成简单的Java方法调用。

4. 功能进阶:实现多轮对话与上下文记忆

单次问答满足了简单需求,但真正的对话是有来有回的。用户可能会问:“我上一句话提到的那个方法,它的时间复杂度是多少?”这就需要AI能记住之前的对话历史。在LangChain4j中,这是通过ChatMemory实现的。

4.1 理解ChatMemory的工作原理

ChatMemory本质上是一个存储对话消息(ChatMessage)的容器。每次交互,用户消息(UserMessage)和AI回复(AiMessage)都会被存入记忆。当下一次请求到来时,这些历史消息会和新的用户消息一起,作为上下文发送给AI模型。

LangChain4j提供了几种内存实现:

  • MessageWindowChatMemory:固定窗口记忆。只保留最近N条消息,防止上下文过长(消耗大量Token且可能超出模型限制)。
  • TokenWindowChatMemory:固定Token数记忆。更精确地控制发送给模型的上下文长度。
  • PersistentChatMemory:可持久化到数据库的记忆,实现会话的长期保存。

对于大多数场景,MessageWindowChatMemory就足够了。

4.2 改造服务以支持多轮对话

我们需要做两处修改:

1. 在AI服务接口中增加记忆参数:

import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.V; import dev.langchain4j.service.spring.AiService; @AiService public interface ConversationAIService { @SystemMessage("你是一个乐于助人且知识渊博的Java技术专家,用中文回答所有问题。") // 新增一个@MemoryId注解的参数。具有相同MemoryId的对话,将共享同一个ChatMemory实例。 String chat(@MemoryId String sessionId, @UserMessage("{{question}}") @V("question") String userQuestion); }

关键点:@MemoryId

  • 这个注解标识了对话的“会话ID”。框架会根据这个ID,去查找或创建一个对应的ChatMemory对象。
  • 在Web应用中,这个sessionId通常可以来自用户的登录ID、前端生成的随机UUID,或者HTTP会话ID。它确保了不同用户或不同对话线程之间的记忆是隔离的。

2. 在配置类中提供ChatMemory Bean:

import dev.langchain4j.memory.ChatMemory; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AiConfig { // ... 之前的OpenAiChatModel Bean ... // 定义一个ChatMemory的Bean。这里使用消息窗口记忆,最大保留10轮对话(即10条用户消息+10条AI消息)。 @Bean public ChatMemory chatMemory() { return MessageWindowChatMemory.withMaxMessages(20); // 注意:参数是总消息条数 } }

4.3 改造控制器并测试

修改控制器,传入一个会话ID。这里为了演示,我们简单地从请求参数中获取:

@RestController public class ConversationController { @Autowired private ConversationAIService conversationAIService; @GetMapping("/chat") public String chat(@RequestParam String sessionId, @RequestParam String question) { // 现在需要传入sessionId return conversationAIService.chat(sessionId, question); } }

现在进行多轮对话测试:

  1. 第一轮:GET /chat?sessionId=user123&question=什么是Spring Boot?
  2. 第二轮:GET /chat?sessionId=user123&question=它和传统的Spring MVC有什么区别?

在第二轮中,AI在回答时,其上下文里会包含第一轮的问题和答案,因此它能理解“它”指代的是Spring Boot,从而给出一个对比性的回答。你可以通过查看请求日志,确认历史消息被正确包含在了后续的请求中。

实操心得:记忆窗口大小的选择MessageWindowChatMemory.withMaxMessages(20)中的数字20需要谨慎选择。这受到两个限制:

  1. 模型上下文长度限制:例如gpt-3.5-turbo通常有16K Token的上下文窗口。一条消息可能包含几十到几百个Token(英文约1 Token对应0.75个单词)。20条消息的文本量一般远未达到上限。
  2. 成本与效率:发送给模型的上下文越长,消耗的Token就越多(费用越高),并且模型处理时间也可能略长。原则是:在保证对话连贯性的前提下,使用尽可能小的记忆窗口。对于一般技术支持、问答场景,保留5-10轮对话通常足够。

5. 能力扩展:为AI装配“工具”

如果AI只能基于训练时的知识回答问题,那它的能力是静态的。而“工具”的引入,让AI具备了与外部世界动态交互的能力,比如查询数据库、调用天气API、执行计算等。在LangChain4j中,你可以将任何Java方法暴露为AI可用的工具。

5.1 定义一个简单的工具

假设我们想让AI能查询当前时间。我们创建一个工具类DateTimeTools

import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Component // 注册为Spring Bean public class DateTimeTools { // 使用 @Tool 注解标记这是一个AI可用的工具。 // “name”和“description”非常重要!AI根据这些描述来决定是否以及何时调用此工具。 @Tool(name = "getCurrentTime", description = "获取当前的日期和时间。当用户询问时间、日期、现在几点时使用此工具。") public String getCurrentTime() { LocalDateTime now = LocalDateTime.now(); // 返回格式化的时间字符串 return now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒")); } // 可以定义更多工具... @Tool(name = "getCurrentDate", description = "获取当前的日期。") public String getCurrentDate() { return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")); } }

工具定义的精髓:namedescription

  • name:工具的唯一标识,AI在内部思考时会引用这个名字。
  • description:这是提示词工程的一部分。你需要用清晰、无歧义的自然语言描述这个工具的功能和调用时机。AI完全依赖这段描述来判断用户的问题是否应该触发这个工具。描述写得越好,AI调用工具的准确率就越高。

5.2 在AI服务中启用工具

我们需要修改AI服务接口和配置,告诉框架我们有哪些工具可用。

1. 修改AI服务接口:

import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.V; import dev.langchain4j.service.spring.AiService; @AiService public interface ConversationAIService { // 在SystemMessage中,可以加入关于工具使用的引导。这不是必须的,但有时有帮助。 @SystemMessage({ "你是一个乐于助人且知识渊博的Java技术专家,用中文回答所有问题。", "如果你需要获取实时信息,例如当前时间,你可以使用我为你提供的工具。" }) String chat(@MemoryId String sessionId, @UserMessage("{{question}}") @V("question") String userQuestion); }

2. 关键步骤:在配置中装配工具

这是将工具“注入”给AI的关键一步。我们需要修改之前的AiConfig,在创建AiService时指定工具Bean。

import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.service.AiServices; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.time.Duration; import java.util.List; @Configuration public class AiConfig { @Bean public OpenAiChatModel openAiChatModel() { // ... 同上,创建模型 ... } @Bean public ChatMemory chatMemory() { // ... 同上,创建记忆 ... } // 注入所有被@Tool注解的工具类。Spring会自动收集它们。 @Autowired private List<Object> toolBeans; // 创建ConversationAIService的Bean。这里我们不再依赖@AiService的自动代理,而是手动创建以获得更多控制。 @Bean public ConversationAIService conversationAIService(OpenAiChatModel model, ChatMemory memory) { return AiServices.builder(ConversationAIService.class) .chatLanguageModel(model) // 绑定模型 .chatMemory(memory) // 绑定记忆 .tools(toolBeans) // 绑定所有工具!这是核心 .build(); } }

注意:当我们手动在配置类中创建AiService的Bean时,原来接口上的@AiService注解就可以移除了,或者保留也不会冲突,但为了清晰,我建议移除。

5.3 测试工具调用

重启应用,进行测试:

  • 提问:现在几点了?今天是几号?

观察控制台日志,你会看到与之前不同的请求结构。AI不会直接回答,而是先输出一个特殊的“工具调用请求”(ToolCall),框架会拦截这个请求,执行对应的getCurrentTime()getCurrentDate()方法,然后将执行结果(例如“2024年05月20日 15时30分25秒”)作为上下文再次发送给AI。最后,AI会生成一个包含该信息的自然语言回复,比如“现在是2024年05月20日 15时30分25秒”。

这个过程完全自动!你不需要在代码中写任何判断逻辑。AI自己决定何时调用工具,框架负责调度和执行。这就是LangChain4j智能体(Agent)能力的雏形。

避坑指南:工具执行权限与安全

  • 工具本质上是执行你编写的Java代码。务必谨慎暴露高权限或危险操作,如执行系统命令、删除文件、访问敏感数据库等。
  • 最佳实践是为AI工具创建一个权限受控的“沙箱环境”。例如,查询数据库的工具只能执行SELECT操作,并且对可访问的表和字段进行严格限制。

6. 工程化实践:优化、监控与问题排查

将AI功能集成到生产环境,除了核心功能,还需要考虑性能、稳定性和可观测性。这里分享几个从项目实践中总结的关键点。

6.1 性能优化与超时控制

大模型API调用是网络IO密集型操作,响应时间从几百毫秒到数十秒不等,必须做好超时和熔断。

1. 配置合理的超时:在创建OpenAiChatModel时,我们已经设置了timeout。但还需要在HTTP客户端层面(如果你用的是默认的OkHttp)和Spring MVC层面进行配置。

@Bean public OpenAiChatModel openAiChatModel() { return OpenAiChatModel.builder() .apiKey(openAiApiKey) .modelName("gpt-3.5-turbo") .temperature(0.7) // 设置整个请求的超时(包括连接、读写) .timeout(Duration.ofSeconds(30)) // 更细粒度的配置(如果框架底层支持) .connectTimeout(Duration.ofSeconds(10)) .readTimeout(Duration.ofSeconds(25)) .logRequests(true) .logResponses(true) .build(); }

2. 使用异步调用避免阻塞:对于高并发场景,同步调用会迅速耗尽Web容器的线程池。可以将AI服务调用改为异步。

import org.springframework.scheduling.annotation.Async; import java.util.concurrent.CompletableFuture; @Service public class AsyncAIService { @Autowired private ConversationAIService conversationAIService; @Async // 需要配合@EnableAsync使用 public CompletableFuture<String> chatAsync(String sessionId, String question) { String answer = conversationAIService.chat(sessionId, question); return CompletableFuture.completedFuture(answer); } } // 在Controller中 @GetMapping("/chat-async") public CompletableFuture<String> chatAsync(@RequestParam String sessionId, @RequestParam String question) { return asyncAIService.chatAsync(sessionId, question); }

6.2 日志、监控与成本控制

1. 结构化日志:开启的logRequestslogResponses在开发时很好用,但在生产环境会产生大量日志。建议将其关闭,或集成到像SLF4J这样的日志框架中,按级别(DEBUG)输出,并记录到独立的文件。

你可以实现一个自定义的StreamingChatLanguageModelListener来更灵活地处理请求和响应日志。

2. Token使用监控:API调用成本直接与Token消耗挂钩。OpenAI的响应头中会包含本次请求消耗的Token数。LangChain4j的响应对象Response<T>也包含这些信息。你应该捕获并记录这些数据。

// 这是一个概念性示例,实际AiServices的返回类型需要调整或使用更低级别的API // 使用 AiServices.create(...) 返回的代理对象,其方法返回值是Response<String> import dev.langchain4j.model.output.Response; // 在配置中创建返回Response的服务 public interface ConversationAIServiceWithResponse { Response<String> chat(@MemoryId String sessionId, @UserMessage String question); } // 调用后可以获取元数据 Response<String> response = service.chat(sessionId, question); String answer = response.content(); TokenUsage tokenUsage = response.tokenUsage(); // 包含prompt, completion, total tokens System.out.println("消耗Token: " + tokenUsage.totalTokenCount());

3. 限流与降级:

  • 限流:使用Guava RateLimiter或Resilience4j对AI服务接口进行限流,防止突发流量导致API费用激增或被供应商限速。
  • 降级:当AI服务不可用或响应超时时,应有备选方案。例如,对于常见问题,可以降级到本地知识库检索;或者返回一个友好的提示信息。

6.3 常见问题排查实录

在开发过程中,我遇到了几个典型问题,这里记录下来供你参考:

问题1:AI不调用我定义的工具。

  • 可能原因1:工具描述不清晰。检查@Tool注解中的description,是否准确描述了工具的功能和使用场景?用更具体、场景化的语言重写。
  • 可能原因2:模型能力不足gpt-3.5-turbo的工具调用能力比gpt-4稍弱。对于复杂的工具调用逻辑,可以尝试切换到gpt-4模型。
  • 排查方法:开启logRequests,查看发送给AI的完整提示词。确认系统提示词和工具描述是否都被正确包含。有时在系统提示词中明确指令“你可以使用以下工具:...”会有帮助。

问题2:多轮对话中,AI似乎忘记了很早之前的内容。

  • 原因ChatMemory的窗口大小(maxMessages)设置得太小。历史消息被移出了记忆窗口。
  • 解决方案:根据对话复杂度和模型上下文长度,适当增加maxMessages。或者,对于需要长期记忆的关键信息,可以考虑在用户或系统消息中主动进行总结并再次强调。

问题3:响应速度慢,有时超时。

  • 原因分析
    • 网络问题:到AI服务提供商的网络不稳定。
    • 模型负载:所选模型(如gpt-4)本身响应较慢,或在高峰期。
    • 提示词过长:过长的上下文会导致模型处理时间变长。
  • 应对策略
    • 设置合理的超时时间(如30秒)并做好超时处理。
    • 考虑使用更快的模型(如gpt-3.5-turbo)处理实时性要求高的请求。
    • 优化ChatMemory策略,定期清理或总结旧对话,缩短上下文长度。
    • 实现客户端重试机制(需注意API的幂等性)。

问题4:AI的回复格式不符合预期。

  • 原因:这属于“提示词工程”的范畴。AI的输出具有随机性。
  • 解决方案:在@SystemMessage@UserMessage中给出更明确的指令。例如:
    @SystemMessage("你是一个Java代码助手。请始终将代码放在Markdown代码块中,并指定语言为java。") @UserMessage("请写一个单例模式。{{code}}")
    你甚至可以要求AI以特定的JSON格式返回,然后使用Json工具类进行解析,实现结构化输出。

通过这个从零到一的入门项目,我们不仅实现了一个能对话的Java程序,更搭建了一个可扩展的AI能力集成框架。LangChain4j通过其优雅的抽象,极大地降低了Java开发者进入AI应用开发领域的门槛。后续,你可以在此基础上探索更多高级特性,如文档检索(RAG)、复杂智能体工作流等,将AI能力深度融入你的业务系统之中。