Files
deerflow-code/docs/问答卡住排查与修复-20260928.md
T

6.7 KiB
Raw Blame History

问答停在执行命令或读取技能:排查与修复

日期:2026-09-28。

本次检查针对普通问答和智能体问答:页面停在工具步骤,上游模型没有收到下一轮请求。 以下是代码审查和受控回归确认的触发路径;尚未收到具体生产对话 ID 和现场日志,不能据此认定某次现场故障只有一个根因。

已修复的等待路径

位置 原来的触发条件 当前行为
LocalSandbox 命令执行 subprocess.run 超时仅终止外层 shell,后代仍持有输出管道;Windows 超时收尾继续无期限 communicate 独立进程组、关闭交互输入、临时文件采集输出;超时清理进程树并限制回收等待
技能视图刷新 进程锁、跨进程文件锁和等待并发校验结果均没有统一等待上限 等待最多 30 秒后抛出明确工具错误;不删除正在执行的刷新,不使用未经校验的旧视图
AIO 沙箱 上次命令未返回时,后续命令或文件操作无限等待同一把锁 锁等待 30 秒后失败;不抢占旧请求的锁,不自动重复执行命令
文件分段读取 超长单行先读完整行才裁剪;FIFO/设备文件可能一直等数据 达到当前行输出预算即返回;本地读取用非阻塞打开并检查普通文件,远端分段读取同步限制
前端结束恢复 后端已 success/error,但旧 SSE 仍开着,SDK isLoading 一直为真;整段 SSE 期间的 sendInFlight 又把同一轮结束快照丢掉 核对轮次及运行 ID 后清理重连标记、关闭本地 reader、应用结束快照;不因此取消后端或覆盖新一轮
前端恢复查询 /runs、检查点或历史请求悬挂,占着 single-flight 恢复槽 普通 SDK 请求设置 30 秒超时;恢复失败仍保留后续检查机会,SSE 不受此总时长限制
后端结束收尾 图已返回,但检查点检查、状态写入、日志刷新或标题同步永远不返回,导致 end 发不出去 终态检查、终态状态写入分别有 10 秒预算;后续元数据收尾共享 10 秒预算,独立 finally 发出 end

技能锁超时走原有工具错误处理链,模型可收到失败结果并决定后续操作;不会把“超时”伪装成成功。 元数据收尾超时只发生在图已经结束后,不给整个研究/子智能体执行增加统一短时限。 终态检查点超时会报告运行失败,不能直接当成模型成功。

配置和上线

本次需同时更新后端代码和前端构建产物,重启后端后生效。只刷新页面或调线程数不能替代修复。 本次未操作生产服务、未发布前端包;正式离线发版仍使用独立的 deerflow-offline-deployment 仓库。

本地命令保留原来的 600 秒默认执行时限,新版允许在实际加载的配置文件中调整:

sandbox:
  # 合并到已有 sandbox 段中,保留 use、mounts 等既有字段。
  command_timeout_seconds: 600

该字段只控制 LocalSandbox 命令执行,必须为有限正数。未配置仍为 600 秒;不要把它误认为模型请求超时。 命令输出每个流最多读入 1 MiB 头尾内容,再经过既有工具输出裁剪。 运行中的命令约每 100ms 检查累计捕获输出,超过 16 MiB 请求终止;这是采样护栏,不是磁盘硬配额。 正常退出后的显式后台进程需要自行重定向输出和管理生命周期。

前端已有的无业务事件检查窗口仍为 90 秒;这是启动后台状态核对的时机,不是宣判任务失败的时限。 30 秒控制请求超时不影响合法的长时间 SSE 和工具执行。

仍需现场证据区分的情况

  • 磁盘/NFS 本身阻塞的刷新执行者仍可能卡在操作系统 I/O;本次限制的是其他调用的等待,不宣称能终止任意内核 I/O。
  • 自定义技能脚本自身请求外部服务不返回,仍会等到本地命令时限;建议技能内各 HTTP 请求也设置连接/读取超时。
  • 整个图中的第三方异步工具、模型前中间件和检查点操作没有统一的“无事件即取消”规则,避免误杀合法研究任务。
  • 检查点回滚是实际数据修改,保持原有完成/取消语义,不把仍在执行的回滚丢到后台后开放下一轮。
  • 已提交给数据库的单条写入未必能被驱动撤销;取消和新轮次检查可阻止旧清理继续发起后续写入,不能保证撤回已经执行的 SQL。

再次出现时,记录对话 ID、运行 ID、最后一步和时间,并对照上游请求日志、运行状态及 RUN_COMPLETION_TIMEOUT run_id=... phase=terminal_checkpoint|terminal_status|metadata_cleanup。 技能锁超时会包含 Timed out waiting for skill view ...;沙箱命令超时会包含 Command timed out。 不要仅凭页面最后显示“读取技能”就断定仍在读文件,它也可能是结束消息没有传到浏览器。

回归入口

  • 后端 packages/harness/tests/test_skill_view_waits.py:锁竞争和并发校验超时、失败后恢复。
  • 后端 packages/harness/tests/test_skill_view_cache.py、test_skill_view_tool_reuse.py:既有启停与缓存安全性。
  • 后端 packages/harness/tests/test_sandbox_command_liveness.py:真实父子进程、输出、非交互输入及特殊文件/长行。
  • 后端 packages/harness/tests/test_run_completion_timeout.py:挂起的结束收尾、取消、新一轮隔离及正常长图执行。
  • 前端 src/core/api/control-timeout.test.ts、src/core/threads/stream-terminal-recovery.test.ts:控制请求超时及真实 SDK reader 恢复。
  • 前端既有 stream-recovery、stream-display、stream-reasoning-replay、stream-cursor 测试继续验证断流重连和消息显示。

本机验证结果

  • 后端 python -m pytest packages/harness/tests -q:263 passed。旧同步日志断言补了显式测试配置,避免当前启用异步日志时与后台队列竞速;异步日志仍由独立测试覆盖,未改实际运行配置。
  • 前端八个相关测试文件打包后用 Node test runner 执行:31 passed;新增测试类型修正后其中三个新增用例再次通过。
  • 修改的后端代码和测试通过 Ruff;git diff --check 通过。
  • 完整前端类型检查未通过:75 个 TypeScript 错误,均在本次未修改的文件,涉及 nextra 缺失、旧 JSX 类型、既有测试类型等。本次修改文件及新增测试无诊断。详细输出保留在 frontend-web/.runtime/hang-typecheck.log(本机文件,不作为发版资产)。
  • 命令进程树测试在 Windows 使用真实子进程;Linux 进程组分支已审查,但本机未做 Linux 实跑,AIO 未连接实际容器。未用生产模型调用或真实业务技能执行验证。