From 182f788857ca8b4c7cbfda030f9d5258b1ad7554 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 12 Aug 2026 12:32:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=97=A5=E6=8A=A5=20A?= =?UTF-8?q?I=20=E6=91=98=E8=A6=81=E5=B4=A9=E6=BA=83('str'=20object=20has?= =?UTF-8?q?=20no=20attribute=20'model')?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _llm_summarize 内 3 处 _llm_call 调用仍传 model 字符串, 与 _llm_call(config: LLMConfig) 新签名不匹配,生产日报 AI 摘要必崩 - 统一改为传 config 对象;删除不再使用的 model 局部变量 - 新增回归测试:单块/多分块 _llm_summarize → _llm_call 全链路(传 config) --- scheduler/reporter.py | 7 +++---- tests/test_report_builder.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/scheduler/reporter.py b/scheduler/reporter.py index 607721c..8957f0a 100644 --- a/scheduler/reporter.py +++ b/scheduler/reporter.py @@ -556,11 +556,10 @@ def _llm_summarize(client, config: LLMConfig, lines: list[str], day_str: str) -> config 为 llm.client.LLMConfig(daily_report 场景),提供 model / temperature。 """ - model = config.model chunks = _split_lines_into_chunks(lines) if len(chunks) == 1: - return _llm_call(client, model, _build_prompt(chunks[0], day_str)) + return _llm_call(client, config, _build_prompt(chunks[0], day_str)) # 多块:每块独立总结 partials: list[str] = [] @@ -570,7 +569,7 @@ def _llm_summarize(client, config: LLMConfig, lines: list[str], day_str: str) -> {chr(10).join(chunk)} 直接输出要点列表:""" - result = _llm_call(client, model, prompt, max_tokens=800) + result = _llm_call(client, config, prompt, max_tokens=800) if result: partials.append(result) logger.info("AI 摘要: 分块 {}/{} 完成 ({} 字)", i, len(chunks), len(result)) @@ -596,7 +595,7 @@ def _llm_summarize(client, config: LLMConfig, lines: list[str], day_str: str) -> 6. 总字数 500 字以内 直接输出要点列表:""" - return _llm_call(client, model, merge_prompt, max_tokens=1500) + return _llm_call(client, config, merge_prompt, max_tokens=1500) def _build_prompt(lines: list[str], day_str: str) -> str: diff --git a/tests/test_report_builder.py b/tests/test_report_builder.py index 3aa844f..528f2a5 100644 --- a/tests/test_report_builder.py +++ b/tests/test_report_builder.py @@ -148,6 +148,28 @@ class TestLlmCallRetry: rep._llm_call(client, self._cfg(), "p") assert n["count"] == 2 # 重试 2 次后放弃 + def test_llm_summarize_single_chunk_passes_config(self) -> None: + """_llm_summarize → _llm_call 全链路:必须传 LLMConfig 而非 model 字符串。 + + 回归保护:生产日报曾因 _llm_call 收到 str 报 + 'str' object has no attribute 'model'。 + """ + from scheduler.reporter import _llm_summarize + client, n = self._fake_client(0) + out = _llm_summarize(client, self._cfg(), ["- 新闻A", "- 新闻B"], "20260812") + assert out == "今日要点摘要" + assert n["count"] == 1 + + def test_llm_summarize_multi_chunk_passes_config(self) -> None: + """多分块场景:每块 + 合并各调用一次 _llm_call,均传 config。""" + from scheduler.reporter import _llm_summarize + client, n = self._fake_client(0) + # 两条长行保证触发分块 + lines = ["- " + "长新闻内容" * 300, "- " + "长新闻内容" * 300] + out = _llm_summarize(client, self._cfg(), lines, "20260812") + assert out == "今日要点摘要" + assert n["count"] == 3 # 2 块 + 1 次合并 + class TestCollectXwlb: """_collect_xwlb 取数逻辑:应查询日报前一日(已播出的联播),并跳过内容提要。"""