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":