diff --git a/agent/task_executor.py b/agent/task_executor.py index e1a0952..06119e8 100644 --- a/agent/task_executor.py +++ b/agent/task_executor.py @@ -595,6 +595,21 @@ Return ONLY the JSON, no other text.""" except Exception as exc: return f"[tool error: {exc}]" + @staticmethod + def _message_text(msg) -> str: + """Robustly extract the assistant's textual answer. qwen3.7-max (a THINKING model) can return + an empty `content` with the real answer in `reasoning_content`; reading only `content` then + yields "" → `json.loads("")` → "Expecting value: line 1 column 1 (char 0)" → task fails. + Fallback chain: content → reasoning_content (attribute or OpenAI-SDK model_extra).""" + text = (getattr(msg, "content", None) or "").strip() + if text: + return text + rc = getattr(msg, "reasoning_content", None) + if not rc: + extra = getattr(msg, "model_extra", None) or {} + rc = extra.get("reasoning_content") if isinstance(extra, dict) else None + return (rc or "").strip() + async def _complete(self, prompt: str, max_tokens: int) -> str: """Call the LLM with optional Jina MCP tools; handles the tool-call loop.""" extra_headers = self._model_attribution_headers() @@ -620,7 +635,7 @@ Return ONLY the JSON, no other text.""" logger.info("[LLM·第%d轮] 思考/回复:\n%s", round_i + 1, msg.content.strip()[:1200]) if not msg.tool_calls: - return msg.content or "" + return self._message_text(msg) # Execute each tool call and feed results back messages.append(msg.model_dump(exclude_unset=True)) @@ -646,7 +661,7 @@ Return ONLY the JSON, no other text.""" extra_headers=extra_headers or None, ) self._record_openai_usage(response) - return response.choices[0].message.content or "" + return self._message_text(response.choices[0].message) async def peer_reply(self, *, query: str, capabilities: list[str], last_summary: Optional[str], max_tokens: Optional[int] = None) -> dict: