最近在做一个结合大模型实现智慧办公的项目,其中有一个功能需求是文本纠错,需要返回纠错信息:纠错前文本、纠错后文本、错误片段位置区间。纠错能力可以通过模型实现,模型输出格式可以通过提示词去优化,但是位置信息模型返回几乎都是错的,因此需要结合代码去实现。
实现类似于百度AI开发平台的文本纠错-高级版API的功能
https://ai.baidu.com/ai-doc/NLP/Flai4y8wc
纠错返回格式如下:
{
"original_text":"原始文本",
"correct_text":"纠错后的文本",
"content_len":"文本长度",
"details":[
{
"type":"纠错类型",
"error_num":"纠错数量",
"erro_list":[
{
"original_part":"原始部分",
"correct_part":"替换部分",
"begin_index":"开始位置",
"end_index":"结束位置"
},
{
"original_part":"原始部分",
"correct_part":"替换部分",
"begin_index":"开始位置",
"end_index":"结束位置"
},
...
]
}
]
}
一、通过提示词使模型返回的纠错信息按照固定格式返回
1、提示词模版配置
这里我自己简单写些提示词,可以自己去优化提示词,使模型返回固定格式,看哪种提示词返回效果更好(可以学习参考一些提示词编写技巧)。
尽量细化纠错类型,这里我写了四类纠错类型:错别字词纠错、标点符号纠错、语法纠错、专名纠错
/**
* 提示词模版配置
*/
public class PromptTemplateConfig {
/**
* 错别字纠错提示词 内容替换符:#{TEXT}
*/
public static final String CHECK_ERROR_WORD = "" +
"任务类型: 字词纠错\n" +
"任务描述:请检查文本中的错别字词错误,且仅检查错别字词错误,请严格按照格式要求返回纠错信息,有错别字词返回格式中必须包含错误字词修改及纠错后文本信息,注意“[]”和“()”符号。没有明显字词错误的文本不进行纠错。\n" +
"输入文本:在这里输入要检查错别字的文本。 \n" +
"有错别字词返回格式:\n" +
" 错误字词修改:[]。\n" +
" 纠错后文本:()。\n" +
"无错别字词返回格式:\n" +
" [未发现错别字词]。\n" +
"\n" +
"示例: \n" +
"输入文本:他有很多好习贯,比如早睡早起,从不吃圾食。 \n" +
"返回格式: \n" +
" 错误字词修改:[习贯=>习惯|圾食=>垃圾食品]。\n" +
" 纠错后文本:(他有很多好习惯,比如早睡早起,从不吃垃圾食品。)。\n" +
"\n" +
"现在,请按照上述要求对以下文本进行错别字纠错: \n" +
"--- \n" +
"\n" +
"输入文本: \n" +
"#{TEXT}\n" +
"--- \n" +
"请按照要求格式输出修正后的文本。";
/**
* 标点符号纠错提示词 内容替换符:#{TEXT}
*/
public static final String CHECK_ERROR_PUNCTUATION = "" +
"任务类型: 标点符号纠错\n" +
"任务描述:检查文本中标点符号错误,且仅检查标点符号错误,确保只在必要时更改标点符号,不要修改任何文字内容或添加/删除任何非标点符号的字符。请严格按照格式要求返回纠错信息,有错误标点符号返回格式中必须包含错误标点符号修改及纠错后文本信息,注意错误标点符号修改信息用“[]”,纠错后文本信息用“()”。没有明显标点符号错误的文本不进行纠错。\n" +
"\n" +
"输入文本:在这里输入要检查标点符号的文本。\n" +
"有错误标点符号返回格式:\n" +
" 错误标点符号修改:[]。\n" +
" 纠错后文本:()。\n" +
"无错误标点符号返回格式:\n" +
" [未发现错误标点符号]。\n" +
"\n" +
"示例: \n" +
"输入文本:他每天都坚持晨炼;身体非常好?\n" +
"返回格式: \n" +
" 错误标点符号修改:[;=>,|?=>!]。\n" +
" 纠错后文本:(他每天都坚持晨炼,身体非常好!)。\n" +
"\n" +
"现在,请按照上述要求对以下文本进行标点符号纠错: \n" +
"--- \n" +
"\n" +
"输入文本: \n" +
"#{TEXT}\n" +
"--- \n" +
"请按照要求格式输出修正后的文本。";
/**
* 检查语法错误提示词 内容替换符:#{TEXT}
*/
public static final String CHECK_ERROR_SYNTAX = "" +
"任务类型: 语法纠错\n" +
"任务描述:请检查文本中的语法错误,且仅检查语法错误,检查并修正给定文本中的语法错误。请确保文本中语法正确,请严格按照格式要求返回纠错信息,有错误语法返回格式中必须包含错误语句修改和纠错后文本。没有明显语法错误的文本不进行纠错。\n" +
"输入文本:在这里输入要检查语法的文本。\n" +
"有错误语法返回格式:\n" +
" 错误语句修改:[]。\n" +
" 纠错后文本:[]。\n" +
"无错误语法返回格式:\n" +
" [未发现语法错误]。\n" +
"有错误语法示例1: \n" +
"输入文本:I go to school yesterday.\n" +
"返回格式: \n" +
" 错误语句修改:[I go to school yesterday.=>I went to school yesterday.]。\n" +
" 纠错后文本:[I went to school yesterday.]。\n" +
"有错误语法示例2: \n" +
"输入文本:I didn't went to school yesterday, because I have a bad cold.\n" +
"返回格式: \n" +
" 错误语句修改:[I didn't went to school yesterday,=>I didn't go to school yesterday,]。\n" +
" 纠错后文本:[I didn't go to school yesterday, because I had a bad cold.]。\n" +
"有错误语法示例3: \n" +
"输入文本:通过老师的耐心指导,使我明白了许多道理。\n" +
"返回格式: \n" +
" 错误语句修改:[通过老师的耐心指导,使我明白了许多道理。=>老师的耐心指导,让我明白了许多道理。]。\n" +
" 纠错后文本:[老师的耐心指导,让我明白了许多道理。]。\n" +
"无错误语法示例: \n" +
"输入文本:I didn't go to school yesterday, because I had a bad cold.\n" +
"返回格式: \n" +
" [未发现语法错误]。\n" +
"现在,请按照上述要求对以下文本进行语法纠错: \n" +
"--- \n" +
"\n" +
"输入文本: \n" +
"#{TEXT}\n" +
"--- \n" +
"请按照要求格式输出修正后的文本。";
/**
* 检查专有名词错误提示词 内容替换符:#{TEXT}
*/
public static final String CHECK_ERROR_PROPER_NOUN = "" +
"任务类型: 专有名词纠错\n" +
"任务描述:检查文本中的专有名词错误,且仅检查专有名词错误。包含专有名词、固定短语、古诗词、谚语等内容的纠错。\n" +
" \n" +
"输入说明: \n" +
"- 文本: 输入待纠错的原始文本,包含可能错误的专有名词。 \n" +
" \n" +
"输出要求: \n" +
"结构化输出纠错结果,有专有名词错误返回格式中必须包含错误专有名词修改和纠错后文本。没有明显专有名词错误的文本不进行纠错。\n" +
"格式定义:\n" +
" 错误专有名词修改:[]。\n" +
" 纠错后文本:()。\n" +
"示例:\n" +
"输入文本:他是北境大学的学生,喜欢使用appel的产品。\n" +
"输出: \n" +
" 错误专有名词修改:[北境大学=>北京大学|appel=>Apple]。\n" +
" 纠错后文本:(他是北京大学的学生,喜欢使用Apple的产品。)。\n" +
"\n" +
"现在,请按照上述要求对以下文本进行语法纠错: \n" +
"--- \n" +
"\n" +
"输入文本: \n" +
"#{TEXT}\n" +
"--- \n" +
"请按照要求格式输出修正后的文本。";
public static final String CHECK_END_PROMPT = "" +
"请结合上述字词纠错、标点符号纠错、语法纠错、专有名词纠错,将文本进行纠错。\n" +
"返回格式为:\n" +
" 纠错后文本:()。\n" +
"\n" +
"--- \n" +
"\n" +
"输入文本: \n" +
"#{TEXT}\n" +
"--- \n" +
"请按照要求格式输出修正后的文本。";
}
2、对模型返回的纠错信息处理
工具类TextProcessUtils
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TextProcessUtils {
public static void main(String[] args) {
// String text1 = "这是第一段文本。它包含一些内容。";
// String text2 = "这是第二段文本。它包含不同的内容。";
// List<Integer> differences = findDifferences(text1, text2);
// System.out.println("Differences at positions: " + differences);
// String text1 = "这是第一端文本。它包含一些内容。";
// String text2 = "这是第二段文本,它包含不同的内容。";
// compareStrings2(text1,text2);
String input = "错误词:[习贯,圾食]。修改词:[习惯,垃圾食品]。纠错后文本:(他有很多好习惯,比如早睡早起,从不吃垃圾食品。)。";
// // 正则表达式匹配 "错误词" 后的 []
// Pattern errorPattern = Pattern.compile("错误词:\\[(.*?)\\]。");
// Matcher errorMatcher = errorPattern.matcher(input);
// if (errorMatcher.find()) {
// System.out.println("错误词:" + errorMatcher.group(1));
// }
//
// // 正则表达式匹配 "修改词" 后的 []
// Pattern correctPattern = Pattern.compile("修改词:\\[(.*?)\\]。");
// Matcher correctMatcher = correctPattern.matcher(input);
// if (correctMatcher.find()) {
// System.out.println("修改词:" + correctMatcher.group(1));
// }
//
// // 正则表达式匹配 "纠错后文本" 后的 ()
// Pattern fixedPattern = Pattern.compile("纠错后文本:\\((.*?)\\)。");
// Matcher fixedMatcher = fixedPattern.matcher(input);
// if (fixedMatcher.find()) {
// System.out.println("纠错后文本:" + fixedMatcher.group(1));
// }
}
public static List<Integer> findDifferences(String text1, String text2) {
List<Integer> diffPositions = new ArrayList<>();
int minLength = Math.min(text1.length(), text2.length());
for (int i = 0; i < minLength; i++) {
if (text1.charAt(i) != text2.charAt(i)) {
diffPositions.add(i+1);
}
}
// If text1 is longer than text2, add remaining positions
for (int i = minLength; i < text1.length(); i++) {
diffPositions.add(i);
}
return diffPositions;
}
/**
* 查询子字符串在字符串中的位置
* @param text
* @param search
*/
public static List<Map<String,Integer>> findSubstringPositions(String text, String search) {
//文本位置
int index = text.indexOf(search);
//查找字符长度
int length = search.length();
//字符可能存在多个 返回多个位置信息
List<Map<String,Integer>> indexMsgList = new ArrayList<>();
while (index >= 0) {
Map<String,Integer> indexMsg = new LinkedHashMap<>();
int endIndex = index + length - 1;
int text_start_index = index+1;//文字开始位置
int text_end_index = endIndex+1;//文字结束位置
// System.out.println("开始位置:" + text_start_index + " 结束位置:" + text_end_index);
indexMsg.put("start_index",text_start_index);
indexMsg.put("end_index",text_end_index);
indexMsgList.add(indexMsg);
//继续往后找
index = text.indexOf(search, index + 1);
}
return indexMsgList;
}
/**
* 比较两个字符串并输出差异位置。
*
* @param str1 第一个字符串
* @param str2 第二个字符串
*/
public static void compareStrings(String str1, String str2) {
List<int[]> differences = new ArrayList<>();
int minLength = Math.min(str1.length(), str2.length());
for (int i = 0; i < minLength; i++) {
if (str1.charAt(i) != str2.charAt(i)) {
differences.add(new int[]{i, str1.charAt(i), str2.charAt(i)});
}
}
// 检查是否有额外的字符
if (str1.length() > minLength) {
for (int i = minLength; i < str1.length(); i++) {
differences.add(new int[]{i, str1.charAt(i), -1});
}
} else if (str2.length() > minLength) {
for (int i = minLength; i < str2.length(); i++) {
differences.add(new int[]{i, -1, str2.charAt(i)});
}
}
// 输出差异位置
for (int[] diff : differences) {
int position = diff[0];
char charInStr1 = diff[1] == -1 ? '\0' : (char) diff[1];
char charInStr2 = diff[2] == -1 ? '\0' : (char) diff[2];
System.out.printf("差异位置:%d, 字符1: '%c', 字符2: '%c'%n", position, charInStr1, charInStr2);
}
}
/**
* 比较两个字符串并输出差异位置。
* @param str1
* @param str2
*/
public static void compareStrings2(String str1, String str2) {
List<String> differences = new ArrayList<>();
int minLength = Math.min(str1.length(), str2.length());
int start = -1; // 记录差异区间的开始位置
for (int i = 0; i < minLength; i++) {
if (str1.charAt(i) != str2.charAt(i)) {
// 如果当前字符不相等
if (start == -1) {
// 如果尚未记录差异区间的开始位置
start = i;
}
} else {
// 如果当前字符相等
if (start != -1) {
// 如果之前存在差异区间
int end = i - 1;
if (start == end) {
// 单个字符的差异
differences.add(String.format("差异位置:%d, 字符1: '%c', 字符2: '%c'",
start, str1.charAt(start), str2.charAt(start)));
} else {
// 连续的差异区间
differences.add(String.format("差异区间:%d-%d", start, end));
}
start = -1; // 重置开始位置
}
}
}
// 检查是否最后一个字符也是差异
if (start != -1 && minLength < str1.length()) {
differences.add(String.format("差异区间:%d-%d", start, str1.length() - 1));
} else if (start != -1 && minLength < str2.length()) {
differences.add(String.format("差异区间:%d-%d", start, str2.length() - 1));
}
// 输出差异位置或区间
for (String diff : differences) {
System.out.println(diff);
}
}
}
字词纠错(位置信息通过字符串在原文中位置查找处理):
if (answer.contains("错误字词修改:")) {
//处理结果 解析结果处理为字词纠错返回格式 位置信息模型返回正确率很低,自己对比处理返回位置
Pattern errorPattern = Pattern.compile("错误字词修改:\\[(.*?)\\]。");
Matcher errorMatcher = errorPattern.matcher(answer);
if (errorMatcher.find()) {
//多个逗号分隔的错误词 格式为 一郭两制=>一国两制,退进=>推进
String errorWords = errorMatcher.group(1).trim();
String[] errorWordArr = errorWords.split("\\|");
for (String errorWord : errorWordArr) {
//如果不包含=>,则跳过
if (!errorWord.contains("=>")){
continue;
}
//如果是标点符号 且仅标点符号,则跳过
if (PunctuationComparisonUtils.containsPunctuation(errorWord) && errorWord.length()==4){
continue;
}
//原始部分
String original_part = errorWord.split("=>")[0];
//修改部分
String correct_part = errorWord.split("=>")[1];
//查询原始部分在原文中的位置信息
List<Map<String, Integer>> indexList = TextProcessUtils.findSubstringPositions(original_text, original_part);
for (Map<String, Integer> indexMap : indexList
) {
int begin_index = indexMap.get("start_index");
int end_index = indexMap.get("end_index");
//字词错误详情 原字词、修改字词、位置信息
Map<String, Object> errorMsgMap = new LinkedHashMap<>();
errorMsgMap.put("original_part", original_part);
errorMsgMap.put("correct_part", correct_part);
errorMsgMap.put("begin_index", begin_index);
errorMsgMap.put("end_index", end_index);
error_list.add(errorMsgMap);
}
}
}
} else {
//没有错别字词。
}
标点符号纠错(位置信息通过java-diff-utils实现):
工具类PunctuationComparisonUtils
POM:
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
<version>4.12</version>
</dependency>
import com.example.qianfan.bean.PunctuationDifference;
import com.github.difflib.DiffUtils;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.DeltaType;
import com.github.difflib.patch.Patch;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class PunctuationComparisonUtils {
public static void main(String[] args) {
String input = "Hello你好";
boolean containsPunctuation = containsPunctuation(input);
System.out.println("字符串包含中英文标点符号: " + containsPunctuation);
//
// // 查找并输出标点符号差异
// List<PunctuationDifference> differences = findPunctuationDifferences(originalText, correctedText);
// for (PunctuationDifference diff : differences) {
//
// //字符位置下标+1
// int index = diff.getPosition() + 1;
// System.out.println("原始标点: " + diff.getOriginal());
// System.out.println("改正标点: " + diff.getCorrected());
// System.out.println("原始标点位置: " + index);
// System.out.println();
// }
}
public static List<PunctuationDifference> findPunctuationDifferences(String original, String corrected) {
// 使用 Unicode 范围定义常见的中英文标点符号
// String punctuationPattern = "[,。!?、;:“”‘’()《》【】]"; // 中文标点
// String englishPunctuationPattern = "[.,?!;:\"'(){}]"; // 英文标点
// String combinedPattern = punctuationPattern + englishPunctuationPattern;
String punctuationRegex = "^[,.;!?:\"'(){},。;!?、:“”‘’()《》【】]+$";
// 将字符串拆分为字符列表
List<String> originalLines = List.of(original.split(""));
List<String> correctedLines = List.of(corrected.split(""));
// 计算差异
Patch<String> patch = DiffUtils.diff(originalLines, correctedLines);
List<PunctuationDifference> differences = new ArrayList<>();
// 遍历差异
for (AbstractDelta<String> delta : patch.getDeltas()) {
if (delta.getType() == DeltaType.CHANGE) {
List<String> originalSymbols = delta.getSource().getLines();
List<String> correctedSymbols = delta.getTarget().getLines();
// 假设原文和纠正文的长度可能不同
for (int i = 0; i < Math.max(originalSymbols.size(), correctedSymbols.size()); i++) {
String originalSymbol = i < originalSymbols.size() ? originalSymbols.get(i) : "";
String correctedSymbol = i < correctedSymbols.size() ? correctedSymbols.get(i) : "";
// 检查是否为标点符号并进行比较
if (originalSymbol.matches(punctuationRegex) && !originalSymbol.equals(correctedSymbol)) {
int position = delta.getSource().getPosition() + i; // 计算位置
differences.add(new PunctuationDifference(originalSymbol, correctedSymbol, position));
}
}
}
}
return differences;
}
public static boolean containsPunctuation(String str) {
// 正则表达式匹配中英文标点符号
String regex = "[\\p{Punct}\\u3000-\\u303F\\uFF00-\\uFFEF]";
Pattern pattern = Pattern.compile(regex);
return pattern.matcher(str).find();
}
}
if (answer.contains("纠错后文本:")) {
Pattern fixedPattern = Pattern.compile("纠错后文本:\\((.*?)\\)。");
Matcher fixedMatcher = fixedPattern.matcher(answer);
if (fixedMatcher.find()) {
//纠错后的文本
String fixedText = fixedMatcher.group(1).trim();
//使用工具对比标点符号 实现原始标点符号 修改后标点符号 原始标点符号位置
List<PunctuationDifference> punctuationDifferences = PunctuationComparisonUtils.findPunctuationDifferences(original_text, fixedText);
for (PunctuationDifference diff : punctuationDifferences) {
//字符位置下标+1
int index = diff.getPosition() + 1;
Map<String, Object> errorMsgMap = new LinkedHashMap<>();
errorMsgMap.put("original_part", diff.getOriginal());
errorMsgMap.put("correct_part", diff.getCorrected());
errorMsgMap.put("begin_index", index);
errorMsgMap.put("end_index", index);
error_list.add(errorMsgMap);
}
}
}
语法纠错(位置信息通过字符串在原文中位置查找处理):
if (answer.contains("错误语法修改:")) {
//处理结果 解析结果处理为语法纠错返回格式 位置信息模型返回正确率很低,自己对比处理返回位置
Pattern errorPattern = Pattern.compile("错误语法修改:\\[(.*?)\\]。");
Matcher errorMatcher = errorPattern.matcher(answer);
if (errorMatcher.find()) {
//多个逗号分隔的错误词 格式为 一郭两制=>一国两制,退进=>推进
String errorSyntaxs = errorMatcher.group(1).trim();
String[] errorSyntaxArr = errorSyntaxs.split("\\|");
for (String errorSyntax : errorSyntaxArr) {
if (!errorSyntax.contains("=>")){
continue;
}
//如果是标点符号 且仅标点符号,则跳过
if (PunctuationComparisonUtils.containsPunctuation(errorSyntax) && errorSyntax.length()==4){
continue;
}
//原始部分
String original_part = errorSyntax.split("=>")[0];
//修改部分
String correct_part = errorSyntax.split("=>")[1];
//查询原始部分在原文中的位置信息
List<Map<String, Integer>> indexList = TextProcessUtils.findSubstringPositions(original_text, original_part);
for (Map<String, Integer> indexMap : indexList
) {
int begin_index = indexMap.get("start_index");
int end_index = indexMap.get("end_index");
//语法错误详情 原语句、修改语句、位置信息
Map<String, Object> errorMsgMap = new LinkedHashMap<>();
errorMsgMap.put("original_part", original_part);
errorMsgMap.put("correct_part", correct_part);
errorMsgMap.put("begin_index", begin_index);
errorMsgMap.put("end_index", end_index);
error_list.add(errorMsgMap);
}
}
}
} else {
//没有错误语法。
}
专有名词纠错(位置信息通过字符串在原文中位置查找处理):
if (answer.contains("错误专有名词修改:")) {
//处理结果 解析结果处理为专有名词纠错返回格式 位置信息模型返回正确率很低,自己对比处理返回位置
Pattern errorPattern = Pattern.compile("错误专有名词修改:\\[(.*?)\\]。");
Matcher errorMatcher = errorPattern.matcher(answer);
if (errorMatcher.find()) {
//多个逗号分隔的错误词 格式为 一郭两制=>一国两制,退进=>推进
String errorProperNouns = errorMatcher.group(1).trim();
String[] errorProperNounArr = errorProperNouns.split("\\|");
for (String errorProperNoun : errorProperNounArr) {
if (!errorProperNoun.contains("=>")){
continue;
}
//如果是标点符号 且仅标点符号,则跳过
if (PunctuationComparisonUtils.containsPunctuation(errorProperNoun) && errorProperNoun.length()==4){
continue;
}
//原始部分
String original_part = errorProperNoun.split("=>")[0];
//修改部分
String correct_part = errorProperNoun.split("=>")[1];
//查询原始部分在原文中的位置信息
List<Map<String, Integer>> indexList = TextProcessUtils.findSubstringPositions(original_text, original_part);
for (Map<String, Integer> indexMap : indexList
) {
int begin_index = indexMap.get("start_index");
int end_index = indexMap.get("end_index");
//专有名词错误详情 原语句、修改语句、位置信息
Map<String,Object> errorMsgMap = new LinkedHashMap<>();
errorMsgMap.put("original_part",original_part);
errorMsgMap.put("correct_part",correct_part);
errorMsgMap.put("begin_index",begin_index);
errorMsgMap.put("end_index",end_index);
error_list.add(errorMsgMap);
}
}
}
} else {
//没有错误专有名词。
}
文本差异对比java-diff-utils:
文本差异对比开源项目GitHub地址:https://github.com/java-diff-utils/java-diff-utils
java-diff-utils 是一款高度灵活且功能丰富的Java库,用于执行文本间的比较操作,无论是计算差异、应用补丁、生成统一格式的diff文件,还是解析和美化显示这些差异,它都能轻松应对。这个项目源自Google Code归档中的一个分支,并在此基础上发展成为一个成熟的解决方案。通过提供一系列便捷的API接口,它填补了市场上对于易用且全面的差异比较库的需求缺口。
=============================================================
标题优化版本(提示词+全文分段纠错开启线程提升纠错效率):
当我们想实现全文纠错时,有些模型query内容有字符限制,我们需要分段截取处理进行纠错。
1、提示词模版配置
/**
* 提示词模版配置
*/
public class PromptTemplateConfig {
/**
* 错别字纠错提示词 内容替换符:#{TEXT}
*/
public static final String CHECK_ERROR_WORD = "" +
"【Role】\n" +
"你是一名字词纠错专家,专注于识别和纠正文本中的错别字词错误,包括检查错误的词语组合,并以简化的JSON格式返回纠错信息。\n" +
"\n" +
"【Goals】\n" +
"1. 检查并纠正给定文本中的错别字词错误,包括检查错误的词语组合。\n" +
"2. 以简化的JSON格式返回所有错误字词的详细信息,不包括位置信息。\n" +
"3. 确保返回的JSON数据易于机器解析和生成。\n" +
"\n" +
"【workflows】\n" +
"1. **文本审查**:审查输入文本,识别所有错别字词。\n" +
"2. **错误记录**:记录每个错别字词及其正确形式。\n" +
"3. **JSON格式化**:将错误信息和更正后的文本格式化为简化的JSON。\n" +
"4. **结果验证**:验证JSON格式的正确性和完整性。\n" +
"\n" +
"【Important】\n" +
"1. **准确性**:确保识别的错误字词和正确形式都是准确的。\n" +
"2. **格式规范**:按照JSON标准格式化返回数据。\n" +
"3. **数据完整性**:确保JSON中包含所有必要的信息。\n" +
"\n" +
"【Attention】\n" +
"1. **仅检查错误字词**:专注于错误字词的纠错,不涉及文本的其他方面。\n" +
"2. **易于解析**:确保JSON结构简单明了,易于解析。\n" +
"\n" +
"【Format】\n" +
"{\n" +
// "\"originalText\": \"输入的原始文本\",\n" +
// "\"correctedText\": \"更正后的文本\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \"错误字词\",\n" +
"\"correct\": \"正确字词\"\n" +
"},\n" +
"...\n" +
"]\n" +
"}\n" +
"\n" +
"【Examples】\n" +
"{\n" +
// "\"originalText\": \"为了改变员工经常食用圾食的习贯,公司成立了转向工作组来推广健康饮食。\",\n" +
// "\"correctedText\": \"为了改变员工经常食用垃圾食品的习惯,公司成立了专项工作组来推广健康饮食。\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \"圾食\",\n" +
"\"correct\": \"垃圾食品\"\n" +
"},\n" +
"{\n" +
"\"incorrect\": \"习贯\",\n" +
"\"correct\": \"习惯\"\n" +
"},\n" +
"{\n" +
"\"incorrect\": \"转向工作组\",\n" +
"\"correct\": \"专项工作组\"\n" +
"}\n" +
"]\n" +
"}\n" +
"\n" +
"【Notes】\n" +
"在执行字词纠错任务时,你需要严格遵循仅进行字词纠错的指令,无明显字词错误时errors数组应为空。请确保输出正确的json格式,不要在json中做多余描述,破坏json格式的完整性。\n" +
"\n" +
"现在,请按照上述要求对以下文本进行错别字词纠错:\n" +
"---\n" +
"#{TEXT}\n" +
"---\n" +
"请按照要求格式输出。\n";
/**
* 标点符号纠错提示词 内容替换符:#{TEXT}
*/
public static final String CHECK_ERROR_PUNCTUATION = "" +
"【Role】\n" +
"你是一名标点符号纠错专家,专注于识别和纠正文本中的标点符号错误,特别是中英文标点符号的使用,并以简化的JSON格式返回纠错信息。\n" +
"\n" +
"【Goals】\n" +
"1. 检查并纠正给定文本中的标点符号错误,特别注意中英文标点符号的正确使用。\n" +
"2. 仅在必要时更改标点符号,不修改任何文字内容或添加/删除任何非标点符号的字符。\n" +
"3. 以JSON格式返回所有标点符号错误的修改信息和纠错后的文本。\n" +
"\n" +
"【workflows】\n" +
"1. **文本审查**:仔细阅读输入文本,识别所有标点符号的使用错误。\n" +
"2. **错误识别**:确定文本中标点符号的错误使用,并决定必要的更改。\n" +
"3. **JSON格式化**:对于每个错误标点符号,记录错误标点符号修改,并生成纠错后的文本,格式化为JSON。\n" +
"4. **最终检查**:复查纠错结果,确保文本的标点符号使用正确,且未对非标点内容进行修改,验证JSON格式的正确性和完整性。\n" +
"\n" +
"【Important】\n" +
"1. **准确性**:确保识别的错误标点符号和修改都是准确的。\n" +
"2. **非侵入性**:不修改任何非标点符号内容,确保文本原意不被改变。\n" +
"3. **格式规范**:按照JSON标准格式化返回纠错信息。\n" +
"\n" +
"【Attention】\n" +
"1. **仅标点纠错**:专注于标点符号的纠错,不涉及文本的其他方面。\n" +
"2. **中英文标点**:特别注意中英文标点符号的区别,如中文逗号(,)与英文逗号(,)的不同使用场景。\n" +
"3. **清晰记录**:清晰记录每个错误标点符号的修改,以及修改后的文本。\n" +
"\n" +
"【Format】\n" +
"{\n" +
// "\"originalText\": \"输入的原始文本\",\n" +
"\"correctedText\": \"更正后的文本\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \"错误标点符号\",\n" +
"\"correct\": \"正确标点符号\"\n" +
"},\n" +
"...\n" +
"]\n" +
"}\n" +
"\n" +
"【Examples】\n" +
"{\n" +
// "\"originalText\": \"他每天都坚持晨炼;身体非常好?\",\n" +
"\"correctedText\": \"他每天都坚持晨炼,身体非常好!\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \";\",\n" +
"\"correct\": \",\"\n" +
"},\n" +
"{\n" +
"\"incorrect\": \"?\",\n" +
"\"correct\": \"!\"\n" +
"}\n" +
"]\n" +
"}\n" +
"\n" +
"【Notes】\n" +
"在执行标点符号纠错任务时,你需要严格遵循仅进行标点符号纠错的指令,无需考虑拼写错误,无明显标点符号错误时errors数组应为空。请确保输出正确的json格式,不要在json中做多余描述,破坏json格式的完整性。\n" +
"\n" +
"现在,请按照上述要求对以下文本进行标点符号纠错:\n" +
"---\n" +
"#{TEXT}\n" +
"---\n" +
"请按照要求格式输出。\n";
/**
* 检查语法错误提示词 内容替换符:#{TEXT}
*/
public static final String CHECK_ERROR_SYNTAX = "" +
"【Role】\n" +
"你是一名语法纠错专家,负责识别和纠正文本中的语法错误,并以简化的JSON格式返回纠错信息。\n" +
"\n" +
"【Goals】\n" +
"1. 检查并纠正给定文本中的语法错误。\n" +
"2. 确保文本在语法上正确无误,同时保持原意不变。\n" +
"3. 以JSON格式返回所有语法错误的修改信息和纠错后的文本。\n" +
"\n" +
"【workflows】\n" +
"1. **文本审查**:仔细阅读输入文本,识别所有语法错误。\n" +
"2. **错误识别**:确定文本中的语法错误,如主谓一致性、时态错误、连接词使用不当等。\n" +
"3. **上下文理解**:考虑上下文含义,确保纠正后的文本在语义上仍然连贯。\n" +
"4. **JSON格式化**:将识别的语法错误及其更正格式化为JSON。\n" +
"5. **结果验证**:复查纠错结果,确保文本的语法正确,且未改变原意。\n" +
"\n" +
"【Important】\n" +
"1. **准确性**:确保识别的语法错误和更正都是准确的。\n" +
"2. **保持原意**:在纠正语法错误的同时,不改变文本的原始意义和语境。\n" +
"3. **格式规范**:按照JSON标准格式化返回纠错信息。\n" +
"\n" +
"【Attention】\n" +
"1. **语法细节**:关注语法细节,如词性正确性等。\n" +
"2. **清晰记录**:清晰记录每个语法错误的修改,以及修改后的文本。\n" +
"\n" +
"【Format】\n" +
"{\n" +
// "\"originalText\": \"输入的原始文本\",\n" +
// "\"originalText\": \"更正后的文本\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \"错误语句\",\n" +
"\"correct\": \"纠正后的语句\",\n" +
"\"description\": \"错误描述\"\n" +
"},\n" +
"...\n" +
"]\n" +
"}\n" +
"\n" +
"【Examples1】\n" +
"{\n" +
// "\"originalText\": \"She go to the store yesterday.\",\n" +
// "\"correctedText\": \"She went to the store yesterday.\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \"She go to the store yesterday.\",\n" +
"\"correct\": \"She went to the store yesterday.\",\n" +
"\"description\": \"使用一般过去时描述过去事件\"\n" +
"}\n" +
"]\n" +
"}\n" +
"【Examples2】\n" +
"{\n" +
// "\"originalText\": \"通过老师的耐心指导,使我明白了许多道理。\",\n" +
// "\"correctedText\": \"老师的耐心指导,让我明白了许多道理。\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \"通过老师的耐心指导,使我明白了许多道理。\",\n" +
"\"correct\": \"老师的耐心指导,让我明白了许多道理。\",\n" +
"\"description\": \"主语残缺的语病错误\"\n" +
"}\n" +
"]\n" +
"}\n" +
"\n" +
"【Notes】\n" +
"在执行语法语句纠错任务时,你需要严格遵循仅进行语法语句纠错的指令,无明显语法错误时errors数组应为空。请确保输出正确的json格式,不要在json中做多余描述,破坏json格式的完整性。\n" +
"\n" +
"现在,请按照上述要求对以下文本进行语法语句纠错: \n" +
"--- \n" +
"#{TEXT}\n" +
"--- \n" +
"请按照要求格式输出。\n";
/**
* 检查专有名词错误提示词 内容替换符:#{TEXT}
*/
public static final String CHECK_ERROR_PROPER_NOUN = "" +
"【Role】\n" +
"你是一名专有名词纠错专家,负责识别和纠正文本中的专有名词错误,并以简化的JSON格式返回纠错信息。。\n" +
"\n" +
"【Goals】\n" +
"1. 检查并纠正给定文本中的专有名词错误,包括但不限于电力行业的专业名词、公司名称、产品名称、古诗词、谚语等。\n" +
"2. 确保文本中的专有名词使用正确无误,同时保持原意不变。\n" +
"3. 以JSON格式返回所有专有名词错误的修改信息和纠错后的文本。\n" +
"\n" +
"【workflows】\n" +
"1. **文本审查**:仔细阅读输入文本,识别所有可能的专有名词使用错误。\n" +
"2. **错误识别**:确定文本中的专有名词错误,并查找正确的专有名词。\n" +
"3. **上下文理解**:考虑上下文含义,确保纠正后的文本在语义上仍然连贯。\n" +
"4. **JSON格式化**:将识别的专有名词错误及其更正格式化为JSON。\n" +
"5. **结果验证**:复查纠错结果,确保文本的专有名词正确,且未改变原意。\n" +
"\n" +
"【Important】\n" +
"1. **准确性**:确保识别的专有名词错误和更正都是准确的。\n" +
"2. **保持原意**:在纠正专有名词错误的同时,不改变文本的原始意义和语境。\n" +
"3. **格式规范**:按照JSON标准格式化返回纠错信息。\n" +
"\n" +
"【Attention】\n" +
"1. **专有名词细节**:关注专有名词的细节,确保使用正确的名称和拼写。\n" +
"2. **清晰记录**:清晰记录每个专有名词错误的修改,以及修改后的文本。\n" +
"\n" +
"【Format】\n" +
"{\n" +
// "\"originalText\": \"输入的原始文本\",\n" +
// "\"correctedText\": \"更正后的文本\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \"错误专有名词\",\n" +
"\"correct\": \"正确的专有名词\"\n" +
"},\n" +
"...\n" +
"]\n" +
"}\n" +
"\n" +
"【Examples】\n" +
"{\n" +
// "\"originalText\": \"北境大学积极探索愿望核储一体化技术,为可持续能源发展贡献智慧。\",\n" +
// "\"correctedText\": \"北京大学积极探索源网荷储一体化技术,为可持续能源发展贡献智慧。\",\n" +
"\"errors\": [\n" +
"{\n" +
"\"incorrect\": \"北境大学\",\n" +
"\"correct\": \"北京大学\"\n" +
"},\n" +
"{\n" +
"\"incorrect\": \"愿望核储\",\n" +
"\"correct\": \"源网荷储\"\n" +
"}\n" +
"]\n" +
"}\n" +
"\n" +
"【Notes】\n" +
"在执行专有名词纠错任务时,你需要严格遵循仅进行专有名词纠错的指令,无明显专有名词错误时errors数组应为空。请确保输出正确的json格式,不要在json中做多余描述,破坏json格式的完整性。\n" +
"\n" +
"现在,请按照上述要求对以下文本进行专有名词纠错: \n" +
"--- \n" +
"#{TEXT}\n" +
"--- \n" +
"请按照要求格式输出。\n";
public static final String CHECK_END_PROMPT = "" +
"【Role】\n" +
"你是一名综合语言审核工程师,负责执行全面的文本审核,包括错别字、标点符号、语法和专有名词的纠错。\n" +
"\n" +
"【Goals】\n" +
"1. 对给定文本进行全面的语言文字审核。\n" +
"2. 结合错别字、标点符号、语法和专有名词纠错,确保文本的准确性和专业性。\n" +
"3. 返回包含原文和纠错后文本的审核结果。\n" +
"\n" +
"【workflows】\n" +
"1. **原文审查**:首先,仔细阅读并理解输入的原始文本。\n" +
"2. **错误识别**:依次执行错别字、标点符号、语法和专有名词的纠错。\n" +
"3. **错误纠正**:对识别出的每一类错误进行纠正,确保文本的正确性。\n" +
"4. **结果整合**:将纠正后的所有文本整合,形成最终的纠错结果。\n" +
"5. **结果验证**:最后,验证纠正后的文本,确保所有错误均已纠正,且文本意义未发生改变。\n" +
"\n" +
"【Important】\n" +
"1. **全面性**:确保审核过程覆盖所有潜在的错误类型。\n" +
"2. **准确性**:纠正错误时,确保不改变原文的意义和语境。\n" +
"3. **格式规范**:按照指定的格式返回包含原文和纠错后文本的审核结果。\n" +
"\n" +
"【Attention】\n" +
"1. **细节关注**:在纠错过程中,对文本的每一个细节给予足够的关注。\n" +
"2. **上下文理解**:在纠正错误时,考虑上下文,确保文本的连贯性和准确性。\n" +
"\n" +
"【Format】\n" +
"{\n" +
// "\"originalText\": \"原文本\",\n" +
"\"correctedText\": \"纠错后的文本\"\n" +
"}\n" +
"\n" +
"【Examples】\n" +
"{\n" +
// "\"originalText\": \"北境大学为了研究先进的能源管理技术。特别成立了转向工作组,该团队正致力于开发和实施“愿望核储”一体化解决方案;以促进更高效的能源利用。\",\n" +
"\"correctedText\": \"北京大学为了研究先进的能源管理技术,特别成立了专项工作组,该团队正致力于开发和实施“源网荷储”一体化解决方案,以促进更高效的能源利用。\"\n" +
"}\n" +
"\n" +
"【Notes】\n" +
"在执行综合纠错任务时,你需要严格按照上述所有指令,确保生成的纠错信息既符合逻辑结构,又精准满足用户期待。专注于文本的全面审核和纠正,以确保文本的准确性和专业性。请确保输出正确的json格式,不要在json中做多余描述,破坏json格式的完整性。\n" +
"\n" +
"现在,请按照上述要求对以下文本进行纠错: \n" +
"--- \n" +
"#{TEXT}\n" +
"--- \n" +
"请按照要求格式输出。\n";
}
对模型返回的纠错信息处理
package com.example.qianfan.service.impl;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.qianfan.config.PromptTemplateConfig;
import com.example.qianfan.controller.OfficeOpenAPIController;
import com.example.qianfan.utils.OfficeUtils;
import com.example.qianfan.utils.PunctuationComparisonUtils;
import com.example.qianfan.utils.TextProcessUtils;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
@Service
@Slf4j
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TextCorrectionService{
@Autowired
@Lazy
TextCorrectionService textCorrectionService;
@Value("${substringFlag}")
public Integer substringFlag;
@Value("${substringSize}")
public Integer substringSize;
private static final int THREAD_POOL_SIZE = 4; // 根据实际情况调整线程池大小
private static final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
private static final String ERROR_NUM_KEY = "error_num";
private static final String ERROR_LIST_KEY = "error_list";
/**
* 文本纠错
*
* @param qianfanAppId 千帆应用ID
* @param sessionId 会话id
* @param text 文本
* @return
*/
public String textCorrection(String qianfanAppId, String sessionId, String text) {
if (StringUtils.isEmpty(text)) {
return null;
}
//原文文本长度
int content_len = text.length();
/**
* 增加针对文本长度过长 8K模型query长度限制。
* 全文纠错 文本过长,8K模型query限制2000字符 截取文本分段纠错(1000字截取一段)
* 1.将5个纠错方法封装到方法中,调用方法进行分段纠错
*/
Map<String, Object> dataMap = new HashMap<>();
if (substringFlag == 1 && content_len > substringSize) {
// dataMap = correctionMethods(qianfanAppId, sessionId, text, true);
dataMap = correctionMethodsAsync(qianfanAppId, sessionId, text, true);
}else {
dataMap = correctionMethods(qianfanAppId, sessionId, text,false);
}
JSONObject jsonObj = new JSONObject();
jsonObj.put("code", 200);
jsonObj.put("msg", "纠错完成");
jsonObj.put("data", dataMap);
return jsonObj.toString();
}
/**
* 最后完整纠错返回纠错后的文本
* @param qianfanAppId
* @param sessionId
* @param originalText
* @return
*/
private String endCheck(String qianfanAppId, String sessionId,String originalText) {
String correct_text = "";
String checkErrorProperNounPrompt = PromptTemplateConfig.CHECK_END_PROMPT.replace("#{TEXT}", originalText);
//请求纠错模型
String bodyStr = null;
if (StringUtils.isNotEmpty(bodyStr)) {
//模型返回结果
JSONObject jsonBodyObj = JSONUtil.parseObj(bodyStr);
if (StringUtils.isNotBlank(jsonBodyObj.getStr("code"))) {
String msg = jsonBodyObj.getStr("message");
} else {
//模型回答结果
String answer = jsonBodyObj.getStr("answer");
log.info("最终纠错模型返回{}", answer);
/**
* 更换提示词 返回json格式数据 修改处理
*/
try {
if (answer.contains("```json")) {
// 使用正则表达式提取 JSON 内容
String jsonStr = extractJson(answer);
// 将提取的字符串转换为 JsonObject
JSONObject errorJsonObject = new JSONObject(jsonStr);
if (errorJsonObject.containsKey("correctedText")){
//纠错后文本
correct_text = errorJsonObject.getStr("correctedText");
}
}
}catch (Exception e){
log.error("最终纠错失败!"+e.getMessage());
}
}
}
return correct_text;
}
/**
* 纠错方法-内部纠错方法异步执行
* @param qianfanAppId
* @param sessionId
* @param originalText
* @param isCutoff 是否截取长度 分段纠错
*/
private Map<String, Object> correctionMethodsAsync(String qianfanAppId, String sessionId, String originalText, boolean isCutoff) {
//原文长度
int allLength = originalText.length();
List<String> chunkList = new ArrayList<>();
//判断是否分段截取纠错
if (isCutoff){
//切割的段落块
String[] chunks = TextProcessUtils.splitTextSafely(originalText, substringSize);
chunkList = Arrays.asList(chunks);
}else {
chunkList.add(originalText);
}
//最终字词纠错详情信息
Map<String, Object> finalErrorWordDetailMap = new LinkedHashMap<>();
AtomicInteger errorWordNum = new AtomicInteger(0);//错误字词数量
List<Map<String, Object>> errorWordDetailList = new ArrayList<>();
//最终标点符号纠错详情信息
Map<String, Object> finalErrorPunctuationDetailMap = new LinkedHashMap<>();
AtomicInteger errorPunctuationNum = new AtomicInteger(0);//错误标点符号数量
List<Map<String, Object>> errorPunctuationDetailList = new ArrayList<>();
//最终语法纠错详情信息
Map<String, Object> finalErrorSyntaxDetailMap = new LinkedHashMap<>();
AtomicInteger errorSyntaxNum = new AtomicInteger(0);//错误语法数量
List<Map<String, Object>> errorSyntaxDetailList = new ArrayList<>();
//最终专有名词纠错详情信息
Map<String, Object> finalErrorProperNounDetailMap = new LinkedHashMap<>();
AtomicInteger errorProperNounNum = new AtomicInteger(0);//错误专有名词数量
List<Map<String, Object>> errorProperNounDetailList = new ArrayList<>();
//截取后分段多次纠错,纠错后将内容拼接返回
//开启线程 优化执行效率
List<CompletableFuture<Map<String, Object>>> wordFutures = new ArrayList<>();
List<CompletableFuture<Map<String, Object>>> punctuationFutures = new ArrayList<>();
List<CompletableFuture<Map<String, Object>>> syntaxFutures = new ArrayList<>();
List<CompletableFuture<Map<String, Object>>> properNounFutures = new ArrayList<>();
// 创建一个单线程的ExecutorService来处理endCheck方法
ExecutorService endCheckExecutorService = Executors.newSingleThreadExecutor();
//最终全文纠错返回文本
StringBuilder correctText = new StringBuilder();
List<CompletableFuture<Void>> endCheckFutures = new ArrayList<>();
for (int i = 0; i < chunkList.size(); i++){
String chunkText = chunkList.get(i);
int chunkIndex = getChunkIndex(chunkList, i); // 获取当前截取的段落块在全文中开始的索引位置
// 并行执行纠错方法
//1.字词纠错
CompletableFuture<Map<String, Object>> errorWordFuture = CompletableFuture.supplyAsync(() -> {
try {
return checkErrorWord(qianfanAppId, sessionId, chunkText, chunkIndex);
} catch (Exception e) {
log.error("分段字词纠错异常!", e);
return new LinkedHashMap<>();
}
}, executorService);
wordFutures.add(errorWordFuture);
//2.标点符号纠错
CompletableFuture<Map<String, Object>> errorPunctuationFuture = CompletableFuture.supplyAsync(() -> {
try {
return checkErrorPunctuation(qianfanAppId, sessionId, chunkText, chunkIndex);
} catch (Exception e) {
log.error("分段标点符号纠错异常!", e);
return new LinkedHashMap<>();
}
}, executorService);
punctuationFutures.add(errorPunctuationFuture);
//3.语法纠错
CompletableFuture<Map<String, Object>> errorSyntaxFuture = CompletableFuture.supplyAsync(() -> {
try {
return checkErrorSyntax(qianfanAppId, sessionId, chunkText, chunkIndex);
} catch (Exception e) {
log.error("分段语法纠错异常!", e);
return new LinkedHashMap<>();
}
}, executorService);
syntaxFutures.add(errorSyntaxFuture);
//4.专有名词纠错
CompletableFuture<Map<String, Object>> errorProperNounFuture = CompletableFuture.supplyAsync(() -> {
try {
return checkErrorProperNoun(qianfanAppId, sessionId, chunkText, chunkIndex);
} catch (Exception e) {
log.error("分段专有名词纠错异常!", e);
return new LinkedHashMap<>();
}
}, executorService);
properNounFutures.add(errorProperNounFuture);
//5.最终调用纠错接口 总结上面4个纠错信息后 返回一个纠错语句
// 单独处理endCheck方法,确保按顺序执行
int finalIndex = i; // 使用final变量来捕获循环变量的值
CompletableFuture<Void> endCheckFuture = CompletableFuture.runAsync(() -> {
String correctedChunk = endCheck(qianfanAppId, sessionId, chunkText);
synchronized (correctText) { // 同步块确保线程安全
if (finalIndex > 0) {
correctText.append(TextProcessUtils.splitTextSafely(originalText, substringSize)[finalIndex - 1].substring(0, 1)); // 添加原文的分隔符,例如逗号、句号等,需要根据实际情况调整
}
correctText.append(correctedChunk);
}
}, endCheckExecutorService);
endCheckFutures.add(endCheckFuture);
}
// 等待所有纠错任务完成,并处理结果
// 等待所有字词纠错任务完成,并处理结果
CompletableFuture.allOf(wordFutures.toArray(new CompletableFuture[0])).join();
for (CompletableFuture<Map<String, Object>> future : wordFutures) {
handleErrorDetails(future.join(), errorWordNum, errorWordDetailList, ERROR_NUM_KEY, ERROR_LIST_KEY);
}
// 等待所有标点符号纠错任务完成,并处理结果
CompletableFuture.allOf(punctuationFutures.toArray(new CompletableFuture[0])).join();
for (CompletableFuture<Map<String, Object>> future : punctuationFutures) {
handleErrorDetails(future.join(), errorPunctuationNum, errorPunctuationDetailList, ERROR_NUM_KEY, ERROR_LIST_KEY);
}
// 等待所有语法纠错任务完成,并处理结果
CompletableFuture.allOf(syntaxFutures.toArray(new CompletableFuture[0])).join();
for (CompletableFuture<Map<String, Object>> future : syntaxFutures) {
handleErrorDetails(future.join(), errorSyntaxNum, errorSyntaxDetailList, ERROR_NUM_KEY, ERROR_LIST_KEY);
}
// 等待所有专有名词纠错任务完成,并处理结果
CompletableFuture.allOf(properNounFutures.toArray(new CompletableFuture[0])).join();
for (CompletableFuture<Map<String, Object>> future : properNounFutures) {
handleErrorDetails(future.join(), errorProperNounNum, errorProperNounDetailList, ERROR_NUM_KEY, ERROR_LIST_KEY);
}
// 等待所有endCheck任务完成
CompletableFuture.allOf(endCheckFutures.toArray(new CompletableFuture[0])).join();
// 关闭endCheck的ExecutorService
endCheckExecutorService.shutdown();
String correctTextResult = correctText.toString();
//字词纠错 -0001, 标点符号纠错 -0002, 语法纠错 -0003, 专名纠错 -0004
//最终字词纠错详情信息
finalErrorWordDetailMap.put("type", "0001");
finalErrorWordDetailMap.put("error_num", errorWordNum);
finalErrorWordDetailMap.put("error_list", errorWordDetailList);
//最终标点符号纠错详情信息
finalErrorPunctuationDetailMap.put("type", "0002");
finalErrorPunctuationDetailMap.put("error_num", errorPunctuationNum);
finalErrorPunctuationDetailMap.put("error_list", errorPunctuationDetailList);
//最终语法纠错详情信息
finalErrorSyntaxDetailMap.put("type", "0003");
finalErrorSyntaxDetailMap.put("error_num", errorSyntaxNum);
finalErrorSyntaxDetailMap.put("error_list", errorSyntaxDetailList);
//最终专有名词纠错详情信息
finalErrorProperNounDetailMap.put("type", "0004");
finalErrorProperNounDetailMap.put("error_num", errorProperNounNum);
finalErrorProperNounDetailMap.put("error_list", errorProperNounDetailList);
//纠错类型集
List<Map<String, Object>> details = new ArrayList<>();
details.add(finalErrorWordDetailMap);
details.add(finalErrorPunctuationDetailMap);
details.add(finalErrorSyntaxDetailMap);
details.add(finalErrorProperNounDetailMap);
//返回json格式数据
Map<String, Object> dataMap = new LinkedHashMap<>();
dataMap.put("original_text", originalText);
dataMap.put("correct_text", correctTextResult);
dataMap.put("content_len", allLength);
dataMap.put("details", details);
//返回数据之前关闭线程池
executorService.shutdown(); // 优雅地关闭线程池,等待所有任务完成
return dataMap;
}
/**
* 纠错方法
* @param qianfanAppId
* @param sessionId
* @param originalText
* @param isCutoff 是否截取长度 分段纠错
*/
private Map<String, Object> correctionMethods(String qianfanAppId, String sessionId, String originalText, boolean isCutoff) {
//原文长度
int allLength = originalText.length();
List<String> chunkList = new ArrayList<>();
//判断是否分段截取纠错
if (isCutoff){
//切割的段落块
String[] chunks = TextProcessUtils.splitTextSafely(originalText, substringSize);
chunkList = Arrays.asList(chunks);
}else {
chunkList.add(originalText);
}
//最终字词纠错详情信息
Map<String, Object> finalErrorWordDetailMap = new LinkedHashMap<>();
AtomicInteger errorWordNum = new AtomicInteger(0);//错误字词数量
List<Map<String, Object>> errorWordDetailList = new ArrayList<>();
//最终标点符号纠错详情信息
Map<String, Object> finalErrorPunctuationDetailMap = new LinkedHashMap<>();
AtomicInteger errorPunctuationNum = new AtomicInteger(0);//错误标点符号数量
List<Map<String, Object>> errorPunctuationDetailList = new ArrayList<>();
//最终语法纠错详情信息
Map<String, Object> finalErrorSyntaxDetailMap = new LinkedHashMap<>();
AtomicInteger errorSyntaxNum = new AtomicInteger(0);//错误语法数量
List<Map<String, Object>> errorSyntaxDetailList = new ArrayList<>();
//最终专有名词纠错详情信息
Map<String, Object> finalErrorProperNounDetailMap = new LinkedHashMap<>();
AtomicInteger errorProperNounNum = new AtomicInteger(0);//错误专有名词数量
List<Map<String, Object>> errorProperNounDetailList = new ArrayList<>();
//最终全文纠错返回文本
StringBuilder correctText = new StringBuilder();
//当前截取的段落块在全文中开始的索引位置
int chunkIndex = 0;
//截取后分段多次纠错,纠错后将内容拼接返回
for (int i = 0; i < chunkList.size(); i++){
String chunkText = chunkList.get(i);
//纠错段落长度
int chunkLength = chunkText.length();
//获取当前截取的段落块在全文中开始的索引位置
if (i>0){
//上一个段落长度
int beforeChunkLength= chunkList.get(i-1).length();
chunkIndex += beforeChunkLength;
}
//1.先进行字词纠错 替换字词纠错的提示词模板
//字词错误详情信息-errorWordDetailMap
Map<String, Object> errorWordDetailMap = new LinkedHashMap<>();
try {
errorWordDetailMap = checkErrorWord(qianfanAppId, sessionId, chunkText,chunkIndex);
} catch (Exception e) {
log.error("分段字词纠错异常!", e);
}
if (MapUtil.isNotEmpty(errorWordDetailMap)){
//处理错误详情
handleErrorDetails(errorWordDetailMap, errorWordNum, errorWordDetailList, ERROR_NUM_KEY, ERROR_LIST_KEY);
}
//2.再进行标点符号纠错 替换标点符号纠错的提示词模板
//标点符号错误详情信息-errorPunctuationMap
Map<String, Object> errorPunctuationDetailMap = new LinkedHashMap<>();
try {
errorPunctuationDetailMap = checkErrorPunctuation(qianfanAppId, sessionId, chunkText,chunkIndex);
} catch (Exception e) {
log.error("分段标点符号纠错异常!", e);
}
if (MapUtil.isNotEmpty(errorPunctuationDetailMap)){
//处理错误详情
handleErrorDetails(errorPunctuationDetailMap, errorPunctuationNum, errorPunctuationDetailList, ERROR_NUM_KEY, ERROR_LIST_KEY);
}
//3.再进行语法纠错 替换语法纠错的提示词模板
//语法错误详情信息-errorSyntaxMap
Map<String, Object> errorSyntaxDetailMap = new LinkedHashMap<>();
try {
errorSyntaxDetailMap = checkErrorSyntax(qianfanAppId, sessionId, chunkText,chunkIndex);
} catch (Exception e) {
log.error("分段语法纠错异常!", e);
}
if (MapUtil.isNotEmpty(errorSyntaxDetailMap)){
//处理错误详情
handleErrorDetails(errorSyntaxDetailMap, errorSyntaxNum, errorSyntaxDetailList, ERROR_NUM_KEY, ERROR_LIST_KEY);
}
//4.再进行专有名词纠错 替换专有名词纠错的提示词模板
//语法错误详情信息-errorProperNounDetailMap
Map<String, Object> errorProperNounDetailMap = new LinkedHashMap<>();
try {
errorProperNounDetailMap = checkErrorProperNoun(qianfanAppId, sessionId, chunkText,chunkIndex);
} catch (Exception e) {
log.error("分段专有名词纠错异常!", e);
}
if (MapUtil.isNotEmpty(errorProperNounDetailMap)){
//处理错误详情
handleErrorDetails(errorProperNounDetailMap, errorProperNounNum, errorProperNounDetailList, ERROR_NUM_KEY, ERROR_LIST_KEY);
}
//5.最终调用纠错接口 总结上面4个纠错信息后 返回一个纠错语句
String correct_text = endCheck(qianfanAppId, sessionId, chunkText);
correctText.append(correct_text);
}
String correctTextResult = correctText.toString();
//字词纠错 -0001, 标点符号纠错 -0002, 语法纠错 -0003, 专名纠错 -0004
//最终字词纠错详情信息
finalErrorWordDetailMap.put("type", "0001");
finalErrorWordDetailMap.put("error_num", errorWordNum);
finalErrorWordDetailMap.put("error_list", errorWordDetailList);
//最终标点符号纠错详情信息
finalErrorPunctuationDetailMap.put("type", "0002");
finalErrorPunctuationDetailMap.put("error_num", errorPunctuationNum);
finalErrorPunctuationDetailMap.put("error_list", errorPunctuationDetailList);
//最终语法纠错详情信息
finalErrorSyntaxDetailMap.put("type", "0003");
finalErrorSyntaxDetailMap.put("error_num", errorSyntaxNum);
finalErrorSyntaxDetailMap.put("error_list", errorSyntaxDetailList);
//最终专有名词纠错详情信息
finalErrorProperNounDetailMap.put("type", "0004");
finalErrorProperNounDetailMap.put("error_num", errorProperNounNum);
finalErrorProperNounDetailMap.put("error_list", errorProperNounDetailList);
//纠错类型集
List<Map<String, Object>> details = new ArrayList<>();
details.add(finalErrorWordDetailMap);
details.add(finalErrorPunctuationDetailMap);
details.add(finalErrorSyntaxDetailMap);
details.add(finalErrorProperNounDetailMap);
//返回json格式数据
Map<String, Object> dataMap = new LinkedHashMap<>();
dataMap.put("original_text", originalText);
dataMap.put("correct_text", correctTextResult);
dataMap.put("content_len", allLength);
dataMap.put("details", details);
return dataMap;
}
/**
* 处理纠错的错误详情
* @param detailMap
* @param errorNum
* @param errorDetailList
* @param errorNumKey
* @param errorListKey
*/
private void handleErrorDetails(Map<String, Object> detailMap, AtomicInteger errorNum, List<Map<String, Object>> errorDetailList, String errorNumKey, String errorListKey) {
if (detailMap.containsKey(errorNumKey) && (Integer) detailMap.get(errorNumKey) > 0) {
int num = (Integer) detailMap.get(errorNumKey);
errorNum.addAndGet(num);
}
if (detailMap.containsKey(errorListKey)) {
List<Map<String, Object>> tempList = (List<Map<String, Object>>) detailMap.get(errorListKey);
if (CollectionUtils.isNotEmpty(tempList)) {
errorDetailList.addAll(tempList);
}
}
}
/**
* 获取当前截取的段落块在全文中开始的索引位置
* @param chunkList
* @param currentIndex
* @return
*/
private int getChunkIndex(List<String> chunkList, int currentIndex) {
int chunkIndex = 0;
for (int i = 0; i < currentIndex; i++) {
chunkIndex += chunkList.get(i).length();
}
return chunkIndex;
}
/**
* 纠错能力-专有名词纠错
*
* @param qianfanAppId
* @param sessionId
* @param original_text
* @return
* @throws Exception
*/
private Map<String, Object> checkErrorProperNoun(String qianfanAppId, String sessionId, String original_text,int nowLength) throws Exception {
Map<String, Object> errorProperNounDetailMap = new LinkedHashMap<>();
String type = "0004";
errorProperNounDetailMap.put("type", type);
String checkErrorProperNounPrompt = PromptTemplateConfig.CHECK_ERROR_PROPER_NOUN.replace("#{TEXT}", original_text);
//请求纠错应用
String bodyStr = null;
if (StringUtils.isNotEmpty(bodyStr)) {
//模型返回结果
JSONObject jsonBodyObj = JSONUtil.parseObj(bodyStr);
if (StringUtils.isNotBlank(jsonBodyObj.getStr("code"))) {
String msg = jsonBodyObj.getStr("message");
throw new Exception(msg);
} else {
//错误数量
int error_num = 0;
错误信息:{"原始部分":"","修改部分":"","位置信息":""}
List<Map<String, Object>> error_list = new ArrayList<>();
//模型回答结果
String answer = jsonBodyObj.getStr("answer");
log.info("专有名词纠错模型返回{}", answer);
/**
* 更换提示词 返回json格式数据 修改处理
*/
try {
if (answer.contains("```json")) {
// 使用正则表达式提取 JSON 内容
String jsonStr = extractJson(answer);
// 将提取的字符串转换为 JsonObject
JSONObject errorJsonObject = new JSONObject(jsonStr);
if (errorJsonObject.containsKey("originalText")){
//原始文本
String originalText = errorJsonObject.getStr("originalText");
}
if (errorJsonObject.containsKey("correctedText")){
//纠错后文本
String correctedText = errorJsonObject.getStr("correctedText");
}
//如果包含errors
if (errorJsonObject.containsKey("errors")){
JSONArray errors = errorJsonObject.getJSONArray("errors");
if (errors.size()>0){
/**
* 去除重复的纠错信息
*/
JSONArray newErrors = deDuplicateJSONArray(errors);
for (int i = 0; i < newErrors.size(); i++) {
JSONObject error = newErrors.getJSONObject(i);
//原始部分
String incorrect = error.getStr("incorrect");
//如果是标点符号 则跳过
if (PunctuationComparisonUtils.containsPunctuation(incorrect)) {
continue;
}
//修改部分
String correct = error.getStr("correct");
//查询原始部分在原文中的位置信息
List<Map<String, Integer>> indexList = TextProcessUtils.findSubstringPositions(original_text, incorrect);
for (Map<String, Integer> indexMap : indexList
) {
int begin_index = indexMap.get("start_index");
int end_index = indexMap.get("end_index");
//专有名词错误详情 原语句、修改语句、位置信息
Map<String, Object> errorMsgMap = new LinkedHashMap<>();
errorMsgMap.put("original_part", incorrect);
errorMsgMap.put("correct_part", correct);
errorMsgMap.put("begin_index", begin_index+nowLength);
errorMsgMap.put("end_index", end_index+nowLength);
error_list.add(errorMsgMap);
}
}
}
}
}
}catch (Exception e){
log.error("调用纠错能力-专有名词纠错失败!");
throw new Exception("调用纠错能力-专有名词纠错失败!"+e.getMessage());
}
errorProperNounDetailMap.put("error_num", error_list.size());
errorProperNounDetailMap.put("error_list", error_list);
}
} else {
log.error("调用纠错能力-专有名词纠错失败!");
throw new Exception("调用纠错能力-专有名词纠错失败!");
}
return errorProperNounDetailMap;
}
/**
* 纠错能力-语法纠错
*
* @param qianfanAppId
* @param sessionId
* @param original_text
* @return
* @throws Exception
*/
private Map<String, Object> checkErrorSyntax(String qianfanAppId, String sessionId, String original_text,int nowLength) throws Exception {
Map<String, Object> errorSyntaxDetailMap = new LinkedHashMap<>();
String type = "0003";
errorSyntaxDetailMap.put("type", type);
String checkErrorSyntaxPrompt = PromptTemplateConfig.CHECK_ERROR_SYNTAX.replace("#{TEXT}", original_text);
//请求纠错应用
String bodyStr = null;
if (StringUtils.isNotEmpty(bodyStr)) {
//模型返回结果
JSONObject jsonBodyObj = JSONUtil.parseObj(bodyStr);
if (StringUtils.isNotBlank(jsonBodyObj.getStr("code"))) {
String msg = jsonBodyObj.getStr("message");
throw new Exception(msg);
} else {
//错误数量
int error_num = 0;
错误信息:{"原始部分":"","修改部分":"","位置信息":""}
List<Map<String, Object>> error_list = new ArrayList<>();
//模型回答结果
String answer = jsonBodyObj.getStr("answer");
log.info("语法纠错模型返回{}", answer);
/**
* 更换提示词 返回json格式数据 修改处理
*/
try {
if (answer.contains("```json")) {
// 使用正则表达式提取 JSON 内容
String jsonStr = extractJson(answer);
// 将提取的字符串转换为 JsonObject
JSONObject errorJsonObject = new JSONObject(jsonStr);
if (errorJsonObject.containsKey("originalText")){
//原始文本
String originalText = errorJsonObject.getStr("originalText");
}
if (errorJsonObject.containsKey("correctedText")){
//纠错后文本
String correctedText = errorJsonObject.getStr("correctedText");
}
//如果包含errors
if (errorJsonObject.containsKey("errors")){
JSONArray errors = errorJsonObject.getJSONArray("errors");
if (errors.size()>0){
/**
* 去除重复的纠错信息
*/
JSONArray newErrors = deDuplicateJSONArray(errors);
for (int i = 0; i < newErrors.size(); i++) {
JSONObject error = newErrors.getJSONObject(i);
//原始部分
String incorrect = error.getStr("incorrect");
//如果是标点符号 且仅标点符号,则跳过
if (PunctuationComparisonUtils.containsPunctuation(incorrect) && incorrect.length() == 1) {
continue;
}
//修改部分
String correct = error.getStr("correct");
//查询原始部分在原文中的位置信息
List<Map<String, Integer>> indexList = TextProcessUtils.findSubstringPositions(original_text, incorrect);
for (Map<String, Integer> indexMap : indexList
) {
int begin_index = indexMap.get("start_index");
int end_index = indexMap.get("end_index");
//语法错误详情 原语句、修改语句、位置信息
Map<String, Object> errorMsgMap = new LinkedHashMap<>();
errorMsgMap.put("original_part", incorrect);
errorMsgMap.put("correct_part", correct);
errorMsgMap.put("begin_index", begin_index+nowLength);
errorMsgMap.put("end_index", end_index+nowLength);
error_list.add(errorMsgMap);
}
}
}
}
}
}catch (Exception e){
log.error("调用纠错能力-语法纠错失败!");
throw new Exception("调用纠错能力-语法纠错失败!"+e.getMessage());
}
errorSyntaxDetailMap.put("error_num", error_list.size());
errorSyntaxDetailMap.put("error_list", error_list);
}
} else {
log.error("调用纠错能力-语法纠错失败!");
throw new Exception("调用纠错能力-语法纠错失败!");
}
return errorSyntaxDetailMap;
}
/**
* 纠错能力-标点符号纠错
*
* @param qianfanAppId
* @param sessionId
* @param original_text 原文
* @return
*/
private Map<String, Object> checkErrorPunctuation(String qianfanAppId, String sessionId, String original_text,int nowLength) throws Exception {
Map<String, Object> errorPunctuationDetailMap = new LinkedHashMap<>();
String type = "0002";
errorPunctuationDetailMap.put("type", type);
String checkErrorPunctuationPrompt = PromptTemplateConfig.CHECK_ERROR_PUNCTUATION.replace("#{TEXT}", original_text);
//请求纠错应用
String bodyStr = null;
if (StringUtils.isNotEmpty(bodyStr)) {
//模型返回结果
JSONObject jsonBodyObj = JSONUtil.parseObj(bodyStr);
if (StringUtils.isNotBlank(jsonBodyObj.getStr("code"))) {
String msg = jsonBodyObj.getStr("message");
throw new Exception(msg);
} else {
//错误数量
int error_num = 0;
//错误信息:{"原始部分":"","修改部分":"","位置信息":""}
List<Map<String, Object>> error_list = new ArrayList<>();
//模型回答结果
String answer = jsonBodyObj.getStr("answer");
log.info("标点符号纠错模型返回{}", answer);
/**
* 更换提示词 返回json格式数据 修改处理
*/
try {
if (answer.contains("```json")) {
// 使用正则表达式提取 JSON 内容
String jsonStr = extractJson(answer);
// 将提取的字符串转换为 JsonObject
JSONObject errorJsonObject = new JSONObject(jsonStr);
if (errorJsonObject.containsKey("originalText")){
//原始文本
String originalText = errorJsonObject.getStr("originalText");
}
if (errorJsonObject.containsKey("correctedText")){
//纠错后文本
String correctedText = errorJsonObject.getStr("correctedText");
//使用工具对比标点符号 实现原始标点符号 修改后标点符号 原始标点符号位置
List<PunctuationDifference> punctuationDifferences = PunctuationComparisonUtils.findPunctuationDifferences(original_text, correctedText);
for (PunctuationDifference diff : punctuationDifferences) {
//字符位置下标+1
int index = diff.getPosition() + 1;
Map<String, Object> errorMsgMap = new LinkedHashMap<>();
errorMsgMap.put("original_part", diff.getOriginal());
errorMsgMap.put("correct_part", diff.getCorrected());
errorMsgMap.put("begin_index", index+nowLength);
errorMsgMap.put("end_index", index+nowLength);
error_list.add(errorMsgMap);
}
}
}
}catch (Exception e){
log.error("调用纠错能力-标点符号纠错失败!");
throw new Exception("调用纠错能力-标点符号纠错失败!"+e.getMessage());
}
errorPunctuationDetailMap.put("error_num", error_list.size());
errorPunctuationDetailMap.put("error_list", error_list);
}
} else {
log.error("调用纠错能力-标点符号纠错失败!");
throw new Exception("调用纠错能力-标点符号纠错失败!");
}
return errorPunctuationDetailMap;
}
/**
* 纠错能力-字词纠错方法,返回错误字词json格式信息
*/
private Map<String, Object> checkErrorWord(String qianfanAppId, String sessionId, String original_text,int nowLength) throws Exception {
Map<String, Object> errorWordDetailMap = new LinkedHashMap<>();
String type = "0001";
errorWordDetailMap.put("type", type);
String checkErrorWordPrompt = PromptTemplateConfig.CHECK_ERROR_WORD.replace("#{TEXT}", original_text);
//请求纠错应用
String bodyStr = null;
//错误数量
int error_num = 0;
errorWordDetailMap.put("error_num", error_num);
if (StringUtils.isNotEmpty(bodyStr)) {
//模型返回结果
JSONObject jsonBodyObj = JSONUtil.parseObj(bodyStr);
if (StringUtils.isNotBlank(jsonBodyObj.getStr("code"))) {
String msg = jsonBodyObj.getStr("message");
throw new Exception(msg);
} else {
错误信息:{"原始部分":"","修改部分":"","位置信息":""}
List<Map<String, Object>> error_list = new ArrayList<>();
//模型回答结果
String answer = jsonBodyObj.getStr("answer");
log.info("字词纠错模型返回{}", answer);
/**
* 更换提示词 返回json格式数据 修改处理
*/
try {
if (answer.contains("```json")) {
// 使用正则表达式提取 JSON 内容
String jsonStr = extractJson(answer);
// 将提取的字符串转换为 JsonObject
JSONObject errorJsonObject = new JSONObject(jsonStr);
if (errorJsonObject.containsKey("originalText")){
//原始文本
String originalText = errorJsonObject.getStr("originalText");
}
if (errorJsonObject.containsKey("correctedText")){
//纠错后文本
String correctedText = errorJsonObject.getStr("correctedText");
}
//如果包含errors
if (errorJsonObject.containsKey("errors")){
JSONArray errors = errorJsonObject.getJSONArray("errors");
if (errors.size()>0){
/**
* 去除重复的纠错信息
*/
JSONArray newErrors = deDuplicateJSONArray(errors);
for (int i = 0; i < newErrors.size(); i++) {
JSONObject error = newErrors.getJSONObject(i);
//原始部分
String incorrect = error.getStr("incorrect");
//如果是标点符号 且仅标点符号,则跳过
if (PunctuationComparisonUtils.containsPunctuation(incorrect)) {
continue;
}
//修改部分
String correct = error.getStr("correct");
//查询原始部分在原文中的位置信息
List<Map<String, Integer>> indexList = TextProcessUtils.findSubstringPositions(original_text, incorrect);
for (Map<String, Integer> indexMap : indexList
) {
int begin_index = indexMap.get("start_index");
int end_index = indexMap.get("end_index");
//字词错误详情 原字词、修改字词、位置信息
Map<String, Object> errorMsgMap = new LinkedHashMap<>();
errorMsgMap.put("original_part", incorrect);
errorMsgMap.put("correct_part", correct);
errorMsgMap.put("begin_index", begin_index+nowLength);
errorMsgMap.put("end_index", end_index+nowLength);
error_list.add(errorMsgMap);
}
}
}
}
}
}catch (Exception e){
log.error("调用纠错能力-字词纠错失败!");
throw new Exception("调用纠错能力-字词纠错失败!"+e.getMessage());
}
errorWordDetailMap.put("error_num", error_list.size());
errorWordDetailMap.put("error_list", error_list);
}
} else {
log.error("调用纠错能力-字词纠错失败!");
throw new Exception("调用纠错能力-字词纠错失败!");
}
return errorWordDetailMap;
}
public static void main(String[] args) {
// 原始字符串
String originalStr = "```json\n{\n \"originalText\": \"为了改变员工经常食用圾食的习贯,公司成立了转向工作组来推广健康饮食。\",\n \"errors\": [\n {\n \"incorrect\": \"圾食\",\n \"correct\": \"垃圾食品\"\n },\n {\n \"incorrect\": \"习贯\",\n \"correct\": \"习惯\"\n },\n {\n \"incorrect\": \"转向工作组\",\n \"correct\": \"专项工作组\"\n }\n ],\n \"correctedText\": \"为了改变员工经常食用垃圾食品的习惯,公司成立了专项工作组来推广健康饮食。\"\n}\n```";
// 使用正则表达式提取 JSON 内容
String jsonStr = extractJson(originalStr);
// 将提取的字符串转换为 JsonObject
JSONObject jsonObject = new JSONObject(jsonStr);
// 打印转换后的 JsonObject
System.out.println(jsonObject.toString()); // 格式化输出
}
private static String extractJson(String input) {
String jsonPattern = "```json\\n(.*?)\\n```";
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(jsonPattern, java.util.regex.Pattern.DOTALL);
java.util.regex.Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
return matcher.group(1).trim(); // 返回第一个匹配的 JSON 字符串
}
return null; // 如果没有找到匹配项,则返回 null
}
/**
* 去重 List<Map<String, Object>>
* @param list
* @return
*/
public static List<Map<String, Object>> deDuplicateList(List<Map<String, Object>> list) {
if (list == null || list.isEmpty()) {
return Collections.emptyList();
}
Set<Map<String, Object>> seenMaps = new LinkedHashSet<>(list.size());
List<Map<String, Object>> result = new ArrayList<>(list.size());
for (Map<String, Object> map : list) {
if (!seenMaps.contains(map)) {
seenMaps.add(map);
result.add(map);
}
}
return result;
}
/**
* 去重 JSONArray
*
* @param jsonArray 需要去重的 JSONArray
* @return 去重后的 JSONArray
*/
public static JSONArray deDuplicateJSONArray(JSONArray jsonArray) {
if (jsonArray == null || jsonArray.isEmpty()) {
return JSONUtil.createArray();
}
// 使用 LinkedHashSet 来保持元素的顺序并去除重复项
Set<String> uniqueJsonStrings = new LinkedHashSet<>();
for (Object obj : jsonArray) {
if (obj instanceof JSONObject) {
uniqueJsonStrings.add(obj.toString());
}
}
// 将去重后的字符串重新转换为 JSONObject 并添加到新的 JSONArray 中
JSONArray resultArray = JSONUtil.createArray();
uniqueJsonStrings.forEach(str -> resultArray.add(new JSONObject(str)));
return resultArray;
}
}
安全切割文本块方法
/**
* 将文本安全地分割成指定大小的块。
* @param text 要分割的文本
* @param size 块的大小
* @return 分割后的文本数组
*/
public static String[] splitTextSafely(String text, int size) {
if (text == null || text.isEmpty()) {
return new String[0];
}
List<String> chunks = new ArrayList<>();
int start = 0;
int allLen = text.length();
while (start < text.length()) {
int end = Math.min(start + size, text.length());
if (end < allLen && !isGoodBreakPoint(text,start, end)) {
// 如果到达了一个不是好断点的位置,向前搜索直到找到一个好的断点
while (end > start && !isGoodBreakPoint(text, start,end)) {
end--;
}
}
chunks.add(text.substring(start, end));
start = end;
}
return chunks.toArray(new String[0]);
}
/**
* 判断是否是好的断点(如空格或标点后)。
* @param text 全文
* @param start 开始位置
* @param index 断点位置
* @return 如果是好的断点返回true,否则返回false
*/
private static boolean isGoodBreakPoint(String text,int start, int index) {
if (index >= text.length()) return true;
String substring = text.substring(start, index);
//用endWitch判断字符串结尾是否是段落换行符
if (substring.endsWith("\n\n") ||substring.endsWith("\\n\\n")){
return true;
}else {
return false;
}
// char ch = text.charAt(index);
// //是否是空格或者标点符号
return Character.isWhitespace(ch) || isPunctuation(ch);
// //是否数段落
// return isParagraph(ch);
}
/**
* 判断字符是否为段落符号。
* @param ch
* @return
*/
private static boolean isParagraph(char ch) {
char[] paragraphSymbols = {'\n', '\r', '\f', '\u2028', '\u2029'};
for (char symbol : paragraphSymbols) {
if (ch == symbol) {
return true;
}
}
return false;
}
/**
* 判断字符是否为标点符号。
* @param ch 字符
* @return 如果是标点符号返回true,否则返回false
*/
private static boolean isPunctuation(char ch) {
return !Character.isLetterOrDigit(ch) && !Character.isWhitespace(ch);
}