fix:优化输出内容

This commit is contained in:
Daniel
2026-04-08 11:39:17 +08:00
parent 222bf2e70d
commit 17591de58f

View File

@@ -34,9 +34,9 @@ def _is_likely_timeout_error(exc: BaseException) -> bool:
return "timed out" in s or "timeout" in s return "timed out" in s or "timeout" in s
# 短文洗稿:5 个自然段、正文总字数上限(含标点 # 短文洗稿:正文目标约 500 字,优先完整性(软约束,不硬截断
MAX_BODY_CHARS = 500
MIN_BODY_CHARS = 80 MIN_BODY_CHARS = 80
TARGET_BODY_CHARS = 500
def _preview_for_log(text: str, limit: int = 400) -> str: def _preview_for_log(text: str, limit: int = 400) -> str:
@@ -54,7 +54,7 @@ SYSTEM_PROMPT = """
1) **忠实原意**:只概括、转述原文已有信息,不编造事实,不偷换主题; 1) **忠实原意**:只概括、转述原文已有信息,不编造事实,不偷换主题;
2) 语气通俗、干脆,避免套话堆砌; 2) 语气通俗、干脆,避免套话堆砌;
3) 只输出合法 JSONtitle, summary, body_markdown 3) 只输出合法 JSONtitle, summary, body_markdown
4) **body_markdown 约束**恰好 **5 个自然段**;段与段之间用一个空行分隔;**不要**使用 # / ## 标题符号;全文(正文)总字数 **不超过 500 字**(含标点) 4) **body_markdown 约束**按内容密度使用 **4~6 个自然段**;段与段之间用一个空行分隔;**不要**使用 # / ## 标题符号;正文以 **约 500 字**为目标,优先完整表达并避免冗长重复
5) title、summary 也要短:标题约 818 字;摘要约 4080 字; 5) title、summary 也要短:标题约 818 字;摘要约 4080 字;
6) 正文每段需首行缩进(建议段首使用两个全角空格「  」),避免顶格; 6) 正文每段需首行缩进(建议段首使用两个全角空格「  」),避免顶格;
7) 关键观点需要加粗:请用 Markdown `**加粗**` 标出 2~4 个重点短语; 7) 关键观点需要加粗:请用 Markdown `**加粗**` 标出 2~4 个重点短语;
@@ -71,19 +71,19 @@ REWRITE_SCHEMA_HINT = """
} }
body_markdown 写法: body_markdown 写法:
- 必须且只能有 **5 段**:每段若干完整句子,段之间 **\\n\\n**(空一行); - 使用 **4~6 段**:每段若干完整句子,段之间 **\\n\\n**(空一行);
- **禁止** markdown 标题(不要用 # - **禁止** markdown 标题(不要用 #
- 正文总长 **500 字**,宁可短而清楚,不要写满废话 - 正文目标约 **500 字**(可上下浮动),以信息完整为先,避免冗长和重复
- 每段段首请保留首行缩进(两个全角空格「  」); - 每段段首请保留首行缩进(两个全角空格「  」);
- 请用 `**...**` 加粗 2~4 个关键观点词; - 请用 `**...**` 加粗 2~4 个关键观点词;
- 内容顺序建议:第 1 段交代在说什么;中间 3 段展开关键信息;最后 1 段收束或提醒(均须紧扣原文,勿乱发挥)。 - 内容顺序建议:段交代在说什么;中间段展开关键信息;段收束或提醒(均须紧扣原文,勿乱发挥)。
""".strip() """.strip()
# 通义等模型若首次过短/结构不对,再要一次 # 通义等模型若首次过短/结构不对,再要一次
_JSON_BODY_TOO_SHORT_RETRY = """ _JSON_BODY_TOO_SHORT_RETRY = """
【系统复检】上一次 body_markdown 不符合要求。请重输出**完整** JSON 【系统复检】上一次 body_markdown 不符合要求。请重输出**完整** JSON
- 正文必须 **恰好 5 个自然段**(仅 \\n\\n 分段),无 # 标题,总字数 **≤500 字** - 正文必须使用 **4~6 个自然段**(仅 \\n\\n 分段),无 # 标题;篇幅尽量收敛到约 500 字,同时保持信息完整
- 忠实原稿、简短高效; - 忠实原稿、简短高效;
- 引号只用「」『』; - 引号只用「」『』;
- 只输出 JSON。 - 只输出 JSON。
@@ -347,7 +347,8 @@ class AIRewriter:
self, req: RewriteRequest, cleaned_source: str, reason: str, trace: dict[str, Any] | None = None self, req: RewriteRequest, cleaned_source: str, reason: str, trace: dict[str, Any] | None = None
) -> RewriteResponse: ) -> RewriteResponse:
sentences = self._extract_sentences(cleaned_source) sentences = self._extract_sentences(cleaned_source)
points = self._pick_key_points(sentences, limit=5) para_count = self._fallback_para_count(cleaned_source)
points = self._pick_key_points(sentences, limit=max(5, para_count))
title = req.title_hint.strip() or self._build_fallback_title(sentences) title = req.title_hint.strip() or self._build_fallback_title(sentences)
summary = self._build_fallback_summary(points, cleaned_source) summary = self._build_fallback_summary(points, cleaned_source)
@@ -358,17 +359,14 @@ class AIRewriter:
t = re.sub(r"\s+", " ", (s or "").strip()) t = re.sub(r"\s+", " ", (s or "").strip())
return t if len(t) <= n else t[: n - 1] + "" return t if len(t) <= n else t[: n - 1] + ""
paras = [ paras = [_one_line(self._build_intro(points, cleaned_source), 105)]
_one_line(self._build_intro(points, cleaned_source), 105), if para_count >= 4:
_one_line(analysis["cause"], 105), paras.append(_one_line(analysis["cause"], 105))
_one_line(analysis["impact"], 105), paras.append(_one_line(analysis["impact"], 105))
_one_line(analysis["risk"], 105), if para_count >= 5:
_one_line(conclusion, 105), paras.append(_one_line(analysis["risk"], 105))
] paras.append(_one_line(conclusion, 105))
body = "\n\n".join(paras) body = "\n\n".join(paras)
if len(body) > MAX_BODY_CHARS:
body = body[: MAX_BODY_CHARS - 1] + ""
normalized = { normalized = {
"title": title, "title": title,
"summary": summary, "summary": summary,
@@ -429,6 +427,14 @@ class AIRewriter:
), ),
} }
def _fallback_para_count(self, source: str) -> int:
length = len((source or "").strip())
if length < 240:
return 4
if length > 1200:
return 6
return 5
def _clean_source(self, text: str) -> str: def _clean_source(self, text: str) -> str:
src = (text or "").replace("\r\n", "\n").strip() src = (text or "").replace("\r\n", "\n").strip()
src = re.sub(r"https?://\S+", "", src) src = re.sub(r"https?://\S+", "", src)
@@ -725,8 +731,6 @@ class AIRewriter:
text = (body or "").strip() text = (body or "").strip()
if not text: if not text:
text = "(正文生成失败,请重试。)" text = "(正文生成失败,请重试。)"
if len(text) > MAX_BODY_CHARS:
text = text[: MAX_BODY_CHARS - 1] + ""
return text return text
def _quality_issues( def _quality_issues(
@@ -749,16 +753,18 @@ class AIRewriter:
paragraphs = [p.strip() for p in re.split(r"\n\s*\n", body) if p.strip()] paragraphs = [p.strip() for p in re.split(r"\n\s*\n", body) if p.strip()]
pc = len(paragraphs) pc = len(paragraphs)
need_p = 4 if lenient else 5 min_p, max_p = (3, 6) if lenient else (4, 6)
if pc < need_p: if pc < min_p:
issues.append(f"正文需约 5 个自然段、空行分隔(当前 {pc} 段)") issues.append(f"正文段落偏少(当前 {pc} 段),建议 {min_p}-{max_p}")
elif not lenient and pc > 6: elif pc > max_p:
issues.append(f"正文段落多(当前 {pc} 段),请合并为 5 段左右") issues.append(f"正文段落多(当前 {pc} 段),建议控制在 {min_p}-{max_p}")
if len(body) > MAX_BODY_CHARS: if len(body) < MIN_BODY_CHARS:
issues.append(f"正文超过 {MAX_BODY_CHARS} 字(当前 {len(body)} 字),请压缩")
elif len(body) < MIN_BODY_CHARS:
issues.append(f"正文过短(当前阈值 ≥{MIN_BODY_CHARS} 字)") issues.append(f"正文过短(当前阈值 ≥{MIN_BODY_CHARS} 字)")
elif len(body) > 900:
issues.append(
f"正文偏长(当前 {len(body)} 字),建议收敛到约 {TARGET_BODY_CHARS} 字(可上下浮动)"
)
if re.search(r"(?m)^#+\s", body): if re.search(r"(?m)^#+\s", body):
issues.append("正文请勿使用 # 标题符号,只用自然段") issues.append("正文请勿使用 # 标题符号,只用自然段")
@@ -805,8 +811,6 @@ class AIRewriter:
title = (normalized.get("title") or "").strip() title = (normalized.get("title") or "").strip()
if len(title) < 4 or len(body) < 40: if len(title) < 4 or len(body) < 40:
return False return False
if len(body) > MAX_BODY_CHARS + 80:
return False
return True return True
def _format_markdown(self, text: str) -> str: def _format_markdown(self, text: str) -> str: