大模型应用开发-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 (默认),几乎可以解析所有格式的文件

未完继续。。。。

相关推荐
小阳拱白菜41 分钟前
java异常学习
java
FrankYoou2 小时前
Jenkins 与 GitLab CI/CD 的核心对比
java·docker
麦兜*2 小时前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
KK溜了溜了2 小时前
JAVA-springboot 整合Redis
java·spring boot·redis
天河归来3 小时前
使用idea创建springboot单体项目
java·spring boot·intellij-idea
weixin_478689763 小时前
十大排序算法汇总
java·算法·排序算法
码荼3 小时前
学习开发之hashmap
java·python·学习·哈希算法·个人开发·小白学开发·不花钱不花时间crud
IT_10243 小时前
Spring Boot项目开发实战销售管理系统——数据库设计!
java·开发语言·数据库·spring boot·后端·oracle
ye904 小时前
银河麒麟V10服务器版 + openGuass + JDK +Tomcat
java·开发语言·tomcat
武昌库里写JAVA4 小时前
Oracle如何使用序列 Oracle序列使用教程
java·开发语言·spring boot·学习·课程设计