当前位置: 首页 > news >正文

LangChain4j (3) :AiService工具类、流式调用、消息注解

1. AiService 工具类

在实际开发中,一般使用 AiService 工具类进行开发。这里有两种方式,一种是写一个配置类将 AI 服务接口类注册为 Bean,另一种直接使用 @AiService 注解,在服务接口类上注解使用。这里我们直接使用注解方法,这种方法扩展性更强也更方便一些。

引入依赖

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

编写 ConsultantService (AI 服务接口类)

这里接口定义了一组 AI 服务,然后通过 @AiService 自动生成实现类,无需我们再去实现这个接口,代码如下:

package com.zjy.consultant.aiservice;import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;@AiService(wiringMode = AiServiceWiringMode.EXPLICIT, // 手动装配指定 chatModelchatModel = "openAiChatModel"
)
public interface ConsultantService {String chat(String message);}

这里的 @AiService 有两个参数,wiringMode 默认为 AiServiceWiringMode.AUTOMATIC ,即自动装配 chatModel,框架会自己去装配 IOC 容器中已有的 chatModel,例如这里框架自带的 openAiChatModel。所以这里其实不用这么写也可以,直接这么写即可:

package com.zjy.consultant.aiservice;import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;@AiService
public interface ConsultantService {String chat(String message);}

application.yml 配置

这个配置和前一节一样,因为我们使用的 chatModel 仍然是 openAiChatModel,配置如下:

langchain4j:open-ai:chat-model:base-url: https://dashscope.aliyuncs.com/compatible-mode/v1api-key: sk-*******************model-name: qwen-pluslog-requests: true # 在终端输出请求参数日志log-responses: true # 在终端输出响应参数日志
logging:level:dev.langchain4j: debug

编写 Controller

package com.zjy.consultant.controller;import com.zjy.consultant.aiservice.ConsultantService;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;@RestController
public class ChatController {@Autowiredprivate ConsultantService consultantService;@RequestMapping("/chat")public String chat(String message) {return consultantService.chat(message);}}

@AiService 注解的 ConsultantService 在控制层进行一个注入。启动服务后访问 http://localhost:8080/chat?message=你是谁

2.流式调用

我们平时在 DeepSeek 或者 ChatGPT 上看到的大模型几个字几个字输出,而不是他生成完所有的答案再一次性全部输出,这就是流式调用,在 LangChain4j (1) :Ollama本地部署与阿里百炼云端部署大模型 中,我们给出了 Ollama 本地调用的调用示例:

curl http://localhost:11434/api/chat -d '{"model": "deepseek-r1","messages": [{"role": "user","content": "how many r in the word strawberry?"}],"think": true,"stream": false
}'

可以看到有一个字段名为 stream 的字段,这个就是流式调用的开关,下面我们就要在代码中使用这个流式调用。

引入依赖

<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>

application.xml 配置

因为流式调用我们不能使用 OpenAiChatModel ,需要使用 OpenAiStreamingChatModel ,所以需要修改配置文件:

langchain4j:open-ai:chat-model:base-url: https://dashscope.aliyuncs.com/compatible-mode/v1api-key: sk-*******************model-name: qwen-pluslog-requests: truelog-responses: truestreaming-chat-model:base-url: https://dashscope.aliyuncs.com/compatible-mode/v1api-key: sk-*******************model-name: qwen-pluslog-requests: truelog-responses: true
logging:level:dev.langchain4j: debug

另外,如果我们的 @AiService 使用的是手动装配,则需要这样修改,用自动装配的就直接写一个 @AiService 就行了,无需修改:

@AiService(wiringMode = AiServiceWiringMode.EXPLICIT, // 手动装配指定 chatModelchatModel = "openAiChatModel",streamingChatModel= "openAiStreamingChatModel"
)

修改接口类

因为流式调用肯定不是直接返回 String 类型的数据,所以接口类中的 chat 方法也需要修改返回类型:

package com.zjy.consultant.aiservice;import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.spring.AiService;
import reactor.core.publisher.Flux;@AiService
public interface ConsultantService {Flux<String> chat(String message);}

修改 Controller

package com.zjy.consultant.controller;import com.zjy.consultant.aiservice.ConsultantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;@RestController
public class ChatController {@Autowiredprivate 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 中加上 produces = "text/html;charset=utf-8" ,否则会出现乱码的情况。全部修改完后就可以重新访问,这是就可以看到浏览器上不会一次性全部输出,而是一点一点输出了,在 Springboot 的控制台上也可以看到流式输出的信息:

3. 消息注解

LangChain4j (1) :Ollama本地部署与阿里百炼云端部署大模型 中我们提到过 "role":"system"content 内容可以设定大模型的 角色对话风格,如下:

{"model": "qwen-plus","messages":[      {"role": "system","content": "You are a helpful assistant."},{"role": "user","content": "你好"}]
}

这节介绍的 消息注解 就是这个作用,我们可以给我们的大模型指定一些角色和设定。

@SystemMessage 注解

这个注解就是指定"role":"system"content 内容,将这个注解加在接口的方法上,代码如下:

package com.zjy.consultant.aiservice;import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.spring.AiService;
import reactor.core.publisher.Flux;@AiService
public interface ConsultantService {@SystemMessage("你是金庸武侠小说专家,只回答和金庸武侠小说的问题")Flux<String> chat(String message);
}

如果内容过长的话,也可以用 @SystemMessage(fromResource="system.txt")来指定。加上这个设定后,我们再来看看启动效果:

再来看一下请求日志,可以看到请求日志中就增加了相应的信息:

@UserMessage 注解

它的作用类似于给 AI 的“指令模板”,可以将方法参数灵活嵌入到预定义的提示结构中,例如:

package com.zjy.consultant.aiservice;import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;
import reactor.core.publisher.Flux;@AiService
public interface ConsultantService {@UserMessage("请结结巴巴地回答,{{it}}")Flux<String> chat(String message);}

{{it}} 就是用来填充用户自己输入的信息,我们启动服务:

可以看到大模型成功结结巴巴地回答了。我们去看一下请求日志:

可以看到在我们自己输入的问题前,加上了 请结结巴巴地回答 的文字,我们也可以用 多参数的复杂模版

@UserMessage("""角色:{{role}}任务:用{{tone}}语气回复:{{message}}""")
String chat(@V("role") String role, @V("tone") String tone,@V("message") String msg);

http://www.dtcms.com/a/338011.html

相关文章:

  • 吴恩达 Machine Learning(Class 2)
  • 数字时代著作权侵权:一场资本与法律的博弈
  • 「Flink」业务搭建方法总结
  • 嵌入式设备Lwip协议栈实现功能
  • 摔倒检测数据集:1w+图像,yolo标注
  • 02.Linux基础命令
  • 8.18 机器学习-决策树(1)
  • docker部署flask并迁移至内网
  • Zephyr下控制ESP32S3的GPIO口
  • RK3568 NPU RKNN(六):RKNPU2 SDK
  • FlycoTabLayout CommonTabLayout 支持Tab选中字体变大 选中tab的加粗效果首次无效的bug
  • 探索性测试:灵活找Bug的“人肉探测仪”
  • 前端 大文件分片下载上传
  • 宝塔面板多Python版本管理与项目部署
  • excel表格 Vue3(非插件)
  • day25|学习前端js
  • Linux: RAID(磁盘冗余阵列)配置全指南
  • 损失函数与反向传播 小土堆pytorch记录
  • FPGA-Vivado2017.4-建立AXI4用于单片机与FPGA之间数据互通
  • 计算机组成原理(9) - 整数的乘除法运算
  • js计算两个经纬度之间的角度
  • Python字符串连接与合并工程实践:从基础到高性能解决方案
  • 【笔记】位错的定义和分类
  • B站 韩顺平 笔记 (Day 22)
  • 【人工智能】2025年AI代理失控危机:构建安全壁垒,守护智能未来
  • 规避(EDR)安全检测--避免二进制文件落地
  • 面向对象爬虫进阶:类封装实现高复用爬虫框架​
  • DP-v2.1-mem-clean学习(3.6.9-3.6.12)
  • Python 爬虫实战:玩转 Playwright 跨浏览器自动化(Chromium/Firefox/WebKit 全支持)
  • 嵌入式第三十二课!!线程间的同步机制与进程间的通信(IPC机制)