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