Files
deerflow-code/frontend-web/docs/multi-agent-background-run-遗留问题与下次继续.md
T

8.9 KiB
Raw Blame History

多智能体「后台挂起」—— 当前状态、遗留问题与下次继续指南

配套设计文档:multi-agent-background-run-dev.md(分 Phase 0-9 的总方案)。 本文记录截至本次会话的实际进展 + 还没解决的遗留问题,方便下次直接续接。


一、整体状态:跑通到哪了

「后台挂起」= Step2 把研讨交给后端进程内驱动真模型(leader→各席位→共识→报告), survive 刷新/关页,对话与报告作为一个 run 写进草稿(和手动跑同一存取),可在历史回看。

真网关已在本地验证可跑通(2026-06-04 日志确认:roundtable-coordinator + 各席位 + roundtable-report 真调 deepseek、真搜索、跑到 report run success)。但还有几个内容/产物层面的遗留问题(见第三节)。

已完成(可用)

  • ✅ 后端持久化:roundtable_jobs 表(迁移 20260604_01 / 20260605_01 dialogues / 20260605_02 step3)。
  • ✅ 编排引擎 packages/harness/deerflow/agents/roundtable_orchestrator/(recommend + chain + 报告,纯逻辑可单测)。
  • ✅ 控制面 app/gateway/routers/roundtable_jobs.py:start/get/by-draft/stream(SSE)/resume/cancel/admin-cleanup。
  • ✅ 执行器 app/gateway/roundtable_job_executor.py:后台任务 + set_current_user + 生命周期 + 每轮把对话作为 job-<id> run 写进 draft.step2.runs[](同手动存取) + done 写 step3。
  • ✅ 真网关 app/gateway/roundtable_inprocess_gateway.py:SimpleNamespace(app) + start_run 进程内驱动 agent;复用 multi_agent._extract_dispatch 解析派活;checkpointer 读最终 messages。
  • ✅ 模拟网关 roundtable_orchestrator/simulated_gateway.py(不调真模型,前端联调用)。
  • ✅ 前端:api/roundtable-jobs.ts、lib/job-types.ts、Step2「后台挂起」按钮、历史下拉 loading、DispatchChainModal/DispatchChainJobModal(流程图 + 对话面板 markdown + 加宽 + 澄清作答 + 取消 + 查看)。
  • ✅ 上下文「广播」(inline):席位/总控 prompt 带上「任务背景 + 前序各席位交付 + 别提问」约束(修「席位拿不到关键信息、反问用户、总控瞎编」)。
  • ✅ 后端测试:tests/test_roundtable_jobs.py / test_roundtable_orchestrator.py / test_roundtable_job_executor.py / test_roundtable_jobs_api.py 全过。

二、怎么启动 / 验证(每次开工先做)

  1. 开真网关(默认是模拟网关):已写进 offline-backend-20260512/.env:

    ROUNDTABLE_JOB_GATEWAY=inprocess
    

    启动日志应出现 [roundtable-jobs] gateway = inprocess (real models)。

  2. 必须用 make gateway(不带 --reload)跑后端! make dev 的 --reload 监视 backend/ 目录,agent 运行时往 .deer-flow/ 写文件 (memory.json / checkpoint / 沙箱输出)会触发热重载,把长时后台任务杀掉。 (改进项:给 uvicorn 加 --reload-exclude '.deer-flow/*',或始终用 make gateway。)

  3. 直接查库验证(最有效,PowerShell 控制台是 GBK,导出到文件看中文):

    cd offline-backend-20260512/backend
    # 查最新作业的 dialogues / step3
    PYTHONPATH=. uv run python -c "import sqlite3,json; con=sqlite3.connect('.deer-flow/data/deerflow.db'); ..."
    
    • 作业表:.deer-flow/data/deerflow.db → 表 roundtable_jobs(dialogues/step3 列)。
    • 草稿表:roundtable_drafts → step2 列里应有 runs[ {id:"job-<jobId>", jobId, jobStatus, step2RoundtableDialogues:[...], step3:{...}} ]。
  4. 前端验证:后台挂起 → 等跑完 → 从历史加载该草稿 → Step2 应显示后台研讨完整对话(走现有 normalizeStep2Runs/hydrateStep2Run)。


三、遗留问题(下次重点,已按优先级排)

P0 —— 报告 agent 不写 HTML 文件(结果绘制拿不到产物)

  • 现象:后台报告 run 跑完,日志 report html: no .html found in ...threads/<id>/user-data/outputs; run.step3.html 回退成报告 agent 的纯文字(不含 <html>),第3步画不出东西。
  • 已做:build_report_prompt(roundtable_orchestrator/engine.py)已对齐前端 useStep3Report.buildReportPrompt,明确「用 write_file 写入 /mnt/user-data/outputs/方案总览.html」; _read_report_html(roundtable_inprocess_gateway.py)已改为扫 outputs 目录任意 .html。
  • 仍没解决:报告 agent(deepseek-chat)还是没调 write_file。
  • 下次怎么查:
    1. 对比前端那条能写出 HTML 的报告 run 的确切调用(useStep3Report.ts 里 streamMultiAgent 调 /api/multi-agent/run/stream 的 body:agent_type/excluded_tools/context 等), 和后台 _run_agent_turn(roundtable_inprocess_gateway.py,agent_name=roundtable-report, excluded_tools=_REPORT_EXCLUDED)逐项比对,找出差异(怀疑 reasoning_effort / thinking / 某个 context 字段 / recursion 步数导致它没走到 write_file)。
    2. 看报告 run 的最终 messages(checkpoint)里有没有 write_file 的 tool_call(被 dangling 截断?)。
    3. 报告 agent 的 SOUL:app/gateway/routers/_roundtable_seed/roundtable-report/SOUL.md——确认它默认行为。

P1 —— 内容质量(本次刚加 inline 广播,待真机验证)

  • 已做:席位/总控 prompt 注入「任务背景 + 前序交付 + 别提问」。
  • 待验证:真机跑一次,看席位是否基于真实上下文产出(不再反问/凭记忆瞎编)。
  • 若 inline 不够:考虑实现真·thread 广播(像 multi_agent._append_thread_message 那样把 「用户意图 / 派活 / 各席位交付」追加进各 thread 历史)。进程内要避开 HTTP loopback 鉴权, 得用 checkpointer 直接 append human 消息(参考 threads 路由 POST /state 的内部逻辑)。这块较重,先看 inline 够不够。

P2 —— 对话「没有步骤(steps)」

  • 现象:手动跑的气泡带工具调用步骤卡(onPhaseChange → appendStepIfNew → Step2Dialogue.steps); 后台对话只存了最终正文,没有 steps。
  • 下次怎么做:后台 _run_agent_turn 跑完后,从 checkpoint 的 messages 里抽出 tool_calls, 映射成 Step2Dialogue.steps({id,key,label,ts,args}),在执行器 _to_step2_dialogue 里带上。 纯展示增强,不影响内容/报告。

P3 —— 双跑浪费 + --reload 坑(工程健壮性)

  • 进 Step2 时前端会自动跑一遍本地研讨(真模型、真线程);点后台挂起后前端那条 run 在后端还会跑完 (stopOrchestration 只断客户端、停不了后端 run)→ 和后台作业短暂双跑、双倍 token。
    • 优化方向:进 Step2 改成「显式开始」而非自动跑;或后台挂起时真正 cancel 前端那条 run。
  • make dev 的 --reload 会杀后台任务(见第二节);改进项:--reload-exclude '.deer-flow/*'。

四、关键文件清单(下次定位用)

后端

文件 作用
app/gateway/roundtable_inprocess_gateway.py 真网关:进程内驱动 leader/seat/re 编排循环 + build_report_prompt + inline 广播(_seat_message/_deliveries_block)
.../roundtable_orchestrator/progress.py 进度落库 + on_dialogue 每轮回调
.../persistence/roundtable_jobs/ job 表 model/sql
app/gateway/deps.py ROUNDTABLE_JOB_GATEWAY 选网关 + 注入 executor(draft_store)

前端

文件 作用
src/roundtable-planning/api/roundtable-jobs.ts 作业 API + SSE
src/roundtable-planning/lib/job-types.ts 类型(RoundtableJob/JobDialogue/...)
src/roundtable-planning/components/DispatchChainModal.tsx / DispatchChainJobModal.tsx 流程图弹窗 + 对话面板
src/roundtable-planning/pages/RoundtablePlanningPage.tsx 后台挂起按钮、历史下拉、runs[] 多运行模型(hydrateStep2Run/getStep2SnapshotWithRuns/normalizeStep2Runs)
src/roundtable-planning/api/drafts.ts DraftStep2RunSnapshot(带 jobId/jobStatus/step3)

五、关键架构事实(避免重复踩坑)

  1. 真网关进程内驱动:start_run(body, thread_id, SimpleNamespace(app=app)) + set_current_user, 不走 HTTP(loopback 调 /api/multi-agent/* 不可行:_auth_headers 只转发用户 token + owner check)。
  2. 后台作业首启自建全新线程(StartParams.is_resume),绝不复用前端本地研讨的线程(会撞 409)。
  3. 存取统一:后台作业 = draft.step2.runs[] 里一个带 jobId 的 run,和手动跑同一存取/hydrate 路径, 不要再开 observe/回显分支(之前试过,被否)。
  4. 聊天记录确实存上了(job.dialogues + draft 的 run);之前"没存上"是测了旧代码 / 看的是前端本地残缺数据。
  5. 多 worker / SSE:状态进 DB、SSE 走 DB 轮询;真网关进程内无 loopback。