Skip to content

feat(tools): add detached async execution - #267

Merged
hm1229 merged 5 commits into
OpenBMB:mainfrom
orhuoxu:feat/detached-async-tools
Sep 9, 2026
Merged

feat(tools): add detached async execution#267
hm1229 merged 5 commits into
OpenBMB:mainfrom
orhuoxu:feat/detached-async-tools

Conversation

@orhuoxu

@orhuoxu orhuoxu commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add tool-level detached execution with StaffDeck-owned task IDs
  • support two explicit strategies: StaffDeck-managed background execution and provider-managed async tasks
  • persist provider task IDs, poll provider status endpoints, and accept authenticated callbacks
  • send a stable Idempotency-Key for provider submissions and avoid replaying requests with ambiguous outcomes
  • expose authenticated task lookup and task status capability to Harness
  • resume only SOP frames from durable checkpoints; regular conversations remain query-only
  • update the tool configuration UI and preserve synchronous HTTP, A2A, and MCP behavior

Behavior

StaffDeck-managed background execution

  • returns a StaffDeck task ID immediately
  • executes the ordinary synchronous HTTP request in the background
  • persists the final result without blocking the current conversation turn

Provider-managed async tasks

  • submits the provider request once with a stable idempotency key
  • extracts the provider task ID using the configured task_id_field
  • stores accepted/working state and polls status_url using the provider task ID
  • maps provider statuses and extracts the configured result field
  • optionally sends callback URL/token headers when EXTERNAL_TASK_CALLBACK_BASE_URL is configured
  • marks an ambiguous post-submission crash as outcome_unknown instead of replaying a potentially non-idempotent request

Conversation and SOP behavior

  • regular conversation tasks finish without preempting the user's next message; users query them by StaffDeck task ID
  • SOP tasks persist the detached receipt, update it with the final external result, and resume from the next step only after successful completion
  • failed or ambiguous SOP tasks resume at the current step with the persisted error context

Configuration

  • EXTERNAL_TASK_CALLBACK_BASE_URL: optional externally reachable StaffDeck base URL used to construct provider callback URLs; production deployments should use HTTPS
  • provider-managed tasks require a status_url and can configure task ID, status, result, mapping, polling interval, and tracking deadline fields

Validation

  • focused backend async/tool/Harness/auth tests: 133 passed
  • full frontend suite: 52 files, 249 tests passed
  • detached tool form tests: 2 passed
  • frontend production build passed
  • i18n and Vite config checks passed
  • backend Ruff and git diff --check passed
  • browser validation: administrator, /enterprise/tools/new, English UI, both async strategies and provider fields verified
  • full backend suite: 2145 passed; 11 unrelated channel/SQLite migration failures were reproduced unchanged on the original PR head

@orhuoxu

orhuoxu commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

补充实现说明:

本 PR 新增 Detached 异步工具执行模式,适用于下单、审批、导出等“提交后立即返回任务号、后续主动查询”的业务流程。

核心流程:

  • 工具配置 execution_mode=detached 后,StaffDeck 本地生成 exttask_* 任务号。
  • 当前 Agent Turn 立即收到受理结果并结束,不等待 Provider 最终业务结果,也不会自动恢复旧 Harness 或主动发送完成消息。
  • 后台 worker 执行原始同步 HTTP 工具请求,并将状态、结果、错误写入 ExternalBusinessTask
  • 支持 Provider callback 和轮询状态同步,任务状态支持 queued/working/completed/failed/expired。
  • 新增 external_task_status Harness 内部能力,用户后续输入“查询 #taskId 状态”时,Agent 按 tenant/user 权限读取任务并生成自然语言结果。
  • 任务提交使用稳定幂等键,后台轮询使用数据库 lease,callback 使用任务级 opaque token 和 event_id 去重。

示例用户体验:

已经帮您提交任务,任务号 #exttask_xxx,正在后台处理。
您随时可以对我说“查询 #exttask_xxx 状态”查看结果。

验证:

  • Backend focused tests: 48 passed
  • Frontend ToolsPage tests: 2 passed
  • Frontend production build passed
  • compileall passed
  • Ruff E/F/I checks for changed modules passed
  • git diff --check passed

当前实现暂不主动推送业务完成通知;任务完成后由用户通过 taskId 主动查询。

@orhuoxu

orhuoxu commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

已补充多步 SOP + 异步工具的可恢复工作流支持。

实现链路:

  • 异步工具调用后,TaskFrame 进入 waiting_external_task,不再被当作普通 SOP 完成。
  • 持久化 task_frame_idresume_step_id,保存 SOP 恢复上下文。
  • 业务 callback 或后台轮询完成后,保存业务结果并将 TaskFrame 标记为 ready_to_resume
  • 用户下一轮消息到达时,优先恢复 ready_to_resume 的 SOP。
  • TaskFrame 重新进入 queued -> running,从 resume_step_id 对应的下一节点继续执行。
  • 外部任务结果注入 TaskFrame slots,供后续 SOP 节点使用。
  • 修复了按 TaskFrame 逻辑 ID 查找恢复记录的关联问题,并覆盖 callback 与恢复期间的竞态场景。

验证:

  • backend/.venv/bin/pytest -q backend/tests/test_external_business_tasks.py backend/tests/test_harness_v2.py backend/tests/test_task_frame_policy.py
  • 结果:108 passed
  • python3 -m compileall -q backend/app 通过
  • git diff --check 通过

当前范围:用户需要发送下一条消息后恢复 SOP;业务完成后的自动唤醒和主动消息推送暂未包含在本次改动中。

@orhuoxu

orhuoxu commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

已补充多步异步 SOP 的完整回归测试,新增提交:3b36fe4

新增测试覆盖完整恢复链路:

节点 1 调用异步工具
  -> TaskFrame 进入 waiting_external_task
  -> 保存异步任务和恢复上下文
  -> 外部任务完成
  -> 业务结果注入 checkpoint/slots
  -> TaskFrame 变为 ready_to_resume
  -> 用户下一轮消息恢复原 TaskFrame
  -> 从 resume_step_id 对应的节点继续
  -> Agent 执行后续节点
  -> 原始 SOP/Turn 完成并输出最终结果

重点验证:

  • 异步工具提交后不会被误判为普通同步完成。
  • 外部任务完成后会标记原 TaskFrame 为 ready_to_resume
  • 恢复时保留原 TaskFrame、SOP 状态和业务结果。
  • Agent checkpoint 能看到异步工具的最终结果。
  • 恢复后的 Agent 能从下一节点继续执行并生成最终回复。
  • 现有 A2A、同步 HTTP 工具和权限逻辑不受影响。

验证结果:

  • 后端相关测试:147 passed
  • 前端生产构建:通过
  • Python compileall:通过
  • git diff --check:通过

本次实现仍保持当前产品语义:业务任务完成后不会自动唤醒 Agent;用户发送下一条消息后恢复原 SOP。

@hm1229
hm1229 self-requested a review September 9, 2026 11:05
@hm1229
hm1229 merged commit 0d39828 into OpenBMB:main Sep 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants