Agent研究学习【Coze、LangChain4j与LangGraph4j】

前言

在AI应用开发领域,LangChain4j和LangGraph4j是两个强大的Java框架,它们为开发者提供了构建复杂AI应用的能力。LangChain4j专注于与大语言模型的交互,而LangGraph4j则提供了工作流编排能力。

coze

扣子是新一代 AI 应用开发平台,借助扣子提供的可视化设计与编排工具,可以通过零代码或低代码的方式,快速搭建出基于大模型的各类 AI 项目,满足个性化需求。

基于Coze打造一个“自动笑话生成并存入文档”的小助手:

  • 调用大模型:让AI创作一个原创的、简短的笑话
  • 自动存文档:把这个笑话自动保存到一个新的在线文档里
  • 返回链接:把这个保存了笑话的文档链接返回给用户

工作流搭建如下图:
在这里插入图片描述
运行结果如下图:

在这里插入图片描述
参考学习:
搭建第一个 Coze 工作流
coze智能体

RAG流程

检索增强生成(RAG)是一项结合了信息检索与自然语言生成的前沿技术,旨在显著提升大语言模型(LLM)的输出质量,使其回答不仅更准确,也更具关联性。

核心工作机制:

  • 数据增强:RAG通过为模型引入外部知识源(如私有数据库、实时信息或模型训练截止日期之后的新数据),有效扩展了大语言模型的固有知识边界,弥补了其依赖静态训练数据的局限性。
  • 检索阶段:从指定的外部知识库中精准检索与用户查询最相关的信息片段。
  • 生成阶段:大语言模型基于检索到的补充信息,结合原有知识,生成最终的自然语言回答。
    在这里插入图片描述
    其核心工作流程如下:
  1. 索引 (Indexing) - 离线准备阶段
    这是一个从原始数据构建可搜索知识库的管道(pipeline),通常预先离线完成。
    它包含三个关键步骤:
    加载(Load):使用文档加载器(Document Loaders)​ 从各类数据源(如文档、数据库)中加载原始数据。
    分割(Split):使用文本分割器(Text Splitters)​ 将大型文档拆分成更小的语义块(chunks)。
    存储(Store):将分割后的文本块转化为向量(使用嵌入模型 / Embeddings model),并存入向量数据库(VectorStore)​ 中建立索引,以便后续进行高效的相似性检索。

  2. 检索和生成 (Retrieval and Generation) - 在线运行阶段
    这是在用户提问时实时运行的RAG链,主要步骤如下:
    检索(Retrieval):当接收到用户查询时,系统将其转化为向量,并从已构建的向量数据库中检索出最相关的若干文本块。
    生成(Generation):将原始的用户查询和检索到的相关文本一同作为上下文,提交给大语言模型(LLM),模型基于这些补充信息,生成更准确、更具上下文的最终答案。

LangChain4j

LangChain4j 的目标是简化在 Java 应用中集成大语言模型(LLM)的过程。
参考链接:LangChain4j官方文档

  1. 统一 API:
    各类 LLM 提供商(如 OpenAI、Google Vertex AI)以及向量数据库(如 Pinecone、Milvus)通常采用各自的专有 API。LangChain4j 提供了统一的 API,使您无需为每个服务单独学习和实现其 API。在尝试不同的 LLM 或向量数据库时,您可以轻松切换,无需重写代码。LangChain4j 目前已支持 15+ 主流 LLM 提供商 和 20+ 向量数据库。
  2. 全面工具箱:
    自 2023 年初以来,社区开发了大量基于 LLM 的应用,逐渐沉淀出常见的抽象、模式和方法。LangChain4j 将这些内容整合成一个开箱即用的工具包。工具箱涵盖从底层的提示模板、聊天记忆管理、函数调用,到高层的 Agents 和 RAG 等模式。每个抽象都配备接口和多种常用实现,便于直接使用。无论是开发聊天机器人,还是构建涵盖数据导入到检索的完整 RAG 流程,LangChain4j 都能提供丰富选择。

LangGraph4j

LangGraph4J是一个专门为Java生态设计的智能体工作流框架。它借鉴了Python中LangGraph的成功经验,但与LangChain4J和Spring AI等主流Java LLM框架深度集成,让Java开发者能够轻松构建复杂的多智能体系统。不再需要手动管理各个智能体之间的状态传递和协作逻辑,而是通过可视化的方式定义整个工作流程。这正是LangGraph4J的核心价值所在——降低AI应用开发门槛,提升开发效率。
参考链接:LangGraph4j简介

一、技术栈概览

1.1 核心依赖

使用以下核心依赖:

<!-- 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>

1.2 配置文件

application.yml 中配置通义千问模型:

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

二、LangChain4j 核心功能实现

LangChain4j 的目标是简化将 LLM 集成到 Java 应用程序中的过程,官方文档:
LangChain4j官方文档

2.1 定义AI服务接口

LangChain4j 的核心思想是通过接口定义AI服务,使用注解来配置系统提示词和用户消息。

package com.example.service;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.Result;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import reactor.core.publisher.Flux;

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);
}

关键注解说明:

  • @SystemMessage:定义系统提示词,用于设定AI的角色和行为规范
  • @UserMessage:标记用户输入的消息参数
  • @MemoryId:用于区分不同的会话,实现多会话管理
  • Result<String>:同步返回结果,包含完整的响应内容
  • Flux<String>:响应式流式返回,支持实时输出(SSE)

2.2 配置AI服务工厂

通过配置类创建AI服务实例,集成记忆管理、RAG检索等功能。

package com.example.config;

import com.example.service.AiCodeHelperService;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.service.AiServices;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@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)
                // 会话记忆提供者:每个会话独立存储最近10条消息
                .chatMemoryProvider(memoryId ->
                        MessageWindowChatMemory.withMaxMessages(10))
                // RAG 检索增强生成
                .contentRetriever(contentRetriever)
                .build();
    }
}

核心配置说明:

  1. ChatModel vs StreamingChatModel

    • ChatModel:同步模型,等待完整响应后返回
    • StreamingChatModel:流式模型,支持实时输出,适合长文本生成
  2. ChatMemoryProvider

    • 为每个会话(memoryId)创建独立的记忆存储
    • MessageWindowChatMemory.withMaxMessages(10):保留最近10条对话记录
    • 实现上下文感知对话
  3. ContentRetriever

    • RAG(检索增强生成)的核心组件
    • 从知识库中检索相关内容,增强AI回答的准确性

2.3 RAG检索增强生成配置

RAG是提升AI回答质量的关键技术,通过检索相关文档来增强生成效果。

package com.example.config;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.splitter.DocumentByParagraphSplitter;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@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. 文档切割:将文档按段落分割
        // 参数1:每个片段最大1000字符
        // 参数2:片段之间重叠200字符(避免语义断裂)
        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)
                // 向量存储(可以是内存、Redis、Elasticsearch等)
                .embeddingStore(embeddingStore)
                .build();
        
        // 执行文档摄取
        ingestor.ingest(documents);
        
        // 4. 创建内容检索器
        return EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(qwenEmbeddingModel)
                .maxResults(5)      // 最多返回5个相关片段
                .minScore(0.80)     // 相似度阈值:过滤低相关性结果
                .build();
    }
}

RAG工作流程:

  1. 文档加载:从文件系统加载Markdown、TXT等文档
  2. 文档切割:将长文档切分为小片段,便于检索
  3. 向量化:使用Embedding模型将文本转换为向量
  4. 存储:将向量存储到向量数据库
  5. 检索:用户提问时,将问题向量化,检索最相关的文档片段
  6. 增强:将检索到的内容作为上下文,与用户问题一起发送给LLM
    在这里插入图片描述

2.4 Controller层实现

提供RESTful API接口,支持流式和非流式两种响应方式。

package com.example.controller;

import com.example.service.AiCodeHelperService;
import jakarta.annotation.Resource;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/api/ai")
public class AiController {

    @Resource
    private AiCodeHelperService aiCodeHelperService;

    /**
     * 流式聊天接口
     * 使用SSE(Server-Sent Events)实现实时响应
     * 
     * @param memoryId 会话ID,用于区分不同用户或会话
     * @param message 用户输入的消息
     * @return 流式响应,前端可以实时显示生成内容
     */
    @GetMapping("/chat")
    public Flux<ServerSentEvent<String>> chat(int memoryId, String message) {
        return aiCodeHelperService.chat(memoryId, message)
                .map(chunk -> ServerSentEvent.<String>builder()
                        .data(chunk)
                        .build());
    }
}

流式响应的优势:

  • 用户体验好:内容逐字显示,类似ChatGPT的效果
  • 降低等待时间:用户无需等待完整响应
  • 适合长文本:生成长文本时不会超时

前端调用示例(JavaScript):

const eventSource = new EventSource('/api/ai/chat?memoryId=1&message=你好');

eventSource.onmessage = (event) => {
    // 实时接收并显示每个文本片段
    console.log(event.data);
    document.getElementById('response').innerHTML += event.data;
};

eventSource.onerror = () => {
    eventSource.close();
};

三、LangGraph4j 工作流编排

LangGraph4j 提供了强大的工作流编排能力,可以将多个AI任务串联成复杂的处理流程。
LangGraph4J是一个专门为Java生态设计的智能体工作流框架。它借鉴了Python中LangGraph的成功经验,但与LangChain4J和Spring AI等主流Java LLM框架深度集成,让Java开发者能够轻松构建复杂的多智能体系统。

核心概念理解
StateGraph:这是你定义应用结构的主要类,通过添加节点和边来创建图结构。
AgentState:代表图的共享状态,是一个可传递的状态容器,每个节点都可以读取和更新它。
节点与边:节点执行具体操作,边定义控制流在节点间的传递路径。

3.1 定义工作流状态

工作流状态用于在不同节点之间传递数据。

package com.example.entity;

import lombok.Data;
import org.bsc.langgraph4j.state.AgentState;

import java.util.HashMap;
import java.util.Map;

/**
 * 提示词优化工作流状态
 * 继承自AgentState,用于在工作流节点间传递数据
 */
@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);
    }
    
    // AI 生成的最终回答(例如:句子的英文翻译)
    public String getAnswer() {
        return (String) data().get("answer");
    }
    
    public void setAnswer(String answer) {
        data().put("answer", answer);
    }
}

3.2 实现工作流服务

构建一个完整的工作流,实现"单词造句→翻译成英文"的流程。

package com.example.service;

import com.example.entity.PromptWorkflowState;
import dev.langchain4j.service.Result;
import lombok.extern.slf4j.Slf4j;
import org.bsc.langgraph4j.CompiledGraph;
import org.bsc.langgraph4j.GraphStateException;
import org.bsc.langgraph4j.StateGraph;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;
import java.util.Map;
import java.util.Optional;

import static org.bsc.langgraph4j.StateGraph.END;
import static org.bsc.langgraph4j.StateGraph.START;
import static org.bsc.langgraph4j.action.AsyncNodeAction.node_async;

/**
 * 提示词优化工作流服务
 * 演示如何使用LangGraph4j构建多步骤AI工作流
 */
@Slf4j
@Service
public class PromptWorkflowService {

    @Resource
    private AiCodeHelperService aiCodeHelperService;

    /**
     * 执行提示词优化工作流
     * 
     * 工作流程:
     * 1. 接收用户输入(一个中文单词)
     * 2. 使用AI生成包含该单词的例句
     * 3. 将例句翻译成英文
     * 
     * @param memoryId 会话ID
     * @param userInput 用户输入的单词
     * @return 包含完整处理结果的状态对象
     */
    public PromptWorkflowState executeWorkflow(int memoryId, String userInput) 
            throws GraphStateException {
        
        // 构建工作流图
        StateGraph<PromptWorkflowState> workflow = 
                new StateGraph<>(PromptWorkflowState::new)
                
                // ========== 节点1: 开始节点 ==========
                // 接收并保存用户输入
                .addNode("start", node_async(state -> {
                    log.info("工作流开始,用户输入: {}", state.getUserInput());
                    state.setUserInput(userInput);
                    return Map.of("userInput", userInput);
                }))
                
                // ========== 节点2: 提示词优化节点 ==========
                // 使用AI将单词转换为完整句子
                .addNode("optimizePrompt", node_async(state -> {
                    log.info("开始优化提示词...");
                    String word = state.getUserInput();
                    
                    // 调用AI服务生成例句
                    Result<String> result = aiCodeHelperService.chatPrompt(
                            memoryId, word);
                    
                    String sentence = result.content();
                    state.setOptimizedPrompt(sentence);
                    
                    log.info("优化后的提示词: {}", sentence);
                    return Map.of("optimizedPrompt", sentence);
                }))
                
                // ========== 节点3: 生成答案节点 ==========
                // 将中文句子翻译成英文
                .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")                    // 开始 -> start节点
                .addEdge("start", "optimizePrompt")         // start -> 优化提示词
                .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("工作流执行失败"));
    }
}

工作流执行流程图:

用户输入(单词)
    ↓
[start节点] - 保存输入
    ↓
[optimizePrompt节点] - AI生成例句
    ↓
[generateAnswer节点] - AI翻译成英文
    ↓
返回结果

关键概念说明:

  1. StateGraph:工作流图的核心类,用于定义节点和边
  2. addNode:添加工作流节点,每个节点执行特定任务
  3. node_async:异步节点动作,支持异步操作
  4. addEdge:定义节点之间的连接关系
  5. compile:编译工作流图,生成可执行的工作流
  6. invoke:执行工作流,传入初始状态
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐