diff --git a/backend/crawler_copy/jd_pc_search/jd_competitor_report.py b/backend/crawler_copy/jd_pc_search/jd_competitor_report.py index c4f00ae..a33b995 100644 --- a/backend/crawler_copy/jd_pc_search/jd_competitor_report.py +++ b/backend/crawler_copy/jd_pc_search/jd_competitor_report.py @@ -1126,10 +1126,10 @@ def build_comment_sentiment_llm_payload( shuffle_seed: str = "", ) -> dict[str, Any]: """ - 供大模型做正/负向语义归纳:附规则统计、关键词分桶抽样,以及 **sample_reviews_semantic_pool** + 供大模型做正/负向语义归纳:附规则统计、按关键词规则**归类**后的抽样,以及 **sample_reviews_semantic_pool** (全量去重后的评价句确定性洗牌抽样,供模型结合语境自行判断褒贬)。 - ``sentiment_bucket_method`` 标明分桶依据为子串词表;条形图与 lexicon 仍与此口径一致, + ``sentiment_bucket_method`` 标明归类依据为子串词表;条形图与 lexicon 仍与此口径一致, 但正文归纳应以模型对 ``sample_reviews_semantic_pool`` 的整句理解为准。 """ pos_only_texts: list[str] = [] @@ -1600,7 +1600,7 @@ def _category_token_meaningless(seg: str) -> bool: def _matrix_display_segment_from_parts(parts: list[str]) -> str | None: """ 与历史逻辑一致的主选段;若该段无意义则自右向左找第一段可读文本 - (避免「仅类目码」或中间段为数字 ID 时整组成品名式乱桶)。 + (避免「仅类目码」或中间段为数字 ID 时,把路径误解析成无意义的细类展示名)。 """ if not parts: return None @@ -2620,7 +2620,7 @@ def build_competitor_markdown( "", "#### 大模型深入解读(主题归因,与词频统计互补)", "", - "> **说明**:基于与上节**同一分桶规则**抽样的评价原文,由大模型归纳**用户在说什么**(尤其是负向的具体事由),与上列条数、条形图**互补**;引文以原评论为准。", + "> **说明**:基于与上节**同一套关键词归类规则**抽样的评价原文,由大模型归纳**用户在说什么**(尤其是负向的具体事由),与上列条数、条形图**互补**;引文以原评论为准。", "", _llm_s, ] diff --git a/backend/pipeline/llm/generate.py b/backend/pipeline/llm/generate.py index 591ceba..70c0dc5 100644 --- a/backend/pipeline/llm/generate.py +++ b/backend/pipeline/llm/generate.py @@ -116,7 +116,7 @@ SENTIMENT_LLM_SYSTEM = """你是电商/食品类用户研究助手。输入 JSON - ``comment_sentiment_lexicon``:子串词表统计(与报告条形图口径一致,**仅作定量参考**;子串命中≠说话人态度)。 - ``positive_lexeme_hits_top`` / ``negative_lexeme_hits_top``:短语级命中摘要(同源)。 -- ``sentiment_bucket_method``:恒为 ``keyword_substring_heuristic``;``sample_reviews_positive_biased`` / ``negative`` / ``mixed_tone`` 是按该词表机械分桶的抽样,**可能与整句真实褒贬不一致**(例如「软硬适中」曾被误归负向)。 +- ``sentiment_bucket_method``:恒为 ``keyword_substring_heuristic``;``sample_reviews_positive_biased`` / ``negative`` / ``mixed_tone`` 是按该词表**机械归类**的抽样,**可能与整句真实褒贬不一致**(例如「软硬适中」曾被误归负向)。 - **``sample_reviews_semantic_pool``**(若有):本批评价经去重后的**随机/洗牌抽样**,覆盖未命中任一关键词的句子。**归纳正/负向体验、引用「」短引文时,优先以此池与上述各列表中的原文为准,自行结合语境理解**:转折、对比(如「没那么甜」「软硬适中」)、先抑后扬/先扬后抑整句态度;**不得以子串是否命中负面词来断言该句为抱怨**。 每条样本通常以 ``【细类:…|SKU:…|品名:…|店铺:…】`` 开头,表示 **§5 细类、SKU、品名、店铺**;写归纳与「」引文时须能还原「哪家店、哪条 SKU、哪款品名」,或保留前缀,**禁止**无指代地写「用户普遍…」。 @@ -125,7 +125,7 @@ SENTIMENT_LLM_SYSTEM = """你是电商/食品类用户研究助手。输入 JSON - **仅输出 Markdown 正文**(不要用 ``` 围栏包裹全文); - **不要编造**样本中未出现的具体事实、品牌、价格、医学功效; - **定量数字**(条数、占比、lexicon 各字段)须与 ``comment_sentiment_lexicon`` **一致**,勿编造; -- **定性归纳**(满意点/抱怨点、引语是否算差评):以**整句语义**为准;若某句在语义上为褒义或中性描述,**不得**放入「质地差、口感硬」等负向归因;若词表分桶与句意冲突,**以句意为准**,并在「使用注意」点明「关键词分桶仅作统计口径」。 +- **定性归纳**(满意点/抱怨点、引语是否算差评):以**整句语义**为准;若某句在语义上为褒义或中性描述,**不得**放入「质地差、口感硬」等负向归因;若词表归类结果与句意冲突,**以句意为准**,并在「使用注意」点明「关键词归类仅作统计口径」。 - 若某措辞**未**出现在任一抽样原文(含前缀后正文)中,**禁止**用引号写成直接引语。 - **不要**只复述「某词出现 N 次」——条形图已展示;你的价值是**语义归纳**。 @@ -133,17 +133,17 @@ SENTIMENT_LLM_SYSTEM = """你是电商/食品类用户研究助手。输入 JSON 1. ``#### 正向体验主题``:3~6 条;概括满意点(口感、甜度、性价比等),**尽量**用「」引用 ``sample_reviews_semantic_pool`` 或其它样本中**语义确为正面**的短句(勿把对比褒义句当差评例子)。 2. ``#### 负向评价主题归因``:**核心段落**。依据你读后判定为**确有不满**的句子,归纳 **4~8 个**问题维度(口味、质地、价格、物流等)。引文优先取自句意确为批评的原文(可来自任一档位键,不限于 ``sample_reviews_negative_biased``);引文须含 ``【细类…|…店铺…】`` 或同义店铺+品名/SKU。 3. ``#### 混合评价中的典型张力``(可选):同一评价里褒贬并存时,说明在争什么;若无则略写。 -4. ``#### 使用注意``:关键词子串统计的局限、``sample_reviews_semantic_pool`` 与分桶的差异、抽样截断、非医学结论。 +4. ``#### 使用注意``:关键词子串统计的局限、``sample_reviews_semantic_pool`` 与词表归类的差异、抽样截断、非医学结论。 总字数约 **700~1600 字**,简体中文,语气客观。""" def generate_comment_sentiment_analysis_llm(payload: dict[str, Any]) -> str: - """基于 lexicon 统计 + 语义池与分桶抽样,生成 §8.2 大模型解读段落(Markdown)。""" + """基于 lexicon 统计 + 语义池与按词表归类的抽样,生成 §8.2 大模型解读段落(Markdown)。""" p = dict(payload) raw = json.dumps(p, ensure_ascii=False) if len(raw) > 88_000: - # 超长时优先压缩关键词分桶样本,再压缩语义池;尽量保留 semantic_pool 条数略多 + # 超长时优先压缩关键词归类样本,再压缩语义池;尽量保留 semantic_pool 条数略多 for k, cap, maxlen in ( ("sample_reviews_positive_biased", 6, 180), ("sample_reviews_mixed_tone", 4, 180), diff --git a/backend/pipeline/matrix_group_label.py b/backend/pipeline/matrix_group_label.py index 182ea68..20fa1ff 100644 --- a/backend/pipeline/matrix_group_label.py +++ b/backend/pipeline/matrix_group_label.py @@ -22,7 +22,7 @@ def category_token_meaningless(seg: str) -> bool: def matrix_display_segment_from_parts(parts: list[str]) -> str | None: """ 与历史逻辑一致的主选段;若该段无意义则自右向左找第一段可读文本 - (避免「仅类目码」或中间段为数字 ID 时整组成品名式乱桶)。 + (避免「仅类目码」或中间段为数字 ID 时,把路径误解析成无意义的细类展示名)。 """ if not parts: return None diff --git a/backend/pipeline/tests/test_competitor_brief.py b/backend/pipeline/tests/test_competitor_brief.py index 3982fce..a966282 100644 --- a/backend/pipeline/tests/test_competitor_brief.py +++ b/backend/pipeline/tests/test_competitor_brief.py @@ -112,7 +112,7 @@ class BuildCompetitorBriefTests(SimpleTestCase): fb = jcr._consumer_feedback_by_matrix_group( merged_rows=merged, comment_rows=[ - {"sku": "222", "tagCommentContent": "缺路径仍不应进细类桶"}, + {"sku": "222", "tagCommentContent": "缺路径仍不应计入按细类统计"}, {"sku": "111", "tagCommentContent": "有路径进细类"}, ], sku_header=sku_h,