ollama run deepseek-v2:16b
ollama pull bge-m3
1、离线听写效果的大幅度提升。50M+ 1.3G(每次初始化都会很慢)---优化到首次初始化+使用0延迟响应。
2、文档问答历史问题处理与优化,文档问答离线策略讨论与参数暴露。
3、离线大模型答复中断处理策略,离线大模型的二次设置与修正。
4、打断模型答复与合成的实现策略。
5、服务的对外提供与生成安装包。
package com.day.util;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class ReadFiles {
public static List<String> readDirectoryFiles(String directoryPath) throws IOException {
List<String> resultList = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(directoryPath))) {
for (Path path : stream) {
if (Files.isRegularFile(path)) {
String fileName = path.getFileName().toString();
String fileContent = "";
try {
if (fileName.toLowerCase().endsWith(".txt")) {
fileContent = readTextFile(path);
} else if (fileName.toLowerCase().endsWith(".docx")) {
fileContent = readDocxFile(path);
} else if (fileName.toLowerCase().endsWith(".doc")) {
fileContent = readDocFile(path);
} else if (fileName.toLowerCase().endsWith(".pdf")) {
fileContent = readPdfFile(path);
}
if (!fileContent.isEmpty()) {
resultList.add("File: " + fileName + "\nContent:\n" + fileContent + "\n");
}
} catch (Exception e) {
System.err.println("Error reading file: " + fileName);
e.printStackTrace();
}
}
}
}
return resultList;
}
private static String readTextFile(Path path) throws IOException {
return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
}
private static String readDocxFile(Path path) throws IOException {
try (InputStream is = Files.newInputStream(path); XWPFDocument doc = new XWPFDocument(is); XWPFWordExtractor extractor = new XWPFWordExtractor(doc)) {
return extractor.getText();
}
}
private static String readDocFile(Path path) throws IOException {
try (InputStream is = Files.newInputStream(path); HWPFDocument doc = new HWPFDocument(is); WordExtractor extractor = new WordExtractor(doc)) {
return extractor.getText();
}
}
private static String readPdfFile(Path path) throws IOException {
try (PDDocument document = PDDocument.load(path.toFile())) {
PDFTextStripper stripper = new PDFTextStripper();
return stripper.getText(document);
}
}
/* public static void main(String[] args) {
try {
List<String> contents = readDirectoryFiles("src/main/resources/");
for (String content : contents) {
System.out.println(content);
System.out.println("-----------------------------");
}
} catch (IOException e) {
e.printStackTrace();
}
}*/
public static List<String> readFilesWork(String dir) {
try {
return readDirectoryFiles(dir);
} catch (IOException e) {
e.printStackTrace();
}
return new ArrayList<>();
}
}
package com.day.util;
import java.util.ArrayList;
import java.util.List;
public class SubString {
public static List<String> splitBySentenceWithinLimit(String input, Integer maxNumber) {
List<String> result = new ArrayList<>();
if (input == null || input.isEmpty()) {
return result;
}
int currentPosition = 0;
int totalLength = input.length();
while (currentPosition < totalLength) {
int endPosition = Math.min(currentPosition + maxNumber, totalLength);
// 截取当前最大可能区间
String currentChunk = input.substring(currentPosition, endPosition);
// 查找最后一个句号的位置
int lastDotIndex = currentChunk.lastIndexOf('。');
if (lastDotIndex != -1) { // 找到句号的情况
int actualEnd = currentPosition + lastDotIndex + 1;
result.add(input.substring(currentPosition, actualEnd));
currentPosition = actualEnd;
} else { // 没有句号的情况
result.add(currentChunk);
currentPosition = endPosition;
}
}
return result;
}
/* public static void main(String[] args) {
// 测试示例
String testStr = "作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。作者:小飞飞,撰写于6月31日。\n" + "想当年,他所带领的军队以锐不可挡之势,横扫大江南北,可以说是在父兄基业上既往开来,成就了一番伟业。原本偏安一隅的小国,从他的手中变成了十三个州,国人对这位领袖的敬意由然而生。威望的增加、权利的扩张丝毫没有改变他原有的样样子,他迈步走进岳楼,回忆起在湖北省张家界市的一段往事。那是一个薄雾蒙蒙的清晨,在急促行军途中他与一位素未谋面的人相逢,虽然之后并没有太多故事,却至今难以忘却,正当他的思绪陷入过往,忽然一阵震天的马蹄声夹杂着士兵的喧闹传来,报:“敌人来袭,我方战线危机,望将军火速驰援”。由于刚刚陷入过往的原因,他稍微愣了愣神,咆哮道:“大军听令,即刻出发”!军令如山。成群的士兵迅速从营房中跑出在校场上整齐队列,方阵如虹、战马昂首、刀枪如林、战旗迎风飘扬,将士身上的盔甲在阳光照射下,闪耀着金属的光泽。看着这支曾跟着他南征北战的队伍,他默默翻身登上战马,走在队伍最前面。营房外的道路两旁站满了欢送的百姓,大家希望将军能带领着军队,再次创造奇迹。\u200B"; // 构造700字符的字符串
List<String> splitResult = splitBySentenceWithinLimit(testStr, 500);
System.out.println("分片数量:" + splitResult.size());
for (int i = 0; i < splitResult.size(); i++) {
System.out.printf("分片%d(长度%d):%s%n", i + 1, splitResult.get(i).length(), splitResult.get(i));
}
}*/
public static List<String> subStringWork(String inputContent, Integer maxNumber) {
// 测试示例
List<String> splitResult = splitBySentenceWithinLimit(inputContent, maxNumber);
System.out.println("分片数量:" + splitResult.size());
/*for (int i = 0; i < splitResult.size(); i++) {
System.out.printf("分片%d(长度%d):%s%n", i + 1, splitResult.get(i).length(), splitResult.get(i));
}*/
return splitResult;
}
}
class Document {
private String id;
private String content;
private List<Double> embedding;
public Document(String id, String content) {
this.id = id;
this.content = content;
}
// Getters and setters
public String getId() {
return id;
}
public String getContent() {
return content;
}
public List<Double> getEmbedding() {
return embedding;
}
public void setEmbedding(List<Double> embedding) {
this.embedding = embedding;
}
}
interface EmbeddingFunction {
List<Double> generateEmbedding(String text) throws Exception;
}
class OllamaEmbeddingFunction implements EmbeddingFunction {
private final String model;
private final String url;
private final OkHttpClient client = new OkHttpClient();
private final ObjectMapper mapper = new ObjectMapper();
public OllamaEmbeddingFunction(String model, String url) {
this.model = model;
this.url = url;
}
@Override
public List<Double> generateEmbedding(String text) throws Exception {
// 构建请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", model);
requestBody.put("prompt", text);
RequestBody body = RequestBody.create(mapper.writeValueAsString(requestBody), MediaType.parse("application/json"));
Request request = new Request.Builder().url(url).post(body).build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败: " + response);
}
// 解析响应
JsonNode root = mapper.readTree(response.body().bytes());
JsonNode embeddingNode = root.get("embedding");
List<Double> embedding = new ArrayList<>();
for (JsonNode node : embeddingNode) {
embedding.add(node.asDouble());
}
return embedding;
}
}
}
class Collection {
private final String name;
private final EmbeddingFunction embeddingFunction;
private final List<Document> documentList = new ArrayList<>();
public Collection(String name, EmbeddingFunction embeddingFunction) {
this.name = name;
this.embeddingFunction = embeddingFunction;
}
public void addDocuments(List<Document> documents) {
int numCores = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(numCores);
List<Future<?>> futures = new ArrayList<>();
for (Document doc : documents) {
futures.add(executor.submit(() -> {
try {
List<Double> embedding = embeddingFunction.generateEmbedding(doc.getContent());
synchronized (this.documentList) {
doc.setEmbedding(embedding);
this.documentList.add(doc);
}
} catch (Exception e) {
e.printStackTrace();
}
}));
}
// 等待所有任务完成
for (Future<?> future : futures) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
executor.shutdown();
}
public Document getById(String id) {
for (Document doc : documentList) {
if (doc.getId().equals(id)) {
return doc;
}
}
return null;
}
public List<Result> query(String queryText, int topK) {
try {
List<Double> queryEmbedding = embeddingFunction.generateEmbedding(queryText);
List<Result> results = new ArrayList<>();
for (Document doc : documentList) {
double similarity = cosineSimilarity(queryEmbedding, doc.getEmbedding());
results.add(new Result(doc, similarity));
}
results.sort((a, b) -> Double.compare(b.getSimilarity(), a.getSimilarity()));
return results.subList(0, Math.min(topK, results.size()));
} catch (Exception e) {
e.printStackTrace();
return Collections.emptyList();
}
}
private double cosineSimilarity(List<Double> vec1, List<Double> vec2) {
double dotProduct = 0.0;
double norm1 = 0.0;
double norm2 = 0.0;
for (int i = 0; i < vec1.size(); i++) {
dotProduct += vec1.get(i) * vec2.get(i);
norm1 += Math.pow(vec1.get(i), 2);
norm2 += Math.pow(vec2.get(i), 2);
}
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
}
}
class Result {
private final Document document;
private final double similarity;
public Result(Document document, double similarity) {
this.document = document;
this.similarity = similarity;
}
public Document getDocument() {
return document;
}
public double getSimilarity() {
return similarity;
}
}