提交 dea18221 编写于 作者: L Linnea Lin

feat: 集成RagFlow AI助手功能

- 配置Retrofit客户端支持动态URL和认证拦截器
- 新增创建聊天和会话的API接口定义
- 实现从配置文件加载提示词模板的功能
- 添加不同类型提示词配置,并补充接口
上级 4194d522
...@@ -3,48 +3,64 @@ package cn.bugstack.xfg.frame.config; ...@@ -3,48 +3,64 @@ package cn.bugstack.xfg.frame.config;
import cn.bugstack.xfg.frame.service.IKnowledgeCenterService; import cn.bugstack.xfg.frame.service.IKnowledgeCenterService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor; import okhttp3.logging.HttpLoggingInterceptor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import retrofit2.Retrofit; import retrofit2.Retrofit;
import retrofit2.converter.jackson.JacksonConverterFactory; import retrofit2.converter.jackson.JacksonConverterFactory;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Slf4j @Slf4j
@Configuration @Configuration
public class Retrofit2Config { public class Retrofit2Config {
private static final String BASE_URL = "https://demo.ragflow.io/";
@Resource
private RagflowProperties ragflowProperties;
@Resource
private AuthInterceptor authInterceptor;
@Bean @Bean
public Retrofit retrofit() { public Retrofit retrofit() {
// 创建日志拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
// 使用 SLF4J 记录日志,便于与 Spring Boot 日志系统集成
log.info("Retrofit: {}", message);
}
});
// 设置日志级别
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// 创建 OkHttpClient 并添加拦截器
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
return new Retrofit.Builder() return new Retrofit.Builder()
.baseUrl(BASE_URL) .baseUrl(ragflowProperties.getApi().getBaseUrl())
.client(okHttpClient) .client(createOkHttpClient())
.addConverterFactory(JacksonConverterFactory.create()).build(); .addConverterFactory(JacksonConverterFactory.create()).build();
} }
private OkHttpClient createOkHttpClient() {
// 创建日志拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// 创建请求头拦截器
okhttp3.Interceptor headerInterceptor = chain -> {
okhttp3.Request originalRequest = chain.request();
// 添加公共请求头
okhttp3.Request newRequest = originalRequest.newBuilder()
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer ragflow-IzZjQ5Y2ZhYWNkZjExZjA5MTA4MDI0Mm") // 认证token
.build();
return chain.proceed(newRequest);
};
return new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS) // 连接超时
.readTimeout(30, TimeUnit.SECONDS) // 读取超时
.writeTimeout(30, TimeUnit.SECONDS) // 写入超时
.addInterceptor(headerInterceptor) // 添加请求头拦截器
.addInterceptor(loggingInterceptor) // 添加日志拦截器
.build();
}
@Bean @Bean
public IKnowledgeCenterService knowledgeCenterService() { public IKnowledgeCenterService knowledgeCenterService() {
return retrofit().create(IKnowledgeCenterService.class); return retrofit().create(IKnowledgeCenterService.class);
......
package cn.bugstack.xfg.frame.controller; package cn.bugstack.xfg.frame.controller;
import cn.bugstack.xfg.frame.common.AssistantType;
import cn.bugstack.xfg.frame.common.Result; import cn.bugstack.xfg.frame.common.Result;
import cn.bugstack.xfg.frame.domain.req.CreateChatReq;
import cn.bugstack.xfg.frame.domain.req.CreateSessionReq;
import cn.bugstack.xfg.frame.domain.req.KeySensorCompletionReq; import cn.bugstack.xfg.frame.domain.req.KeySensorCompletionReq;
import cn.bugstack.xfg.frame.domain.res.ChatIdRes;
import cn.bugstack.xfg.frame.domain.res.CompletionRes; import cn.bugstack.xfg.frame.domain.res.CompletionRes;
import cn.bugstack.xfg.frame.domain.res.SessionRes;
import cn.bugstack.xfg.frame.domain.res.UserRes; import cn.bugstack.xfg.frame.domain.res.UserRes;
import cn.bugstack.xfg.frame.domain.vo.UserVO; import cn.bugstack.xfg.frame.domain.vo.UserVO;
import cn.bugstack.xfg.frame.service.IKnowledgeCenterService; import cn.bugstack.xfg.frame.service.IKnowledgeCenterService;
import cn.bugstack.xfg.frame.service.IPromptService;
import cn.bugstack.xfg.frame.service.IUserService; import cn.bugstack.xfg.frame.service.IUserService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -44,13 +50,49 @@ public class UserController { ...@@ -44,13 +50,49 @@ public class UserController {
@RequestMapping("/queryAI") @RequestMapping("/queryAI")
public String queryAI() throws IOException { public String queryAI() throws IOException {
// todo 构建ai (提示词) // todo 构建ai (提示词)
// 根据这里的key 拿到提示词位置
AssistantType generalAssistant= AssistantType.fromConfigKey("general");
// 获取这个type 对应的提示词
String sysPrompt = IPromptService.loadPromptFromFile(generalAssistant);
// 3. 构建 PromptConfig
CreateChatReq.PromptConfig promptConfig = CreateChatReq.PromptConfig.builder()
.prompt(sysPrompt)
.build();
// 5. 构建创建聊天请求
CreateChatReq createChatReq = CreateChatReq.builder()
.name(generalAssistant.getDescription() + " - " + System.currentTimeMillis())
.promptConfig(promptConfig)
.build();
Call<ChatIdRes> chat = knowledgeCenterService.createChat(createChatReq);
ChatIdRes chatIdRes = chat.execute().body();
log.info("chatIdRes: {}", chatIdRes);
// ========================
log.info("开始创建session");
CreateSessionReq newSession = CreateSessionReq.builder().name("new session").build();
Call<SessionRes> session = knowledgeCenterService.createSession(
chatIdRes.getId(),
newSession
);
SessionRes sessionRes = session.execute().body();
log.info("sessionRes: {}", sessionRes);
log.info("sessionId: {}", sessionRes.getData().getId());
log.info("=======》成功拿到session");
// todo 根据上一步拿到的aichat id 创建一个session // todo 根据上一步拿到的aichat id 创建一个session
// 构建请求参数 // 构建请求参数
KeySensorCompletionReq.KeySensorCompletionReqBuilder reqBuilder = KeySensorCompletionReq.KeySensorCompletionReqBuilder reqBuilder =
KeySensorCompletionReq.builder() KeySensorCompletionReq.builder()
.question("请给我一个5个字") .question("hello")
.style("normal"); .style("normal");
Call<CompletionRes> call = knowledgeCenterService.getCompletions("Bearer ragflow-djN2QwZjc0YTNlNzExZjBiYjBlNDIwMT", reqBuilder.build(), "56a8f80aa85911f08a8a42010a8e0005"); Call<CompletionRes> call = knowledgeCenterService.getCompletions(reqBuilder.build(), sessionRes.getData().getId());
CompletionRes completionRes = call.execute().body(); CompletionRes completionRes = call.execute().body();
log.info("completionRes: {}", completionRes); log.info("completionRes: {}", completionRes);
return "test"; return "test";
......
package cn.bugstack.xfg.frame.service; package cn.bugstack.xfg.frame.service;
import cn.bugstack.xfg.frame.domain.req.CreateChatReq;
import cn.bugstack.xfg.frame.domain.req.CreateSessionReq;
import cn.bugstack.xfg.frame.domain.req.KeySensorCompletionReq; import cn.bugstack.xfg.frame.domain.req.KeySensorCompletionReq;
import cn.bugstack.xfg.frame.domain.res.ChatIdRes;
import cn.bugstack.xfg.frame.domain.res.CompletionRes; import cn.bugstack.xfg.frame.domain.res.CompletionRes;
import cn.bugstack.xfg.frame.domain.res.SessionRes;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.*; import retrofit2.http.*;
public interface IKnowledgeCenterService { public interface IKnowledgeCenterService {
/**
* 创建聊天助手
*
* @param req 创建聊天请求参数
* @return 聊天 ID 响应
*/
@POST("api/v1/chats")
Call<ChatIdRes> createChat(
@Body CreateChatReq req
);
/**
* 创建会话
*
* @param chatId 聊天助手 ID
* @param req 创建会话请求参数
* @return 会话响应
*/
@POST("api/v1/chats/{chat_id}/sessions")
Call<SessionRes> createSession(
@Path("chat_id") String chatId,
@Body CreateSessionReq req
);
/** /**
* 对接 completion 接口 * 对接 completion 接口
* *
* @param authorization 认证头信息
* @param req 请求参数对象 * @param req 请求参数对象
* @param chat_id 聊天 ID
* @return 响应结果 * @return 响应结果
*/ */
@POST("api/v1/chats/{chat_id}/completions") @POST("api/v1/chats/{chat_id}/completions")
Call<CompletionRes> getCompletions( Call<CompletionRes> getCompletions(
@Header("Authorization") String authorization,
@Body KeySensorCompletionReq req, @Body KeySensorCompletionReq req,
@Path("chat_id") String chat_id @Path("chat_id") String chat_id
); );
......
package cn.bugstack.xfg.frame.service; package cn.bugstack.xfg.frame.service;
import cn.bugstack.xfg.frame.common.AssistantType;
import cn.bugstack.xfg.frame.config.RagflowProperties;
import cn.bugstack.xfg.frame.domain.req.KSReq; import cn.bugstack.xfg.frame.domain.req.KSReq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -13,6 +21,7 @@ import java.util.stream.Collectors; ...@@ -13,6 +21,7 @@ import java.util.stream.Collectors;
* @Description xx类 * @Description xx类
* @Date 2025/10/18 * @Date 2025/10/18
*/ */
@Slf4j
public class IPromptService { public class IPromptService {
private String buildBatchPrompt(List<KSReq> ksReqs){ private String buildBatchPrompt(List<KSReq> ksReqs){
StringBuilder batchPrompt = new StringBuilder(); StringBuilder batchPrompt = new StringBuilder();
...@@ -66,4 +75,27 @@ public class IPromptService { ...@@ -66,4 +75,27 @@ public class IPromptService {
, otherContextIdStr , otherContextIdStr
); );
} }
/**
* 从文件加载 prompt 内容
* @param assistantType 助手类型
* @return prompt 内容
*/
public static String loadPromptFromFile(AssistantType assistantType) {
try {
String promptFilePath = assistantType.getPromptFilePath();
ClassPathResource resource = new ClassPathResource(promptFilePath);
try (InputStream inputStream = resource.getInputStream()) {
String content = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("成功加载 {} 助手的 prompt 模板,文件路径: {}", assistantType.getDescription(), promptFilePath);
return content;
}
} catch (IOException e) {
log.error("加载 {} 助手的 prompt 模板失败: {}", assistantType.getDescription(), e.getMessage());
throw new RuntimeException("加载 prompt 模板失败", e);
}
}
} }
...@@ -10,4 +10,24 @@ spring: ...@@ -10,4 +10,24 @@ spring:
mybatis: mybatis:
mapper-locations: classpath:/mybatis/mapper/*.xml mapper-locations: classpath:/mybatis/mapper/*.xml
config-location: classpath:/mybatis/config/mybatis-config.xml config-location: classpath:/mybatis/config/mybatis-config.xml
\ No newline at end of file
# RagFlow 配置
ragflow:
api:
base-url: http://47.107.166.69/
api-key: ragflow-IzZjQ5Y2ZhYWNkZjExZjA5MTA4MDI0Mm
prompts:
general:
file: prompts/general-assistant.txt
opener: "Hi! I'm your general assistant. What can I help you with today?"
empty-response: "Sorry! No relevant content was found in the knowledge base!"
technical:
file: prompts/technical-assistant.txt
opener: "Hello! I'm your technical assistant. What technical question can I help you solve?"
empty-response: "Sorry! No relevant technical information was found in the knowledge base!"
customer-service:
file: prompts/customer-service.txt
opener: "Hello! Welcome to our customer service. How may I assist you today?"
empty-response: "Sorry! I couldn't find relevant information to help with your inquiry. Please contact our support team for further assistance."
\ No newline at end of file
...@@ -10,4 +10,24 @@ spring: ...@@ -10,4 +10,24 @@ spring:
mybatis: mybatis:
mapper-locations: classpath:/mybatis/mapper/*.xml mapper-locations: classpath:/mybatis/mapper/*.xml
config-location: classpath:/mybatis/config/mybatis-config.xml config-location: classpath:/mybatis/config/mybatis-config.xml
\ No newline at end of file
# RagFlow 配置
ragflow:
api:
base-url: http://47.107.166.69/
api-key: ragflow-IzZjQ5Y2ZhYWNkZjExZjA5MTA4MDI0Mm
prompts:
general:
file: prompts/general-assistant.txt
opener: "Hi! I'm your general assistant. What can I help you with today?"
empty-response: "Sorry! No relevant content was found in the knowledge base!"
technical:
file: prompts/technical-assistant.txt
opener: "Hello! I'm your technical assistant. What technical question can I help you solve?"
empty-response: "Sorry! No relevant technical information was found in the knowledge base!"
customer-service:
file: prompts/customer-service.txt
opener: "Hello! Welcome to our customer service. How may I assist you today?"
empty-response: "Sorry! I couldn't find relevant information to help with your inquiry. Please contact our support team for further assistance."
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册