|
|
@@ -585,8 +585,15 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
|
|
|
// 不再做 inner.toString() 兜底——整串 Result 里有 files=[]/inputs=[] 等空数组
|
|
|
// 会被误当成 AI 输出的 JSON 数组(如 [models]),污染解析。
|
|
|
String text = extractCleanText(r.getResult());
|
|
|
- //去掉text中<think>...</think>之间的内容,<think>...</think>也不保留
|
|
|
- text = text.replaceAll("<think>.*?</think>", "");
|
|
|
+ // 去掉 <think>...</think>(含标签本身)。思考块是多行的,必须用 stripThinkBlocks 里的 DOTALL 正则,
|
|
|
+ // 直接写 replaceAll("<think>.*?</think>", "") 会因为 . 不匹配换行而整段删不掉。
|
|
|
+ text = stripThinkBlocks(text);
|
|
|
+ if (text == null) {
|
|
|
+ log.warn("AI 返回内容为空,emailId={}", marketEmailsEntity.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 删掉思考块后正文开头/结尾常留下一串空行:连续 3 个以上换行压成 2 个(保留段落间隔),再去首尾空白
|
|
|
+ text = text.replaceAll("\\n{3,}", "\n\n").trim();
|
|
|
marketEmailsEntity.setContent(text);
|
|
|
|
|
|
this.updateById(marketEmailsEntity);
|
|
|
@@ -1820,6 +1827,35 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
|
|
|
return t == null ? null : t.toString();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 剥掉 AI 输出里的思考块。
|
|
|
+ * <p>
|
|
|
+ * 规则:<strong>凡是落在 {@code <think>} 与 {@code </think>} 之间的内容,连同这两个标签本身一起删掉</strong>;
|
|
|
+ * 标签之外的正文原样保留(多个思考块夹着正文时,只删块、保留正文)。
|
|
|
+ * <p>
|
|
|
+ * 两个坑:
|
|
|
+ * <ol>
|
|
|
+ * <li><strong>必须开 DOTALL</strong>。模型的思考过程几乎都是多行的,而 Java 正则里 {@code .} 默认
|
|
|
+ * 不匹配 {@code \n}。写成 {@code "<think>.*?</think>"} 时多行块整个匹配不上,
|
|
|
+ * 结果就是一整段思考过程原样留在邮件正文里 —— 本次的 bug 就在这里。</li>
|
|
|
+ * <li>兼容"只有 {@code <think>} 没有 {@code </think>}"的截断情况(思考过程超长被砍断很常见),
|
|
|
+ * 此时从开标签一直删到文本结尾,避免思考片段混进正文。</li>
|
|
|
+ * </ol>
|
|
|
+ *
|
|
|
+ * @param text 原始文本,允许为 null
|
|
|
+ * @return 去掉思考块后的文本;null 原样返回
|
|
|
+ */
|
|
|
+ private String stripThinkBlocks(String text) {
|
|
|
+ if (text == null || text.isEmpty()) {
|
|
|
+ return text;
|
|
|
+ }
|
|
|
+ // (?s) 让 . 匹配换行;(?i) 兼容 <Think>;[^>]* 兼容带属性的开标签(如 <think lang="zh">)
|
|
|
+ // (?:...</think>|$) 兜底未闭合:有闭合标签就截到闭合处,没有就截到结尾
|
|
|
+ String cleaned = text.replaceAll("(?si)<think\\b[^>]*>[\\s\\S]*?(?:</think>|$)", "");
|
|
|
+ // 再兜一层:清掉可能残留的孤立标签(比如正文里孤零零一个 </think>)
|
|
|
+ return cleaned.replaceAll("(?i)</?think\\b[^>]*>", "");
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 从 AI 文本中解析排期阶段列表。
|
|
|
* 兼容:思考块包裹、json 代码块、裸数组、全角标点/括号,以及模型把输出数组重复拼接的情况。
|
|
|
@@ -1943,7 +1979,7 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
|
|
|
*/
|
|
|
private List<String> extractJsonCandidates(String text) {
|
|
|
List<String> result = new ArrayList<>();
|
|
|
- String cleaned = text.replaceAll("(?s)<think>[\\s\\S]*?</think>", "");
|
|
|
+ String cleaned = stripThinkBlocks(text);
|
|
|
int from = 0;
|
|
|
while (true) {
|
|
|
int start = cleaned.indexOf('[', from);
|