From 50a9867d0218feff21389e6a433735ae2456275a Mon Sep 17 00:00:00 2001 From: hub-gif <2487812171@qq.com> Date: Tue, 21 Apr 2026 11:43:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(pipeline):=208.3=E6=83=85=E6=84=9F=E5=AD=90?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E9=99=8D=E4=B8=BA=E4=BA=94=E7=BA=A7=E4=BB=A5?= =?UTF-8?q?=E4=BE=BF=E7=BB=86=E7=B1=BB=E6=8A=A5=E5=91=8A=E6=91=98=E5=BD=95?= =?UTF-8?q?=E5=91=BD=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strategy 侧 load_report_matrix_group_evidence 按 #### 细类 抽取时在下一行 #### 即截断,导致正/负向主题正文丢失;在 matrix_group_focus 成稿后对四个固定小节做 ####→##### 后处理,并补充单测。 Made-with: Cursor --- backend/pipeline/llm/generate_sections.py | 30 ++++++++++++++++++- .../reporting/report_matrix_group_evidence.py | 2 ++ .../test_report_matrix_group_evidence.py | 22 ++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/backend/pipeline/llm/generate_sections.py b/backend/pipeline/llm/generate_sections.py index c51f857..3d002ee 100644 --- a/backend/pipeline/llm/generate_sections.py +++ b/backend/pipeline/llm/generate_sections.py @@ -34,6 +34,31 @@ SENTIMENT_LLM_SYSTEM = """你是电商/食品类用户研究助手。输入 JSON **篇幅**:若 JSON 含 ``matrix_group_focus``(单细类范围),本节总字数约 **500~1200 字**,勿再按全关键词池写「全行业泛化」;若**不含**该字段(全量池),总字数约 **700~1600 字**。简体中文,语气客观。""" +# 嵌入报告 8.3 时外层为 ``#### {细类名}``;若内文仍用同级 ``#### 正向体验主题``, +# ``extract_level4_sections_by_group_title`` 会在第一个子 ``####`` 处截断,导致策略摘录/心得侧「同细类报告摘录」拿不到正文。 +_SENTIMENT_INNER_H4_TITLES: frozenset[str] = frozenset( + { + "正向体验主题", + "负向评价主题归因", + "混合评价中的典型张力", + "使用注意", + } +) + + +def demote_sentiment_inner_h4_to_h5_for_matrix_group(md: str) -> str: + """将情感归纳四个固定小节从 ``####`` 降为 ``#####``,以便嵌在 ``#### 细类`` 下仍能被按细类抽取。""" + out_lines: list[str] = [] + for line in (md or "").splitlines(): + m = re.match(r"^####\s+(.+)$", line) + if m: + title = m.group(1).strip() + if title in _SENTIMENT_INNER_H4_TITLES: + out_lines.append(f"##### {title}") + continue + out_lines.append(line) + return "\n".join(out_lines) + def generate_comment_sentiment_analysis_llm(payload: dict[str, Any]) -> str: """基于 lexicon 统计 + 语义池与按词表归类的抽样,生成评价情感归纳段落(Markdown);**默认不**嵌入竞品报告正文。""" @@ -60,7 +85,10 @@ def generate_comment_sentiment_analysis_llm(payload: dict[str, Any]) -> str: if len(raw) > 88_000: raw = raw[:82_000] + "\n\n…(输入过长已截断,请勿编造截断外内容)\n" user = "请根据以下 JSON 按系统说明输出 Markdown:" + scope_note + "\n\n" + raw - return call_llm(SENTIMENT_LLM_SYSTEM, user) + out = call_llm(SENTIMENT_LLM_SYSTEM, user) + if isinstance(mg, str) and mg.strip(): + out = demote_sentiment_inner_h4_to_h5_for_matrix_group(out) + return out def split_competitor_report_for_bridges( diff --git a/backend/pipeline/reporting/report_matrix_group_evidence.py b/backend/pipeline/reporting/report_matrix_group_evidence.py index bf75156..98d91f7 100644 --- a/backend/pipeline/reporting/report_matrix_group_evidence.py +++ b/backend/pipeline/reporting/report_matrix_group_evidence.py @@ -4,6 +4,8 @@ 报告生成侧约定:矩阵/价盘/促销/评论/场景等 LLM 小节均以 ``#### `` + 与矩阵一致的细类名为小节标题 (见 ``generate_group_summaries`` 系统提示)。 +**8.3 评价正/负向主题**:细类下内层小节使用 ``#####``(由 ``demote_sentiment_inner_h4_to_h5_for_matrix_group`` 处理), +避免与外层 ``#### 细类名`` 同级导致本节正文抽取为空。 """ from __future__ import annotations diff --git a/backend/pipeline/tests/test_report_matrix_group_evidence.py b/backend/pipeline/tests/test_report_matrix_group_evidence.py index bf0e0c5..54b95da 100644 --- a/backend/pipeline/tests/test_report_matrix_group_evidence.py +++ b/backend/pipeline/tests/test_report_matrix_group_evidence.py @@ -3,6 +3,7 @@ from __future__ import annotations from django.test import SimpleTestCase +from pipeline.llm.generate_sections import demote_sentiment_inner_h4_to_h5_for_matrix_group from pipeline.reporting.report_matrix_group_evidence import ( extract_level4_sections_by_group_title, ) @@ -37,3 +38,24 @@ B 段评论归纳。 extract_level4_sections_by_group_title("## 二\n", "饼干"), [], ) + + def test_extract_8_3_sentiment_when_inner_subsections_are_h5(self) -> None: + """8.3 在 #### 细类 下须用 ##### 子标题,否则抽取在首个 #### 子节处断开。""" + inner = demote_sentiment_inner_h4_to_h5_for_matrix_group( + "#### 正向体验主题\n\n正文A\n\n#### 负向评价主题归因\n\n正文B\n" + ) + md = ( + "### 8.3 评价正/负向主题\n\n" + "#### 饼干\n\n" + f"{inner}\n" + ) + parts = extract_level4_sections_by_group_title(md, "饼干") + self.assertEqual(len(parts), 1) + self.assertIn("正文A", parts[0]) + self.assertIn("正文B", parts[0]) + + def test_demote_known_h4_titles_only(self) -> None: + raw = "#### 正向体验主题\nx\n#### 饼干\ny\n" + out = demote_sentiment_inner_h4_to_h5_for_matrix_group(raw) + self.assertIn("##### 正向体验主题", out) + self.assertIn("#### 饼干", out)