From e04b56d9679f751b5daaccdc7b7af302969bc105 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Fri, 28 Aug 2026 20:12:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(sub-agent):=20=E6=B7=B1=E5=BA=A6=E5=8E=8B?= =?UTF-8?q?=E7=BC=A9=E9=87=8D=E5=BB=BA=20system=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E5=8C=85=E8=A3=85=E4=B8=BA=20dict=EF=BC=8C=E6=A0=B9=E6=B2=BB?= =?UTF-8?q?=E5=8E=8B=E7=BC=A9=E5=90=8E=E9=A6=96=E6=AC=A1=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E5=BF=85=E5=B4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _deep_compress_messages 此前把 _rebuild_system_prompt 返回的裸字符串直接 作为 messages[0],压缩后首次模型调用在 _merge_system_messages 的 msg.get("role") 处崩溃('str' object has no attribute 'get'), 任何触发 150k tokens 压缩阈值的子智能体 100% 必崩 - _merge_system_messages 入口增加非 dict 归一防御(包装为 system 消息), 同时避免下游 sanitizer 静默丢弃导致 system prompt 内容丢失 --- modules/sub_agent/task.py | 5 ++++- utils/api_client/message_mixin.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/sub_agent/task.py b/modules/sub_agent/task.py index 528fe8c7..5983a4fb 100644 --- a/modules/sub_agent/task.py +++ b/modules/sub_agent/task.py @@ -1099,7 +1099,10 @@ class SubAgentTask: summary_text = "\n".join(summary_lines) # 重建消息列表:冻结的 system prompt + 压缩摘要 system + 保留的最近消息 - frozen_system = self._rebuild_system_prompt() + # 注意:_rebuild_system_prompt 返回裸字符串,必须包装为标准消息 dict; + # 否则下游 API 客户端(_merge_system_messages 的 msg.get("role"))会在 + # 压缩后的首次模型调用崩溃('str' object has no attribute 'get')。 + frozen_system = {"role": "system", "content": self._rebuild_system_prompt()} compressed_system = { "role": "system", "content": summary_text, diff --git a/utils/api_client/message_mixin.py b/utils/api_client/message_mixin.py index b5e1600b..34e9c0f0 100644 --- a/utils/api_client/message_mixin.py +++ b/utils/api_client/message_mixin.py @@ -44,6 +44,20 @@ class APIClientMessageMixin: if not messages: return messages + # 防御:上游偶发会把裸字符串混进消息列表(如子智能体压缩重建的 system + # prompt 文本),直接 .get() 会崩。统一包装为标准 system 消息,保留内容。 + # 下游 sanitizer 对非 dict 是静默丢弃,先行归一也能避免内容丢失。 + normalized: List[Dict] = [] + for item in messages: + if isinstance(item, dict): + normalized.append(item) + else: + normalized.append({ + "role": "system", + "content": item if isinstance(item, str) else json.dumps(item, ensure_ascii=False), + }) + messages = normalized + merged_contents: List[str] = [] idx = 0 while idx < len(messages) and messages[idx].get("role") == "system":