datawhalechina/hello-agents · error · RuntimeError
辩论未正常结束
Error message
辩论未正常结束
What it means
The collect/run wrapper in debate_orchestrator.py consumes every event yielded by run_historical_debate and afterwards requires the last event to be {"event": "complete"}; otherwise it raises RuntimeError('辩论未正常结束'). The generator only emits 'complete' after all four phases finish, so this error means the event stream terminated early — most often an LLM call raised mid-debate, or the generator was closed/exhausted by the consumer before completion.
Source
Thrown at Co-creation-projects/meiguanxiHXX-historyReviewAgent/historical_review/debate_orchestrator.py:285
llm_timeout: int | None = None,
) -> str:
"""执行两轮角色辩论 + 终局综合报告(无流式,供 CLI 等)。"""
last: dict[str, Any] | None = None
for ev in iter_debate_events(
topic,
llm=llm,
use_evidence_bundle=use_evidence_bundle,
debate_temperature=debate_temperature,
synthesizer_temperature=synthesizer_temperature,
llm_api_key=llm_api_key,
llm_base_url=llm_base_url,
llm_model=llm_model,
llm_max_tokens=llm_max_tokens,
llm_timeout=llm_timeout,
):
last = ev
if not last or last.get("event") != "complete":
raise RuntimeError("辩论未正常结束")
md = last.get("markdown")
if not isinstance(md, str):
raise RuntimeError("缺少完整 Markdown")
return md
def debate_event_json(ev: dict[str, Any]) -> str:
"""序列化单条事件(SSE data 行)。"""
return json.dumps(ev, ensure_ascii=False)
View on GitHub (pinned to 606a07d341)
Solutions
- Inspect the events actually yielded before the raise (log each ev) — the last one names the phase that died (e.g. round2_start), which pinpoints the failing LLM call.
- Verify the LLM config first: valid api_key, reachable base_url, correct model id, generous llm_timeout for multi-round debates.
- Retry the debate with backoff; transient provider 429/5xx errors commonly end streams early.
- If you iterate the generator yourself, do not break early — consume all events, or propagate exceptions from inside the loop.
- Raise llm_max_tokens if phases truncate and downstream code mistakes truncation for failure.
Example fix
# before
for ev in run_historical_debate(topic, ...):
last = ev
if not last or last.get("event") != "complete":
raise RuntimeError("辩论未正常结束")
# after
events = []
try:
for ev in run_historical_debate(topic, ...):
events.append(ev)
except Exception as e:
raise RuntimeError(f"辩论在第 {events[-1].get('event') if events else 'start'} 阶段失败: {e}") from e
if not events or events[-1].get("event") != "complete":
raise RuntimeError(f"辩论未正常结束,最后事件: {events[-1] if events else None}") Defensive patterns
Strategy: retry
Validate before calling
# preflight the LLM config that most often kills mid-stream debates
import os
assert (os.getenv("OPENROUTER_API_KEY") or os.getenv("LLM_API_KEY")), "LLM key missing"
assert llm_model and llm_base_url, "LLM model/base_url required" Type guard
def is_complete_event(ev: dict) -> bool:
return ev.get("event") == "complete" Try / catch
import time
for attempt in range(3):
last = None
try:
for ev in run_historical_debate(topic, ...):
last = ev
if last and last.get("event") == "complete":
break
raise RuntimeError(f"辩论未正常结束,最后事件: {last}")
except RuntimeError:
if attempt == 2:
raise
time.sleep(2 ** attempt) # transient provider failures are common Prevention
- Log every debate event so the failing phase is identifiable
- Validate LLM key/model/base_url before starting a multi-phase debate
- Never break out of the event loop early; consume the generator fully
When it happens
Trigger: An LLM API error (auth failure, rate limit, timeout) inside round1/digest/round2/synthesis aborts the generator before 'complete'; the caller breaks out of the iteration loop early (e.g. SSE client disconnect triggers GeneratorExit) so `last` is a mid-phase event; create_llM given an invalid model/base_url so the first completion call fails; max_tokens/timeout misconfiguration kills a later phase.
Common situations: Invalid or exhausted OpenRouter key used for a long multi-phase debate; network drop mid-run; server-side SSE route that stops consuming on client disconnect and then still tries to build the final markdown; changing llm_model to a name the provider rejects.
Related errors
AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14).
Data as JSON: /api/errors/b2ecc82b28f333f2.
Report an issue: GitHub.