「Java AI实战」LangChain4J - 记忆缓存

LangChain4j持久化存储深度解析:构建企业级AI应用的记忆中枢 在人工智能应用开发中,会话记忆与知识库的持久化存储是决定系统智能水平的关键因素。作为Java生态中最成熟的LLM集成框架,LangChain4j通过模块化设计和多层级存储方案,为企业级AI应用提供了灵活高效的持久化解决方案。 阅读详情

系列文章目录

第一章 「Java AI实战」LangChain4J - 接入Xinference本地大模型
第二章 「Java AI实战」LangChain4J - ChatAPI 及常用配置
第三章 「Java AI实战」LangChain4J - 向量数据库接入与语义检索
第四章 「Java AI实战」LangChain4J - Agent智能体开发
第五章 「Java AI实战」LangChain4J - 记忆缓存
第六章 「Java AI实战」LangChain4J - 文本分类器实践
第七章 「Java AI实战」LangChain4J - 多 Agent 智能体协作



前言:为什么要在 Java 项目里上“记忆”?

很多团队在做「大模型接入」的时候,第一步都是一样的:
前端给个输入框,后端用 Java 调一下 OpenAI / DeepSeek / 本地大模型,然后把回答原样返回给前端——这类「纯问答」模式上线很快,但用一段时间你会发现几个明显的痛点:

  • 每次请求都是“短期记忆”:模型只看到这一条 prompt,看不到之前聊过什么。
  • 无法真正多轮对话:用户刚刚介绍过自己的背景、上下文,下一轮又要重复一遍。
  • 做不到个性化:不知道用户是谁、之前问过什么,回答永远是“第一次见面”的状态。

要让 AI 真正“像个助手”而不是“一个高配搜索框”,就离不开:记忆 。


1. 记忆缓存:让对话串起来

所谓“记忆”,本质上就是给每个会话维护一段上下文,把历史对话按一定策略保存下来。它能带来的能力非常直接:

  • 多轮对话:上下文能串起来,AI 可以基于上一轮甚至多轮的内容继续回答。
  • 用户画像 / 会话状态:记住用户的偏好、最近聊过的主题,在回答时自动“对口味”。
  • 分布式部署可用:配合 Redis 做分布式会话记忆,多实例部署时也能共享同一段对话历史。

在 Java 项目里,我们完全可以利用 LangChain4j 的 ChatMemory 体系,加上 Redis 等基础设施,把“记忆”做成一个 可配置、可扩展的中间层能力,而不是写死在某个 Controller 里。

本文会基于 LangChain4j + Spring Boot,从零实现非常贴近实战的能力: 一个带“记忆”的对话 AI

  • 支持内存会话记忆(开发调试方便)
  • 支持基于 Redis 及 Mysql 的分布式会话记忆(适合生产部署)

一、效果预览

1. 多轮对话效果:AI 真的能“记住你”

先来看最直观的——带记忆的多轮对话。

第一步:用户先介绍自己信息

用户第一次进来时,可以先做一个自我介绍,比如:

我是在农商银行做数据治理的 Java 工程师,平时主要写 Spring Boot 和大数据相关的东西。

这时候,后台会把这段话作为“当前会话”的一部分,写入 LangChain4j 的 ChatMemory。如果配置了 Redis,则会同步存到 Redis 里,对应当前的 sessionId。

第二步:不重复输入,AI 还能记住并复述

接下来,用户只问一句很短的问题:

那你觉得我适合用 LangChain4j 做哪些东西?

在这一步里,前端没有再重复任何“我是谁、在哪工作、做什么”的信息,但是 AI 在回答时会自动带上之前的上下文,例如:

结合你现在在农商行做数据治理、用 Spring Boot 的场景,其实很适合用 LangChain4j 做两块:
1)面向数据工程师的 SQL 助手;
2)基于你们数据标准的字段智能对标……

这说明模型已经利用了记忆里的用户画像,而不是把你当“陌生人”。

第三步:重启浏览器 / 换窗口,对话还能从 Redis 记忆接着聊(可选)

如果你在项目中给每个用户绑定了一个稳定的 sessionId(比如登录用户 ID + 浏览器端生成的 UUID),并且记忆使用的是 Redis 持久化存储,那么:

  • 即使你重启浏览器、换个页面,只要 sessionId 还在,对话就能无缝接上;
  • 后端可以在任意一台实例上拿到这段会话的历史记录,做到真正的分布式会话记忆。

二、开发环境与项目初始化

1. 技术栈与版本选择

  • JDK:17+
  • Spring Boot:3.x
  • 构建工具:Maven
  • LangChain4j
  • Redis

2. 模型选型:本地模型优先

本文选用 本地模型 作为主角,通过 OpenAI 协议接入 LangChain4J:本地部署继续选用:
Xinference。其使用见本系列第一篇文章:

https://blog.youkuaiyun.com/OFFTime_we/article/details/147198108?spm=1001.2014.3001.5501


三、给对话 AI 加上「记忆缓存」

这一节我们从 0 到 1,把一个“只会单轮回答”的 Chat 模型,升级成带记忆的多轮对话 AI,并且支持 InMemory + Redis 两种记忆实现。


3.1 基础 ChatLanguageModel 配置

先把最核心的 Chat 模型跑起来,这一步先不考虑记忆,就是一个「无记忆版本」的 AI 助手。

3.1.1 创建 ChatLanguageModelConfig

示例基于 Spring Boot + LangChain4j,使用 OpenAI 协议模型;

package com.example.ai.config;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;

@Configuration
public class ChatLanguageModelConfig {

    @Bean
    public ChatLanguageModel chatLanguageModel() {
        return OpenAiChatModel.builder()
                .apiKey(System.getenv("LANGCHAIN4J_API_KEY"))
                .baseUrl("http://本地ip:9997/v1") // 比如 DeepSeek / 自建 OpenAI 网关
                .modelName("deepseek-chat")        // 替换成你实际使用的模型名
                .temperature(0.3)                  // 稳定一点的回复
                .timeout(Duration.ofSeconds(15))   // 超时时间
                .logRequests(true)                 // 开启日志,便于排查问题
                .logResponses(true)
                .build();
    }
}

baseUrl / modelName 按你实际接入的Xinference模型调整即可。

3.1.2 封装 AiService 接口(无记忆版本)

LangChain4j 推荐通过 AiServices 把模型封装成一个接口,像调普通 Service 一样去用。

package com.example.ai.service;

import dev.langchain4j.service.UserMessage;

public interface ChatAssistant {

    String chat(@UserMessage String message); // 先做一个无记忆的简单版本
}

对应的配置:

package com.example.ai.config;

import com.example.ai.service.ChatAssistant;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatAssistantConfig {

    @Bean
    public ChatAssistant chatAssistant(ChatLanguageModel chatLanguageModel) {
        return AiServices.create(ChatAssistant.class, chatLanguageModel);
    }
}

到这一步,就已经有了一个最基础的 Java AI 助手,可以在任意 Service / Controller 中注入 ChatAssistant 来完成简单问答。接下来开始“上记忆”。

3.2 接入内存记忆:MessageWindowChatMemory

先用 InMemory 的方式,在单机环境跑通“多轮对话 + 记忆”,之后再切到 Redis。

3.2.1 AiService + ChatLanguageModelConfig

LangChain4j 内置了一个“窗口记忆”的实现:MessageWindowChatMemory,只保存最近 N 条消息,防止上下文过长导致 token 暴涨。
在实际业务里,我们更常见的是每个用户 / 每个会话有自己的记忆。这时可以用 @MemoryId + chatMemoryProvider.

package com.example.ai.service;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;

public interface ChatAssistant {

    String chat(@MemoryId String sessionId,
                @UserMessage String message);
}

对应配置

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import org.example.service.AiAssistant;
import org.example.service.ChatAssistant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration(proxyBeanMethods = false)
public class ChatLanguageModelConfig {
    @Bean
    public ChatLanguageModel chatLanguageModel() {
        return OpenAiChatModel.builder()
                .apiKey(System.getenv("LANGCHAIN4J_API_KEY"))
                .baseUrl("http://本地ip:9997/v1") 
                .modelName("deepseek-chat")        // 替换成你实际使用的模型名
                .temperature(0.3)                  // 稳定一点的回复
                .timeout(Duration.ofSeconds(15))   // 超时时间
                .logRequests(true)                 // 开启日志,便于排查问题
                .logResponses(true)
                .build();
    }
    
    @Bean
    public AiAssistant aiAssistant(ChatLanguageModel chatLanguageModel) {
        return AiServices.builder(AiAssistant.class)
                .chatLanguageModel(chatLanguageModel)
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(100))
                .build();
    }
}

这里的 memoryId 一般就是 sessionId,比如用户登录 ID + 浏览器端 UUID 等。

3.2.2 controller 类


import cn.hutool.core.date.DateUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.service.AiAssistant;
import org.example.service.ChatAssistant;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@Slf4j
public class ChatLanguageModelController {
    @Resource
    private ChatAssistant chatAssistant;
    @Resource
    private AiAssistant aiAssistant;

    @GetMapping(value = "/chatmemory/test1")
    public String testWithOutChatMemory() {
        String chat = chatAssistant.chat("你好,我的名字叫张三");
        System.out.println("chat返回结果:"+chat);

        String chat2 = chatAssistant.chat("我的名字是什么");
        System.out.println("chat2返回结果:"+chat2);
        return "success : "+ DateUtil.now();
    }

    @GetMapping(value = "/chatmemory/test2")
    public String testWithChatMemory() {
        aiAssistant.chat(1L, "你好!我的名字是Java.");
        aiAssistant.chat(3L, "你好!我的名字是C++");

        String chat = aiAssistant.chat(1L, "我的名字是什么");
        System.out.println(chat);
        
        chat = aiAssistant.chat(3L, "我的名字是什么");
        System.out.println(chat);
        
        return "testWithChatMemory success : "+ DateUtil.now();
    }
}

3.3 接入 Redis 记忆:RedisChatMemoryStore

InMemory 记忆只适合单机 / 本地开发,一旦你有多实例部署 / 容器重启 / 横向扩容,记忆就会丢失。这时就需要 Redis 上场。

3.3.1 为什么要引入 Redis?

  • 多实例部署 / 会话共享:
    用户从网关随机打到不同应用实例,只有 Redis 能保证所有实例看到的对话历史是一致的。
  • 防止 JVM 重启导致记忆丢失:
    应用重启之后,从 Redis 里把历史对话重新拿出来,对用户而言是“无感”的。

3.3.2 RedisChatMemoryStore

LangChain4j 提供了基于 Redis 的 ChatMemoryStore 实现

package org.example.config;

import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;


@Component
public class RedisChatMemoryStore implements ChatMemoryStore {

    public static final String CHAT_MEMORY_PREFIX = "CHAT_MEMORY:";

    @Resource
    private RedisTemplate<String,String> redisTemplate;


    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        String retValue = redisTemplate.opsForValue().get(CHAT_MEMORY_PREFIX + memoryId);
        return ChatMessageDeserializer.messagesFromJson(retValue);
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        redisTemplate.opsForValue().set(CHAT_MEMORY_PREFIX + memoryId, ChatMessageSerializer.messagesToJson(messages));
    }

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

3.3.3 redis配置

package org.example.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@Slf4j
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactor) {
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactor);
        //设置key序列化方式string
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

3.3.4 LLMConfig

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import org.example.service.AiAssistant;
import org.example.service.ChatAssistant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration(proxyBeanMethods = false)
public class ChatLanguageModelConfig {
	@Resource
    private RedisChatMemoryStore redisChatMemoryStore;
    @Bean
    public ChatLanguageModel chatLanguageModel() {
        return OpenAiChatModel.builder()
                .apiKey(System.getenv("LANGCHAIN4J_API_KEY"))
                .baseUrl("http://本地ip:9997/v1")  // 比如 DeepSeek / 自建 OpenAI 网关
                .modelName("deepseek-chat")        // 替换成你实际使用的模型名
                .temperature(0.3)                  // 稳定一点的回复
                .timeout(Duration.ofSeconds(15))   // 超时时间
                .logRequests(true)                 // 开启日志,便于排查问题
                .logResponses(true)
                .build();
    }
    
@Bean
    public ChatMemoryAssistantRedis chatAssistantWithRedis(ChatLanguageModel chatLanguageModel) {
        return AiServices.builder(ChatMemoryAssistantRedis.class)
                .chatLanguageModel(chatLanguageModel)
                // 根据 memoryId(sessionId) + Redis 做持久化记忆
                .chatMemoryProvider(memoryId ->
                        MessageWindowChatMemory.withMaxMessages(20, memoryId, redisChatMemoryStore))
                .build();
    }
}

重点是:MessageWindowChatMemory.withMaxMessages(20, memoryId, chatMemoryStore),把 Redis 作为底层持久化实现,这样每个 sessionId 的消息都能存入 Redis。

3.3.5 ChatMemoryAssistantRedis

package com.example.ai.service;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;

public interface ChatMemoryAssistantRedis{

    String chat(@MemoryId String sessionId,
                @UserMessage String message);
}

3.3.6 controller 类


import cn.hutool.core.date.DateUtil;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.service.ChatMemoryAssistant;
import org.example.service.ChatMemoryAssistantMySQL;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


/**
 * @Date 2025-05-04 15:55
 * @Description: TODO
 */
@RestController
@Slf4j
public class ChatMemoryController {
    @Resource
    private ChatMemoryAssistant chatMemoryAssistant;
    @Resource
    private ChatMemoryAssistantMySQL chatMemoryAssistantMySQL;

    @GetMapping(value = "/chatmemory/redis")
    public String testWithChatMemoryStore() {
        chatMemoryAssistant.chat(1L, "你好!我的名字是redis");
        chatMemoryAssistant.chat(3L, "你好!我的名字是nacos");
        String chat = chatMemoryAssistant.chat(1L, "我的名字是什么");
        System.out.println(chat);
        chat = chatMemoryAssistant.chat(3L, "我的名字是什么");
        System.out.println(chat);
        return "testWithChatMemoryStore success : "+ DateUtil.now();
    }
}

3.4 接入Mysql记忆:MysqlChatMemoryStore

此处只是展示如何使用关系型数据库存储记忆。此处展示多缓存并存的进阶使用。

3.4.1 MysqlChatMemoryStore

package org.example.config;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import jakarta.annotation.Resource;
import org.example.entities.ChatMessageDO;
import org.example.mapper.ChatMessageDOMapper;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Date 2025-05-05 17:46
 * @Description: TODO
 */
@Component
public class MysqlChatMemoryStore implements ChatMemoryStore {

    @Resource
    private ChatMessageDOMapper chatMessageDOMapper;

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        LambdaQueryWrapper<ChatMessageDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ChatMessageDO::getConversationId,memoryId);
        return chatMessageDOMapper.selectList(lambdaQueryWrapper).stream().map(e ->
                new UserMessage(e.getContext())).collect(Collectors.toList());
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        this.deleteMessages(memoryId);
        List<ChatMessageDO> messageDOList = new ArrayList<>();
        messages.forEach(chatMessage -> {
            ChatMessageDO chatMessageDO = new ChatMessageDO();
            chatMessageDO.setConversationId(memoryId.toString());
            chatMessageDO.setRole(chatMessage.type().name());
            chatMessageDO.setContext(chatMessage.toString());
            messageDOList.add(chatMessageDO);
        });
        chatMessageDOMapper.insert(messageDOList);
    }

    @Override
    public void deleteMessages(Object memoryId) {
        LambdaQueryWrapper<ChatMessageDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ChatMessageDO::getConversationId,memoryId);
        chatMessageDOMapper.delete(lambdaQueryWrapper);
    }
}

3.4.2 Mapper

public interface ChatMessageDOMapper extends BaseMapper<ChatMessageDO> {

}

3.4.2 ChatMessageDO

package org.example.entities;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;


@TableName(value ="chat_message")
@Data
public class ChatMessageDO implements Serializable {
    /**
     * 
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 
     */
    private String conversationId;

    /**
     * 
     */
    private String role;

    /**
     * 
     */
    private String context;

    /**
     * 
     */
    private Date createdAt;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

3.4.2 LLMConfig

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import org.example.service.AiAssistant;
import org.example.service.ChatAssistant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration(proxyBeanMethods = false)
public class ChatLanguageModelConfig {
 	@Resource
    private MysqlChatMemoryStore mysqlChatMemoryStore;
    
    @Bean
    public ChatLanguageModel chatLanguageModel() {
        return OpenAiChatModel.builder()
                .apiKey(System.getenv("LANGCHAIN4J_API_KEY"))
                .baseUrl("http://本地ip:9997/v1")  // 比如 DeepSeek / 自建 OpenAI 网关
                .modelName("deepseek-chat")        // 替换成你实际使用的模型名
                .temperature(0.3)                  // 稳定一点的回复
                .timeout(Duration.ofSeconds(15))   // 超时时间
                .logRequests(true)                 // 开启日志,便于排查问题
                .logResponses(true)
                .build();
    }

	@Bean
    public ChatMemoryAssistantMySQL chatAssistantWithRedis(ChatLanguageModel chatLanguageModel) {
        return AiServices.builder(ChatMemoryAssistantMySQL.class)
                .chatLanguageModel(chatLanguageModel)
                // 根据 memoryId(sessionId) + mysql做持久化记忆
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(20, memoryId, mysqlChatMemoryStore))
                .build();
    }
}

3.4.3 ChatMemoryAssistantMySQL

public interface ChatMemoryAssistantMySQL {
    /**
     * 聊天
     *
     * @param userId  用户 ID
     * @param message 消息
     * @return {@link String }
     */
    String chat(@MemoryId Long userId, @UserMessage String message);
}

3.4.4 controller类


import cn.hutool.core.date.DateUtil;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.service.ChatMemoryAssistant;
import org.example.service.ChatMemoryAssistantMySQL;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


/**
 * @Date 2025-05-04 15:55
 * @Description: TODO
 */
@RestController
@Slf4j
public class ChatMemoryController {
    @Resource
    private ChatMemoryAssistant chatMemoryAssistant;
    @Resource
    private ChatMemoryAssistantMySQL chatMemoryAssistantMySQL;

  @GetMapping(value = "/chatmemory/mysql")
    public String testWithChatMemoryStoreMysql() {
        chatMemoryAssistantMySQL.chat(1L, "你好!我的名字是redis");
        chatMemoryAssistantMySQL.chat(3L, "你好!我的名字是nacos");
        String chat = chatMemoryAssistantMySQL.chat(1L, "我的名字是什么");
        System.out.println(chat);
        chat = chatMemoryAssistantMySQL.chat(3L, "我的名字是什么");
        System.out.println(chat);
        return "testWithChatMemoryStoreMysql success : "+ DateUtil.now();
    }
}

总结

本篇核心是一件事:让 Java 大模型「有记忆」。我们从最简单的 ChatLanguageModel 出发,接入 MessageWindowChatMemory,实现基于 sessionId 的多轮对话,再扩展到 Redis / MySQL,把 LangChain4j 的记忆能力做成可插拔组件。下一篇将展开「LangChain4j 文本向量化分类与字段智能对标」以及 Agent 工具调用,把记忆 真正落到业务场景中,让这套能力不仅能聊,还能帮你做事。

LangChain4j 性能优化与监控:构建高效的 AI 应用(详细版) 通过对 LLM 调用优化、向量数据库与多模态处理调优、并发处理与缓存机制以及监控调试策略的详细说明,本文为开发者提供了系统化的性能优化方案。未来,在业务持续扩展和 AI 技术不断更新的背景下,持续的性能调优、自动化监控和智能预警机制将成为保障系统稳定运行和高效响应的关键。希望这份详细的指南能够为各位开发者在项目中提供切实可行的参考,并不断推动 LangChain4j 在各类应用场景中的实践与落地。 阅读详情

相关推荐

Langchain4j 整合MongoDB 实现会话持久化存储详解

Langchain4j 整合MongoDB 实现会话持久化存储详解

congge 3607

Java对接AI大模型(二)【Langchain4j记忆化】

上文已经提到Langchain4j基本的对接模型应用、日志、流式响应等功能。可细心的同学使用的时候应该发现啦,这个大模型他并没有任何记忆。例如你的第一句话是我叫张三。而下一句话请问他你是谁时,LLM似乎并没有记下来你的话。所以本文主要目的就是来介绍一下Langchai4j框架对接的大模型如何让他有记忆。注意:若要跟着实现,务必阅读上文文章过后再接着学习。

m0_65013257的博客 2074

使用 Spring AI 和 LangChain4j 实现聊天机器人对比分析

使用 Spring AI 和 LangChain4j 实现聊天机器人对比分析

互联网架构师笔记 2424

LangChain4j实战之六:聊天记忆,低级API版

通过编码实战学习LangChain4j提供的对话记忆功能,一共两篇,本篇学习通过低级API实现

程序员欣宸的博客 1293

别光看手册!手把手教你用SGM2036 LDO设计一个稳定3.3V转1.8V的MCU供电电路(附PCB布局避坑指南)

本文详细介绍了如何使用SGM2036 LDO设计一个稳定的3.3V转1.8V的MCU供电电路,包括需求分析、芯片选型、外围器件计算、PCB布局避坑指南及实测验证。通过实际案例和计算示例,帮助工程师快速掌握LDO应用技巧,确保系统电源稳定可靠。

weixin_42548893的博客 504

基于Sentinel-2(哨兵2)遥感卫星的植被覆盖度提取

基于Sentinel-2(哨兵2)遥感卫星的植被覆盖度提取 一、Sentinel-2简单介绍 哨兵-2系列卫星的主要载荷多光谱成像仪拥有13个谱段,从可见光到近红外再到短波红外,空间分辨率从10m到60m,能实现全所未有的陆地海洋监测水平。其幅宽高达290KM,且A、B双星组合,覆盖能力比美国Landsat8卫星强很多。目前哨兵-2A和哨兵-2B已经实现了5天的重访时间,即使在之前仅有单颗卫星时,...

weixin_44143671的博客 2万+

Cursor使用及接入教程(非常详细),零基础入门到精通,看这一篇就够了

Cursor使用及接入教程(非常详细),零基础入门到精通,看这一篇就够了

Python单行客的博客 1万+

什么是Merkle树?用“文件柜”类比理解区块链的核心技术

Merkle树是一种用于区块链的数据结构,像一个高效的“文件柜”,用于存储和验证大量交易数据。比特币通过Merkle树将交易哈希组织成树状结构,层层汇总,最终生成一个唯一的根哈希,存储在区块头中。这种设计大幅减少数据存储和验证成本,支持快速校验交易的完整性和存在性。Merkle树通过密码学保证数据不可篡改,广泛应用于比特币、区块链及其他分布式系统。它的优势在于高效、轻量和安全,尤其适合轻节点验证和数据同步。理解Merkle树就像掌握区块链的“索引秘籍”,是Web3世界的关键技术。

在路上的专栏 1192

LangChain4j实战之七:聊天记忆,高级API版

用LangChain4j的高级API解决聊天记忆问题,还有分用户的记忆,以及记忆内容的持久化

程序员欣宸的博客 1244

f_fs.c 初始化流程(进行中)

前言 之所以选择f_fs.c作为这系列的开始,因为usb在/driver/usb下面的内容实在太多太多,如果讲各种各样点会太分散了。所以决定切入点以实际使用为主,就是f_fs了,目前我了解这是做bulk裸传输使用的,具体的通信协议自己确定。 初始化部分 开始点从这个路径下说起, /drivers/usb/gadget/function/f_fs.c 然后f_fs.c第一个使用的是include/composite.h中的宏定义function #define DECLARE_USB_FUNCTION(_n

weixin_36406973的博客 1966

VTK 显示大量点云数据及交互(点云拾取、着色、测量等)功能

VTK (Visualization Toolkit) 是一个强大的开源可视化库,非常适合处理点云数据。下面将介绍如何使用 VTK 显示大量点云数据,并实现点云拾取、着色、测量等功能。

深之JohnChen的专栏 1992

LangChain4j系列:LangChain4j ChatMemory聊天记忆详解与实战

LangChain4j系列:LangChain4j ChatMemory聊天记忆详解与实战

优快云_224022的博客 4276

STM32入门教程:无人机控制

我们将会一步一步引导您完成无人机的控制代码,包括无人机的姿态控制、飞行控制和通信控制等。第三步:配置无人机姿态控制 无人机的姿态控制是指控制无人机在空中的倾斜和转向。为了实现姿态控制,我们需要使用加速度计和陀螺仪传感器来获取无人机的姿态信息,并使用PID控制算法来控制无人机的姿态。在这一步骤中,您需要根据您使用的具体传感器和控制算法的要求进行更详细的配置和编码。在通信控制中,您需要考虑到通信的稳定性和可靠性。第五步:通信控制 无人机的通信控制是指与地面站或其他设备进行通信,传输飞行数据和接收飞行指令。

m0_75244442的博客 747

【Java+AI】一篇文章带你学会LangChain4j框架(下)

这是来自系统的消息。通常,作为开发人员,你应该定义这条消息的内容。这是由AI生成的消息,通常是对UserMessage的响应。在LangChain4j中,我们主要利用SystemMessage来实现这一点,SystemMessage具有高优先级,能有效地指导模型的整体行为。根据LLM支持的模式,UserMessage可以只包含文本(String),或其他模态。记忆缓存是聊天系统中的一个重要组件,用于存储和管理对话的上下文信息。它的主要作用是让AI助手能够‘记住’之前的对话内容,从而提供连贯和个性化的回复。

qq_38196449的博客 2427

【WRF模拟】土地利用类型的确定:最大覆盖法/瓦片法

土地利用类型的确定:最大覆盖法/瓦片法

WW、forever的博客 1529

华为OD机试真题 新系统2026-04-15 Python&JS 实现【失灵的键盘】

有一个键盘有2个按键失灵了,按下这些键时会连续输出其他键对应的字符两次。具体如下: 1. 按下j键一次,屏幕上显示uu(两个连续的u);  按下b键一次,屏幕上显示t(两个连续的t) 2. u键和t键是好的,按下u键一次时,屏幕只会显示一次u(正常按键);按下t键一次时,屏幕只会显示一次t(正常按键) 3. 假定屏幕上连续显示两个t一定是按了一次b键,而不是两次t键;假定按键t之后不会紧接着按键b,即tttt转义为两个b,而不可能是tb

MISAYAONE的博客 522

Langchain4j 系列之八 - 聊天记忆

聊天记忆就是将用户之前与大模型的对话过程,在问下一个问题的时候,也将这部分对话过程的记录扔给大语言模型,这样大语言模型就能知道之前谈论什么,不会导致没有上下文的情况下,大模型答非所问。聊天记录保留策略:由于大模型每次输入的token长度有限,因此用户不可能将之前所有的聊天记录都扔给大模型,因此用户必须有一个保留策略,可以是最近几条、也可以是提炼后的结果等等。Langchain4j 目前提供默认两种,一种是保留最近几条,一种是以token数量限制筛选最近几条。会话隔离。

代码让AI扣 1174

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

「Java AI实战」LangChain4J接入Xinference本地大模型

随着大语言模型(LLM)的持续火热,LangChain 成为构建智能应用的事实标准。而对于 Java 开发者而言,LangChain4J 则是一把通往 AI 应用开发的新钥匙。它不仅封装了与 OpenAI、HuggingFace、Baidu、Azure 等模型服务的接入能力,更提供了类似 LangChain 的链式调用编排、上下文管理、Agent 机制、工具集成等核心功能,让 Java 开发者也能便捷地构建问答系统、RAG 应用、多轮对话、智能代理等 LLM 应用。

OFFTime_we的博客 1787

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

「Java AI实战」LangChain4J - ChatAPI 及常用配置

本文将聚焦于 LangChain4J 提供的 Chat API ,结合本地模型(通过 Xinference 部署的deepseek-chat 模型,通过maxkb创建应用,这两块的文档见往期文章)进行调用演示,帮助快速理解其核心机制,完成从模型接入到对话响应的 Java 化实现。本文简单的介绍了 LangChain4J 中 Chat API 的基本原理和使用方式,成功实现了与本地大模型的对话集成。

OFFTime_we的博客 1333

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

7. LangChain4j + 记忆缓存详细说明

本文介绍了LangChain4j中的记忆缓存功能,该功能用于存储和管理对话上下文,使AI助手能记住历史对话内容。文章详细讲解了两种记忆缓存实现方式:基于提问条数(推荐)和基于token数目(不推荐)。实战部分展示了如何通过Spring Boot项目集成该功能,包括依赖配置、编码设置、接口定义及大模型配置。特别强调了通义千问长对话模型的使用,并提供了关键注解@MemoryId和@UserMessage的用法说明,帮助开发者实现连贯的对话体验。

weixin_61635597的博客 926

LangChain4j会话记忆:从原理到Redis持久化实战

本文深入探讨了大模型应用开发中的会话记忆实现方案。首先分析了大模型无状态的本质,指出需要通过传递历史对话来实现记忆功能。接着详细介绍了LangChain4j的ChatMemory接口设计及其两种实现方式。针对多用户场景,提出了基于memoryId的会话隔离解决方案。最后重点讲解了Redis持久化实现,包括Redis配置、序列化处理和过期管理,确保服务重启后记忆不丢失。文章还提供了性能优化、内存管理等最佳实践,为开发者构建连贯对话体验提供了完整的技术方案。

2403_87729201的博客 1384

langchain4j(上)

LangChain4j 是一个 Java 版本的 LangChain 实现,旨在为 Java 开发者提供构建基于大语言模型(LLM)应用程序的工具和框架。

xzacfn的博客 2655

Java开发者LLM实战——使用LangChain4j构建本地RAG系统

RAG(Retrieval-Augmented Generation)的核心思想是:将传统的信息检索(IR)技术与现代的生成式大模型(如chatGPT)结合起来。具体来说,RAG模型在生成答案之前,会首先从一个大型的文档库或知识库中检索到若干条相关的文档片段。再将这些检索到的片段作为额外的上下文信息,输入到生成模型中,从而生成更为准确和信息丰富的文本。RAG的工作原理可以分为以下几个步骤:1.接收请求:首先,系统接收到用户的请求(例如提出一个问题)。

2401_85325726的博客 3880

「Java AI实战」LangChain4J - 向量数据库接入与语义检索

相比 Python 社区的 LangChain,LangChain4J 为 Java 提供了相似的链式语言模型编排能力,兼顾工程化可维护性与复杂场景的灵活性。而在众多场景中,向量数据库(如 FAISS、Milvus、Qdrant)扮演了大模型“记忆体”的角色,尤其适用于构建基于语义理解的问答系统(RAG)、文档检索、智能客服等系统。在传统数据库中,检索通常依赖于关键词、精确匹配或范围查询。而在自然语言处理、图像识别等 AI 场景中,我们更需要的是语义相似度检索,即“查找与某个内容在语义上最接近的内容”。

OFFTime_we的博客 2234

LangChain4j实战之一:准备工作

《LangChain4j实战系列》是欣宸在AI应用领域的原创教程,面向Java开发者介绍如何将AI能力集成到微服务中,从本篇开始,欣宸将陪伴您一同学习AI应用开发

程序员欣宸的博客 1434

LangChain4j实战四:聊天记忆

低级API,最原始方法,手动凑齐历史记录提交到大模型低级API,手动把历史记录放入ChatMemory对象低级API,使用ConversationalChain工具,把手动凑齐历史记录的步骤简化掉高级API,实现全局记忆,数据放在内存中高级API,实现为每个用户的记忆,数据放在内存中高级API,实现全局记忆,数据放在数据库高级API,实现为每个用户的记忆,数据放在数据库。

weixin_54009596的博客 642
上一篇: LangChain4j + MCP:从工具协议到生产级 Agent
下一篇: 「Java AI实战」LangChain4J - 文本分类器实践
TA  zhao
博客等级 码龄7年 58粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值