|
...
|
...
|
@@ -13,10 +13,7 @@ import dev.langchain4j.data.embedding.Embedding; |
|
|
|
import dev.langchain4j.data.segment.TextSegment;
|
|
|
|
import dev.langchain4j.model.output.Response;
|
|
|
|
import org.apache.commons.io.FilenameUtils;
|
|
|
|
import org.apache.poi.xwpf.usermodel.IBodyElement;
|
|
|
|
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
|
|
|
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
|
|
|
import org.apache.poi.xwpf.usermodel.XWPFTable;
|
|
|
|
import org.apache.poi.xwpf.usermodel.*;
|
|
|
|
import org.jeecg.common.api.vo.Result;
|
|
|
|
import org.jeecg.modules.airag.app.entity.QuestionEmbedding;
|
|
|
|
import org.jeecg.modules.airag.app.mapper.QuestionEmbeddingMapper;
|
|
...
|
...
|
@@ -67,12 +64,14 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService { |
|
|
|
|
|
|
|
@Value("${jeecg.upload.path}")
|
|
|
|
private String uploadPath;
|
|
|
|
@Value("${jeecg.ai-chat.embedId}")
|
|
|
|
private String embedId;
|
|
|
|
|
|
|
|
private static final Set<String> ALLOWED_EXTENSIONS = Set.of("txt", "doc", "docx");
|
|
|
|
private static final Pattern SPECIAL_CHARS_PATTERN = Pattern.compile("[^a-zA-Z0-9\\u4e00-\\u9fa5\\s]");
|
|
|
|
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}");
|
|
|
|
|
|
|
|
// 新增:数据库连接配置
|
|
|
|
// 数据库连接配置
|
|
|
|
private static final String DB_URL = "jdbc:postgresql://192.168.100.104:5432/postgres";
|
|
|
|
private static final String DB_USER = "postgres";
|
|
|
|
private static final String DB_PASSWORD = "postgres";
|
|
...
|
...
|
@@ -188,56 +187,59 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService { |
|
|
|
saveSegmentsToDatabase(segments, originalFileName, storedFileName, knowledgeId);
|
|
|
|
|
|
|
|
// 新增逻辑:同时保存到embeddings表
|
|
|
|
saveToEmbeddingsTable(segments, originalFileName, knowledgeId);
|
|
|
|
}
|
|
|
|
saveToEmbeddingsTable(segments, originalFileName, storedFileName, knowledgeId);
|
|
|
|
|
|
|
|
// 新增方法:将内容保存到embeddings表
|
|
|
|
private void saveToEmbeddingsTable(List<String> segments, String originalFileName, String knowledgeId) {
|
|
|
|
if (segments.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private void saveToEmbeddingsTable(List<String> segments, String originalFileName, String storedFileName, String knowledgeId) {
|
|
|
|
if (segments.isEmpty()) return;
|
|
|
|
|
|
|
|
// 获取无UUID和扩展名的文件名用于显示
|
|
|
|
String displayFileName = removeUuidSuffix(originalFileName);
|
|
|
|
displayFileName = FilenameUtils.removeExtension(displayFileName);
|
|
|
|
|
|
|
|
// 为整个文档生成一个唯一的docId
|
|
|
|
String docId = UUID.randomUUID().toString();
|
|
|
|
|
|
|
|
// 合并所有段落作为完整内容
|
|
|
|
String fullContent = String.join("\n\n", segments);
|
|
|
|
|
|
|
|
try (Connection conn = getConnection()) {
|
|
|
|
// 准备元数据
|
|
|
|
Map<String, Object> metadata = new HashMap<>();
|
|
|
|
metadata.put("docId", docId);
|
|
|
|
metadata.put("docName", originalFileName);
|
|
|
|
metadata.put("knowledgeId", knowledgeId);
|
|
|
|
|
|
|
|
// 获取文本的向量表示
|
|
|
|
Response<Embedding> embeddingResponse = aiModelUtils.getEmbedding("1925730210204721154", displayFileName + ": " + fullContent);
|
|
|
|
float[] embeddingVector = embeddingResponse.content().vector();
|
|
|
|
|
|
|
|
// 插入到embeddings表
|
|
|
|
String sql = "INSERT INTO embeddings (embedding_id, embedding, text, metadata) VALUES (?, ?, ?, ?::jsonb)";
|
|
|
|
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
|
|
stmt.setString(1, UUID.randomUUID().toString());
|
|
|
|
stmt.setObject(2, new PGvector(embeddingVector));
|
|
|
|
stmt.setString(3, fullContent);
|
|
|
|
|
|
|
|
PGobject jsonObject = new PGobject();
|
|
|
|
jsonObject.setType("json");
|
|
|
|
jsonObject.setValue(new ObjectMapper().writeValueAsString(metadata));
|
|
|
|
stmt.setObject(4, jsonObject);
|
|
|
|
|
|
|
|
stmt.executeUpdate();
|
|
|
|
for (String segment : segments) {
|
|
|
|
if (segment.trim().isEmpty()) continue;
|
|
|
|
|
|
|
|
// 回答内容是整个段落
|
|
|
|
String[] parts = segment.split("\\r?\\n", 2);
|
|
|
|
if (parts.length < 2) continue;
|
|
|
|
|
|
|
|
String titlePath = parts[0].trim();
|
|
|
|
String answer = segment.trim(); // 整个回答段(含标题 + 内容)
|
|
|
|
|
|
|
|
// 获取 embedding
|
|
|
|
Response<Embedding> embeddingResponse = aiModelUtils.getEmbedding(embedId, answer);
|
|
|
|
float[] embeddingVector = embeddingResponse.content().vector();
|
|
|
|
|
|
|
|
// 准备 metadata
|
|
|
|
Map<String, Object> metadata = new HashMap<>();
|
|
|
|
metadata.put("docName", originalFileName);
|
|
|
|
metadata.put("storedFileName", storedFileName);
|
|
|
|
metadata.put("knowledgeId", knowledgeId);
|
|
|
|
metadata.put("title", displayFileName + ": " + titlePath);
|
|
|
|
|
|
|
|
// 插入
|
|
|
|
String sql = "INSERT INTO embeddings (embedding_id, embedding, text, metadata) VALUES (?, ?, ?, ?::jsonb)";
|
|
|
|
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
|
|
stmt.setString(1, UUID.randomUUID().toString());
|
|
|
|
stmt.setObject(2, new PGvector(embeddingVector));
|
|
|
|
stmt.setString(3, answer);
|
|
|
|
|
|
|
|
PGobject jsonObject = new PGobject();
|
|
|
|
jsonObject.setType("json");
|
|
|
|
jsonObject.setValue(new ObjectMapper().writeValueAsString(metadata));
|
|
|
|
stmt.setObject(4, jsonObject);
|
|
|
|
|
|
|
|
stmt.executeUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
log.error("保存到embeddings表失败", e);
|
|
|
|
log.error("保存分段到embeddings表失败", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 新增方法:获取数据库连接
|
|
|
|
|
|
|
|
// 获取数据库连接
|
|
|
|
private Connection getConnection() throws SQLException {
|
|
|
|
return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
|
|
|
|
}
|
|
...
|
...
|
@@ -253,8 +255,8 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService { |
|
|
|
}
|
|
|
|
|
|
|
|
private String cleanText(String text) {
|
|
|
|
// 保留基本的标点符号,包括 . : - 等
|
|
|
|
Pattern preservedCharsPattern = Pattern.compile("[^a-zA-Z0-9\\u4e00-\\u9fa5\\s.,:、,:;。;-]");
|
|
|
|
// 保留基本的标点符号
|
|
|
|
Pattern preservedCharsPattern = Pattern.compile("[^a-zA-Z0-9\\u4e00-\\u9fa5\\s.,:、,:;。;#;-]");
|
|
|
|
text = preservedCharsPattern.matcher(text).replaceAll("");
|
|
|
|
|
|
|
|
// 将多个换行符缩减为一个换行符
|
|
...
|
...
|
@@ -299,182 +301,183 @@ public class QuestionEmbeddingServiceImpl implements IQuestionEmbeddingService { |
|
|
|
.collect(Collectors.toList());
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<String> splitWordDocument(String filePath) throws Exception {
|
|
|
|
List<String> result = new ArrayList<>();
|
|
|
|
String ext = FilenameUtils.getExtension(filePath).toLowerCase();
|
|
|
|
StringBuilder fullContent = new StringBuilder();
|
|
|
|
|
|
|
|
// 获取无UUID的文件名用于显示
|
|
|
|
String displayFileName = removeUuidSuffix(new File(filePath).getName());
|
|
|
|
displayFileName = FilenameUtils.removeExtension(displayFileName);
|
|
|
|
// 后备分割方案:按段落结构分割
|
|
|
|
private List<String> splitByContentStructure(XWPFDocument doc) {
|
|
|
|
List<String> segments = new ArrayList<>();
|
|
|
|
StringBuilder currentSegment = new StringBuilder();
|
|
|
|
final int MAX_SEGMENT_LENGTH = 1000; // 最大分段长度
|
|
|
|
|
|
|
|
if (ext.equals("docx")) {
|
|
|
|
try (XWPFDocument doc = new XWPFDocument(new FileInputStream(filePath))) {
|
|
|
|
StringBuilder currentSection = new StringBuilder();
|
|
|
|
boolean isTableSection = false;
|
|
|
|
|
|
|
|
for (IBodyElement element : doc.getBodyElements()) {
|
|
|
|
if (element instanceof XWPFParagraph) {
|
|
|
|
XWPFParagraph para = (XWPFParagraph) element;
|
|
|
|
String text = cleanText(para.getText());
|
|
|
|
fullContent.append(text).append("\n");
|
|
|
|
|
|
|
|
if (isTableSection) {
|
|
|
|
result.add(currentSection.toString().trim());
|
|
|
|
currentSection = new StringBuilder();
|
|
|
|
isTableSection = false;
|
|
|
|
}
|
|
|
|
for (IBodyElement element : doc.getBodyElements()) {
|
|
|
|
String text = "";
|
|
|
|
if (element instanceof XWPFParagraph) {
|
|
|
|
text = ((XWPFParagraph) element).getText().trim();
|
|
|
|
} else if (element instanceof XWPFTable) {
|
|
|
|
text = extractTableContent((XWPFTable) element);
|
|
|
|
}
|
|
|
|
|
|
|
|
String style = para.getStyle();
|
|
|
|
if (style != null && style.matches("Heading\\d")) {
|
|
|
|
if (!currentSection.isEmpty()) {
|
|
|
|
result.add(currentSection.toString().trim());
|
|
|
|
}
|
|
|
|
currentSection = new StringBuilder(text).append("\n");
|
|
|
|
} else {
|
|
|
|
currentSection.append(text).append("\n");
|
|
|
|
}
|
|
|
|
} else if (element instanceof XWPFTable) {
|
|
|
|
String tableContent = extractTableContent((XWPFTable) element);
|
|
|
|
fullContent.append(tableContent).append("\n");
|
|
|
|
|
|
|
|
if (!isTableSection) {
|
|
|
|
if (!currentSection.isEmpty()) {
|
|
|
|
result.add(currentSection.toString().trim());
|
|
|
|
}
|
|
|
|
currentSection = new StringBuilder();
|
|
|
|
isTableSection = true;
|
|
|
|
}
|
|
|
|
currentSection.append(tableContent).append("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (text.isEmpty()) continue;
|
|
|
|
|
|
|
|
if (!currentSection.isEmpty()) {
|
|
|
|
result.add(currentSection.toString().trim());
|
|
|
|
}
|
|
|
|
// 当遇到空行或达到最大长度时分段
|
|
|
|
if (currentSegment.length() + text.length() > MAX_SEGMENT_LENGTH
|
|
|
|
&& currentSegment.length() > 0) {
|
|
|
|
segments.add(currentSegment.toString().trim());
|
|
|
|
currentSegment = new StringBuilder();
|
|
|
|
}
|
|
|
|
} else if (ext.equals("doc")) {
|
|
|
|
try (HWPFDocument doc = new HWPFDocument(new FileInputStream(filePath))) {
|
|
|
|
Range range = doc.getRange();
|
|
|
|
StringBuilder currentSection = new StringBuilder();
|
|
|
|
boolean isTableSection = false;
|
|
|
|
|
|
|
|
for (int i = 0; i < range.numParagraphs(); i++) {
|
|
|
|
Paragraph para = range.getParagraph(i);
|
|
|
|
String text = cleanText(para.text());
|
|
|
|
fullContent.append(text).append("\n");
|
|
|
|
|
|
|
|
if (para.isInTable()) {
|
|
|
|
if (!isTableSection) {
|
|
|
|
if (!currentSection.isEmpty()) {
|
|
|
|
result.add(currentSection.toString().trim());
|
|
|
|
}
|
|
|
|
currentSection = new StringBuilder();
|
|
|
|
isTableSection = true;
|
|
|
|
}
|
|
|
|
currentSection.append(text).append("\n");
|
|
|
|
} else {
|
|
|
|
if (isTableSection) {
|
|
|
|
result.add(currentSection.toString().trim());
|
|
|
|
currentSection = new StringBuilder();
|
|
|
|
isTableSection = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isHeading(para, range)) {
|
|
|
|
if (!currentSection.isEmpty()) {
|
|
|
|
result.add(currentSection.toString().trim());
|
|
|
|
}
|
|
|
|
currentSection = new StringBuilder(text).append("\n");
|
|
|
|
} else {
|
|
|
|
currentSection.append(text).append("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentSegment.append(text).append("\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentSegment.length() > 0) {
|
|
|
|
segments.add(currentSegment.toString().trim());
|
|
|
|
}
|
|
|
|
return segments;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!currentSection.isEmpty()) {
|
|
|
|
result.add(currentSection.toString().trim());
|
|
|
|
// 按标题分割文本
|
|
|
|
private List<String> splitByHeadings(String content) {
|
|
|
|
List<String> segments = new ArrayList<>();
|
|
|
|
StringBuilder currentSegment = new StringBuilder();
|
|
|
|
String[] lines = content.split("\\r?\\n");
|
|
|
|
|
|
|
|
for (String line : lines) {
|
|
|
|
// 检测标题行(以1-6个#开头,后面跟着空格)
|
|
|
|
if (line.trim().matches("^#{1,6}\\s+.*")) {
|
|
|
|
// 保存当前分段
|
|
|
|
if (!currentSegment.isEmpty()) {
|
|
|
|
segments.add(currentSegment.toString().trim());
|
|
|
|
currentSegment = new StringBuilder();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentSegment.append(line).append("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fullContent.length() < 1000) {
|
|
|
|
return Collections.singletonList(displayFileName + "\n" + fullContent.toString().trim());
|
|
|
|
// 添加最后一个分段
|
|
|
|
if (!currentSegment.isEmpty()) {
|
|
|
|
segments.add(currentSegment.toString().trim());
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
return segments;
|
|
|
|
}
|
|
|
|
|
|
|
|
private String extractTableContent(XWPFTable table) {
|
|
|
|
StringBuilder tableContent = new StringBuilder("\n"); // 表格前加换行
|
|
|
|
StringBuilder tableContent = new StringBuilder();
|
|
|
|
table.getRows().forEach(row -> {
|
|
|
|
StringBuilder rowContent = new StringBuilder("| ");
|
|
|
|
row.getTableCells().forEach(cell -> {
|
|
|
|
// 处理单元格内容中的多个换行
|
|
|
|
String cellText = cleanText(cell.getText()).replaceAll("(\r?\n){2,}", "\n");
|
|
|
|
tableContent.append("| ").append(cellText).append(" ");
|
|
|
|
String cellText = cell.getText().replaceAll("(\r?\n){2,}", " ");
|
|
|
|
rowContent.append(cellText).append(" | ");
|
|
|
|
});
|
|
|
|
tableContent.append("|\n");
|
|
|
|
tableContent.append(rowContent.toString().trim()).append("\n");
|
|
|
|
});
|
|
|
|
return tableContent.toString();
|
|
|
|
}
|
|
|
|
public List<String> splitWordDocument(String filePath) throws Exception {
|
|
|
|
List<String> result = new ArrayList<>();
|
|
|
|
String ext = FilenameUtils.getExtension(filePath).toLowerCase();
|
|
|
|
|
|
|
|
if (!ext.equals("docx")) return result;
|
|
|
|
|
|
|
|
try (XWPFDocument doc = new XWPFDocument(new FileInputStream(filePath))) {
|
|
|
|
StringBuilder currentContent = new StringBuilder();
|
|
|
|
List<String> titlePath = new ArrayList<>();
|
|
|
|
String lastOutput = null;
|
|
|
|
|
|
|
|
for (IBodyElement element : doc.getBodyElements()) {
|
|
|
|
if (element instanceof XWPFParagraph) {
|
|
|
|
XWPFParagraph para = (XWPFParagraph) element;
|
|
|
|
String text = para.getText().trim();
|
|
|
|
if (text.isEmpty()) continue;
|
|
|
|
|
|
|
|
int headingLevel = getHeadingLevel(para);
|
|
|
|
if (headingLevel > 0) {
|
|
|
|
// 存在之前内容,保存上一个段
|
|
|
|
if (currentContent.length() > 0 && !titlePath.isEmpty()) {
|
|
|
|
String fullBlock = String.join("", titlePath) + "\n" + currentContent.toString().trim();
|
|
|
|
result.add(fullBlock);
|
|
|
|
currentContent.setLength(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void saveSegmentsToDatabase(List<String> segments, String originalFileName, String storedFileName, String knowledgeId) {
|
|
|
|
if (segments.isEmpty()) {
|
|
|
|
return;
|
|
|
|
// 更新标题路径
|
|
|
|
while (titlePath.size() >= headingLevel) {
|
|
|
|
titlePath.remove(titlePath.size() - 1);
|
|
|
|
}
|
|
|
|
titlePath.add(text);
|
|
|
|
} else {
|
|
|
|
currentContent.append(text).append("\n");
|
|
|
|
}
|
|
|
|
} else if (element instanceof XWPFTable) {
|
|
|
|
String tableText = extractTableContent((XWPFTable) element);
|
|
|
|
currentContent.append(tableText).append("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 最后一段
|
|
|
|
if (currentContent.length() > 0 && !titlePath.isEmpty()) {
|
|
|
|
String fullBlock = String.join("", titlePath) + "\n" + currentContent.toString().trim();
|
|
|
|
result.add(fullBlock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 获取标题等级
|
|
|
|
private int getHeadingLevel(XWPFParagraph para) {
|
|
|
|
String style = para.getStyle();
|
|
|
|
if (style != null && style.matches("Heading\\d|标题\\d|\\d")) {
|
|
|
|
return Integer.parseInt(style.replaceAll("[^\\d]", ""));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 从存储文件名中提取UUID部分作为docId
|
|
|
|
String docId = storedFileName.substring(
|
|
|
|
storedFileName.lastIndexOf('_') + 1,
|
|
|
|
storedFileName.lastIndexOf('.')
|
|
|
|
);
|
|
|
|
if (para.getRuns().size() > 0) {
|
|
|
|
XWPFRun run = para.getRuns().get(0);
|
|
|
|
if (run.isBold() || (run.getFontSize() > 12 && run.getFontSize() != -1)) {
|
|
|
|
return 2; // 可能是二级标题
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 获取无UUID和扩展名的文件名用于显示
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
private void saveSegmentsToDatabase(List<String> segments, String originalFileName, String storedFileName, String knowledgeId) {
|
|
|
|
if (segments.isEmpty()) return;
|
|
|
|
|
|
|
|
String docId = storedFileName.substring(storedFileName.lastIndexOf('_') + 1, storedFileName.lastIndexOf('.'));
|
|
|
|
String displayFileName = removeUuidSuffix(originalFileName);
|
|
|
|
displayFileName = FilenameUtils.removeExtension(displayFileName);
|
|
|
|
|
|
|
|
// 判断是否是单一段落
|
|
|
|
boolean isSingleSegment = segments.size() == 1;
|
|
|
|
|
|
|
|
for (String segment : segments) {
|
|
|
|
if (segment.trim().isEmpty()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (segment.trim().isEmpty()) continue;
|
|
|
|
|
|
|
|
QuestionEmbedding record = new QuestionEmbedding();
|
|
|
|
record.setId(UUID.randomUUID().toString());
|
|
|
|
String[] parts = segment.split("\\r?\\n", 2);
|
|
|
|
if (parts.length < 2) continue;
|
|
|
|
|
|
|
|
if (isSingleSegment) {
|
|
|
|
record.setQuestion(displayFileName);
|
|
|
|
record.setAnswer(segment.trim());
|
|
|
|
} else {
|
|
|
|
String firstLine = segment.lines().findFirst().orElse("");
|
|
|
|
record.setQuestion(displayFileName + ": " + cleanText(firstLine));
|
|
|
|
record.setAnswer(segment.trim());
|
|
|
|
}
|
|
|
|
String titleLine = parts[0].trim(); // 如 掌静脉设备: 产品特点
|
|
|
|
String content = parts[1].trim();
|
|
|
|
|
|
|
|
// 构造问题:文件名: 标题层级路径
|
|
|
|
String question = displayFileName + titleLine;
|
|
|
|
|
|
|
|
QuestionEmbedding record = new QuestionEmbedding();
|
|
|
|
record.setId(UUID.randomUUID().toString());
|
|
|
|
record.setQuestion(question);
|
|
|
|
record.setAnswer(titleLine + "\n" + content);
|
|
|
|
record.setText("");
|
|
|
|
|
|
|
|
// 构建metadata JSON对象
|
|
|
|
Map<String, String> metadata = new LinkedHashMap<>(); // 使用LinkedHashMap保持字段顺序
|
|
|
|
Map<String, String> metadata = new LinkedHashMap<>();
|
|
|
|
metadata.put("docId", docId);
|
|
|
|
metadata.put("docName", originalFileName); // 上传前的原始文件名
|
|
|
|
metadata.put("storedFileName", storedFileName); // 上传后的带UUID的文件名
|
|
|
|
metadata.put("docName", originalFileName);
|
|
|
|
metadata.put("storedFileName", storedFileName);
|
|
|
|
metadata.put("knowledgeId", knowledgeId);
|
|
|
|
|
|
|
|
// 使用ObjectMapper转换为JSON字符串
|
|
|
|
try {
|
|
|
|
record.setMetadata(new ObjectMapper().writeValueAsString(metadata));
|
|
|
|
} catch (JsonProcessingException e) {
|
|
|
|
log.error("生成metadata JSON失败", e);
|
|
|
|
// 使用默认值
|
|
|
|
record.setMetadata(String.format(
|
|
|
|
"{\"docId\":\"%s\",\"docName\":\"%s\",\"storedFileName\":\"%s\",\"knowledgeId\":\"%s\"}",
|
|
|
|
docId, originalFileName, storedFileName, knowledgeId
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
Response<Embedding> embeddingResponse = aiModelUtils.getEmbedding("1925730210204721154", record.getQuestion());
|
|
|
|
log.info("保存分段: title={}, content_length={}", question, segment.length());
|
|
|
|
|
|
|
|
Response<Embedding> embeddingResponse = aiModelUtils.getEmbedding(embedId, record.getQuestion());
|
|
|
|
record.setEmbedding(embeddingResponse.content().vector());
|
|
|
|
record.setKnowledgeId(knowledgeId);
|
|
|
|
questionEmbeddingMapper.insert(record);
|
...
|
...
|
|