欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

LangChain4j实战全系列链接

  1. 准备工作
  2. 极速开发体验
  3. 细说聊天API
  4. 集成到spring-boot
  5. 图像模型
  6. 聊天记忆,低级API版
  7. 聊天记忆,高级API版

本篇概览

  • 本篇是《LangChain4j实战》系列的第七篇文章,也是前文《聊天记忆,低级API版》的续篇,前文创建了子工程memory,使用LangChain4j的低级API实现了聊天记忆功能,让大模型知道聊天的前文,可以根据之前的聊天内容来回答问题
  • 本文我们会在memory子工程中继续添加代码,演示如何用LangChain4j的高级API实现聊天记忆功能,就是下图的红框部分
    在这里插入图片描述

源码下载(觉得作者啰嗦的,直接在这里下载)

  • 如果您只想快速浏览完整源码,可以在GitHub下载代码直接运行,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本篇的源码在langchain4j-tutorials文件夹下,如下图红色箭头所示:
    在这里插入图片描述

编码,高级API,内存存储,全局记忆

  • 本篇最先体验的是最简单的,基于高级API实现的记忆功能
  • 打开配置类LangChain4jConfig.java,通过AiServices.builder方法生成了模式服务代理类的实例,通过chatMemory方法绑定ChatMemory实例,这样就能在对话时把记录存入ChatMemory中
    @Bean
    public Assistant assistantRamGlobal(OpenAiChatModel chatModel) {
        // 创建一个ChatMemory实例,通过token数量限制记忆长度,记录在内存中
        ChatMemory chatMemory = Tools.createRamChatMemoryInstance();

        // 生成Assistant服务实例已经绑定了chatMemory
        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .chatMemory(chatMemory)
                .build();
    }
  • 在服务类QwenService中添加方法,使用高级API来对话,如下所示,可见只有最简单的对话功能,开发者无需关注如何处理聊天记录,都被代理类在内部处理了
@Autowired
private Assistant assistantRamGlobal;

    /**
     * 4. 高级API,基于内存的全局记忆
     * 
     * @param prompt
     * @return
     */
    public String highLevelRamGlobal(String prompt) {
        String answer = assistantRamGlobal.simpleChat(prompt);
        logger.info("响应:" + answer);
        return answer + "[from highLevelRamGlobal]";
    }
  • 在controller类QwenController中增加http接口响应以及上述服务类方法的调用
    @PostMapping("/highlevel/ram/global")
    public ResponseEntity<Response> highLevelRamGlobal(@RequestBody PromptRequest request) {
        ResponseEntity<Response> checkRlt = check(request);
        if (checkRlt != null) {
            return checkRlt;
        }

        try {
            // 使用基于内存的全局记忆功能
            String response = qwenService.highLevelRamGlobal(request.getPrompt());
            return ResponseEntity.ok(new Response(response));
        } catch (Exception e) {
            // 捕获异常并返回错误信息
            return ResponseEntity.status(500).body(new Response("请求处理失败: " + e.getMessage()));
        }
    }
  • 开发完毕,执行命令mvn spring-boot:run启动应用,然后用vscode的REST Client插件发起http请求,信息如下
POST http://localhost:8080/api/qwen/highlevel/ram/global
Content-Type: application/json
Accept: application/json

{
  "prompt": "一百字介绍曹操"
}
  • 收到响应
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 08:43:18 GMT
Connection: close

{
  "result": "曹操(155年-220年),字孟德,东汉末年杰出的政治家、军事家、文学家,魏国奠基者。他统一北方,推行屯田、唯才是举等政策,促进经济文化恢复。虽被传统史书称为“奸雄”,但其雄才大略与诗文成就(如《观沧海》)影响深远,是三国时代最具争议又极具魅力的历史人物。[from highLevelRamGlobal]"
}
  • 再请求
POST http://localhost:8080/api/qwen/highlevel/ram/global
Content-Type: application/json
Accept: application/json

{
  "prompt": "他在军事上的最高成就是什么?"
}
  • 收到响应,可见聊天记忆功能正常
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 08:43:57 GMT
Connection: close

{
  "result": "曹操在军事上的最高成就是**统一中国北方**。他通过一系列关键战役,如**官渡之战(200年)以少胜多击败袁绍**,奠定了统一基础;随后平定河北、北击乌桓、收服并州与凉州,至207年基本完成对黄河流域及中原地区的整合。这一成就不仅结束了北方长期战乱,也为后来曹魏政权的建立和三国鼎立格局的形成奠定了坚实基础。[from highLevelRamGlobal]"
}

编码,高级API,内存存储,指定用户ID

  • 接下来要实现的功能具有一定的实用性:假设有两个用户在使用咱们开发的这个memory应用,聊天记录应该是下面这样
用户A:介绍一下曹操
大模型:曹操是...

用户B:介绍一下刘备
大模型:刘备是...

用户A:他有那些军事成就
大模型:刘备的军事成就是...
  • 显然,用户A第二次问题的答案不是他想要的,但是,从大模型的视角,它不知道有两个人在同时问他呀,对这个问题,LangChain4j给出了解决方案,就是通过不同的用户ID来分别保存聊天记忆,也就是说调用LangChain4j的高级API聊天时只要带上不同的用户ID即可
  • 接下来开始编码实现分用户的聊天记忆功能
  • 打开接口Assistant.java,增加一个方法声明,该方法入参除了提示词还有用户id,注意要用注解MemoryId
    /**
     * 带记忆的对话,返回助手的回答
     * 
     * @param memoryId    记忆ID
     * @param userMessage 用户消息
     * @return 助手生成的回答
     */
    String chatByMemoryId(@MemoryId int memoryId, @UserMessage String userMessage);
  • 打开配置类LangChain4jConfig.java,通过AiServices.builder方法生成了模式服务代理类的实例,注意这里调用了chatMemoryProvider方法,而入参是个Lambda表达式,很明显,每个memoryId都会创建一个ChatMemory实例
    @Bean
    public Assistant assistantRamById(OpenAiChatModel chatModel) {
        // 注意,这里通过chatMemoryProvider来指定每个id和chatMemory的对应关系,记录在内存中
        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .chatMemoryProvider(memoryId -> Tools.createRamChatMemoryInstance())
                .build();
    }
  • 然后在服务类中增加一个方法,聊天时只要使用assistantRamById就能实现按用户区分的聊天以及功能
    /**
     * 5. 高级API,基于内存的用户记忆
     * 
     * @param userID
     * @param prompt
     * @return
     */
    public String highLevelRamByUserID(int userID, String prompt) {
        String secondAnswer = assistantRamById.chatByMemoryId(userID, prompt);
        logger.info("响应:" + secondAnswer);
        return secondAnswer + "[from highLevelRamByUserID]";
    }
  • 在controller类中增加一个接口响应
    @PostMapping("/highlevel/ram/byuserid")
    public ResponseEntity<Response> highLevelRamByUserID(@RequestBody PromptRequest request) {
        System.out.println("request : " + request);
        ResponseEntity<Response> checkRlt = check(request);
        if (checkRlt != null) {
            return checkRlt;
        }
        try {
            // 使用基于内存的用户ID记忆功能
            String response = qwenService.highLevelRamByUserID(request.getUserId(), request.getPrompt());
            return ResponseEntity.ok(new Response(response));
        } catch (Exception e) {
            // 捕获异常并返回错误信息
            return ResponseEntity.status(500).body(new Response("请求处理失败: " + e.getMessage()));
        }
    }
  • 编码完成,来看看效果,用户1001先问曹操,注意请求body中多了userId字段
POST http://localhost:8080/api/qwen/highlevel/ram/byuserid
Content-Type: application/json
Accept: application/json

{
  "userId": 1001,
  "prompt": "一百字介绍曹操"
}
  • 响应
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 10:02:29 GMT
Connection: close

{
  "result": "曹操(155年-220年),字孟德,东汉末年杰出的政治家、军事家、文学家,魏国奠基者。他统一北方,推行屯田、唯才是举,强化中央集权;在文学上开创建安风骨,代表作有《观沧海》《短歌行》等。虽被传统史书称为“奸雄”,但其雄才大略对结束乱世、推动历史发展影响深远。[from highLevelRamByUserID]"
}
  • 然后把userId的值改一下,相当于另一个用户提问,问的是刘备的情况
POST http://localhost:8080/api/qwen/highlevel/ram/byuserid
Content-Type: application/json
Accept: application/json

{
  "userId": 1002,
  "prompt": "一百字介绍刘备"
}
  • 响应自然是刘备的信息
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 10:26:12 GMT
Connection: close

{
  "result": "刘备(161年-223年),字玄德,东汉末年幽州涿郡人,三国时期蜀汉开国皇帝。他仁德宽厚,礼贤下士,以“兴复汉室”为志,三顾茅庐请诸葛亮出山,联合孙权于赤壁大败曹操。后占据益州,建立蜀汉政权,称帝于成都。虽屡遭挫折,却始终坚韧不拔,被后世誉为仁君典范。[from highLevelRamByUserID]"
}
  • 接下来,把userId的值改回1001再提问,看看回答的是不是曹操有关的结果
POST http://localhost:8080/api/qwen/highlevel/ram/byuserid
Content-Type: application/json
Accept: application/json

{
  "userId": 1001,
  "prompt": "他的政治思想的根源是哪里?"
}
  • 响应如下,可见聊天记忆确实已经按照id区分开了
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 12:05:29 GMT
Connection: close

{
  "result": "曹操政治思想的根源主要来自法家与儒家的融合,尤以法家为主。他身处东汉末年乱世,社会秩序崩坏,因此强调“乱世用重典”,主张严明法纪、赏罚分明,重视实效与集权,这明显承袭自商鞅、韩非等法家传统。同时,他也吸收儒家“尊王攘夷”“选贤任能”的理念,提出“唯才是举”,打破门第限制,并尊奉汉室以争取政治合法性。此外,曹操深受当时经学与现实政治环境影响,其思想体现出务实、权变与实用主义特征,形成“外儒内法”的统治策略。[from highLevelRamByUserID]"
}
  • 至此,基于用户的聊天记忆功能算是已经具备了,但是还有个问题,考虑下面这两种情况:况都无法正常工作
  1. 应用进程出现问题后重启
  2. 如果前面是一个nginx,后面有多个memory进程,用户第一次请求到了一号memory进程,第二次请求到了二号memory进程
  • 由于聊天记录都保存在进程的堆内存中,上述两种情况显然聊天记忆都找不到了,为了解决此问题,LangChain4j支持将聊天记录进行持久化保存,也就是保存在数据库中

编码,高级API,持久化存储,全局记忆

  • 接下来要尝试的是把聊天历史保存在持久化存储的功能,先看全局聊天记录的,如下图红框
    在这里插入图片描述
  • 由于LangChain4j对持久化的支持是通过接口实现的,因此并未限制具体的方式,这里为了简单用了嵌入式数据库mapdb,您也可以按照自己的习惯选择其他数据库,例如MySQL
  • 新增EmbeddedGlobalDb.java,实现了ChatMemoryStore接口,这样就可以支持聊天持久化能力了,可以看到getMessages、updateMessages、deleteMessages这些方法的具体实现可以随心所欲,这里是对dbmap数据库进行读写,您可以改为其他数据库的操作
package com.bolingcavalry.persistence;

import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.mapdb.DB;

import java.util.List;
import java.util.Map;

import static dev.langchain4j.data.message.ChatMessageDeserializer.messagesFromJson;
import static dev.langchain4j.data.message.ChatMessageSerializer.messagesToJson;

public class EmbeddedGlobalDb implements ChatMemoryStore {

    private DB db;
    private Map<String, String> map;

    /**
     * 带Map参数的构造方法,使用用户提供的映射
     * 
     * @param map 用户提供的映射实例
     */
    public EmbeddedGlobalDb(DB db, Map<String, String> map) {
        this.db = db;
        this.map = map;
    }

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        String json = map.get((String) memoryId);
        return messagesFromJson(json);
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        String json = messagesToJson(messages);
        map.put((String) memoryId, json);
        // 只有当db不为null时才提交事务
        if (db != null) {
            db.commit();
        }
    }

    @Override
    public void deleteMessages(Object memoryId) {
        map.remove((String) memoryId);
        // 只有当db不为null时才提交事务
        if (db != null) {
            db.commit();
        }
    }
}
  • 再增加一个实现类EmbeddedByIdDb,这是按照用户id保存聊天记录时用到的,也就是下一个功能是才会用到
package com.bolingcavalry.persistence;

import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.mapdb.DB;

import java.util.List;
import java.util.Map;

import static dev.langchain4j.data.message.ChatMessageDeserializer.messagesFromJson;
import static dev.langchain4j.data.message.ChatMessageSerializer.messagesToJson;

public class EmbeddedByIdDb implements ChatMemoryStore {

    private DB db;
    private Map<Integer, String> map;

    /**
     * 带Map参数的构造方法,使用用户提供的映射
     * 
     * @param map 用户提供的映射实例
     */
    public EmbeddedByIdDb(DB db, Map<Integer, String> map) {
        this.db = db;
        this.map = map;
    }

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        String json = map.get((int) memoryId);
        return messagesFromJson(json);
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        String json = messagesToJson(messages);
        map.put((int) memoryId, json);
        // 只有当db不为null时才提交事务
        if (db != null) {
            db.commit();
        }
    }

    @Override
    public void deleteMessages(Object memoryId) {
        map.remove((int) memoryId);
        // 只有当db不为null时才提交事务
        if (db != null) {
            db.commit();
        }
    }
}
  • 在Tools.java中增加一个静态方法createStoreInstance,用于封装ChatMemoryStore实例的创建过程
    /**
     * 创建一个ChatMemoryStore实例,用于存储聊天记忆,存在数据库中
     * 
     * @return ChatMemoryStore实例
     */
    public static ChatMemoryStore createStoreInstance(String dbName, boolean isById) {
        ChatMemoryStore rlt = null;
        // 创建一个MapDB实例,用于存储聊天记忆
        DB db = DBMaker.fileDB(dbName).transactionEnable().make();

        if (isById) {
            Map<Integer, String> dbMap = db.hashMap("messages", INTEGER, STRING).createOrOpen();
            rlt = new EmbeddedByIdDb(db, dbMap);
        } else {
            Map<String, String> dbMap = db.hashMap("messages", STRING, STRING).createOrOpen();
            rlt = new EmbeddedGlobalDb(db, dbMap);
        }

        return rlt;
    }
  • 再在Tools.java中增加一个方法createDbChatMemoryInstance,作用是创建ChatMemory实例,在创建时通过chatMemoryStore方法把前面的ChatMemoryStore传入,这样一来,往ChatMemory传入的对话记录就会被持久化到数据库中
    /**
     * 创建一个ChatMemory实例,用于存储聊天记忆,存在数据库中
     * 
     * @return ChatMemory实例
     */
    public static ChatMemory createDbChatMemoryInstance(String dbName, boolean isById) {
        // 创建一个MapDB实例,用于存储聊天记忆
        ChatMemoryStore store = createStoreInstance(dbName, isById);

        return MessageWindowChatMemory
                .builder()
                .maxMessages(100)
                .chatMemoryStore(store)
                .build();
    }
  • 也就是说,createDbChatMemoryInstance方法可以创建一个具有持久化能力的ChatMemory实例

  • 打开配置类LangChain4jConfig.java,通过AiServices.builder方法生成了模式服务代理类的实例,通过chatMemory方法绑定ChatMemory实例,而且这个ChatMemory实例具备持久化保存对话记录的能力

    @Bean
    public Assistant assistantDbGlobal(OpenAiChatModel chatModel) {
        // 创建一个ChatMemory实例,通过消息数量限制记忆长度,记录在数据库中
        ChatMemory chatMemory = Tools.createDbChatMemoryInstance("chat-memory-global.db", false);

        // 生成Assistant服务实例已经绑定了chatMemory
        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .chatMemory(chatMemory)
                .build();
    }
  • 在服务类QwenService中添加方法highLevelDbGlobal,里面使用的是前面创建的assistantDbGlobal实例,这样对话记录就会被持久化
    @Autowired
    private Assistant assistantDbGlobal;
    
    /**
     * 6. 高级API,基于数据库的全局记忆
     * 
     * @param prompt
     * @return
     */
    public String highLevelDbGlobal(String prompt) {
        String secondAnswer = assistantDbGlobal.simpleChat(prompt);
        logger.info("响应:" + secondAnswer);
        return secondAnswer + "[from highLevelDbGlobal]";
    }
  • controller中增加http响应以及对前面highLevelDbGlobal方法的调用
    @PostMapping("/highlevel/db/global")
    public ResponseEntity<Response> highLevelDbGlobal(@RequestBody PromptRequest request) {
        ResponseEntity<Response> checkRlt = check(request);
        if (checkRlt != null) {
            return checkRlt;
        }

        try {
            // 使用基于数据库的全局记忆功能
            String response = qwenService.highLevelDbGlobal(request.getPrompt());
            return ResponseEntity.ok(new Response(response));
        } catch (Exception e) {
            // 捕获异常并返回错误信息
            return ResponseEntity.status(500).body(new Response("请求处理失败: " + e.getMessage()));
        }
    }
  • 运行起来,第一次对话请求
POST http://localhost:8080/api/qwen/highlevel/db/global
Content-Type: application/json
Accept: application/json

{
  "prompt": "一百字介绍曹操"
}
  • 得到响应
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 14:31:57 GMT
Connection: close

{
  "result": "曹操(155年-220年),字孟德,东汉末年杰出的政治家、军事家、文学家,魏国奠基者。他统一北方,推行屯田、唯才是举等政策,稳定社会秩序;在文学上开创建安风骨。虽被传统史书视为“奸雄”,但其雄才大略对结束乱世、推动历史发展具有深远影响。[from highLevelDbGlobal]"
}
  • 重启该应用,如果聊天记录持久化存储在数据库,接下来的对话大模型应该能知道是在聊曹操
  • 重启后再次请求
POST http://localhost:8080/api/qwen/highlevel/db/global
Content-Type: application/json
Accept: application/json

{
  "prompt": "他面临过的最大挑战是什么?"
}
  • 得到响应,可见重启后大模型依旧知道问的是曹操,所以持久化功能是符合预期的
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 14:34:31 GMT
Connection: close

{
  "result": "若指曹操,他一生面临的最大挑战是**在东汉末年群雄割据、天下大乱的局势中,既要对抗强大的军事对手,又要维系内部稳定与合法性**。其中尤为关键的是:\n\n1. **官渡之战(200年)**:以弱胜强,击败兵力数倍于己的袁绍,此战决定北方霸权归属,若败则可能彻底覆灭。\n2. **赤壁之战(208年)**:意图统一全国,却遭孙刘联军火攻大败,从此形成三国鼎立格局,统一理想受挫。\n3. **政治合法性困境**:虽“挟天子以令诸侯”,但始终面临汉室忠臣与士族对其“篡汉”野心的质疑与反抗。\n\n这些挑战考验其军事、政治与战略智慧,也塑造了他复杂的历史形象。[from highLevelDbGlobal]"
}

编码,高级API,持久化存储,指定用户ID

  • 最后要尝试的是既支持持久化,又支持不同用户的聊天记忆,如下图红框
    在这里插入图片描述
  • 打开配置类LangChain4jConfig.java,注意这里要先创建ChatMemoryProvider对象,也就是根据不同用户id分别生成具有持久化能力的ChatMemory对象,最后通过AiServices.builder来创建模型服务的代理类实例
    @Bean
    public Assistant assistantDbById(OpenAiChatModel chatModel) {
        ChatMemoryStore chatMemoryStore = Tools.createStoreInstance("chat-memory-byid.db", true);

        ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(100)
                .chatMemoryStore(chatMemoryStore)
                .build();

        // 生成Assistant服务实例已经绑定了chatMemory
        return AiServices.builder(Assistant.class)
                .chatModel(chatModel)
                .chatMemoryProvider(chatMemoryProvider)
                .build();
    }
  • 在服务类QwenService中添加方法highLevelDbGlobal,里面使用的是前面创建的assistantDbById实例,这样每个用户的对话记录就会被持久化
    @Autowired
    private Assistant assistantDbById;

    /**
     * 7. 高级API,基于数据库的用户记忆
     * 
     * @param userID
     * @param prompt
     * @return
     */
    public String highLevelDbByUserID(int userID, String prompt) {
        String answer = assistantDbById.chatByMemoryId(userID, prompt);
        logger.info("响应:" + answer);
        return answer + "[from highLevelDbByUserID]";

    }
  • 最后是controller类增加接口响应,调用前面服务类的highLevelDbByUserID方法
    @PostMapping("/highlevel/db/byuserid")
    public ResponseEntity<Response> highLevelDbByUserID(@RequestBody PromptRequest request) {
        ResponseEntity<Response> checkRlt = check(request);
        if (checkRlt != null) {
            return checkRlt;
        }

        try {
            // 使用基于数据库的用户ID记忆功能
            String response = qwenService.highLevelDbByUserID(request.getUserId(), request.getPrompt());
            return ResponseEntity.ok(new Response(response));
        } catch (Exception e) {
            // 捕获异常并返回错误信息
            return ResponseEntity.status(500).body(new Response("请求处理失败: " + e.getMessage()));
        }
    }
  • 启动服务验证,1001用户先问,问的是曹操
POST http://localhost:8080/api/qwen/highlevel/db/byuserid
Content-Type: application/json
Accept: application/json

{
  "userId": 1001,
  "prompt": "一百字介绍曹操"
}
  • 回答是
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 14:58:00 GMT
Connection: close

{
  "result": "曹操(155年-220年),字孟德,东汉末年杰出的政治家、军事家、文学家。他统一北方,挟天子以令诸侯,奠定曹魏基业。虽被传统史书称为“奸雄”,但其唯才是举、屯田安民、诗文慷慨,展现出复杂而卓越的才能。其子曹丕称帝后,追尊为魏武帝。[from highLevelDbByUserID]"
}
  • 1002用户继续问,问的是刘备
POST http://localhost:8080/api/qwen/highlevel/db/byuserid
Content-Type: application/json
Accept: application/json

{
  "userId": 1002,
  "prompt": "一百字介绍刘备"
}
  • 回答的自然是刘备的信息
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 14:59:02 GMT
Connection: close

{
  "result": "刘备(161年-223年),字玄德,东汉末年幽州涿郡人,三国时期蜀汉开国皇帝。他以仁德著称,礼贤下士,三顾茅庐请诸葛亮出山,联合孙权于赤壁大破曹操,后夺取益州、汉中,建立蜀汉政权。虽出身寒微,却志在匡扶汉室,一生重情重义,与关羽、张飞桃园结义传为佳话。[from highLevelDbByUserID]"
}
  • 为了验证持久化能力,现在把应用重启
  • 重启后以1001用户的身份继续提问
POST http://localhost:8080/api/qwen/highlevel/db/byuserid
Content-Type: application/json
Accept: application/json

{
  "userId": 1001,
  "prompt": "他的军事思想对后世有什么影响?"
}
  • 得到响应如下,可见聊天记忆功能正常,并且准确的针对1001稳过的曹操进行了回复
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 15:06:20 GMT
Connection: close

{
  "result": "曹操的军事思想强调“兵无常势,水无常形”,主张灵活应变、因敌制胜。他重视情报侦察(“知彼知己”)、后勤保障(推行屯田以供军需)、赏罚分明与治军严整,并善于用奇兵、诈术和心理战(如官渡之战以少胜多)。其注释《孙子兵法》是中国现存最早的《孙子》注本,对后世兵学影响深远。他的实战经验与理论总结,成为历代将领研习的重要范本,推动了中国古代军事思想的发展。[from highLevelDbByUserID]"
}
  • 这时候把userId改为1002,大模型应该基于刘备回答才对
POST http://localhost:8080/api/qwen/highlevel/db/byuserid
Content-Type: application/json
Accept: application/json

{
  "userId": 1002,
  "prompt": "他的军事思想对后世有什么影响?"
}
  • 响应如下,确实是基于刘备身份的回复,可见持久化聊天记忆+按用户区分的能力同时生效了
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 13 Dec 2025 15:06:41 GMT
Connection: close

{
  "result": "刘备本人并非以系统的军事理论著称,其军事思想主要体现在实践层面,对后世影响有限但具有象征意义。他强调“仁义为本、得人者昌”,重视民心向背与人才招揽(如三顾茅庐),主张联合抗强(如联吴抗曹),体现出务实灵活的战略思维。这种以道义凝聚人心、以联盟制衡强敌的策略,被后世视为乱世中弱势集团崛起的典范。更重要的是,通过《三国演义》的文学塑造,刘备“仁德之主”的形象深入人心,成为儒家理想政治领袖的代表,间接影响了后世对统帅品德与治军理念的重视,而非纯粹的军事谋略传承。[from highLevelDbByUserID]"
}
  • 至此,LangChain4j的聊天记忆能力就全部实践完毕了,希望这两篇文章能给您一些参考,帮助您开发出使用的大模型对话能力
  • 《LangChain4j实战》还没有结束,接下来还会继续深入学习LangChain4j,鉴于最近几篇的内容都过于庞大,接下来咱们学点简单的:体验响应流式传输

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列
Logo

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

更多推荐