大模型应用开发专栏

(一)手把手教学:LangChain4j实现Java与AI大模型深度对话
(二)Windows搭建AI大模型应用开发环境以及踩过的坑
(三)Windows搭建AI大模型应用开发环境 - 向量数据库pgvector
(四)手把手教学:SpringBoot+LangChain4j实战全攻略
(五)手把手教学:SpringBoot整合LangChain4j实现知识库RAG检索
(六)手把手教学:SpringBoot + MCP + Cherry Studio实战

Springboot集成LangChain4j实现以下功能
1、基于系统提示词AI对话
2、流式输出
3、会话记忆(多轮对话)
4、对话隔离
5、Function Calling(函数调用)
6、联网搜索
7、多模态 - 图生文
8、多模态 - 文生图

1、LangChain4j 目前支持的LLM:

2、langchain4j工程结构

LangChain4j 在两个抽象层次上运行:

  • 底层 (opens new window)。这层,你拥有最大自由,可以访问所有底层组件,如 ChatLanguageModelUserMessageAiMessageEmbeddingStoreEmbedding 等。这些是 LLM 应用程序的“原语”。你可完全控制如何组合它们,但需编写更多代码
  • 高级 (opens new window)。这层,你通过高级 API(如 AiServices)与 LLM 进行交互,这些 API 屏蔽所有复杂性和样板代码。你仍可灵活调整和微调行为,但是以声明方式完成

开发环境

  1. Jdk21

  2. apache-maven-3.8.5

  3. 用的是阿里的百炼(通义千问)大模型

pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.coco</groupId>
    <artifactId>langchain4j</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>langchain4j</name>
    <description>Demo project for Spring Boot langchain4j</description>

    <properties>
        <java.version>21</java.version>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <langchain4j.version>1.0.0-beta2</langchain4j.version>
    </properties>

    <dependencies>

        <!--核心-->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        </dependency>


        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-web-search-engine-searchapi</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-community-bom</artifactId>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
application.yaml
server:
    port: 8080


langchain4j:
  community:
    dashscope:
      chat-model:
        api-key: 你的apikey
        #model-name: wanx2.1-t2i-turbo
        #model-name: qwen-vl-max-latest
        model-name: qwen-max-latest
      streaming-chat-model:
          api-key: 你的apikey
          model-name: qwen-max-latest

search:
  apiKey: 你的apikey
  engine: baidu

一、系统提示词

方式一:直接注入ChatLanguageModel对象即可,在配置文件中可配置使用的模型

package org.coco.lc.controller;

import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatLanguageModel;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequestMapping("/api")
@RequiredArgsConstructor
@RestController
public class ChatController {

    final ChatLanguageModel chatLanguageModel;

    @GetMapping("/chat")
    public String chat(@RequestParam(value = "message") String message) {

        return chatLanguageModel.chat(List.of(SystemMessage.systemMessage("假如你是周杰伦,接下来请以周杰伦的语气来对话"),
                UserMessage.userMessage(message)
        )).aiMessage().text();
//       普通对话
//        return chatLanguageModel.chat(UserMessage.from(message)).aiMessage().text();

    }

}

运行的结果如下:

方式二:使用对话助来进行对话,这种方式用注解就可以,比第一种方式简单

1、创建AI助手接口

package org.coco.lc.service;

import dev.langchain4j.service.SystemMessage;


public interface Assistant {

    @SystemMessage("假如你是周杰伦,接下来请以周杰伦的语气来对话")
    String chat(String message);

}

2、初始化AI助手

package org.coco.lc.config;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;
import lombok.RequiredArgsConstructor;
import org.coco.lc.service.Assistant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class AssistantInit {

    final ChatLanguageModel chatLanguageModel;

    @Bean
    public Assistant init() {
        return AiServices.builder(Assistant.class)
                .chatLanguageModel(chatLanguageModel).build();

    }

}

3、ChatController中使用AI助手对话

    final Assistant assistant;

    @GetMapping("/assistantChat")
    public String assistantChat(@RequestParam(value = "message") String message) {
        return assistant.chat(message);
    }

二、流式输出

pom.xml 配置 Flux

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-webflux</artifactId>

        </dependency>

application.yaml配置文件

langchain4j:
  community:
    dashscope:
      streaming-chat-model:
          api-key: 你的apikey
          model-name: qwen-max

代码如下:

 @RequestMapping(value = "/stream", produces = "text/stream;charset=UTF-8")
    public Flux<String> stream(@RequestParam(value = "message") String message) {

        Flux<String> flux = Flux.create(fluxSink -> {

            qwenStreamingChatModel.chat(message, new StreamingChatResponseHandler() {
                @Override
                public void onPartialResponse(String partialResponse) {
                    System.out.println("响应文本:" + partialResponse);
                    fluxSink.next(partialResponse);
                }

                @Override
                public void onCompleteResponse(ChatResponse completeResponse) {
                    System.out.println("结束");
                    fluxSink.complete();
                }

                @Override
                public void onError(Throwable error) {
                    System.out.println("错误");
                    fluxSink.error(error);
                }
            });


        });
        return flux;

    }

运行结果如下:

三、会话记忆(多轮对话)

Langchain4j内置了两个chatMemory的实现

  1. MessageWindowChatMemory

  2. TokenWindowChatMemory

实现原理

多轮对话的实现原理就是将之前的对话和响应重新发送给大模型,如果多轮对话要将前几次发送和响应的参数重新带到大模型发起新的对应,所以,langchain4j基于动态代理对此进行了封装

 Assistant动态代理对象  chat  --->  对话内容存储ChatMemory----> 聊天记录ChatMemory取出来 ---->放入到当前对话中

        ChatLanguageModel model = QwenChatModel
                .builder()
                .apiKey("你的apkkey")
                .modelName("模型名称")
                .build();

        UserMessage message1 = UserMessage.userMessage("我叫周杰伦");
        ChatResponse resp1 = model.chat(message1);
        AiMessage aiMessage1 = resp1.aiMessage();
        System.out.println(aiMessage1.text());
        System.out.println("------");
        ChatResponse resp2 = moel.chat(message1, aiMessage1, UserMessage.userMessage("我叫什么名字"));            
        System.out.println(resp2.aiMessage().text());
AssistantInit代码如下:
    @Bean
    public Assistant assistant(ChatLanguageModel chatLanguageModel, StreamingChatLanguageModel streamingChatLanguageModel) {
        //设置最大存储记录,默认存在内存中
        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
        
        // 为Assistant动态代理对象  chat  --->  对话内容存储ChatMemory----> 聊天记录ChatMemory取出来 ---->放入到当前对话中
        Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(chatLanguageModel)
                .streamingChatLanguageModel(streamingChatLanguageModel)
                .chatMemory(chatMemory)//绑定对话记忆对象
                .build();
        return assistant;
    }
ChatController 代码如下:
    @RequestMapping(value = "/memory_chat")
    public String memoryChat(@RequestParam(value = "message") String message) {
        return assistant.chat(message);
    }

测试两轮对话

1、我叫周杰伦

2、我叫什么名字 (如果基于上一个问题回答结果中包含周杰伦说明会话记忆生效了)

四、对话隔离

新增AssistantUnique接口

    public interface AssistantUnique {
        String chat(@MemoryId int memoryId, @UserMessage String userMessage);
    }

构建AI对话助手

    @Bean
    public AssistantUnique assistantUnique(ChatLanguageModel qwenChatModel,
                                           StreamingChatLanguageModel qwenStreamingChatModel) {
        AssistantUnique assistant = AiServices.builder(AssistantUnique.class)
                .chatLanguageModel(qwenChatModel)
                .streamingChatLanguageModel(qwenStreamingChatModel)
                .chatMemoryProvider(memoryId ->
                        MessageWindowChatMemory.builder().maxMessages(10)
                                .id(memoryId).build()
                )
                .build();
        return assistant;
    }
ChatController

    final AssistantUnique assistantUnique;

    @GetMapping("/memory_chat_isolation")
    public String memoryChatIsolation(@RequestParam(value = "userId") Integer userId, @RequestParam(value = "message") String message) {
        return assistantUnique.chat(userId, message);
    }

测试:UserId = 1,我是周杰伦

测试:UserId = 2,我是谁(从结果来看,并不知道我是谁,根据UserId隔离了对话)

五、Function Calling(函数调用)

定义ToolsService接口

@Tool 用于告诉 AI 什么对话调用这个方法

package org.coco.lc.service;

import dev.langchain4j.agent.tool.Tool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class ToolsService {

    @Tool("计算两数之和")
    public int sum(int a, int b) {
        log.info("sum a:{},b:{}", a, b);
        return a + b;
    }

    @Tool("计算两数之差")
    public int sub(int a, int b) {
        log.info("sub a:{},b:{}", a, b);
        return a - b;
    }

}
AssistantInit.java
结合通过AiService配置tools , 这里用的是前面记忆对话时配置的Assistant
    @Bean
    public Assistant functionCalling(ToolsService toolsService) {
        return AiServices.builder(Assistant.class)
                .tools(toolsService)
                .chatLanguageModel(chatLanguageModel).build();
    }
ChatController.java
 @RequestMapping(value = "/func")
    public String functionCalling(@RequestParam(value = "message") String message) {
        return assistant.chat(message);
    }

六、联网搜索

大模型本身并不具备原生的网络访问能力 。其核心能力始终聚焦在自然语言理解、知识推理和文本生成等认知层面。所谓的"联网搜索"功能,本质是检索增强生成(Retrieval-Augmented Generation, RAG)架构 的工程化实现。

SearchApi 是一个强大的实时 SERP API,可提供来自 Google 搜索、Google 招聘、YouTube、Google 新闻等搜索引擎集合的结构化数据。系统默认使用 google 作为网页搜索引擎,但您也可以切换到 bingbaidugoogle_newsbing_newsgoogle_scholargoogle_patents 等其他引擎。

1. 申请 Search API Key

请在 SearchApi申请 API Key。

2.pom.xml新增search api依赖

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-web-search-engine-searchapi</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

3. SearchConfig 配置

package org.coco.lc.config;


import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "search")
@Data
public class SearchConfig {

    private String engine;

    private String apiKey;

}
4. application.yaml 新增配置
search:
  apiKey: fjfqQZM4WAx7WfAvSfm4a4qS
  engine: baidu

5. 初始化WebSearchInit

package org.coco.lc.config;

import dev.langchain4j.web.search.searchapi.SearchApiWebSearchEngine;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;

@Configuration
@RequiredArgsConstructor
public class WebSearchInit {

    final SearchConfig searchConfig;


    @Bean
    public SearchApiWebSearchEngine initWebSearchEngine() {

        return SearchApiWebSearchEngine.builder()
            .engine(searchConfig.getEngine())
            .apiKey(searchConfig.getApiKey())
                .timeout(Duration.ofMinutes(3))
            .build();

    }
}

6. 基于tools调用联网搜索

    @Bean
    public Assistant initAssistant(SearchApiWebSearchEngine searchApiWebSearchEngine) {
        return AiServices.builder(Assistant.class)
                .chatLanguageModel(chatLanguageModel)
                .tools(new WebSearchTool(searchApiWebSearchEngine))
                .build();
    }

没有联网搜索之前,提问:"深圳今天天气怎么样,请联网查询",结果如下

有了联网搜索功能之后,提问:"深圳今天天气怎么样,请联网查询"  结果如下:

问题1:调用search api 是有时间会出现 read time out或time out,实际请求已经调用了search api ,search api 中可以看调用历史,多试几次

问题2:回答不准确,不是想要的结果,请更换比较强大的模型,如"qwen-max-latest"

七、多模态 - 图生文

1、设置支持图生文的大模型,如通义千问的"model-name: qwen-vl-max-latest"

2、ChatController 代码如下

    @SneakyThrows
    @PostMapping("/img")
    public String img(@RequestParam(defaultValue = "图片是什么") String message, @RequestParam("img") MultipartFile img) {
        String base64Str = Base64.getEncoder().encodeToString(img.getBytes());
        UserMessage um = UserMessage.from(TextContent.from(message),
                ImageContent.from(base64Str, "image/png"));
        return chatLanguageModel.chat(List.of(um)).aiMessage().text();
    }

3、上传本地的图片,让AI 解释图片的内容,运行结果如下

八、多模态 - 文生图

1、设置支持文生图的大模型,如通义千问的"model-name: wanx2.1-t2i-turbo"

2、代码

    @RequestMapping(value = "/genImg")
    public String generateImage(@RequestParam(value = "message") String message) {
        WanxImageModel wanxImageModel = WanxImageModel.builder()
                .modelName("wanx2.1-t2i-turbo")
                .apiKey(System.getenv("ALI_AI_KEY"))
                .build();
        Response<Image>  response= wanxImageModel.generate(message);
        System.out.println(response.content().url());
        return response.content().url().toString();
    }

3、运行结果如下

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐