大模型应用开发-LangChain4j

以开发一个高考志愿填报系统为例,入门大模型应用开发

一、Ollama

Ollama 是一个开源工具,允许用户在本地计算机上快速下载、运行和管理大型语言模型(LLMs)。它支持多种热门模型,无需依赖云端服务,适合开发、隐私保护或离线使用场景。

官网地址: https://ollama.com​​​​​

以这个为例Thinking · Ollama Blog可知Ollama默认端口11434,本机部署调用模型API:

二、请求参数

大模型服务请求相关的配置选项

2.1 model(模型名称)

作用:指定要调用的模型(如 llama2、mistral、gemma 等)。

复制代码
{
  "model": "qwen-plus"
}
2.2 messages(对话消息)

作用:输入对话历史,通常是一个包含 role(角色)和 content(内容)的数组,用于多轮对话。

角色类型:

user: 用户输入的消息。

assistant: AI 助手之前的回复。

system: 系统指令(如设定 AI 行为)

复制代码
{
  "messages": [
    {"role": "system", "content": "你是一个翻译助手,将中文翻译成英文。"},
    {"role": "user", "content": "你好,世界"}
  ]
}
2.3 stream(流式响应)

作用:是否启用流式传输(逐字返回结果,类似 ChatGPT 的效果)。

默认值:false(关闭,一次性返回完整响应)。

复制代码
{
  "stream": true
}
2.4 enable_search(联网搜索)

作用:是否允许模型联网搜索最新信息(部分模型支持,如 llama3 的联网插件)。

默认值:false(禁用,仅依赖模型本地知识)。

注意:需模型本身支持此功能,且可能影响响应速度。

复制代码
{
  "enable_search": true
}

三、会话功能入门

大模型部署===>登录阿里云 https://www.aliyun.com/===\>开通 大模型服务平台百炼 服务===>申请百炼平台 API-KEY===>选择模型并使用

3.1 依赖引入

(1)引入Langchian4j依赖

复制代码
    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-open-ai</artifactId>
      <version>1.0.1</version>
    </dependency>

(2)构建OpenAiChatModel对象

在电脑用户变量中配置API-KEY

复制代码
        //构建  OpenAiChatModel 对象
        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .apiKey(System.getenv("API-KEY"))
                .modelName("qwen-plus")
                .build();

(3)调用chat方法与大模型交互

复制代码
        // 3.调用chat方法交互
        String result = model.chat("你是谁");
        System.out.println(result);

运行结果如图所示:

3.2 打印日志信息

项目缺少 SLF4J 的日志实现库(如 Logback、Log4j2)。

解决方案:添加日志实现依赖(推荐 Logback)

复制代码
<!-- Maven -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.5.18</version>
</dependency>

引入logback依赖后,并设置logRequests和logResponses,代码部分如下:

复制代码
        //构建  OpenAiChatModel 对象
        OpenAiChatModel model = OpenAiChatModel.builder()
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .apiKey(System.getenv("API-KEY"))
                .modelName("qwen-plus")
                .logRequests(true)
                .logResponses(true)
                .build();

代码运行结果显示:

四、项目开发

4.1 springboot项目创建

1.构建springboot项目

2.引入起步依赖

复制代码
        <!--langchain4j起步依赖 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
            <version>1.0.1-beta6</version>
        </dependency>

3.application.yml中配置大模型

复制代码
langchain4j:
  open-ai:
    chat-model:
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api-key: ${API-KEY}
      model-name: qwen-plus

4.开发接口,调用大模型

复制代码
@RestController
public class ChatController {

    @Autowired
    private OpenAiChatModel model;

    @RequestMapping("/chat")
    public String chat(String message){//浏览器传递的用户问题
        String result = model.chat(message);
        return result;
    }


}

运行结果截图:

打印日志

复制代码
langchain4j:
  open-ai:
    chat-model:
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api-key: ${API-KEY}
      model-name: qwen-plus
      log-requests: true
      log-responses: true

logging:
  level.dev.langchain4j: debug

运行截图如下:

4.2 AiServices工具类

1.引入依赖

复制代码
            <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
            <version>1.0.1-beta6</version>

2.声明接口

复制代码
public interface ConsultantService {
    //用于聊天的方法
    public String chat(String message);
}

3.使用AiServices为接口手动创建代理对象

复制代码
@Configuration
public class CommonConfig {

    @Autowired
    private OpenAiChatModel model;

    @Bean
    public ConsultantService consultantService(){
        ConsultantService cs = AiServices.builder(ConsultantService.class)
                .chatModel(model)
                .build();
        return cs;
    }

}

4.在Controller中注入并使用

复制代码
@RestController
public class ChatController {

    @Autowired
    private ConsultantService consultantService;

    @RequestMapping("/chat")
    public String chat(String message){

        String result = consultantService.chat(message);
        return result;

    }

}

运行结果如下:

注释

复制代码
@Configuration
public class CommonConfig {

    @Autowired
    private OpenAiChatModel model;

    /*@Bean
    public ConsultantService consultantService(){
        ConsultantService cs = AiServices.builder(ConsultantService.class)
                .chatModel(model)
                .build();
        return cs;
    }*/

}

使用 @AiService 注解接口

复制代码
@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT, // 手动装配
        chatModel = "openAiChatModel"              // 指定模型
)
public interface ConsultantService {
    //用于聊天的方法
    public String chat(String message);
}

查看@AiService注解:默认自动装配

4.3 流式调用

1.引入依赖

复制代码
        <!-- 引入流式调用相关的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
            <version>1.0.1-beta6</version>
        </dependency>

2.配置流式模型对象

复制代码
    streaming-chat-model:
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api-key: ${API-KEY}
      model-name: qwen-plus
      log-requests: true
      log-responses: true

3.切换接口中方法的返回值类型

复制代码
@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT, // 手动装配
        chatModel = "openAiChatModel"  ,            // 指定模型
        streamingChatModel = "openAiStreamingChatModel"
)

public interface ConsultantService {
    //用于聊天的方法
    //public String chat(String message);
    public Flux<String> chat(String message);
}

4.修改Controller中的代码

复制代码
@RestController
public class ChatController {

    @Autowired
    private ConsultantService consultantService;

    @RequestMapping(value = "/chat",produces = "text/html;charset=utf-8")
    public Flux<String> chat(String message){

        Flux<String> result = consultantService.chat(message);
        return result;

    }
    
}

运行截图:

运行截图:

如果没设置@RequestMapping(value = "/chat",produces = "text/html;charset=utf-8")返回的字符类型,那么运行结果如下:

添加前端页面:

4.4 消息注解

在AI对话系统或聊天应用中,消息注解(Message Annotations) 是指为消息添加的额外元数据或标记,用于控制消息的处理方式、显示效果或业务逻辑。

@SystemMessage

@UserMessage

区别

  • @SystemMessage:设定 AI 角色

  • 效果:所有对话都会基于这个角色设定生成回答。

  • 注意:通常只需定义一次,类似"系统提示词"。

    @SystemMessage("""
    你是一名高考志愿填报专家,熟悉全国高校的录取分数线和就业情况。
    回答需满足:
    1. 按【冲稳保】三档推荐学校
    2. 优先考虑省内院校
    3. 给出具体分数线参考
    """)
    public interface GaokaoAdvisor {
    // ...
    }

  • @UserMessage:动态用户输入

    public interface GaokaoAdvisor {
    @UserMessage("""
    我的分数是{{score}}分,{{province}}省{{subject}}科,
    请推荐适合的学校和专业。
    """)
    String recommend(int score, String province, String subject);
    }

4.4.1 @SystemMessage

通过 @SystemMessage 注解定义 AI 角色,并声明一个返回 Flux<String> 的流式聊天方法

复制代码
    @SystemMessage("高考志愿填报助手")
    public Flux<String> chat(String message);

运行结果如下:

查看日志信息:

通过 @SystemMessage 注解定义 AI 角色,并声明一个返回 Flux<String> 的流式聊天方法

复制代码
    @SystemMessage(fromResource = "system.txt")
    public Flux<String> chat(String message);

查看system.txt文件:

运行结果如下:

4.4.2 @UserMessage

默认规则:{{it}} 的用途
{{it}} 是 Kotlin 风格的默认参数占位符,会自动绑定到方法的第一个参数

复制代码
    @UserMessage("你是高考填报志愿助手{{it}}")
    public Flux<String> chat(String message);

运行结果如下:

如果不使用默认{{it}}

显式命名参数--直接使用参数名

复制代码
@UserMessage("你是高考填报志愿助手{{message}}")
Flux<String> chat(String message);
4.5 会话功能
4.5.1 会话记忆

大模型是不具备记忆能力的,要想让大模型记住之前聊天的内容,唯一的办法就是把之前聊天的内容与新的提示词一起发给大模型。

LangChain4j提供了会话记忆(Memory)功能,允许对话系统记住之前的交互历史。以下是配置会话记忆功能的方式:

ChatMemory 接口是 LangChain4j 中会话记忆功能的基础接口。

复制代码
public interface ChatMemory {
    Object id();          // 记忆存储对象的唯一标识
    void add(ChatMessage var1);  // 添加一条会话记忆
    List<ChatMessage> messages(); // 获取所有会话记忆
    void clear();         // 清除所有会话记忆
}

1.定义会话记忆对象

复制代码
    //构建会话记忆对象
    @Bean
    public ChatMemory chatMemory(){
        return MessageWindowChatMemory.builder()
                .maxMessages(20)//最多保留最近的 20 条消息
                .build();
    }

2.配置会话记忆对象

复制代码
@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT, // 手动装配
        chatModel = "openAiChatModel"  ,            // 指定模型
        streamingChatModel = "openAiStreamingChatModel",
        chatMemory = "chatMemory"//配置会话记忆对象
)

运行截图如下:

查看日志信息:

4.5.2 会话记忆隔离

刚才我们做的会话记忆,所有会话使用的是同一个记忆存储对象,因此不同会话之间的记忆并没有做到隔离。例如:

正常情况应该这样:

实现会话记忆隔离:

ChatMemoryProvider 是 LangChain4j 中一个重要的接口,它负责提供和管理 ChatMemory 实例。这个接口通常用于需要根据不同会话/用户动态获取对应记忆的场景。

复制代码
public interface ChatMemoryProvider {
    ChatMemory get(Object var1);
}

1.定义会话记忆对象提供者

复制代码
    @Bean
    public ChatMemoryProvider chatMemoryProvider(){
        ChatMemoryProvider chatMemoryProvider = new ChatMemoryProvider() {
            @Override
            public ChatMemory get(Object memoryId) {
                return MessageWindowChatMemory.builder()
                        .id(memoryId)
                        .maxMessages(20)
                        .build();
            }
        };
        return chatMemoryProvider;
    }

2.配置会话记忆对象提供者

复制代码
@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT, // 手动装配
        chatModel = "openAiChatModel"  ,            // 指定模型
        streamingChatModel = "openAiStreamingChatModel",
        //chatMemory = "chatMemory",//配置会话记忆对象
        chatMemoryProvider = "chatMemoryProvider"//配置会话记忆提供者
)

3.ConsultantService接口方法中添加参数memoryId

复制代码
    @SystemMessage(fromResource = "system.txt")
    public Flux<String> chat(@MemoryId String memoryId,@UserMessage String message);

4.Controller中chat接口接收memoryId

复制代码
    @RequestMapping(value = "/chat",produces = "text/html;charset=utf-8")
    public Flux<String> chat(String memoryId,String message){
        Flux<String> result = consultantService.chat(memoryId,message);
        return result;
    }

5.前端页面请求时传递memoryId

运行结果如下:

发现会话记忆隔离:

4.5.3 会话记忆持久化

刚才我们做的会话记忆,只要后端重启,会话记忆就没有了,由于服务重启时,内存中的会话数据会丢失。

实现持久化的会话记忆方法如下:

这里选用使用Redis持久化:

1.准备redis环境

2.引入redis起步依赖

复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

3.配置redis连接信息

复制代码
spring:
  data:
    redis:
      host: localhost
      port: 6379

4.提供ChatMemoryStore实现类

复制代码
@Repository
public class RedisChatMemoryStore implements ChatMemoryStore {

    //注入redisTemplate
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        //获取会话消息
        String json = redisTemplate.opsForValue().get(memoryId);

        //把json字符串转成list
        return ChatMessageDeserializer.messagesFromJson(json);
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> list) {
        //更新会话
        //1.把list转换成json数据
        String json = ChatMessageSerializer.messagesToJson(list);

        //2.把json数据存储到redis中
        redisTemplate.opsForValue().set(memoryId.toString(),json, Duration.ofDays(1));
    }

    @Override
    public void deleteMessages(Object memoryId) {
        redisTemplate.delete(memoryId.toString());
    }
}

5.配置ChatMemoryStore

复制代码
    @Autowired
    private ChatMemoryStore redisChatMemoryStore ;

    @Bean
    public ChatMemoryProvider chatMemoryProvider(){
        ChatMemoryProvider chatMemoryProvider = new ChatMemoryProvider() {
            @Override
            public ChatMemory get(Object memoryId) {
                return MessageWindowChatMemory.builder()
                        .id(memoryId)
                        .maxMessages(20)
                        .chatMemoryStore(redisChatMemoryStore)
                        .build();
            }
        };
        return chatMemoryProvider;
    }

运行结果如下:

后端重启,会话记忆从redis获取

五、RAG知识库

5.1 原理

没有使用检索增强生成(RAG)的情况,那么意味着系统完全依赖大语言模型(LLM)的内部参数化知识,而不从外部知识库检索信息。对时效性强的查询(如最新新闻、政策变化)可能给出过时或错误的回答。当模型遇到训练数据中未涵盖的问题时,可能生成看似合理但错误的答案。

RAG, Retrieval Augmented Generation, 检索增强生成。通过检索外部知识库的方式增强大模型的生成能力。

向量数据库包括Milvus、Chroma、Pinecone、RedisSearch(Reids)、pgvector(PostgreSQL)

向量余弦相似度,用于表示坐标系中两个点之间的距离远近

在第一象限中, cosθ(向量余弦相似度)的取值范围为(0,1)

余弦相似度越大,说明向量方向越接近,两点之间的距离越小

例如:

用户输入的内容,借助于向量模型转化为向量后,与数据库中的向量通过计算余弦相似度的方式,找出相似度比较高的文本片段。

例如:

两个向量的余弦相似度越高,说明向量对应的文本相似度越高

5.2 依赖引入

1.存储(构建向量数据库操作对象)

引入依赖

复制代码
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-easy-rag</artifactId>
            <version>1.0.1-beta6</version>
        </dependency>

加载知识数据文档==>构建向量数据库操作对象==>把文档切割、向量化并存储到向量数据库中

复制代码
    @Bean
    public EmbeddingStore store(){
        //1.加载文档进内存
        List<Document> documents = ClassPathDocumentLoader.loadDocuments("content");

        //2.构建向量数据库操作对象
        InMemoryEmbeddingStore store = new InMemoryEmbeddingStore();

        //3.构建一个EmbeddingStoreIngestor对象,完成文本数据切割,向量化,存储
        EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
                .embeddingStore(store)
                .build();
        ingestor.ingest(documents);

        return store;

    }

2.检索(构建向量数据库检索对象)

构建向量数据库检索对象

复制代码
    //构建向量数据库检索对象
    @Bean
    public ContentRetriever contentRetriever(EmbeddingStore store){
        return EmbeddingStoreContentRetriever.builder()
                .embeddingStore(store)
                .minScore(0.5)
                .maxResults(3)
                .build();
    }

配置向量数据库检索对象

复制代码
@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT, // 手动装配
        chatModel = "openAiChatModel"  ,            // 指定模型
        streamingChatModel = "openAiStreamingChatModel",
        //chatMemory = "chatMemory",//配置会话记忆对象
        chatMemoryProvider = "chatMemoryProvider",//配置会话记忆提供者
        contentRetriever = "contentRetriever"//配置向量数据库检索对象
)

运行结果如下:

查看日志,后面加入了一些我们文档提供的信息

5.3 核心API
5.3.1 文档加载器

文档加载器,用于把磁盘或者网络中的数据加载进程序

FileSystemDocumentLoader, 根据本地磁盘绝对路径加载
ClassPathDocumentLoader ,相对于类路径加载
UrlDocumentLoader ,根据 url 路径加载

5.3.2 文档解析器

文档解析器,用于解析使用文档加载器加载进内存的内容,把非纯文本数据转化成纯文本

TextDocumentParser,解析纯文本格式的文件

ApachePdfBoxDocumentParser,解析pdf格式文件
ApachePoiDocumentParser ,解析微软的 office 文件,例如 DOC 、 PPT 、 XLS
ApacheTikaDocumentParser (默认),几乎可以解析所有格式的文件

未完继续。。。。

相关推荐
坐吃山猪4 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫5 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao5 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区6 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT7 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy7 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss9 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续9 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0449 小时前
ReAct模式解读
java·ai