diff --git a/offline-backend-20260512/backend/app/gateway/roundtable_job_executor.py b/offline-backend-20260512/backend/app/gateway/roundtable_job_executor.py index c097d897..a4538c55 100644 --- a/offline-backend-20260512/backend/app/gateway/roundtable_job_executor.py +++ b/offline-backend-20260512/backend/app/gateway/roundtable_job_executor.py @@ -29,6 +29,15 @@ from deerflow.runtime.user_context import reset_current_user, set_current_user logger = logging.getLogger(__name__) +# ⚠️ 临时禁用「后台挂起」的 Step3 结果绘制(报告 HTML 生成)。 +# 进程内驱动报告 agent 在真模型环境下仍无法稳定写出 HTML(前端手动调 +# /api/multi-agent/report/init + run/stream 可以,但后台 start_run 路径不行, +# 根因尚未定位)。临时方案:后台只跑完 Step2 研讨即收尾,不自动画报告——用户需要 +# 报告时可在前端手动跑 Step3(那条路是通的)。 +# 待修复后把本开关改回 True 即恢复后台自动报告。 +# 详见 frontend-web/docs/multi-agent-background-run-遗留问题与下次继续.md「后台报告 HTML」。 +ROUNDTABLE_BACKGROUND_REPORT_ENABLED = False + class _JobUser: """最小 CurrentUser:满足 user_context 的 ``.id`` 结构协议。""" @@ -164,6 +173,9 @@ class StartParams: is_resume: bool = False # chain 模式来源链条(写进草稿 run 用,展示)。 chain: dict[str, Any] | None = None + # 是否生成 Step3 报告。默认沿用全局开关(当前临时禁用,见 ROUNDTABLE_BACKGROUND_REPORT_ENABLED)。 + # 测试可显式传 True 覆盖以验证报告链路。 + enable_report: bool = ROUNDTABLE_BACKGROUND_REPORT_ENABLED # 网关工厂:给定一次启动参数,返回一个 RoundtableGateway 实例。 @@ -229,6 +241,7 @@ class RoundtableJobExecutor: seed_message=params.seed_message, mode=params.mode, intent_text=params.intent_text, + enable_report=params.enable_report, ) # 终态:把最终对话 + step3 + jobStatus 落进同一个 run(含 furthest_step=3)。 await self._write_run_to_draft( @@ -320,8 +333,11 @@ class RoundtableJobExecutor: # 顶层 draft.step3 也写一份(同 Step2 把对话写进 run 的同一次落库一并带上)。 # 前端 hydrateStep3 用**顶层** draft.step3 重建报告气泡(结果绘制的聊天记录 + HTML 预览); # 只存 run 内 step3 时,加载会被 hydrateStep3(draft.step3=None) 覆盖成空 → 报告聊天记录丢失。 - update_kwargs: dict[str, Any] = {"step2": step2, "furthest_step": 3 if status == "done" else 2} - if effective_step3 is not None: + # furthest_step 跟随**是否真有 step3**:done 且有报告→3(落到结果绘制); + # 否则停在 2(Step2 研讨)。后台禁用报告时 step3=None → 停在 Step2,用户可在前端手动画报告。 + has_report = effective_step3 is not None + update_kwargs: dict[str, Any] = {"step2": step2, "furthest_step": 3 if (status == "done" and has_report) else 2} + if has_report: update_kwargs["step3"] = effective_step3 try: await self._draft_store.update_draft(params.draft_id, params.user_id, **update_kwargs) diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/agents/roundtable_orchestrator/engine.py b/offline-backend-20260512/backend/packages/harness/deerflow/agents/roundtable_orchestrator/engine.py index 1dd314ac..30203932 100644 --- a/offline-backend-20260512/backend/packages/harness/deerflow/agents/roundtable_orchestrator/engine.py +++ b/offline-backend-20260512/backend/packages/harness/deerflow/agents/roundtable_orchestrator/engine.py @@ -72,6 +72,7 @@ async def run_orchestration( seed_message: str, mode: str = "recommend", intent_text: str = "", + enable_report: bool = True, ) -> OrchestrationResult: """运行一次完整编排(循环 → 共识 → 报告绘制)。 @@ -85,6 +86,9 @@ async def run_orchestration( seed_message: 第一轮 leader 的 prompt(启动=初始意图;resume=用户答案续轮)。 mode: "recommend" | "chain"。 intent_text: 任务意图,用于报告 prompt(缺省退回 seed_message)。 + enable_report: 是否在收口后执行 Step3 报告绘制。False 时只跑完 Step2 研讨即收尾、 + 不生成报告(step3=None)。当前「后台挂起」临时禁用报告(进程内驱动报告 agent + 在真模型下仍无法稳定写出 HTML,详见 multi-agent-background-run-遗留问题与下次继续.md)。 """ intent = (intent_text or seed_message or "").strip() try: @@ -104,17 +108,19 @@ async def run_orchestration( dialogues=progress.dialogues(), ) - # consensus 或 max_cycles:均收口 → 报告绘制 + # consensus 或 max_cycles:均收口。报告绘制按 enable_report 开关执行。 await progress.consensus(100) - step3 = await _run_report( - gateway, - progress, - agents=agents, - model=model, - intent_text=intent_text or seed_message, - consensus_text=outcome.last_leader_content, - deliveries=outcome.deliveries, - ) + step3 = None + if enable_report: + step3 = await _run_report( + gateway, + progress, + agents=agents, + model=model, + intent_text=intent_text or seed_message, + consensus_text=outcome.last_leader_content, + deliveries=outcome.deliveries, + ) await progress.done() return OrchestrationResult( status="done",