作者 lixiang

智能助手设置集成

... ... @@ -6,13 +6,16 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.airag.airagchatsetting.entity.AiragChatsetting;
import org.jeecg.modules.airag.app.entity.AiragButton;
import org.jeecg.modules.airag.app.service.IAiragButtonService;
import org.jeecg.modules.airag.app.service.RedisChatSetService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
... ... @@ -35,6 +38,8 @@ import java.util.List;
public class AiragButtonController extends JeecgController<AiragButton, IAiragButtonService> {
@Autowired
private IAiragButtonService airagButtonService;
@Autowired
private RedisChatSetService redisChatSetService;
/**
* 分页列表查询
... ... @@ -67,12 +72,20 @@ public class AiragButtonController extends JeecgController<AiragButton, IAiragBu
@Operation(summary="按钮表单-智能助手按钮列表")
@GetMapping(value = "/buttonList")
public Result<List<AiragButton>> buttonList() {
AiragChatsetting chatSetting = redisChatSetService.getChatSetting();
String buttonId = chatSetting.getButtonId();
if (StringUtils.isNotBlank(buttonId)) {
String[] split = buttonId.split(",");
QueryWrapper<AiragButton> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("button_switch", "Y");
queryWrapper.in("id", split);
List<AiragButton> list = airagButtonService.list(queryWrapper);
return Result.OK(list);
}
return Result.OK();
}
/**
* 添加
... ...
... ... @@ -2,6 +2,7 @@ package org.jeecg.modules.airag.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.airag.airagchatsetting.entity.AiragChatsetting;
import org.jeecg.modules.airag.app.entity.QuestionEmbedding;
import org.springframework.web.multipart.MultipartFile;
... ... @@ -15,7 +16,7 @@ public interface IQuestionEmbeddingService {
int update(QuestionEmbedding record);
int removeByIds(List<String> ids);
int deleteById(String id);
List<QuestionEmbedding> similaritySearchByQuestion(String question, int limit, Double minSimilarity);
List<QuestionEmbedding> similaritySearchByQuestion(String question, int limit, Double minSimilarity, AiragChatsetting chatSetting);
List<QuestionEmbedding> similaritySearch(float[] vector, int limit);
Result<?> processZipUpload(MultipartFile file, String knowledgeId);
... ...
... ... @@ -2,6 +2,7 @@ package org.jeecg.modules.airag.app.service;
import lombok.extern.log4j.Log4j2;
import org.jeecg.modules.airag.airagchatsetting.entity.AiragChatsetting;
import org.jeecg.modules.airag.airagchatsetting.service.IAiragChatsettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
... ... @@ -13,6 +14,8 @@ public class RedisChatSetService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private IAiragChatsettingService airagChatsettingService;
// 存值
public void saveChatSetting(AiragChatsetting setting) {
... ... @@ -23,6 +26,10 @@ public class RedisChatSetService {
// 取值
public AiragChatsetting getChatSetting() {
AiragChatsetting chatsetting = (AiragChatsetting) redisTemplate.opsForValue().get(CHAT_SETTING_KEY);
if (chatsetting == null) {
chatsetting = airagChatsettingService.getOne(null);
redisTemplate.opsForValue().set(CHAT_SETTING_KEY, chatsetting);
}
log.info("获取配置成功:{}" , chatsetting);
return chatsetting;
}
... ...
... ... @@ -4,9 +4,11 @@ import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.model.output.Response;
import org.jeecg.modules.airag.airagchatsetting.entity.AiragChatsetting;
import org.jeecg.modules.airag.app.entity.Embeddings;
import org.jeecg.modules.airag.app.mapper.PgVectorMapper;
import org.jeecg.modules.airag.app.service.IEmbeddingsService;
import org.jeecg.modules.airag.app.service.RedisChatSetService;
import org.jeecg.modules.airag.app.utils.AiModelUtils;
import org.postgresql.util.PGobject;
import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -29,8 +31,8 @@ public class IEmbeddingsServiceImpl implements IEmbeddingsService {
private PgVectorMapper pgVectorMapper;
@Autowired
private AiModelUtils aiModelUtils;
@Value("${jeecg.ai-chat.embedId}")
private String embedId;
@Autowired
private RedisChatSetService redisChatSetService;
@Override
public Page<Embeddings> findAll(Page<Embeddings> page, Embeddings embeddings) {
... ... @@ -47,8 +49,9 @@ public class IEmbeddingsServiceImpl implements IEmbeddingsService {
}
public int insert(Embeddings record) {
AiragChatsetting chatSetting = redisChatSetService.getChatSetting();
record.setId(UUID.randomUUID().toString());
Response<Embedding> embedding = aiModelUtils.getEmbedding(embedId, record.getText());
Response<Embedding> embedding = aiModelUtils.getEmbedding(chatSetting.getEmbeddingId(), record.getText());
record.setEmbedding(embedding.content().vector());
return pgVectorMapper.insert(record);
}
... ... @@ -65,7 +68,8 @@ public class IEmbeddingsServiceImpl implements IEmbeddingsService {
jsonObject.setValue(mataData.toJSONString());
record.setMetadata(mataData);
Response<Embedding> embedding = aiModelUtils.getEmbedding(embedId, record.getText());
AiragChatsetting chatSetting = redisChatSetService.getChatSetting();
Response<Embedding> embedding = aiModelUtils.getEmbedding(chatSetting.getEmbeddingId(), record.getText());
record.setEmbedding(embedding.content().vector());
... ...
package org.jeecg.modules.airag.app.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hwpf.usermodel.CharacterRun;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Range;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.splitter.DocumentByParagraphSplitter;
... ... @@ -17,27 +11,20 @@ import dev.langchain4j.model.output.Response;
import org.apache.commons.io.FilenameUtils;
import org.apache.poi.xwpf.usermodel.*;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.airag.airagchatsetting.entity.AiragChatsetting;
import org.jeecg.modules.airag.app.entity.Embeddings;
import org.jeecg.modules.airag.app.entity.QuestionEmbedding;
import org.jeecg.modules.airag.app.mapper.EmbeddingsMapper;
import org.jeecg.modules.airag.app.mapper.PgVectorMapper;
import org.jeecg.modules.airag.app.mapper.QuestionEmbeddingMapper;
import org.jeecg.modules.airag.app.service.IQuestionEmbeddingService;
import org.jeecg.modules.airag.app.service.RedisChatSetService;
import org.jeecg.modules.airag.app.utils.AiModelUtils;
import org.jeecg.modules.airag.common.handler.IAIChatHandler;
import org.postgresql.util.PGobject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pgvector.PGvector;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.io.File;
import java.io.FileInputStream;
... ... @@ -69,8 +56,10 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService {
@Value("${jeecg.upload.path}")
private String uploadPath;
@Value("${jeecg.ai-chat.embedId}")
private String embedId;
@Autowired
private RedisChatSetService redisChatSetService;
// @Value("${jeecg.ai-chat.embedId}")
// private String embedId;
private static final Set<String> ALLOWED_EXTENSIONS = Set.of("txt", "doc", "docx");
private static final Pattern UUID_PATTERN = Pattern.compile("_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}");
... ... @@ -95,7 +84,8 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService {
@Override
public int insert(QuestionEmbedding record) {
if (StringUtils.isNotBlank(record.getQuestion())){
Response<Embedding> embedding = aiModelUtils.getEmbedding(embedId, record.getQuestion());
AiragChatsetting chatSetting = redisChatSetService.getChatSetting();
Response<Embedding> embedding = aiModelUtils.getEmbedding(chatSetting.getEmbeddingId(), record.getQuestion());
record.setEmbedding(embedding.content().vector());
}
... ... @@ -106,7 +96,8 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService {
@Override
public int update(QuestionEmbedding record) {
if (StringUtils.isNotBlank(record.getQuestion())){
Response<Embedding> embedding = aiModelUtils.getEmbedding(embedId, record.getQuestion());
AiragChatsetting chatSetting = redisChatSetService.getChatSetting();
Response<Embedding> embedding = aiModelUtils.getEmbedding(chatSetting.getEmbeddingId(), record.getQuestion());
record.setEmbedding(embedding.content().vector());
}
return questionEmbeddingMapper.update(record);
... ... @@ -123,8 +114,8 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService {
}
@Override
public List<QuestionEmbedding> similaritySearchByQuestion(String question, int limit, Double minSimilarity) {
Response<Embedding> embedding = aiModelUtils.getEmbedding(embedId, question);
public List<QuestionEmbedding> similaritySearchByQuestion(String question, int limit, Double minSimilarity, AiragChatsetting chatSetting) {
Response<Embedding> embedding = aiModelUtils.getEmbedding(chatSetting.getEmbeddingId(), question);
return questionEmbeddingMapper.similaritySearchByQuestion(embedding.content().vector(), limit, minSimilarity);
}
... ... @@ -202,7 +193,7 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService {
}
private void saveToEmbeddingsTable(List<String> segments, String originalFileName, String storedFileName, String knowledgeId) {
if (segments.isEmpty()) return;
AiragChatsetting chatSetting = redisChatSetService.getChatSetting();
String displayFileName = removeUuidSuffix(originalFileName);
displayFileName = FilenameUtils.removeExtension(displayFileName);
... ... @@ -218,7 +209,7 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService {
String answer = segment.trim();
// 获取 embedding
Response<Embedding> embeddingResponse = aiModelUtils.getEmbedding(embedId, answer);
Response<Embedding> embeddingResponse = aiModelUtils.getEmbedding(chatSetting.getEmbeddingId(), answer);
float[] embeddingVector = embeddingResponse.content().vector();
Map<String, Object> metadata = new HashMap<>();
... ... @@ -390,8 +381,8 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService {
log.info("保存分段: title={}, content_length={}", question, segment.length());
Response<Embedding> embeddingResponse = aiModelUtils.getEmbedding(embedId, record.getQuestion());
AiragChatsetting chatSetting = redisChatSetService.getChatSetting();
Response<Embedding> embeddingResponse = aiModelUtils.getEmbedding(chatSetting.getEmbeddingId(), record.getQuestion());
record.setEmbedding(embeddingResponse.content().vector());
record.setKnowledgeId(knowledgeId);
insert(record);
... ...
... ... @@ -8,10 +8,12 @@ import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.service.TokenStream;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.airag.airagchatsetting.entity.AiragChatsetting;
import org.jeecg.modules.airag.app.entity.AiragLog;
import org.jeecg.modules.airag.app.entity.QuestionEmbedding;
import org.jeecg.modules.airag.app.service.IAiragLogService;
import org.jeecg.modules.airag.app.service.IQuestionEmbeddingService;
import org.jeecg.modules.airag.app.service.RedisChatSetService;
import org.jeecg.modules.airag.app.utils.FileToBase64Util;
import org.jeecg.modules.airag.common.handler.IAIChatHandler;
import org.jeecg.modules.airag.llm.handler.EmbeddingHandler;
... ... @@ -39,21 +41,26 @@ public class AiragResponseServiceImpl implements AiragResponseService {
private String uploadPath;
@Autowired
private IAiragLogService airagLogService;
@Autowired
private RedisChatSetService redisChatSetService;
@Override
public SseEmitter handleStreamRequest(String questionText, String code, Integer codeType, String user) {
AiragChatsetting chatSetting = redisChatSetService.getChatSetting();
SseEmitter emitter = new SseEmitter(300000L);
String modelId = "1926875898187878401";
String modelId = chatSetting.getLlmId();
AiragLog logRecord = createLogRecord(questionText, code, codeType, user, modelId);
String cleanedQuestionText = cleanQuestionText(questionText);
try {
// 处理问题库匹配
if (handleQuestionEmbeddingMatch(emitter, cleanedQuestionText, logRecord)) {
if (handleQuestionEmbeddingMatch(emitter, cleanedQuestionText, logRecord,chatSetting)) {
return emitter;
}
// 处理知识库搜索
handleKnowledgeBaseSearch(emitter, cleanedQuestionText, logRecord, modelId);
handleKnowledgeBaseSearch(emitter, cleanedQuestionText, logRecord, modelId,chatSetting);
} catch (Exception e) {
handleError(emitter, logRecord, e);
}
... ... @@ -101,13 +108,15 @@ public class AiragResponseServiceImpl implements AiragResponseService {
/**
* 匹配问题库
*
* @param emitter 流式返回
* @param questionText 问题原文本
* @param logRecord 日志对象
* @param chatSetting
* @return 返回是否匹配成功
*/
private boolean handleQuestionEmbeddingMatch(SseEmitter emitter, String questionText, AiragLog logRecord) throws Exception {
List<QuestionEmbedding> questionEmbeddings = questionEmbeddingService.similaritySearchByQuestion(questionText, 1, 0.8);
private boolean handleQuestionEmbeddingMatch(SseEmitter emitter, String questionText, AiragLog logRecord, AiragChatsetting chatSetting) throws Exception {
List<QuestionEmbedding> questionEmbeddings = questionEmbeddingService.similaritySearchByQuestion(questionText, 1, 0.8, chatSetting);
if (questionEmbeddings.isEmpty()) {
return false;
}
... ... @@ -140,13 +149,15 @@ public class AiragResponseServiceImpl implements AiragResponseService {
/**
* 知识库匹配
*
* @param emitter 流式返回
* @param questionText 问题原文本
* @param logRecord 日志对象
* @param modelId 模型id
* @param chatSetting
*/
private void handleKnowledgeBaseSearch(SseEmitter emitter, String questionText, AiragLog logRecord, String modelId) throws Exception {
String knowId = "1926872137990148098";
private void handleKnowledgeBaseSearch(SseEmitter emitter, String questionText, AiragLog logRecord, String modelId, AiragChatsetting chatSetting) throws Exception {
String knowId =chatSetting.getKnowledgeId();
List<Map<String, Object>> maps = embeddingHandler.searchEmbedding(knowId, questionText, 2, 0.78);
if (CollectionUtil.isEmpty(maps)) {
... ... @@ -159,7 +170,7 @@ public class AiragResponseServiceImpl implements AiragResponseService {
String fileName = generateFileDocName(firstMatch.get("metadata").toString());
String storedFileName = generateFilePath(firstMatch.get("metadata").toString());
String systemPrompt = createSystemPrompt();
String systemPrompt = chatSetting.getPrompt();
String userPrompt = createUserPrompt(questionText, content);
processLLMResponse(emitter, logRecord, modelId, systemPrompt, userPrompt, firstMatch, fileName, storedFileName);
... ...