1. 项目概述
作为一名长期奋战在AI应用开发一线的工程师,我最近深入研究了LangChain4j和LangGraph4j这两个强大的Java框架。它们为开发者提供了构建复杂AI应用的能力,让我在项目中实现了许多以前难以想象的功能。LangChain4j专注于与大语言模型的交互,而LangGraph4j则提供了工作流编排能力,两者结合使用可以创造出非常强大的AI应用。
本文将分享我在使用这些框架时的实战经验,包括如何通过Coze平台快速搭建AI工作流,如何实现RAG(检索增强生成)技术,以及如何利用LangChain4j和LangGraph4j构建复杂的AI应用。无论你是刚接触AI开发的Java工程师,还是已经有一定经验的开发者,相信这些内容都能给你带来启发和帮助。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈概览
2.1 核心依赖配置
在开始之前,我们需要配置项目的核心依赖。以下是Maven项目中需要添加的关键依赖:
xml复制<!-- LangChain4j 核心库 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.1.0</version>
</dependency>
<!-- LangChain4j Spring Boot Starter -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>1.1.0-beta7</version>
</dependency>
<!-- LangChain4j 响应式支持 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
<version>1.1.0-beta7</version>
</dependency>
<!-- 阿里云通义千问集成 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
<version>1.1.0-beta7</version>
</dependency>
<!-- LangGraph4j 核心库 -->
<dependency>
<groupId>org.bsc.langgraph4j</groupId>
<artifactId>langgraph4j-core</artifactId>
<version>1.6.0-beta6</version>
</dependency>
<!-- LangGraph4j 与 LangChain4j 集成 -->
<dependency>
<groupId>org.bsc.langgraph4j</groupId>
<artifactId>langgraph4j-langchain4j</artifactId>
<version>1.6.0-beta6</version>
</dependency>
这些依赖包含了我们需要的所有核心功能。LangChain4j提供了与大语言模型交互的基础能力,而LangGraph4j则为我们提供了工作流编排的能力。
2.2 模型配置
在application.yml中配置通义千问模型:
yaml复制langchain4j:
community:
dashscope:
chat-model:
model-name: qwen-max
api-key: your-api-key-here
streaming-chat-model:
model-name: qwen-max
api-key: your-api-key-here
embedding-model:
model-name: text-embedding-v4
api-key: your-api-key-here
这里我们配置了三种类型的模型:
- chat-model:用于普通的聊天交互
- streaming-chat-model:支持流式响应的聊天模型
- embedding-model:用于文本向量化的模型
3. Coze平台快速入门
3.1 Coze平台简介
Coze是新一代AI应用开发平台,它提供了可视化设计与编排工具,可以通过零代码或低代码的方式,快速搭建出基于大模型的各类AI项目。对于不熟悉编程的业务人员来说,这是一个非常友好的工具。
3.2 构建"自动笑话生成并存入文档"小助手
下面是一个使用Coze构建的简单示例,实现"自动笑话生成并存入文档"的功能:
- 调用大模型:让AI创作一个原创的、简短的笑话
- 自动存文档:把这个笑话自动保存到一个新的在线文档里
- 返回链接:把这个保存了笑话的文档链接返回给用户
这个工作流的搭建过程非常简单直观,完全通过可视化界面完成。对于快速原型开发或者简单的AI应用来说,Coze是一个非常高效的选择。
4. RAG技术深度解析
4.1 RAG核心概念
检索增强生成(RAG)是一项结合了信息检索与自然语言生成的前沿技术,旨在显著提升大语言模型(LLM)的输出质量,使其回答不仅更准确,也更具关联性。
RAG的核心价值在于:
- 扩展了大语言模型的固有知识边界
- 弥补了其依赖静态训练数据的局限性
- 可以接入私有数据源,提供更专业的回答
4.2 RAG工作流程
RAG的工作流程可以分为两个主要阶段:
4.2.1 索引阶段(离线准备)
这是一个从原始数据构建可搜索知识库的管道(pipeline),通常预先离线完成。它包含三个关键步骤:
- 加载:使用文档加载器(Document Loaders)从各类数据源(如文档、数据库)中加载原始数据
- 分割:使用文本分割器(Text Splitters)将大型文档拆分成更小的语义块(chunks)
- 存储:将分割后的文本块转化为向量(使用嵌入模型/Embeddings model),并存入向量数据库(VectorStore)中建立索引
4.2.2 检索和生成阶段(在线运行)
这是在用户提问时实时运行的RAG链,主要步骤如下:
- 检索:当接收到用户查询时,系统将其转化为向量,并从已构建的向量数据库中检索出最相关的若干文本块
- 生成:将原始的用户查询和检索到的相关文本一同作为上下文,提交给大语言模型(LLM),模型基于这些补充信息,生成更准确、更具上下文的最终答案
4.3 RAG实现代码
以下是使用LangChain4j实现RAG的配置代码:
java复制@Configuration
public class RagConfig {
@Resource
private EmbeddingModel qwenEmbeddingModel;
@Resource
private EmbeddingStore<TextSegment> embeddingStore;
@Bean
public ContentRetriever contentRetriever() {
// 1. 加载文档:从指定目录加载所有文档
List<Document> documents = FileSystemDocumentLoader
.loadDocuments("src/main/resources/docs");
// 2. 文档切割:将文档按段落分割
DocumentByParagraphSplitter paragraphSplitter =
new DocumentByParagraphSplitter(1000, 200);
// 3. 文档摄取器:将文档转换为向量并存储
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
.documentSplitter(paragraphSplitter)
.textSegmentTransformer(textSegment -> TextSegment.from(
textSegment.metadata().getString("file_name") + "\n"
+ textSegment.text(),
textSegment.metadata()
))
.embeddingModel(qwenEmbeddingModel)
.embeddingStore(embeddingStore)
.build();
// 执行文档摄取
ingestor.ingest(documents);
// 4. 创建内容检索器
return EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(qwenEmbeddingModel)
.maxResults(5) // 最多返回5个相关片段
.minScore(0.80) // 相似度阈值:过滤低相关性结果
.build();
}
}
这段代码实现了完整的RAG流程:
- 从文件系统加载文档
- 将文档分割成适当大小的段落
- 使用嵌入模型将文本转换为向量
- 将向量存储到向量数据库中
- 创建内容检索器,用于后续的检索操作
5. LangChain4j核心功能实现
5.1 定义AI服务接口
LangChain4j的一个核心特性是可以通过接口定义AI服务。这种方式非常直观,让开发者可以像调用普通Java方法一样调用AI服务。
java复制public interface AiCodeHelperService {
@SystemMessage("你是一位专业的中文语言教师,专注于用给定词汇创作自然、地道的例句。\n" +
"- 用户会提供一个中文单词,你需要根据该词语的常用含义和语境,生成一个典型的中文例句。\n" +
"- 句子长度适中,结构完整,符合日常交流或书面表达习惯。\n" +
"- 若词语有多义项,选择最常用的义项进行造句。\n" +
"- 只需输出句子本身,无需额外解释。")
Result<String> chatPrompt(@MemoryId int memoryId, @UserMessage String userMessage);
@SystemMessage("你是资深的一名后端开发工程师,帮助用户编写需求代码")
Flux<String> chat(@MemoryId int memoryId, @UserMessage String userMessage);
@SystemMessage("你是一位专业的中英翻译官,擅长将中文句子准确、流畅地翻译成英文。\n" +
"- 用户会提供一句中文句子,你需要将其翻译成英文。\n" +
"- 翻译需符合英文表达习惯,用词自然、语法正确,同时尽量保留原句的语境和情感色彩。\n" +
"- 只需输出英文翻译,无需额外说明或注释。")
Result<String> chat1(@MemoryId int memoryId, @UserMessage String userMessage);
}
这个接口定义了三种AI服务:
- chatPrompt:用给定的中文单词造句子
- chat:代码助手服务,支持流式响应
- chat1:中英翻译服务
5.2 配置AI服务工厂
接下来我们需要配置AI服务的工厂类:
java复制@Configuration
public class AiCodeHelperServiceFactory {
@Resource
private ContentRetriever contentRetriever;
@Resource
private ChatModel qwenChatModel;
@Resource
private StreamingChatModel qwenStreamingChatModel;
@Bean
public AiCodeHelperService aiCodeHelperService() {
return AiServices.builder(AiCodeHelperService.class)
.chatModel(qwenChatModel)
.streamingChatModel(qwenStreamingChatModel)
.chatMemoryProvider(memoryId ->
MessageWindowChatMemory.withMaxMessages(10))
.contentRetriever(contentRetriever)
.build();
}
}
这个配置类做了以下几件事:
- 指定了同步和异步的聊天模型
- 配置了聊天记忆,每个会话保留最近10条消息
- 添加了RAG的内容检索器
- 构建了AI服务实例
5.3 控制器实现
最后,我们可以通过控制器暴露这些AI服务:
java复制@RestController
@RequestMapping("/api/ai")
public class AiController {
@Resource
private AiCodeHelperService aiCodeHelperService;
@GetMapping("/chat")
public Flux<ServerSentEvent<String>> chat(int memoryId, String message) {
return aiCodeHelperService.chat(memoryId, message)
.map(chunk -> ServerSentEvent.<String>builder()
.data(chunk)
.build());
}
}
这个控制器提供了一个流式聊天的接口,前端可以通过Server-Sent Events实时接收AI生成的内容。
6. LangGraph4j工作流编排
6.1 LangGraph4j核心概念
LangGraph4J是一个专门为Java生态设计的智能体工作流框架。它借鉴了Python中LangGraph的成功经验,但与LangChain4J和Spring AI等主流Java LLM框架深度集成,让Java开发者能够轻松构建复杂的多智能体系统。
核心概念包括:
- StateGraph:定义应用结构的主要类
- AgentState:代表图的共享状态
- 节点与边:节点执行具体操作,边定义控制流
6.2 定义工作流状态
首先我们需要定义工作流的状态:
java复制@Data
public class PromptWorkflowState extends AgentState {
public PromptWorkflowState(Map<String, Object> initData) {
super(initData);
}
public PromptWorkflowState() {
super(new HashMap<>());
}
public String getUserInput() {
return (String) data().get("userInput");
}
public void setUserInput(String userInput) {
data().put("userInput", userInput);
}
public String getOptimizedPrompt() {
return (String) data().get("optimizedPrompt");
}
public void setOptimizedPrompt(String optimizedPrompt) {
data().put("optimizedPrompt", optimizedPrompt);
}
public String getAnswer() {
return (String) data().get("answer");
}
public void setAnswer(String answer) {
data().put("answer", answer);
}
}
这个状态类用于在工作流的不同节点之间传递数据。
6.3 实现工作流服务
接下来是实现完整的工作流服务:
java复制@Slf4j
@Service
public class PromptWorkflowService {
@Resource
private AiCodeHelperService aiCodeHelperService;
public PromptWorkflowState executeWorkflow(int memoryId, String userInput)
throws GraphStateException {
StateGraph<PromptWorkflowState> workflow =
new StateGraph<>(PromptWorkflowState::new)
.addNode("start", node_async(state -> {
log.info("工作流开始,用户输入: {}", state.getUserInput());
state.setUserInput(userInput);
return Map.of("userInput", userInput);
}))
.addNode("optimizePrompt", node_async(state -> {
log.info("开始优化提示词...");
String word = state.getUserInput();
Result<String> result = aiCodeHelperService.chatPrompt(
memoryId, word);
String sentence = result.content();
state.setOptimizedPrompt(sentence);
log.info("优化后的提示词: {}", sentence);
return Map.of("optimizedPrompt", sentence);
}))
.addNode("generateAnswer", node_async(state -> {
log.info("开始生成最终答案...");
String sentence = state.getOptimizedPrompt();
Result<String> result = aiCodeHelperService.chat1(
memoryId, sentence);
String translation = result.content();
state.setAnswer(translation);
log.info("最终答案: {}", translation);
return Map.of("answer", translation);
}))
.addEdge(START, "start")
.addEdge("start", "optimizePrompt")
.addEdge("optimizePrompt", "generateAnswer")
.addEdge("generateAnswer", END);
CompiledGraph<PromptWorkflowState> compiledWorkflow = workflow.compile();
PromptWorkflowState initialState = new PromptWorkflowState();
initialState.setUserInput(userInput);
Optional<PromptWorkflowState> result = compiledWorkflow.invoke(initialState);
return result.orElseThrow(() ->
new RuntimeException("工作流执行失败"));
}
}
这个工作流实现了以下功能:
- 接收用户输入的一个中文单词
- 使用AI生成包含该单词的例句
- 将例句翻译成英文
- 返回最终结果
7. 实战经验与注意事项
7.1 性能优化建议
- 向量数据库选择:对于生产环境,建议使用专业的向量数据库如Pinecone或Milvus,而不是内存中的实现
- 批处理文档:当需要处理大量文档时,考虑使用批处理模式,避免内存溢出
- 缓存机制:对于频繁查询的内容,可以添加缓存层减少重复计算
7.2 常见问题排查
-
检索结果不相关:
- 检查嵌入模型是否适合你的领域
- 调整文本分割策略,确保语义完整性
- 调整相似度阈值
-
响应速度慢:
- 检查网络延迟
- 考虑使用更轻量级的嵌入模型
- 优化向量数据库的索引配置
-
内存泄漏:
- 监控长时间运行的进程
- 定期清理不再使用的会话状态
- 使用专业的内存分析工具定位问题
7.3 扩展思路
- 多模态支持:结合图像、音频等其他模态的数据
- 实时数据接入:连接实时数据源,如数据库变更流
- 复杂工作流:构建更复杂的工作流,结合业务规则和决策逻辑
在实际项目中,我发现这些框架的组合使用可以大大提升开发效率,同时也需要注意合理设计系统架构,避免过度依赖单一技术栈。
