别再羡慕Python开发者了!Java生态终于有了完整的LLM开发框架,一文搞懂LangChain4j所有核心功能
为什么值得关注:Java开发者的LLM开发困境与破局之道
LLM浪潮下的语言之争
2023年开始,大语言模型(LLM)浪潮席卷整个科技行业。从ChatGPT到Claude,从GPT-4到各类开源模型,AI能力正在重塑软件开发的方式。然而,对于长期使用Java生态的开发者而言,这场AI革命似乎显得有些”遥远”。
当你想要在Java项目中集成LLM能力时,你会发现:
- 官方SDK往往只提供Python版本
- 现有的Java包装库功能残缺,很多只是简单的HTTP调用封装
- 缺乏像LangChain这样的一站式解决方案
- 内存管理、提示词模板、链式调用等高级功能需要从零实现
- 流式输出、工具调用、代理等能力在Java世界几乎是空白
LangChain4j是什么
LangChain4j正是为解决这些问题而生。它是LangChain的Java实现,专为JVM生态设计。它不仅仅是一个API客户端,而是一套完整的LLM应用开发框架,提供了:
- 统一的模型抽象:支持OpenAI、Anthropic、Google、Azure、HuggingFace等数十种LLM提供商
- 丰富的组件库:包括提示词模板、对话记忆、链式调用、工具系统、代理机制等
- 企业级特性:流式响应、异步处理、错误重试、熔断降级
- RAG支持:内置嵌入模型、向量存储、检索增强生成完整支持
- 活跃的社区:持续更新,积极响应开发者需求
截至目前,LangChain4j已经在GitHub上获得了大量star,被众多Java项目采用,是Java生态中最具影响力的LLM开发框架。
环境搭建:从零开始的完整指南
前置要求
在开始之前,请确保你的开发环境满足以下要求:
- JDK 8或更高版本:推荐使用JDK 17或JDK 21,以获得更好的性能和最新的语言特性
- 构建工具:Maven 3.8+ 或 Gradle 7+
- IDE:IntelliJ IDEA(推荐)、Eclipse或VS Code均可
Maven项目配置
创建一个新的Maven项目,在pom.xml中添加LangChain4j的核心依赖。LangChain4j采用模块化设计,你需要根据实际需求引入相应的模块:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>langchain4j-tutorial</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<langchain4j.version>1.0.0-beta1</langchain4j.version>
</properties>
<dependencies>
<!-- 核心模块:必需 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- OpenAI模块:如果使用OpenAI API -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- 流式响应支持 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
<classifier>bleeding-edge</classifier>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!-- 日志框架 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Gradle项目配置
如果你更喜欢使用Gradle,配置同样简洁:
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '1.0.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
// 核心模块
implementation 'dev.langchain4j:langchain4j:1.0.0-beta1'
// OpenAI支持
implementation 'dev.langchain4j:langchain4j-open-ai:1.0.0-beta1'
// JSON处理
implementation 'com.google.code.gson:gson:2.10.1'
// 日志
implementation 'org.slf4j:slf4j-api:2.0.9'
runtimeOnly 'ch.qos.logback:logback-classic:1.4.14'
// 测试
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1'
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
application {
mainClass = 'com.example.App'
}
tasks.named('test') {
useJUnitPlatform()
}
配置API密钥
LangChain4j支持多种LLM提供商,你需要配置相应的API密钥。最常见的是OpenAI:
public class ApiKeyConfig {
// 方式一:通过环境变量(推荐方式)
// 设置环境变量 OPENAI_API_KEY=sk-xxxxxx
// 方式二:在代码中直接配置(仅用于演示,生产环境请勿使用)
public static final String OPENAI_API_KEY = System.getenv("OPENAI_API_KEY");
// 方式三:通过配置文件管理
// 创建config.properties文件,内容如下:
// openai.api.key=sk-xxxxxx
// 然后通过Properties类加载
private static final String CONFIG_FILE = "config.properties";
public static String getApiKey() {
// 优先使用环境变量
String apiKey = System.getenv("OPENAI_API_KEY");
if (apiKey != null && !apiKey.isEmpty()) {
return apiKey;
}
// 次优方案:从配置文件读取
try {
java.util.Properties props = new java.util.Properties();
props.load(new java.io.FileInputStream(CONFIG_FILE));
return props.getProperty("openai.api.key");
} catch (java.io.IOException e) {
throw new RuntimeException("请设置OPENAI_API_KEY环境变量或创建config.properties文件", e);
}
}
}
创建日志配置文件
在src/main/resources目录下创建logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 调整LangChain4j日志级别以查看详细请求响应 -->
<logger name="dev.langchain4j" level="debug"/>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
核心概念与架构:深入理解LangChain4j的设计哲学
整体架构概览
LangChain4j的设计遵循了LangChain的核心思想,但在Java生态中做了大量适配和优化。其架构可以分为以下几个层次:
第一层:模型抽象层
这是整个框架的基础。LangChain4j定义了一系列接口来抽象不同类型的模型:
ChatLanguageModel:对话模型接口StreamingChatLanguageModel:流式对话模型接口EmbeddingModel:嵌入模型接口ImageModel:图像模型接口
这种抽象设计使得你可以在不修改业务代码的情况下,切换不同的模型提供商。
第二层:组件层
建立在模型抽象之上的各种组件:
PromptTemplate:提示词模板,用于构建动态提示词ChatMemory:对话记忆,管理对话历史Tool:工具/函数调用扩展OutputParser:输出解析器,将模型输出转换为结构化数据
第三层:编排层
将各种组件组合成完整的应用逻辑:
Chain:链式调用接口Agent:代理,智能决策和执行Service:AI服务,自动组装各种组件
核心接口详解
让我们深入了解几个最重要的接口:
// 对话模型的抽象接口
public interface ChatLanguageModel {
// 同步调用,返回完整响应
AiMessage sendMessages(List<ChatMessage> messages);
// 带系统提示的便捷方法
default AiMessage sendUserMessage(String userMessage) {
return sendMessages(List.of(UserMessage.from(userMessage)));
}
}
// 消息类型定义
public interface ChatMessage {
// 用户消息
record UserMessage(String text) implements ChatMessage {}
// AI回复
record AiMessage(String text) implements ChatMessage {}
// 系统提示
record SystemMessage(String text) implements ChatMessage {}
}
// 流式响应的接口
public interface StreamingChatLanguageModel {
void sendMessages(
List<ChatMessage> messages,
StreamingResponseHandler<AiMessage> handler // 流式处理器
);
}
设计模式的应用
LangChain4j大量使用了设计模式来实现灵活性和可扩展性:
建造者模式:几乎所有配置类都使用建造器模式
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey("your-api-key")
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.maxTokens(1000)
.timeout(Duration.ofSeconds(30))
.build();
策略模式:不同的模型提供商实现相同的接口,可以自由切换
装饰器模式:可以通过装饰器添加缓存、重试、熔断等功能
ChatLanguageModel originalModel = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// 添加缓存装饰器
ChatLanguageModel cachedModel = CachingChatLanguageModel.builder()
.delegate(originalModel)
.cache(TokenCache.byToken(8192))
.build();
// 添加重试装饰器
ChatLanguageModel retryingModel = RetryingChatLanguageModel.builder()
.delegate(cachedModel)
.maxAttempts(3)
.delayBeforeRetry(Duration.ofSeconds(1))
.build();
AI Service:最简化的使用方式
什么是AI Service
AI Service是LangChain4j提供的高级抽象,它简化了与LLM交互的复杂度。对于大多数场景,使用AI Service是最佳选择。它会自动处理:
- 提示词模板的构建
- 对话历史的维护
- 输出的解析和转换
- 工具的注册和调用
基本用法
首先定义一个接口来描述AI的能力:
import dev.langchain4j.service.Agent;
import dev.langchain4j.service.tool.Tool;
// 定义AI服务接口
public interface Assistant {
// 简单对话
String chat(String userMessage);
// 带流式响应的对话
String chatStreaming(String userMessage);
}
然后使用注解和建造器创建实现:
import dev.langchain4j.service.AIServiceArguments;
import dev.langchain4j.service.AIServiceForStreaming;
import dev.langchain4j.service.spring.ai.AiServices;
public class AssistantExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.build())
.build();
// 使用AI服务
String response = assistant.chat("什么是Java?");
System.out.println(response);
}
}
带记忆的对话
通过添加ChatMemory来实现多轮对话:
// 首先定义带记忆的接口
public interface ChattyAssistant {
String chat(String userMessage);
}
// 创建记忆实现
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(20);
ChattyAssistant assistant = AiServices.builder(ChattyAssistant.class)
.chatLanguageModel(OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.build())
.chatMemory(memory) // 添加记忆组件
.build();
// 多轮对话
System.out.println(assistant.chat("我叫张三")); // 第一轮
System.out.println(assistant.chat("我叫什么名字?")); // 第二轮,应该记住"张三"
System.out.println(assistant.chat("再说一遍我的名字")); // 第三轮
LangChain4j提供了多种记忆实现:
// 消息窗口记忆:只保留最近N条消息
ChatMemory windowMemory = MessageWindowChatMemory.withMaxMessages(10);
// 基于Token的记忆:按Token数量限制
ChatMemory tokenMemory = TokenWindowChatMemory.withMaxTokens(500);
// 文件持久化记忆:重启后仍能记住对话
ChatMemory persistentMemory = FileChatMemory.builder()
.directory(Path.of("./chat-memory"))
.maxMessages(100)
.build();
带工具调用的AI服务
这是LangChain4j最强大的功能之一。你可以让AI调用自定义的工具来扩展其能力:
// 定义工具类
public class WeatherService {
// 工具注解标记此方法可以被AI调用
@Tool("查询指定城市的当前天气")
String getWeather(@P("city") String city) {
// 实际项目中这里会调用天气API
return switch (city) {
case "北京" -> "北京今天天气晴朗,温度15-25°C";
case "上海" -> "上海今天多云,温度18-28°C";
case "广州" -> "广州今天有雨,温度22-30°C";
default -> "未知的城市";
};
}
}
// 定义工具类
public class Calculator {
@Tool("计算两个数字的加减乘除")
String calculate(@P("a") double a,
@P("b") double b,
@P("operation") String operation) {
return switch (operation) {
case "加", "+" -> String.valueOf(a + b);
case "减", "-" -> String.valueOf(a - b);
case "乘", "*" -> String.valueOf(a * b);
case "除", "/" -> b != 0 ? String.valueOf(a / b) : "除数不能为零";
default -> "不支持的操作";
};
}
}
// 定义支持工具调用的AI接口
public interface SmartAssistant {
// content参数会显示工具调用的结果
@UserMessage("{message}")
String chat(@V("message") String message);
}
// 创建带有工具的AI服务
SmartAssistant assistant = AiServices.builder(SmartAssistant.class)
.chatLanguageModel(OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo-1106") // 需要支持工具调用的模型
.build())
.tools(new WeatherService(), new Calculator()) // 注册工具
.build();
// AI会根据对话内容自动决定是否调用工具
String response = assistant.chat("北京现在多少度?"); // 会调用天气工具
System.out.println(response);
提示词模板:构建动态提示词的利器
PromptTemplate基础
PromptTemplate用于构建动态的提示词,支持变量替换、条件逻辑等功能:
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.input.variables.StringVariable;
public class PromptTemplateExample {
public static void main(String[] args) {
// 定义模板,使用{{variableName}}作为变量占位符
PromptTemplate template = PromptTemplate.from(
"请为以下产品写一段营销文案:\n" +
"产品名称:{{productName}}\n" +
"产品特点:{{features}}\n" +
"目标用户:{{targetAudience}}\n\n" +
"语气要求:{{tone}}"
);
// 填充变量
Prompt prompt = template.apply(Map.of(
"productName", "LangChain4j",
"features", "简单易用、功能强大、企业级支持",
"targetAudience", "Java开发者",
"tone", "专业且富有激情"
));
System.out.println(prompt.text());
// 输出:
// 请为以下产品写一段营销文案:
// 产品名称:LangChain4j
// 产品特点:简单易用、功能强大、企业级支持
// 产品用户:Java开发者
// 语气要求:专业且富有激情
}
}
条件模板
支持if/else条件逻辑:
PromptTemplate conditionalTemplate = PromptTemplate.from(
"{{#if isVIP}}尊敬的大客户您好!{{/if}}" +
"{{#if isVIP}}作为VIP用户,您将享受专属折扣。{{else}}您当前是普通用户。{{/if}}"
);
// VIP用户场景
Prompt vipPrompt = conditionalTemplate.apply(Map.of(
"isVIP", true
));
System.out.println(vipPrompt.text());
// 输出:尊敬的大客户您好!作为VIP用户,您将享受专属折扣。
// 普通用户场景
Prompt normalPrompt = conditionalTemplate.apply(Map.of(
"isVIP", false
));
System.out.println(normalPrompt.text());
// 输出:您当前是普通用户。
列表和循环
支持处理列表类型的变量:
PromptTemplate listTemplate = PromptTemplate.from(
"请评价以下产品:\n" +
"{{#each products}}" +
"- {{name}}:{{description}}\n" +
"{{/each}}"
);
Prompt prompt = listTemplate.apply(Map.of(
"products", List.of(
Map.of("name", "产品A", "description", "高品质"),
Map.of("name", "产品B", "description", "高性价比"),
Map.of("name", "产品C", "description", "创新设计")
)
));
System.out.println(prompt.text());
// 输出:
// 请评价以下产品:
// - 产品A:高品质
// - 产品B:高性价比
// - 产品C:创新设计
高级模板技巧
// 支持默认值的变量
PromptTemplate templateWithDefault = PromptTemplate.from(
"为{{name}}{{#if age}}({{age}}岁){{/if}}写一句生日祝福"
);
// 省略可选参数
Prompt p1 = templateWithDefault.apply(Map.of("name", "张三"));
System.out.println(p1.text());
// 输出:为张三写一句生日祝福
// 提供可选参数
Prompt p2 = templateWithDefault.apply(Map.of(
"name", "李四",
"age", 30
));
System.out.println(p2.text());
// 输出:为李四(30岁)写一句生日祝福
// 支持日期、数字格式化
PromptTemplate dateTemplate = PromptTemplate.from(
"今天是{{date date 'yyyy年MM月dd日'}}"
);
LocalDate today = LocalDate.now();
Prompt datePrompt = dateTemplate.apply(Map.of(
"date", today
));
System.out.println(datePrompt.text());
// 输出:今天是2024年01月15日
对话记忆:让AI记住上下文
记忆的重要性
在多轮对话场景中,如果每次都只发送当前消息,AI就无法理解对话的上下文。例如:
// 没有记忆的情况
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// 第一轮
model.sendMessages(List.of(
UserMessage.from("我叫张三")
));
// 第二轮(没有上下文)
model.sendMessages(List.of(
UserMessage.from("我叫什么名字?") // AI不知道上一轮说了什么
));
简单记忆实现
public class SimpleMemoryExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// 使用ArrayList存储对话历史
List<ChatMessage> conversationHistory = new ArrayList<>();
// 第一轮对话
conversationHistory.add(UserMessage.from("我叫张三"));
AiMessage response1 = model.sendMessages(conversationHistory);
conversationHistory.add(response1);
System.out.println("AI: " + response1.text());
// 第二轮对话
conversationHistory.add(UserMessage.from("我叫什么名字?"));
AiMessage response2 = model.sendMessages(conversationHistory);
conversationHistory.add(response2);
System.out.println("AI: " + response2.text());
// 这次AI能正确回答"张三"
// 第三轮对话
conversationHistory.add(UserMessage.from("再说一遍"));
AiMessage response3 = model.sendMessages(conversationHistory);
System.out.println("AI: " + response3.text());
}
}
消息窗口记忆
使用MessageWindowChatMemory可以限制保留的消息数量,防止超出模型的上下文窗口:
public class WindowMemoryExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// 限制最多保留10条消息
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(10);
// 模拟多次对话
for (int i = 1; i <= 15; i++) {
String userMessage = "这是第" + i + "轮对话";
memory.add(UserMessage.from(userMessage));
AiMessage response = model.sendMessages(memory.messages());
memory.add(response);
System.out.println("轮次 " + i + " - 消息数量: " + memory.messages().size());
}
// 当超过10条消息时,最早的消息会被移除
// 只保留最近的10条
}
}
Token计数记忆
更精确的控制是使用基于Token数量的限制:
ChatMemory tokenMemory = TokenWindowChatMemory.withMaxTokens(
4096, // 最大Token数
new OpenAiTokenizer("gpt-3.5-turbo") // 指定模型以正确计算Token
);
// 或者更简单的创建方式
ChatMemory autoTokenMemory = ChatMemory.builder()
.maxTokens(4096)
.build();
记忆持久化
对于需要跨会话保留记忆的场景,可以使用文件存储:
public class PersistentMemoryExample {
public static void main(String[] args) throws Exception {
String apiKey = ApiKeyConfig.getApiKey();
// 指定存储目录
Path memoryDirectory = Path.of("./data/chat-memory");
// 创建持久化记忆,自动保存和加载
PersistentChatMemory memory = PersistentChatMemory.builder()
.directory(memoryDirectory)
.maxMessages(100)
.build();
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// 第一次运行:添加记忆
memory.add(UserMessage.from("我叫王五"));
memory.add(UserMessage.from("我是一名Java开发工程师"));
AiMessage response = model.sendMessages(memory.messages());
memory.add(response);
// 记忆会自动保存到文件
System.out.println("记忆已保存,当前消息数: " + memory.messages().size());
// 第二次运行:加载记忆继续对话
// PersistentChatMemory会自动从目录加载之前的对话
}
}
链式调用:组合多个处理步骤
Chain接口
Chain是LangChain4j中用于组合多个处理步骤的核心抽象。一个Chain接收输入,通过一系列转换后产生输出:
public interface Chain<I, O> {
O execute(I input);
}
内置Chain类型
LangChain4j提供了多种预置的Chain实现:
TransformationChain:数据转换链
import dev.langchain4j.chain.TransformationChain;
public class TransformationChainExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// 创建转换链:输入文本 -> 翻译成英文 -> 情感分析
Chain<String, String> translateAndAnalyze = TransformationChain.builder()
.inputTransformer(userInput ->
"请将以下中文翻译成英文:\n" + userInput
)
.chatLanguageModel(model)
.outputParser(new StringOutputParser())
.build();
// 第一步:翻译
String translated = translateAndAnalyze.execute("今天天气真好");
System.out.println("翻译结果: " + translated);
// 第二步:情感分析
Chain<String, String> sentimentChain = TransformationChain.builder()
.inputTransformer(text ->
"分析以下英文文本的情感(正面/负面/中性):\n" + text
)
.chatLanguageModel(model)
.outputParser(new StringOutputParser())
.build();
String sentiment = sentimentChain.execute(translated);
System.out.println("情感分析: " + sentiment);
}
}
ConversationChain:带记忆的对话链
import dev.langchain4j.chain.ConversationChain;
public class ConversationChainExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(20);
ConversationChain chain = ConversationChain.builder()
.chatLanguageModel(model)
.chatMemory(memory)
.build();
// 多轮对话
String response1 = chain.execute("我想学习Java");
System.out.println("AI: " + response1);
String response2 = chain.execute("有什么推荐的学习资源吗?");
System.out.println("AI: " + response2);
// AI会记住之前的"我想学习Java"
String response3 = chain.execute("能详细说说第一点吗?");
System.out.println("AI: " + response3);
}
}
SummarizingChain:文本摘要链
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.input.variables.StringVariable;
public class SummarizingChainExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// 创建摘要提示模板
PromptTemplate summarizeTemplate = PromptTemplate.from(
"请用简洁的语言总结以下文本的核心内容,不超过50字:\n\n{{text}}"
);
Chain<String, String> summarizingChain = Chain.builder()
.promptTemplate(summarizeTemplate)
.chatLanguageModel(model)
.outputParser(new StringOutputParser())
.build();
String longText = """
人工智能(AI)是计算机科学的一个分支,致力于开发能够模拟、延伸和扩展人类智能的理论、方法、技术及应用系统。
它包括机器学习、自然语言处理、计算机视觉、机器人技术等多个子领域。
近年来,随着深度学习技术的发展,AI在图像识别、语音识别、自然语言生成等方面取得了突破性进展。
AI技术已经广泛应用于医疗诊断、金融风控、自动驾驶、智能客服等众多领域。
""";
String summary = summarizingChain.execute(longText);
System.out.println("摘要: " + summary);
}
}
自定义Chain
你也可以创建自己的Chain:
import dev.langchain4j.chain.Chain;
// 定义一个专门处理代码审查的Chain
public class CodeReviewChain implements Chain<CodeReviewRequest, CodeReviewResult> {
private final ChatLanguageModel model;
private final PromptTemplate template;
public CodeReviewChain(ChatLanguageModel model) {
this.model = model;
this.template = PromptTemplate.from(
"请审查以下Java代码,重点关注:\n" +
"1. 代码风格和命名规范\n" +
"2. 潜在的Bug和安全问题\n" +
"3. 性能优化建议\n" +
"4. 设计模式的使用\n\n" +
"代码:\n" +
"{{code}}\n\n" +
"语言:{{language}}"
);
}
@Override
public CodeReviewResult execute(CodeReviewRequest request) {
// 构建提示词
Prompt prompt = template.apply(Map.of(
"code", request.code(),
"language", request.language()
));
// 发送给模型
AiMessage response = model.sendMessages(List.of(
SystemMessage.from(prompt.text())
));
// 解析结果(实际项目中应该使用专门的解析器)
return new CodeReviewResult(response.text(), LocalDateTime.now());
}
}
// 数据类
public record CodeReviewRequest(String code, String language) {}
public record CodeReviewResult(String review, LocalDateTime timestamp) {}
// 使用自定义Chain
public class CustomChainExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
CodeReviewChain chain = new CodeReviewChain(model);
CodeReviewRequest request = new CodeReviewRequest(
"""
public int calculateSum(List<Integer> numbers) {
int sum = 0;
for (int i = 0; i <= numbers.size(); i++) {
sum += numbers.get(i);
}
return sum;
}
""",
"Java"
);
CodeReviewResult result = chain.execute(request);
System.out.println("审查结果:\n" + result.review());
}
}
Chain组合
多个Chain可以组合成更复杂的流程:
public class ChainCompositionExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// Chain 1: 问题分类
Chain<String, String> classifier = TransformationChain.builder()
.inputTransformer(input ->
"将以下问题分类为:技术问题、投诉、咨询、其他\n" +
"问题:" + input + "\n\n只输出分类结果"
)
.chatLanguageModel(model)
.outputParser(new StringOutputParser())
.build();
// Chain 2: 技术问题处理
Chain<String, String> techSupport = TransformationChain.builder()
.inputTransformer(input ->
"作为技术支持工程师,请回答:\n" + input
)
.chatLanguageModel(model)
.outputParser(new StringOutputParser())
.build();
// Chain 3: 投诉处理
Chain<String, String> complaintHandler = TransformationChain.builder()
.inputTransformer(input ->
"作为客服经理,请处理以下投诉并提供安抚方案:\n" + input
)
.chatLanguageModel(model)
.outputParser(new StringOutputParser())
.build();
// 根据分类结果选择不同的处理Chain
String userQuestion = "我的程序运行很慢,怎么优化?";
String category = classifier.execute(userQuestion).trim();
System.out.println("问题分类: " + category);
String response;
if (category.contains("技术")) {
response = techSupport.execute(userQuestion);
} else if (category.contains("投诉")) {
response = complaintHandler.execute(userQuestion);
} else {
response = "感谢您的提问,我们会尽快回复您。";
}
System.out.println("回复: " + response);
}
}
RAG实战:构建智能问答系统
RAG概述
检索增强生成(Retrieval-Augmented Generation,RAG)是当前最流行的LLM应用架构之一。它通过从外部知识库检索相关信息,然后让LLM基于检索结果生成回答,有效解决了LLM的”幻觉”问题和知识时效性问题。
LangChain4j提供了完整的RAG支持,包括:
- 文档分割
- 嵌入模型
- 向量存储
- 检索器
- 答案生成
完整的RAG实现
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.OpenAiEmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
public class RAGExample {
public static void main(String[] args) throws Exception {
String apiKey = ApiKeyConfig.getApiKey();
// ========================================
// 步骤1:准备知识库文档
// ========================================
String knowledgeContent = """
LangChain4j入门指南
什么是LangChain4j?
LangChain4j是一个Java库,旨在简化大语言模型应用的开发。
它提供了统一的API来对接多种LLM提供商,包括OpenAI、Google、HuggingFace等。
主要特性
1. 统一的模型抽象层
2. 丰富的组件库
3. 支持RAG(检索增强生成)
4. 工具调用和代理功能
5. 对话记忆管理
安装方法
在Maven项目中添加依赖:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.0.0-beta1</version>
</dependency>
快速开始
第一步:创建ChatLanguageModel实例
第二步:使用AiServices创建AI服务
第三步:调用chat方法进行对话
注意事项
1. 请妥善保管API密钥
2. 注意控制Token使用量
3. 合理设置温度参数
""";
Document document = Document.from(knowledgeContent);
// ========================================
// 步骤2:分割文档
// ========================================
// 将文档分割成小块,便于检索
List<TextSegment> segments = DocumentSplitters
.recursive(500, 50) // 每块最大500字符,重叠50字符
.splitDocument(document);
System.out.println("文档已分割为 " + segments.size() + " 个片段");
segments.forEach(seg ->
System.out.println("- [" + seg.text().length() + "字符] " +
seg.text().substring(0, Math.min(50, seg.text().length())) + "...")
);
// ========================================
// 步骤3:创建嵌入模型
// ========================================
EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
.apiKey(apiKey)
.modelName("text-embedding-ada-002")
.build();
// ========================================
// 步骤4:生成嵌入并存入向量存储
// ========================================
EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
List<Embedding> embeddings = embeddingModel.embedAll(
segments.stream().map(TextSegment::text).toList()
).content();
// 为每个片段生成嵌入并存储
List<Embedded<TextSegment>> embedded = new ArrayList<>();
for (int i = 0; i < segments.size(); i++) {
embedded.add(Embedded.of(segments.get(i), embeddings.get(i)));
}
embeddingStore.addAll(embedded);
System.out.println("\n知识库已建立,共存储 " + embedded.size() + " 个嵌入向量");
// ========================================
// 步骤5:创建检索器和生成模型
// ========================================
EmbeddingStoreRetriever retriever = EmbeddingStoreRetriever.from(
embeddingStore,
embeddingModel
);
ChatLanguageModel chatModel = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.3) // RAG场景建议降低温度以提高准确性
.build();
// ========================================
// 步骤6:实现RAG问答
// ========================================
String userQuestion = "如何安装LangChain4j?";
// 6.1 检索相关文档
List<TextSegment> relevantSegments = retriever.retrieve(userQuestion, 3);
System.out.println("\n检索到 " + relevantSegments.size() + " 个相关片段:");
for (TextSegment seg : relevantSegments) {
System.out.println("- " + seg.text().substring(0, Math.min(100, seg.text().length())) + "...");
}
// 6.2 构建RAG提示词
String context = relevantSegments.stream()
.map(TextSegment::text)
.collect(Collectors.joining("\n\n"));
String ragPrompt = """
你是一个问答助手,请根据以下参考资料回答用户的问题。
参考资料:
%s
问题:%s
要求:
1. 只根据参考资料回答,不要编造信息
2. 如果参考资料中没有相关信息,请说明"我没有足够的信息来回答这个问题"
3. 回答要简洁明了
""".formatted(context, userQuestion);
// 6.3 生成回答
AiMessage response = chatModel.sendMessages(List.of(
SystemMessage.from(ragPrompt)
));
System.out.println("\n回答: " + response.text());
}
}
使用RAG AI服务
LangChain4j还提供了更简洁的RAG实现方式:
import dev.langchain4j.service.AIServices;
import dev.langchain4j.service.RetryableAiService;
import dev.langchain4j.service.tool.ToolSpecification;
public class RAGServiceExample {
public interface KnowledgeBaseAssistant {
// 用户问题会被自动用于检索
@Retain
String answer(@UserMessage String question);
}
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
// 创建组件
EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
.apiKey(apiKey)
.modelName("text-embedding-ada-002")
.build();
EmbeddingStore<TextSegment> embeddingStore = createEmbeddingStore();
ChatLanguageModel chatModel = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.3)
.build();
// 创建AI服务,自动配置RAG
KnowledgeBaseAssistant assistant = AIServices.builder(KnowledgeBaseAssistant.class)
.chatLanguageModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.contentRetriever(EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(3)
.build())
.build();
// 直接使用,检索和回答自动完成
String answer = assistant.answer("LangChain4j支持哪些LLM提供商?");
System.out.println(answer);
}
private static EmbeddingStore<TextSegment> createEmbeddingStore() {
// 实际项目中这里会初始化真正的向量存储
// 例如:Pinecone、Weaviate、Milvus等
return new InMemoryEmbeddingStore<>();
}
}
支持多种向量存储
LangChain4j支持多种向量存储后端:
// InMemory:内存存储,适合小规模数据和测试
EmbeddingStore<TextSegment> inMemoryStore = new InMemoryEmbeddingStore<>();
// Chroma:本地向量数据库
EmbeddingStore<TextSegment> chromaStore = ChromaEmbeddingStore.builder()
.baseUrl("http://localhost:8000")
.collectionName("my_collection")
.build();
// Pinecone:云端向量数据库
EmbeddingStore<TextSegment> pineconeStore = PineconeEmbeddingStore.builder()
.apiKey("your-api-key")
.environment("gcp-starter")
.index("my-index")
.build();
// Weaviate
EmbeddingStore<TextSegment> weaviateStore = WeaviateEmbeddingStore.builder()
.scheme("http")
.host("localhost:8080")
.apiKey("your-api-key")
.indexName("MyClass")
.build();
// Qdrant
EmbeddingStore<TextSegment> qdrantStore = QdrantEmbeddingStore.builder()
.host("localhost")
.port(6334)
.collectionName("my_collection")
.build();
工具与代理:构建自主行动的AI
工具系统概述
工具(Tools)是扩展AI能力的关键机制。通过注册工具,AI可以执行实际操作,如查询数据库、调用API、操作文件系统等。
定义工具
import dev.langchain4j.service.tool.Tool;
import dev.langchain4j.service.tool.P;
// ========================================
// 搜索引擎工具
// ========================================
public class SearchEngine {
@Tool("搜索互联网获取相关信息")
String search(@P("searchQuery") String searchQuery) {
// 实际项目中这里会调用真实的搜索引擎API
// 例如:Google Search API、Bing Search API等
// 模拟搜索结果
return switch (searchQuery.toLowerCase()) {
case String s when s.contains("java") ->
"Java是一种广泛使用的编程语言,由Sun Microsystems于1995年发布...";
case String s when s.contains("python") ->
"Python是一种高级编程语言,以其简洁易读的语法著称...";
case String s when s.contains("langchain") ->
"LangChain是一个用于构建LLM应用的框架,支持Python和Java...";
default ->
"关于'" + searchQuery + "'的搜索结果暂无记录";
};
}
}
// ========================================
// 日历工具
// ========================================
public class CalendarService {
@Tool("查询指定日期范围的日程安排")
List<String> getEvents(
@P("startDate") String startDate,
@P("endDate") String endDate
) {
// 实际项目中会连接日历服务API
return List.of(
"2024-01-15 10:00 团队周会",
"2024-01-16 14:00 项目评审",
"2024-01-18 09:00 代码审查"
);
}
@Tool("创建新的日程事件")
String createEvent(
@P("title") String title,
@P("dateTime") String dateTime,
@P("description") String description
) {
return "日程已创建:" + title + ",时间:" + dateTime;
}
}
// ========================================
// 文件操作工具
// ========================================
public class FileOperations {
@Tool("读取指定路径的文件内容")
String readFile(@P("filePath") String filePath) {
try {
return Files.readString(Path.of(filePath));
} catch (IOException e) {
return "读取文件失败:" + e.getMessage();
}
}
@Tool("在指定路径创建新文件")
String writeFile(
@P("filePath") String filePath,
@P("content") String content
) {
try {
Files.writeString(Path.of(filePath), content);
return "文件创建成功:" + filePath;
} catch (IOException e) {
return "创建文件失败:" + e.getMessage();
}
}
}
创建代理
import dev.langchain4j.agent.ToolSpecifications;
public class AgentExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
// 创建工具实例
SearchEngine searchEngine = new SearchEngine();
CalendarService calendarService = new CalendarService();
FileOperations fileOperations = new FileOperations();
// 创建聊天模型(需要支持函数调用)
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo-1106") // 支持函数调用
.temperature(0.3)
.build();
// 从工具类生成工具规范
List<ToolSpecification> toolSpecs = ToolSpecifications.toolSpecificationsFrom(
searchEngine,
calendarService,
fileOperations
);
// 创建内存
ChatMemory memory = MessageWindowChatMemory.withMaxMessages(50);
// 使用Agent构建器创建代理
Agent agent = Agent.builder()
.chatLanguageModel(model)
.chatMemory(memory)
.toolSpecifications(toolSpecs)
.tools(searchEngine, calendarService, fileOperations)
.build();
// 演示代理执行任务
demonstrateAgent(agent, "帮我搜索Java的最新资讯");
demonstrateAgent(agent, "查看这周有什么日程安排");
demonstrateAgent(agent, "帮我整理一份LangChain4j的学习计划");
}
private static void demonstrateAgent(Agent agent, String task) {
System.out.println("\n" + "=".repeat(50));
System.out.println("任务: " + task);
System.out.println("=".repeat(50));
String response = agent.execute(task);
System.out.println("结果: " + response);
}
}
代理执行流程详解
代理的执行过程是一个循环:
// 伪代码展示代理的执行流程
public String agentExecutionLoop(Agent agent, String userTask) {
// 1. 将用户任务添加到记忆
memory.add(UserMessage.from(userTask));
while (true) {
// 2. 获取当前对话历史
List<ChatMessage> messages = memory.messages();
// 3. 发送给LLM,让它决定是否调用工具
AiMessage response = model.sendMessages(messages);
memory.add(response);
// 4. 检查是否有函数调用
if (response.hasToolCalls()) {
// 5. 执行工具调用
for (ToolCall toolCall : response.toolCalls()) {
String toolName = toolCall.name();
Map<String, Object> args = toolCall.args();
// 调用实际工具
String toolResult = executeTool(toolName, args);
// 6. 将工具结果添加为工具消息
memory.add(ToolMessage.from(toolResult, toolCall.id()));
}
// 7. 继续循环,让LLM基于工具结果生成回答
continue;
}
// 8. 没有工具调用,返回最终回答
return response.text();
}
}
流式响应:实时展示AI输出
为什么需要流式响应
传统的API调用需要等待模型生成完整响应后才能返回。对于长文本,这可能导致用户等待数十秒。流式响应通过逐块返回生成的内容,显著改善了用户体验。
实现流式响应
import dev.langchain4j.model.StreamingResponseHandler;
import dev.langchain4j.model.output.Response;
public class StreamingExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
// 创建流式聊天模型
StreamingChatLanguageModel streamingModel = OpenAiStreamingChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.build();
// 演示基本流式输出
System.out.println("开始流式输出演示:\n");
System.out.println("AI: ", "");
streamingModel.sendMessages(
List.of(UserMessage.from("给我讲一个关于人工智能的简短故事")),
new StreamingResponseHandler<>() {
private StringBuilder fullResponse = new StringBuilder();
@Override
public void onPartialToken(String token) {
// 每个token到达时调用
System.out.print(token);
fullResponse.append(token);
}
@Override
public void onComplete(Response<AiMessage> response) {
// 流式输出完成
System.out.println("\n\n--- 流式输出完成 ---");
System.out.println("完整响应: " + fullResponse);
}
@Override
public void onError(Throwable error) {
System.err.println("发生错误: " + error.getMessage());
}
}
);
// 等待流式输出完成
try {
Thread.sleep(30000); // 等待最多30秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
带进度显示的流式响应
public class ProgressStreamingExample {
private static final char[] LOADING_CHARS = {'|', '/', '-', '\\'};
private static int loadingIndex = 0;
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
StreamingChatLanguageModel model = OpenAiStreamingChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.build();
String userMessage = """
请详细解释什么是微服务架构,包括:
1. 定义和核心概念
2. 主要优势
3. 常见挑战
4. 最佳实践
""";
System.out.println("问题: " + userMessage);
System.out.println("\nAI回答: ");
final int[] tokenCount = {0};
final boolean[] isComplete = {false};
// 启动进度显示线程
Thread progressThread = new Thread(() -> {
while (!isComplete[0]) {
System.out.print("\r[处理中] " + LOADING_CHARS[loadingIndex++ % 4] +
" 已生成 " + tokenCount[0] + " 个token");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
}
});
progressThread.start();
model.sendMessages(
List.of(UserMessage.from(userMessage)),
new StreamingResponseHandler<>() {
private final StringBuilder response = new StringBuilder();
@Override
public void onPartialToken(String token) {
response.append(token);
tokenCount[0]++;
// 可以选择性地显示部分内容
if (tokenCount[0] % 10 == 0) {
System.out.print("●");
}
}
@Override
public void onComplete(Response<AiMessage> response) {
isComplete[0] = true;
System.out.println("\r" + " ".repeat(50)); // 清除进度行
System.out.println("\n" + "=".repeat(50));
System.out.println("生成完成!共 " + tokenCount[0] + " 个token");
}
@Override
public void onError(Throwable error) {
isComplete[0] = true;
System.out.println("\n错误: " + error.getMessage());
}
}
);
// 等待完成
synchronized (StreamingExample.class) {
try {
StreamingExample.class.wait();
} catch (InterruptedException e) {
// 忽略
}
}
}
}
在AI服务中使用流式
public interface StreamingAssistant {
// 使用@UserMessage指定用户消息
// 配合流式模型自动支持流式响应
void chat(@UserMessage String message, StreamingResponseHandler<String> handler);
}
public class StreamingServiceExample {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
// 使用支持流式的模型
StreamingChatLanguageModel streamingModel = OpenAiStreamingChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.build();
// 转换为普通ChatLanguageModel以便与AiServices配合
ChatLanguageModel model = StreamingChatLanguageModel.toChatModel(streamingModel);
StreamingAssistant assistant = AiServices.builder(StreamingAssistant.class)
.chatLanguageModel(streamingModel) // 传入流式模型
.build();
System.out.println("请描述你最喜欢的编程语言:");
assistant.chat("请详细描述Java语言的特点和应用场景", new StreamingResponseHandler<>() {
private StringBuilder fullText = new StringBuilder();
@Override
public void onPartialToken(String token) {
System.out.print(token);
fullText.append(token);
}
@Override
public void onComplete(Response<AiMessage> response) {
System.out.println("\n\n回答完成!");
}
@Override
public void onError(Throwable error) {
System.out.println("发生错误: " + error.getMessage());
}
});
}
}
实战项目:构建企业级AI助手
项目概述
让我们构建一个综合性的企业AI助手项目,整合本教程中学到的所有功能:
// ========================================
// 企业AI助手 - 完整实现
// ========================================
// 主接口定义
public interface EnterpriseAssistant {
// 智能对话(带记忆)
String chat(String message);
// 流式对话
void chatStreaming(String message, StreamingResponseHandler<String> handler);
// 文档问答(基于RAG)
String answerFromKnowledgeBase(String question);
// 执行任务(使用工具)
String executeTask(String task);
}
// 工具类定义
public class EnterpriseTools {
static class DateTimeTools {
@Tool("获取当前日期和时间")
String getCurrentDateTime() {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
return "当前时间:" + now.format(formatter);
}
}
static class CalculatorTools {
@Tool("执行数学计算")
double calculate(
@P("expression") String expression
) {
// 使用ScriptEngine进行简单计算
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
Object result = engine.eval(expression);
return Double.parseDouble(result.toString());
} catch (Exception e) {
throw new RuntimeException("计算错误: " + e.getMessage());
}
}
}
static class WeatherTools {
private static final Map<String, String> weatherData = Map.of(
"北京", "晴,15-25°C,空气质量良好",
"上海", "多云,18-28°C,有轻微雾霾",
"广州", "小雨,22-30°C,建议带伞",
"深圳", "晴,23-31°C,适宜户外活动",
"杭州", "阴,16-24°C,温度适宜"
);
@Tool("查询城市天气")
String getWeather(@P("city") String city) {
return weatherData.getOrDefault(city,
"未找到" + city + "的天气数据");
}
}
}
// 知识库管理
public class KnowledgeBaseManager {
private final EmbeddingStore<TextSegment> embeddingStore;
private final EmbeddingModel embeddingModel;
private final EmbeddingStoreContentRetriever contentRetriever;
public KnowledgeBaseManager(String apiKey) {
this.embeddingModel = OpenAiEmbeddingModel.builder()
.apiKey(apiKey)
.modelName("text-embedding-ada-002")
.build();
this.embeddingStore = new InMemoryEmbeddingStore<>();
this.contentRetriever = EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(5)
.build();
}
public void addDocument(String content, String metadata) {
Document doc = Document.from(content + "\n[来源: " + metadata + "]");
List<TextSegment> segments = DocumentSplitters
.recursive(500, 50)
.splitDocument(doc);
List<Embedding> embeddings = embeddingModel.embedAll(
segments.stream().map(TextSegment::text).toList()
).content();
List<Embedded<TextSegment>> embedded = new ArrayList<>();
for (int i = 0; i < segments.size(); i++) {
embedded.add(Embedded.of(segments.get(i), embeddings.get(i)));
}
embeddingStore.addAll(embedded);
System.out.println("已添加文档片段: " + segments.size());
}
public List<String> retrieve(String query, int maxResults) {
return contentRetriever.retrieve(query, maxResults)
.stream()
.map(TextSegment::text)
.collect(Collectors.toList());
}
}
// AI服务实现
public class EnterpriseAssistantImpl implements EnterpriseAssistant {
private final ChatLanguageModel chatModel;
private final StreamingChatLanguageModel streamingModel;
private final ChatMemory chatMemory;
private final KnowledgeBaseManager knowledgeBase;
private final Agent agent;
public EnterpriseAssistantImpl(String apiKey) {
// 初始化聊天模型
this.chatModel = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.maxTokens(2000)
.build();
// 初始化流式模型
this.streamingModel = OpenAiStreamingChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.build();
// 初始化记忆
this.chatMemory = MessageWindowChatMemory.withMaxMessages(50);
// 初始化知识库
this.knowledgeBase = new KnowledgeBaseManager(apiKey);
// 初始化工具
List<Object> tools = List.of(
new EnterpriseTools.DateTimeTools(),
new EnterpriseTools.CalculatorTools(),
new EnterpriseTools.WeatherTools()
);
// 初始化代理
this.agent = Agent.builder()
.chatLanguageModel(chatModel)
.chatMemory(chatMemory)
.toolSpecifications(ToolSpecifications.toolSpecificationsFrom(tools))
.tools(tools)
.build();
// 初始化知识库内容
initializeKnowledgeBase();
}
private void initializeKnowledgeBase() {
// 添加企业知识库文档
knowledgeBase.addDocument(
"""
公司成立于2010年,专注于人工智能技术研发。
主要产品包括智能客服系统、数据分析平台、AI开发工具。
公司价值观:创新、协作、责任、卓越。
""",
"公司简介"
);
knowledgeBase.addDocument(
"""
员工福利政策:
1. 五险一金全额缴纳
2. 弹性工作制
3. 年度体检
4. 培训发展机会
5. 带薪年假15天起
""",
"人事政策"
);
knowledgeBase.addDocument(
"""
技术栈包括:
- 后端:Java、Spring Boot、Kotlin
- 前端:React、Vue.js
- 大数据:Hadoop、Spark
- 机器学习:TensorFlow、PyTorch
- 云服务:AWS、阿里云
""",
"技术文档"
);
System.out.println("知识库初始化完成!");
}
@Override
public String chat(String message) {
chatMemory.add(UserMessage.from(message));
AiMessage response = chatModel.sendMessages(chatMemory.messages());
chatMemory.add(response);
return response.text();
}
@Override
public void chatStreaming(String message, StreamingResponseHandler<String> handler) {
chatMemory.add(UserMessage.from(message));
StringBuilder fullResponse = new StringBuilder();
streamingModel.sendMessages(
chatMemory.messages(),
new StreamingResponseHandler<>() {
@Override
public void onPartialToken(String token) {
fullResponse.append(token);
handler.onPartialToken(token);
}
@Override
public void onComplete(Response<AiMessage> response) {
chatMemory.add(response.content());
handler.onComplete(Response.from(fullResponse.toString()));
}
@Override
public void onError(Throwable error) {
handler.onError(error);
}
}
);
}
@Override
public String answerFromKnowledgeBase(String question) {
// 检索相关文档
List<String> relevantDocs = knowledgeBase.retrieve(question, 5);
if (relevantDocs.isEmpty()) {
return "抱歉,知识库中没有找到相关信息。";
}
// 构建RAG提示词
String context = relevantDocs.stream()
.collect(Collectors.joining("\n\n---\n\n"));
String prompt = """
请基于以下参考资料回答用户的问题。如果资料中没有相关信息,请说明。
参考资料:
%s
用户问题:%s
""".formatted(context, question);
AiMessage response = chatModel.sendMessages(
List.of(SystemMessage.from(prompt))
);
return response.text();
}
@Override
public String executeTask(String task) {
return agent.execute(task);
}
}
// 主程序入口
public class EnterpriseAssistantDemo {
public static void main(String[] args) {
String apiKey = ApiKeyConfig.getApiKey();
System.out.println("=".repeat(50));
System.out.println("企业AI助手演示");
System.out.println("=".repeat(50));
EnterpriseAssistant assistant = new EnterpriseAssistantImpl(apiKey);
// ========================================
// 演示1:普通对话(带记忆)
// ========================================
System.out.println("\n【演示1:智能对话】");
System.out.println("- - - - - - - - - - - - - - - - - - -");
String response1 = assistant.chat("我叫张三,是一名Java开发工程师");
System.out.println("用户: 我叫张三,是一名Java开发工程师");
System.out.println("AI: " + response1);
String response2 = assistant.chat("我的工号是多少?");
System.out.println("\n用户: 我的工号是多少?");
System.out.println("AI: " + response2);
String response3 = assistant.chat("刚才我说我是什么职位?");
System.out.println("\n用户: 刚才我说我是什么职位?");
System.out.println("AI: " + response3);
// ========================================
// 演示2:知识库问答
// ========================================
System.out.println("\n\n【演示2:知识库问答】");
System.out.println("- - - - - - - - - - - - - - - - - - -");
String q1 = "公司有哪些员工福利?";
String a1 = assistant.answerFromKnowledgeBase(q1);
System.out.println("问题: " + q1);
System.out.println("回答: " + a1);
String q2 = "公司的技术栈是什么?";
String a2 = assistant.answerFromKnowledgeBase(q2);
System.out.println("\n问题: " + q2);
System.out.println("回答: " + a2);
// ========================================
// 演示3:工具调用
// ========================================
System.out.println("\n\n【演示3:智能任务执行】");
System.out.println("- - - - - - - - - - - - - - - - - - -");
String task1 = "现在几点了?顺便告诉我北京的天气";
String result1 = assistant.executeTask(task1);
System.out.println("任务: " + task1);
System.out.println("执行结果: " + result1);
String task2 = "帮我计算 (15 + 25) * 2 等于多少?";
String result2 = assistant.executeTask(task2);
System.out.println("\n任务: " + task2);
System.out.println("执行结果: " + result2);
// ========================================
// 演示4:流式响应
// ========================================
System.out.println("\n\n【演示4:流式响应】");
System.out.println("- - - - - - - - - - - - - - - - - - -");
assistant.chatStreaming(
"请介绍一下人工智能的发展历史",
new StreamingResponseHandler<>() {
private int tokenCount = 0;
@Override
public void onPartialToken(String token) {
System.out.print(token);
tokenCount++;
}
@Override
public void onComplete(Response<String> response) {
System.out.println("\n\n流式输出完成!共 " + tokenCount + " 个token");
}
@Override
public void onError(Throwable error) {
System.out.println("错误: " + error.getMessage());
}
}
);
// 等待流式输出
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// 忽略
}
}
}
常见使用场景与最佳实践
场景一:智能客服系统
public class CustomerServiceBot {
private final ChatLanguageModel model;
private final ChatMemory memory;
private final Agent orderAgent;
private final Agent refundAgent;
public CustomerServiceBot(String apiKey) {
this.model = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.5)
.build();
// 按客户会话隔离记忆
this.memory = MessageWindowChatMemory.withMaxMessages(30);
// 初始化订单查询工具
this.orderAgent = Agent.builder()
.chatLanguageModel(model)
.chatMemory(memory)
.tools(new OrderQueryTool())
.build();
// 初始化退款处理工具
this.refundAgent = Agent.builder()
.chatLanguageModel(model)
.chatMemory(memory)
.tools(new RefundTool())
.build();
}
public String handleCustomer(String customerId, String message) {
// 根据意图路由到不同的处理逻辑
String intent = detectIntent(message);
return switch (intent) {
case "order_query" -> orderAgent.execute(message);
case "refund" -> refundAgent.execute(message);
default -> generalResponse(message);
};
}
private String detectIntent(String message) {
// 意图识别逻辑
String lowerMessage = message.toLowerCase();
if (lowerMessage.contains("订单") || lowerMessage.contains("物流")
|| lowerMessage.contains("发货")) {
return "order_query";
}
if (lowerMessage.contains("退款") || lowerMessage.contains("退货")) {
return "refund";
}
return "general";
}
private String generalResponse(String message) {
String prompt = """
你是一个专业的电商客服,请礼貌地回复客户的问题。
客户消息:%s
回复要求:
1. 礼貌友好
2. 简洁明了
3. 如无法解决问题,引导客户联系人工客服
""".formatted(message);
return model.sendMessages(List.of(
SystemMessage.from(prompt)
)).text();
}
}
场景二:代码助手
public class CodeAssistant {
private final ChatLanguageModel model;
private final PromptTemplate codeReviewTemplate;
private final PromptTemplate codeExplainTemplate;
private final PromptTemplate codeOptimizeTemplate;
public CodeAssistant(String apiKey) {
this.model = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.3) // 代码场景使用低温度
.build();
this.codeReviewTemplate = PromptTemplate.from(
"请审查以下%s代码,重点检查:\n" +
"1. 代码规范和风格\n" +
"2. 潜在Bug和安全问题\n" +
"3. 性能优化建议\n" +
"4. 错误处理是否完善\n\n" +
"代码:\n```%s\n%s\n```"
);
this.codeExplainTemplate = PromptTemplate.from(
"请详细解释以下%s代码的功能和工作原理:\n\n" +
"代码:\n```%s\n%s\n```\n\n" +
"请包含:\n" +
"1. 整体功能说明\n" +
"2. 主要方法和类的作用\n" +
"3. 关键逻辑的执行流程"
);
this.codeOptimizeTemplate = PromptTemplate.from(
"请优化以下%s代码,在保持功能不变的前提下:\n\n" +
"代码:\n```%s\n%s\n```\n\n" +
"优化目标:\n" +
"1. 提高性能\n" +
"2. 改善可读性\n" +
"3. 增强可维护性\n\n" +
"请同时提供优化建议和优化后的代码。"
);
}
public String reviewCode(String language, String code) {
String prompt = codeReviewTemplate.apply(Map.of(
"language", language,
"language2", language,
"code", code
)).text();
return model.sendMessages(List.of(SystemMessage.from(prompt))).text();
}
public String explainCode(String language, String code) {
String prompt = codeExplainTemplate.apply(Map.of(
"language", language,
"language2", language,
"code", code
)).text();
return model.sendMessages(List.of(SystemMessage.from(prompt))).text();
}
public String optimizeCode(String language, String code) {
String prompt = codeOptimizeTemplate.apply(Map.of(
"language", language,
"language2", language,
"code", code
)).text();
return model.sendMessages(List.of(SystemMessage.from(prompt))).text();
}
}
场景三:数据提取与分析
public class DataExtractor {
private final ChatLanguageModel model;
private final OutputParser<ExtractedData> jsonParser;
public DataExtractor(String apiKey) {
this.model = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.1) // 数据提取使用极低温度
.build();
this.jsonParser = new GsonOutputParser<>(ExtractedData.class);
}
public ExtractedData extractFromText(String text, String schema) {
String prompt = """
请从以下文本中提取信息,并按照指定的JSON Schema格式输出。
文本内容:
%s
JSON Schema:
%s
要求:
1. 只输出JSON,不要包含任何其他文字
2. 如果某字段在文本中找不到对应信息,使用null
3. 日期格式使用ISO 8601标准
""".formatted(text, schema);
AiMessage response = model.sendMessages(List.of(
SystemMessage.from(prompt)
));
// 解析JSON响应
try {
return jsonParser.parse(response.text());
} catch (Exception e) {
throw new RuntimeException("解析失败: " + e.getMessage(), e);
}
}
}
// 数据类
public record ExtractedData(
String name,
String email,
String phone,
String company,
String title,
List<String> skills
) {}
// 自定义JSON解析器
public class GsonOutputParser<T> implements OutputParser<T> {
private final Class<T> clazz;
private final Gson gson = new Gson();
public GsonOutputParser(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T parse(String text) {
// 尝试提取JSON部分
String json = extractJson(text);
return gson.fromJson(json, clazz);
}
private String extractJson(String text) {
// 移除markdown代码块标记
text = text.trim();
if (text.startsWith("```json")) {
text = text.substring(7);
} else if (text.startsWith("```")) {
text = text.substring(3);
}
if (text.endsWith("```")) {
text = text.substring(0, text.length() - 3);
}
return text.trim();
}
}
最佳实践总结
提示词工程
// 实践1:使用结构化提示词
PromptTemplate structuredPrompt = PromptTemplate.from(
"""
任务:{{task}}
角色:{{role}}
输出格式:
- 标题:[提取的标题]
- 要点:[提取的要点列表]
- 建议:[相关建议]
内容:
{{content}}
"""
);
// 实践2:Few-shot示例
PromptTemplate fewShotPrompt = PromptTemplate.from(
"""
你是一个文本分类器。
示例:
输入:"这个产品太棒了!"
输出:正面
输入:"服务态度很差"
输出:负面
输入:"今天天气不错"
输出:中性
现在请分类:
输入:"{{input}}"
输出:
"""
);
// 实践3:链式思考提示
PromptTemplate cotPrompt = PromptTemplate.from(
"""
问题:{{question}}
请按以下步骤思考:
1. 理解问题的核心要求
2. 分析相关因素
3. 制定解决方案
4. 给出最终答案
你的思考过程:
"""
);
性能优化
// 优化1:使用批量API减少网络开销
public class BatchOptimization {
public List<String> batchProcess(List<String> inputs, ChatLanguageModel model) {
// 将多个请求合并为一个提示词
String combinedPrompt = inputs.stream()
.map(input -> "任务" + (inputs.indexOf(input) + 1) + ": " + input)
.collect(Collectors.joining("\n\n"));
// 单次API调用
String response = model.sendMessages(List.of(
SystemMessage.from("请依次完成以下任务:\n" + combinedPrompt)
)).text();
// 解析结果
return parseBatchResults(response, inputs.size());
}
}
// 优化2:使用缓存减少重复调用
public class CachingExample {
public static ChatLanguageModel createCachedModel(String apiKey) {
ChatLanguageModel baseModel = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
// 使用内存缓存
return CachingChatLanguageModel.builder()
.delegate(baseModel)
.cache(Cache.builder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(30))
.build())
.build();
}
}
// 优化3:合理设置Token限制
public class TokenOptimization {
public static ChatLanguageModel createOptimizedModel(String apiKey) {
return OpenAiChatModel.builder()
.apiKey(apiKey)
.maxTokens(500) // 根据实际需求设置
.build();
}
}
错误处理与重试
public class ResilientExample {
public static ChatLanguageModel createResilientModel(String apiKey) {
ChatLanguageModel baseModel = OpenAiChatModel.builder()
.apiKey(apiKey)
.build();
return RetryingChatLanguageModel.builder()
.delegate(baseModel)
.maxAttempts(3)
.delayBeforeRetry(Duration.ofSeconds(1))
.retryOn(List.of(
RateLimitException.class,
HttpStatusException.class
))
.build();
}
}
高级特性与扩展
自定义模型集成
如果需要集成LangChain4j不支持的LLM提供商,可以实现自定义模型接口:
public class CustomModelExample {
// 实现自定义聊天模型
public static class MyCustomChatModel implements ChatLanguageModel {
private final String apiKey;
private final String baseUrl;
public MyCustomChatModel(String apiKey, String baseUrl) {
this.apiKey = apiKey;
this.baseUrl = baseUrl;
}
@Override
public AiMessage sendMessages(List<ChatMessage> messages) {
// 将LangChain4j消息格式转换为你的API格式
MyRequest request = convertToRequest(messages);
// 调用自定义API
HttpResponse response = HttpClient.newHttpClient()
.send(
HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/chat"))
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(toJson(request)))
.build(),
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() != 200) {
throw new RuntimeException("API调用失败: " + response.statusCode());
}
// 解析响应并转换为LangChain4j格式
MyResponse myResponse = fromJson(response.body(), MyResponse.class);
return AiMessage.from(myResponse.getContent());
}
private MyRequest convertToRequest(List<ChatMessage> messages) {
// 实现消息格式转换逻辑
return new MyRequest(/* 转换后的数据 */);
}
// Builder模式
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String apiKey;
private String baseUrl;
public Builder apiKey(String apiKey) {
this.apiKey = apiKey;
return this;
}
public Builder baseUrl(String baseUrl) {
this.baseUrl = baseUrl;
return this;
}
public MyCustomChatModel build() {
return new MyCustomChatModel(apiKey, baseUrl);
}
}
}
// 使用自定义模型
public static void main(String[] args) {
MyCustomChatModel customModel = MyCustomChatModel.builder()
.apiKey("your-api-key")
.baseUrl("https://api.your-provider.com")
.build();
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(customModel)
.build();
String response = assistant.chat("你好");
System.out.println(response);
}
}
与Spring Boot集成
// Spring Boot配置类
@Configuration
public class LangChain4jConfig {
@Value("${openai.api.key}")
private String openaiApiKey;
@Bean
public ChatLanguageModel chatLanguageModel() {
return OpenAiChatModel.builder()
.apiKey(openaiApiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.build();
}
@Bean
public StreamingChatLanguageModel streamingChatLanguageModel() {
return OpenAiStreamingChatModel.builder()
.apiKey(openaiApiKey)
.modelName("gpt-3.5-turbo")
.temperature(0.7)
.build();
}
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.withMaxMessages(50);
}
@Bean
public Assistant assistant(
ChatLanguageModel chatLanguageModel,
ChatMemory chatMemory) {
return AiServices.builder(Assistant.class)
.chatLanguageModel(chatLanguageModel)
.chatMemory(chatMemory)
.build();
}
}
// Spring Boot控制器
@RestController
@RequestMapping("/api/ai")
public class AiController {
private final Assistant assistant;
public AiController(Assistant assistant) {
this.assistant = assistant;
}
@PostMapping("/chat")
public Map<String, String> chat(@RequestBody Map<String, String> request) {
String message = request.get("message");
String response = assistant.chat(message);
return Map.of("response", response);
}
@PostMapping("/chat/stream")
public Flux<ServerSentEvent<String>> chatStream(
@RequestBody Map<String, String> request,
HttpServletResponse response) {
String message = request.get("message");
return Flux.create(emitter -> {
assistant.chat(message, new StreamingResponseHandler<>() {
@Override
public void onPartialToken(String token) {
emitter.next(ServerSentEvent.<String>builder()
.data(token)
.build());
}
@Override
public void onComplete(Response<String> response) {
emitter.complete();
}
@Override
public void onError(Throwable error) {
emitter.error(error);
}
});
});
}
}
总结与资源推荐
核心要点回顾
通过本教程,我们详细探讨了LangChain4j的各个方面:
- 为什么值得关注:LangChain4j填补了Java生态在LLM应用开发领域的空白,提供了完整的一站式解决方案
- 环境搭建:通过Maven或Gradle可以快速集成,配合简单的配置即可开始开发
- AI Service:最高层次的抽象,通过接口定义和注解即可快速构建AI应用
- 提示词模板:提供了强大的模板引擎,支持变量、条件、循环等复杂逻辑
- 对话记忆:支持多种记忆实现,可以根据场景选择合适的方案
- Chain系统:灵活的链式调用机制,可以组合出复杂的业务逻辑
- RAG支持:完整的检索增强生成支持,可以构建知识密集型应用
- 工具与代理:让AI能够执行实际操作,实现真正的智能自动化
- 流式响应:提升用户体验,特别适合长文本生成场景
LangChain4j与其他框架对比
| 特性 | LangChain4j | Spring AI | Direct API |
|---|---|---|---|
| 学习曲线 | 中等 | 较高 | 低 |
| 功能完整性 | 完整 | 一般 | 需自行实现 |
| 多模型支持 | 丰富 | 一般 | 无 |
| RAG支持 | 完善 | 一般 | 需自行实现 |
| 社区活跃度 | 活跃 | 一般 | 无 |
| 文档质量 | 优秀 | 一般 | 无 |
推荐的延伸学习方向
- 深入学习LLM原理:了解Transformer架构、注意力机制等
- 提示词工程:学习高级提示技巧,如CoT、ReAct等
- 向量数据库:学习使用Pinecone、Weaviate等生产级向量存储
- Agent架构:探索更复杂的Agent设计模式
- 性能优化:学习Prompt压缩、KV Cache等技术
相关资源链接
官方资源
- GitHub仓库:https://github.com/langchain4j/langchain4j
- 官方文档:https://docs.langchain4j.dev/
- 示例项目:https://github.com/langchain4j/langchain4j-examples
- Maven中央仓库:https://central.sonatype.com/artifact/dev.langchain4j/langchain4j
模型提供商
- OpenAI API:https://platform.openai.com/
- Anthropic Claude:https://www.anthropic.com/
- Google AI Studio:https://aistudio.google.com/
- HuggingFace:https://huggingface.co/
向量数据库
- Pinecone:https://www.pinecone.io/
- Weaviate:https://weaviate.io/
- Qdrant:https://qdrant.tech/
- Milvus:https://milvus.io/
展望未来
LLM应用开发正在快速发展,LangChain4j也在持续迭代。未来值得期待的方向包括:
- 更好的多模态支持(图像、音频、视频)
- 更强大的Agent框架
- 更丰富的RAG优化
- 与更多Java生态框架的深度集成
- 性能优化和资源管理改进
无论你是Java老手还是新人,LangChain4j都为你打开了一扇通往AI应用开发的大门。现在就开始你的LangChain4j之旅吧!
祝你编码愉快!
“`
评论区