fix(llm): 评论细类归纳载荷移除 focus_hit_lines 并对齐系统提示

build_comment_groups_llm_payload 不再传入关注词或生成子串命中摘要;COMMENT_GROUPS_SYSTEM 仅依据 effective_text_lines 与 sample_text_snippets。探针合并与 PROBE 提示词去掉 focus_hit_lines 表述;runner/演示脚本去掉无效的 comment_focus_words 装配。

Made-with: Cursor
This commit is contained in:
hub-gif 2026-04-21 10:35:52 +08:00
parent 30ea9783b3
commit 396ccd941a
6 changed files with 30 additions and 77 deletions

View File

@ -220,12 +220,11 @@ def build_promo_groups_llm_payload(
def build_comment_groups_llm_payload( def build_comment_groups_llm_payload(
*, *,
feedback_groups: list[tuple[str, list[dict[str, str]], list[str]]], feedback_groups: list[tuple[str, list[dict[str, str]], list[str]]],
focus_words: tuple[str, ...],
merged_rows: list[dict[str, str]], merged_rows: list[dict[str, str]],
sku_header: str, sku_header: str,
title_h: str, title_h: str,
) -> list[dict[str, Any]]: ) -> list[dict[str, Any]]:
"""供 ``generate_comment_group_summaries_llm``。""" """供 ``generate_comment_group_summaries_llm``:仅含评价文本单元与短摘录,不含关注词子串计数摘要。"""
if not feedback_groups: if not feedback_groups:
return [] return []
sku_meta: dict[str, tuple[str, str, str]] = {} sku_meta: dict[str, tuple[str, str, str]] = {}
@ -245,10 +244,6 @@ def build_comment_groups_llm_payload(
for gname, cr, tu in feedback_groups: for gname, cr, tu in feedback_groups:
if not tu and not cr: if not tu and not cr:
continue continue
gh = _group_keyword_hits(cr, tu, focus_words=focus_words)
focus_hit_lines = [
f"「{w}」{n} 次" for w, n in gh.most_common(14) if n > 0
]
snippets: list[str] = [] snippets: list[str] = []
for row in cr[:48]: for row in cr[:48]:
txt = _cell(row, _COMMENT_CSV_BODY, "tagCommentContent") txt = _cell(row, _COMMENT_CSV_BODY, "tagCommentContent")
@ -277,7 +272,6 @@ def build_comment_groups_llm_payload(
"group": gname, "group": gname,
"comment_flat_rows": f"评价行 {len(cr)};有效文本单元 {len(tu)}", "comment_flat_rows": f"评价行 {len(cr)};有效文本单元 {len(tu)}",
"effective_text_lines": eff, "effective_text_lines": eff,
"focus_hit_lines": focus_hit_lines,
"sample_text_snippets": snippets, "sample_text_snippets": snippets,
} }
) )

View File

@ -100,14 +100,14 @@ PROBE_TEXT_MINING_SYSTEM = """你是用户研究与文本挖掘方向的助手
- ``probe_status``:``ok`` 表示该细类已完成分词与统计;``skipped`` 表示样本过少等未下钻。 - ``probe_status``:``ok`` 表示该细类已完成分词与统计;``skipped`` 表示样本过少等未下钻。
- ``word_freq_top`` / ``tfidf_top`` / ``cooccurrence_top``:统计型特征(开放词表)。 - ``word_freq_top`` / ``tfidf_top`` / ``cooccurrence_top``:统计型特征(开放词表)。
- ``lda``:无监督自动归纳的主题词;**仅为探索**,同一词可出现在多主题,**禁止**当作严格品类或固定标签。 - ``lda``:无监督自动归纳的主题词;**仅为探索**,同一词可出现在多主题,**禁止**当作严格品类或固定标签。
- ``focus_hit_lines`` / ``sample_text_snippets``:与正式管线摘取方式**类似**,仅用于**对照语境**;若与关键词突出度焦点冲突,以**整句原文**为准,并在段末或「使用注意」中可点明「统计与语义可能不一致」。 - ``sample_text_snippets``:与正式报告管线同源的评价短摘录(若有),仅用于**对照语境**;若与关键词突出度焦点冲突,以**整句原文**为准,并在段末或「使用注意」中可点明「统计与语义可能不一致」。
**任务**:对 ``groups`` 中**每一项**输出对应 Markdown(**顺序与输入一致**): **任务**:对 ``groups`` 中**每一项**输出对应 Markdown(**顺序与输入一致**):
- 对 ``probe_status == "ok"``:以 ``#### `` + 与该条 ``group`` 字段**完全一致**的细类名作为小节标题(勿用 ``##`` 一级标题);每段约 **100~260 字**。 - 对 ``probe_status == "ok"``:以 ``#### `` + 与该条 ``group`` 字段**完全一致**的细类名作为小节标题(勿用 ``##`` 一级标题);每段约 **100~260 字**。
- 内容须包含:①用 **1~2 句**概括该细类评论**主要讨论焦点**(综合词频与关键词突出度,**不要罗列具体数字**);② **1~2 句**说明共现词对**暗示**哪些维度常一起出现(**非因果**);③若 ``lda.topics`` 非空,**1~2 句**说明主题粗分侧重点,并**明确**算法无监督、**不**与矩阵细类一一对应;④用 **1~2 句**体现 ``sample_text_snippets`` / ``focus_hit_lines`` 中的**用户语气与关切**(以**转述**为主);若必须引用原文,**全小节合计**仅 **一处**极短引号内容(**≤40 字**,**不要**输出 ``【细类…SKU…店铺…】`` 等长前缀);若无可用摘录则写明;⑤ **使用场景(仅从评论推断)**:用 **0~2 句**概括**何时、何地、何人、如何搭配**等(如早餐、加餐、控糖人群、配牛奶等)——**只能**依据本细类 ``word_freq``/``tfidf``/``cooccurrence``/``lda`` 与摘录中**已出现或可合理概括**的信息;**禁止**套用正式报告「场景分组」或其它外部场景分类;若统计与摘录中**均无**场景线索,**一句**写明「评论中未体现清晰使用场景」即可。 - 内容须包含:①用 **1~2 句**概括该细类评论**主要讨论焦点**(综合词频与关键词突出度,**不要罗列具体数字**);② **1~2 句**说明共现词对**暗示**哪些维度常一起出现(**非因果**);③若 ``lda.topics`` 非空,**1~2 句**说明主题粗分侧重点,并**明确**算法无监督、**不**与矩阵细类一一对应;④用 **1~2 句**体现 ``sample_text_snippets`` 中的**用户语气与关切**(以**转述**为主);若必须引用原文,**全小节合计**仅 **一处**极短引号内容(**≤40 字**,**不要**输出 ``【细类…SKU…店铺…】`` 等长前缀);若无可用摘录则写明;⑤ **使用场景(仅从评论推断)**:用 **0~2 句**概括**何时、何地、何人、如何搭配**等(如早餐、加餐、控糖人群、配牛奶等)——**只能**依据本细类 ``word_freq``/``tfidf``/``cooccurrence``/``lda`` 与摘录中**已出现或可合理概括**的信息;**禁止**套用正式报告「场景分组」或其它外部场景分类;若统计与摘录中**均无**场景线索,**一句**写明「评论中未体现清晰使用场景」即可。
- 对 ``probe_status == "skipped"``:该小节仅 **一句**说明原因。 - 对 ``probe_status == "skipped"``:该小节仅 **一句**说明原因。
**禁止**:编造数据中未出现的品牌、价格、医学功效或疗效承诺;不要把 ``keyword`` 监测词写进「用户原话」;不要输出 Markdown 表格;不要声称本段与「正式报告第八章末」完全同源——本任务为**补充分析解读**。**禁止**把输入里的 ``sample_text_snippets`` / ``focus_hit_lines`` **逐条罗列**、**多条整段复制**到输出(那不是归纳,是重复贴评论)。 **禁止**:编造数据中未出现的品牌、价格、医学功效或疗效承诺;不要把 ``keyword`` 监测词写进「用户原话」;不要输出 Markdown 表格;不要声称本段与「正式报告第八章末」完全同源——本任务为**补充分析解读**。**禁止**把输入里的 ``sample_text_snippets`` **逐条罗列**、**多条整段复制**到输出(那不是归纳,是重复贴评论)。
全文末可另起一段 **「使用注意」**(简短):点明开放词表统计与人工阅读差异、主题归纳局限、小样本细类不可靠。 全文末可另起一段 **「使用注意」**(简短):点明开放词表统计与人工阅读差异、主题归纳局限、小样本细类不可靠。
@ -381,23 +381,6 @@ def _narrative_stub(
return "\n".join(lines) return "\n".join(lines)
def _effective_focus_words(run_dir: Path) -> tuple[str, ...]:
"""与 ``runner.write_competitor_analysis_for_run_dir`` 一致:优先 ``effective_report_config.json``。"""
p = run_dir / "effective_report_config.json"
if p.is_file():
try:
eff = json.loads(p.read_text(encoding="utf-8"))
except json.JSONDecodeError:
eff = None
if isinstance(eff, dict):
fw_src = eff.get("comment_focus_words") or list(jcr.COMMENT_FOCUS_WORDS)
fw_tuple = tuple(
str(x).strip() for x in fw_src if str(x).strip()
) or jcr.COMMENT_FOCUS_WORDS
return fw_tuple
return jcr.COMMENT_FOCUS_WORDS
def _truncate_probe_payload(payload: dict[str, Any]) -> dict[str, Any]: def _truncate_probe_payload(payload: dict[str, Any]) -> dict[str, Any]:
"""压缩摘录长度,避免单次 JSON 顶满上下文。""" """压缩摘录长度,避免单次 JSON 顶满上下文。"""
out: dict[str, Any] = dict(payload) out: dict[str, Any] = dict(payload)
@ -407,6 +390,7 @@ def _truncate_probe_payload(payload: dict[str, Any]) -> dict[str, Any]:
groups.append(g) groups.append(g)
continue continue
g2 = dict(g) g2 = dict(g)
g2.pop("focus_hit_lines", None)
sn = g2.get("sample_text_snippets") sn = g2.get("sample_text_snippets")
if isinstance(sn, list): if isinstance(sn, list):
# 条数略减,降低模型「照抄罗列」倾向;仍以转述为主见系统提示 # 条数略减,降低模型「照抄罗列」倾向;仍以转述为主见系统提示
@ -421,7 +405,6 @@ def _merge_snippets_from_comment_groups(
*, *,
merged_rows: list[dict[str, str]], merged_rows: list[dict[str, str]],
comment_rows: list[dict[str, str]], comment_rows: list[dict[str, str]],
run_dir: Path,
) -> None: ) -> None:
"""把正式 ``build_comment_groups_llm_payload`` 中的摘录并入补充分析行(原地修改)。""" """把正式 ``build_comment_groups_llm_payload`` 中的摘录并入补充分析行(原地修改)。"""
sku_h = MERGED_FIELD_TO_CSV_HEADER["sku_id"] sku_h = MERGED_FIELD_TO_CSV_HEADER["sku_id"]
@ -431,10 +414,8 @@ def _merge_snippets_from_comment_groups(
comment_rows=comment_rows, comment_rows=comment_rows,
sku_header=sku_h, sku_header=sku_h,
) )
fw = _effective_focus_words(run_dir)
pl = jcr.build_comment_groups_llm_payload( pl = jcr.build_comment_groups_llm_payload(
feedback_groups=fb, feedback_groups=fb,
focus_words=fw,
merged_rows=merged_rows, merged_rows=merged_rows,
sku_header=sku_h, sku_header=sku_h,
title_h=title_h, title_h=title_h,
@ -448,9 +429,6 @@ def _merge_snippets_from_comment_groups(
if not src: if not src:
continue continue
row["comment_flat_rows"] = src.get("comment_flat_rows") row["comment_flat_rows"] = src.get("comment_flat_rows")
fh = src.get("focus_hit_lines")
if isinstance(fh, list):
row["focus_hit_lines"] = [str(x) for x in fh[:8]]
sn = src.get("sample_text_snippets") sn = src.get("sample_text_snippets")
if isinstance(sn, list): if isinstance(sn, list):
row["sample_text_snippets"] = [str(x)[:220] for x in sn[:8]] row["sample_text_snippets"] = [str(x)[:220] for x in sn[:8]]
@ -689,15 +667,14 @@ def build_markdown(
probe_rows, probe_rows,
merged_rows=merged, merged_rows=merged,
comment_rows=comments, comment_rows=comments,
run_dir=run_dir,
) )
llm_payload: dict[str, Any] = { llm_payload: dict[str, Any] = {
"schema_version": 1, "schema_version": 1,
"keyword": kw, "keyword": kw,
"probe_note": ( "probe_note": (
"中文分词 + 停用词;关键词突出度/共现/主题归纳为统计库;" "中文分词 + 停用词;关键词突出度/共现/主题归纳为统计库;"
"与正式报告的关注词规则统计、已废弃的预设口语短语情感口径均不同;" "与正式报告第八章第二节图表中的关注词计数等规则统计、已废弃的预设口语短语情感口径均不同;"
"评价摘录字段合并自 build_comment_groups_llm_payload,供语境对照;" "评价短摘录合并自 build_comment_groups_llm_payload(与正式管线同源,不含关注词子串计数摘要),供语境对照;"
"「使用场景」若出现,须仅能从本 JSON 内统计与摘录推断,不接入正式场景分组。" "「使用场景」若出现,须仅能从本 JSON 内统计与摘录推断,不接入正式场景分组。"
), ),
"groups": probe_rows, "groups": probe_rows,

View File

@ -354,13 +354,8 @@ def main() -> None:
comment_rows=comment_rows, comment_rows=comment_rows,
sku_header=sku_h, sku_header=sku_h,
) )
fw_src = eff_rc.get("comment_focus_words") or list(jcr.COMMENT_FOCUS_WORDS)
fw_tuple = tuple(
str(x).strip() for x in fw_src if str(x).strip()
) or jcr.COMMENT_FOCUS_WORDS
pl_cg = jcr.build_comment_groups_llm_payload( pl_cg = jcr.build_comment_groups_llm_payload(
feedback_groups=fb, feedback_groups=fb,
focus_words=fw_tuple,
merged_rows=merged, merged_rows=merged,
sku_header=sku_h, sku_header=sku_h,
title_h=title_h, title_h=title_h,
@ -374,7 +369,7 @@ def main() -> None:
return generate_comment_group_summaries_llm(pl_cg, keyword=keyword) return generate_comment_group_summaries_llm(pl_cg, keyword=keyword)
_run_one( _run_one(
"§8 细类评价与关注词(comment_groups)", "§8 细类评价要点(comment_groups)",
_cg, _cg,
live=args.live, live=args.live,
preview_chars=args.preview_chars, preview_chars=args.preview_chars,

View File

@ -679,13 +679,8 @@ def write_competitor_analysis_for_run_dir(
comment_rows=comment_rows, comment_rows=comment_rows,
sku_header=sku_h, sku_header=sku_h,
) )
fw_src = eff_rc.get("comment_focus_words") or list(jcr.COMMENT_FOCUS_WORDS)
fw_tuple = tuple(
str(x).strip() for x in fw_src if str(x).strip()
) or jcr.COMMENT_FOCUS_WORDS
pl_cg = jcr.build_comment_groups_llm_payload( pl_cg = jcr.build_comment_groups_llm_payload(
feedback_groups=fb_cg, feedback_groups=fb_cg,
focus_words=fw_tuple,
merged_rows=merged_rows, merged_rows=merged_rows,
sku_header=sku_h, sku_header=sku_h,
title_h=title_h, title_h=title_h,

View File

@ -50,15 +50,14 @@ def generate_matrix_group_summaries_llm(
COMMENT_GROUPS_SYSTEM = """你是用户研究与品类顾问。输入为 JSON:``keyword`` 与 ``groups``。 COMMENT_GROUPS_SYSTEM = """你是用户研究与品类顾问。输入为 JSON:``keyword`` 与 ``groups``。
每个 group 含 ``group``(与 第五章矩阵一致的细分类目名)、``comment_flat_rows``、``effective_text_lines``、 每个 group 含 ``group``(与 第五章矩阵一致的细分类目名)、``comment_flat_rows``、``effective_text_lines``(该细类下从评价中抽取的短文本单元)、``sample_text_snippets``(带 SKU/品名/店铺前缀的评价短摘录,已截断)。
``focus_hit_lines``(关注词子串命中摘要,与 **第八章第二节**(关注词与场景路径)左栏同源)、``sample_text_snippets``(评价短摘录,已截断)。 摘录行通常以 ``【细类:…|SKU:…|品名:…|店铺:…】`` 开头:**品名/SKU/店铺**表示该句具体出自哪条链接;归纳时若引用原话,**须交代是「哪家店、哪条 SKU、哪款品名」上的反馈**,勿只写「有用户说口感差」而不指代产品。
摘录行通常以 ``【细类:…|SKU:…|品名:…|店铺:…】`` 开头:细类可与本 group 名对照,**品名/SKU/店铺**表示该句具体出自哪条链接;归纳时若引用原话,**须交代是「哪家店、哪条 SKU、哪款品名」上的反馈**,勿只写「有用户说口感差」而不指代产品。 请**以整句语义**判断褒贬(如「软硬适中」「没那么甜」常为满意表述),不得仅凭片段词就写成负面结论。
关注词命中为子串统计,可能与句意不一致;**请以整句语义**判断褒贬(如「软硬适中」「没那么甜」常为满意表述,不得据此写成质地问题)。
请**为每个细类**输出一小段 Markdown(全部 groups 都要写,顺序与输入一致): 请**为每个细类**输出一小段 Markdown(全部 groups 都要写,顺序与输入一致):
- 以 ``#### `` + 与该 group 字段**完全一致**的细类名作为小节标题(不要使用 ``##`` 一级标题); - 以 ``#### `` + 与该 group 字段**完全一致**的细类名作为小节标题(不要使用 ``##`` 一级标题);
- 每段约 **100~220 字**中文:归纳该细类下**消费者在讨论什么**(口感、价格、物流、功效疑虑等)、**关注词命中反映的诉求**;勿编造摘录中未出现的品牌、医学结论; - 每段约 **100~220 字**中文:归纳该细类下**消费者在讨论什么**(口感、价格、物流、功效疑虑等);勿编造摘录中未出现的品牌、医学结论;
- **去重与可证(本章仅评论侧)**:本段**只**依据评价/关注词摘录,**禁止**把 ``keyword``、品类常识或商品标题卖点套话写成「用户评价」;**禁止**各细类段首复用同一句总括(如「整体上满足了消费者对低 GI、高蛋白、便携性的需求」);每段开头句式须**有变化**,并至少一句体现**该细类与相邻细类在讨论焦点上的差异**。**利益/诉求词**(低 GI、高蛋白、便携、代餐、控糖等)**仅当**在 ``sample_text_snippets``、``effective_text_lines`` 或 ``focus_hit_lines`` 的**原文**中可子串命中或可明确同义(便携↔随身、小包装、单片、独立装等)时才写;若上述字段中**未**出现「蛋白」「便携」「随身」「小包装」「单片」等,则**不得**写「高蛋白」「便携性」等;**禁止**为凑齐常见卖点组合而脑补未在输入中出现的词。 - **去重与可证(本章仅评论侧)**:本段**只**依据 ``sample_text_snippets`` 与 ``effective_text_lines`` 中的**原文**,**禁止**把 ``keyword``、品类常识或商品标题卖点套话写成「用户评价」;**禁止**各细类段首复用同一句总括(如「整体上满足了消费者对低 GI、高蛋白、便携性的需求」);每段开头句式须**有变化**,并至少一句体现**该细类与相邻细类在讨论焦点上的差异**。**利益/诉求词**(低 GI、高蛋白、便携、代餐、控糖等)**仅当**在上述字段的**原文**中可子串命中或可明确同义(便携↔随身、小包装、单片、独立装等)时才写;若**未**出现「蛋白」「便携」「随身」「小包装」「单片」等,则**不得**写「高蛋白」「便携性」等;**禁止**为凑齐常见卖点组合而脑补未在输入中出现的词。
- **禁止**输出 Markdown 表格、禁止逐条复述全部评价; - **禁止**输出 Markdown 表格、禁止逐条复述全部评价;
- 若 ``effective_text_lines`` 很少,明确写「样本较少,归纳供启发」。 - 若 ``effective_text_lines`` 很少,明确写「样本较少,归纳供启发」。
@ -66,7 +65,7 @@ COMMENT_GROUPS_SYSTEM = """你是用户研究与品类顾问。输入为 JSON:
COMMENT_GROUPS_USER_PREFIX = ( COMMENT_GROUPS_USER_PREFIX = (
"请根据以下 JSON 撰写竞品报告第八章末「细类评论与关注词要点归纳」正文(Markdown)。\n\n" "请根据以下 JSON 撰写竞品报告第八章末「细类评论要点归纳」正文(Markdown)。\n\n"
) )
@ -85,7 +84,6 @@ def generate_comment_group_summaries_llm(
eff_max: int, eff_max: int,
sn_n: int, sn_n: int,
sn_max: int, sn_max: int,
fh_n: int,
) -> dict[str, Any]: ) -> dict[str, Any]:
g2: dict[str, Any] = { g2: dict[str, Any] = {
"group": g.get("group"), "group": g.get("group"),
@ -101,11 +99,6 @@ def generate_comment_group_summaries_llm(
g2["sample_text_snippets"] = [str(x)[:sn_max] for x in sn[:sn_n]] g2["sample_text_snippets"] = [str(x)[:sn_max] for x in sn[:sn_n]]
else: else:
g2["sample_text_snippets"] = [] g2["sample_text_snippets"] = []
fh = g.get("focus_hit_lines")
if isinstance(fh, list):
g2["focus_hit_lines"] = [str(x) for x in fh[:fh_n]]
else:
g2["focus_hit_lines"] = []
return g2 return g2
ctx = llm_context_window_size() ctx = llm_context_window_size()
@ -115,24 +108,24 @@ def generate_comment_group_summaries_llm(
est = estimate_chat_input_tokens(system, user_p) est = estimate_chat_input_tokens(system, user_p)
return est < 15_500 return est < 15_500
levels: list[tuple[int, int, int, int, int]] = [ levels: list[tuple[int, int, int, int]] = [
(14, 260, 10, 200, 10), (14, 260, 10, 200),
(12, 220, 8, 180, 8), (12, 220, 8, 180),
(10, 180, 8, 160, 6), (10, 180, 8, 160),
(8, 150, 6, 140, 6), (8, 150, 6, 140),
(6, 120, 5, 120, 5), (6, 120, 5, 120),
(5, 100, 4, 100, 4), (5, 100, 4, 100),
(4, 80, 3, 80, 3), (4, 80, 3, 80),
(3, 70, 3, 70, 3), (3, 70, 3, 70),
(3, 50, 2, 60, 2), (3, 50, 2, 60),
] ]
user = "" user = ""
chosen = levels[-1] chosen = levels[-1]
for level in levels: for level in levels:
chosen = level chosen = level
eff_n, eff_max, sn_n, sn_max, fh_n = level eff_n, eff_max, sn_n, sn_max = level
trimmed = [ trimmed = [
_compact_one(g, eff_n=eff_n, eff_max=eff_max, sn_n=sn_n, sn_max=sn_max, fh_n=fh_n) _compact_one(g, eff_n=eff_n, eff_max=eff_max, sn_n=sn_n, sn_max=sn_max)
for g in groups for g in groups
if isinstance(g, dict) if isinstance(g, dict)
] ]
@ -144,9 +137,9 @@ def generate_comment_group_summaries_llm(
break break
else: else:
tail = "\n\n…(JSON 已截断以适配上下文;仅依据可见字段撰写。)\n" tail = "\n\n…(JSON 已截断以适配上下文;仅依据可见字段撰写。)\n"
eff_n, eff_max, sn_n, sn_max, fh_n = chosen eff_n, eff_max, sn_n, sn_max = chosen
trimmed = [ trimmed = [
_compact_one(g, eff_n=eff_n, eff_max=eff_max, sn_n=sn_n, sn_max=sn_max, fh_n=fh_n) _compact_one(g, eff_n=eff_n, eff_max=eff_max, sn_n=sn_n, sn_max=sn_max)
for g in groups for g in groups
if isinstance(g, dict) if isinstance(g, dict)
] ]

View File

@ -1,4 +1,4 @@
"""§8 末「细类评论与关注词要点归纳」大模型:单细类烟测(mock 网关,不调真实 API)。""" """§8 末「细类评论要点归纳」大模型:单细类烟测(mock 网关,不调真实 API)。"""
from __future__ import annotations from __future__ import annotations
import json import json
@ -44,7 +44,6 @@ def _payload_single_category_biscuit() -> tuple[list[dict], str, str]:
) )
pl = jcr.build_comment_groups_llm_payload( pl = jcr.build_comment_groups_llm_payload(
feedback_groups=fb, feedback_groups=fb,
focus_words=jcr.COMMENT_FOCUS_WORDS,
merged_rows=merged, merged_rows=merged,
sku_header=sku_h, sku_header=sku_h,
title_h=title_h, title_h=title_h,
@ -68,7 +67,7 @@ class CommentGroupSummariesLlmTests(SimpleTestCase):
mock_llm.assert_called_once() mock_llm.assert_called_once()
_sys, user = mock_llm.call_args[0] _sys, user = mock_llm.call_args[0]
self.assertIn("细类评论与关注词要点归纳", user) self.assertIn("细类评论要点归纳", user)
self.assertIn("低GI测试", user) self.assertIn("低GI测试", user)
raw = user.split("正文(Markdown)。\n\n", 1)[-1] raw = user.split("正文(Markdown)。\n\n", 1)[-1]
data = json.loads(raw.strip()) data = json.loads(raw.strip())