工具的等待与后台执行¶
生成报告或委派子任务时,工具可能需要一段时间才能完成。如果 Agent 还有独立工作,可以让工具先返回“处理中”,完成后再把结果交给 Agent。
async def 只表示 Python 异步函数,不会自动让 Agent 越过工具继续。下面用 SDK 的工具注册、steer() 和对话历史组合实现后台执行。
选择等待方式¶
| 方式 | 适合什么情况 | 示例辅助代码的参数 |
|---|---|---|
| 等待完成 | 下一步必须依赖结果 | wait_seconds=None |
| 直接后台执行 | 可以先做其他工作 | wait_seconds=0 |
| 等待一段时间后转后台 | 希望尽快取得结果,又不想一直等待 | wait_seconds=5 |
这些参数属于下面的示例辅助代码,不是 SDK 内置配置。超时只结束本次等待,原任务继续运行。
生成一份后台报告¶
完成模型配置,从仓库根目录运行:
python -m examples.runtime.background_tools
示例包含两部分:本页的工具代码,以及共用的 examples/runtime/background_support.py。从仓库运行时无需额外安装辅助包。
"""从仓库根目录运行:python -m examples.runtime.background_tools"""
import asyncio
import os
import bumblehive
from examples.runtime.background_support import BackgroundTools, Conversation
async def main() -> None:
config = bumblehive.RuntimeArguments(
model=os.environ["BUMBLEHIVE_MODEL"],
api_key=os.environ["BUMBLEHIVE_API_KEY"],
base_url=os.environ["BUMBLEHIVE_BASE_URL"],
skill_names=[],
tool_names=["make_report"],
)
conversation = Conversation()
jobs = BackgroundTools(conversation.notify)
async with bumblehive.from_config(config) as runtime:
@runtime.tools.tool(description=(
"生成统计报告。background=true 立即后台执行;false 等待完成。"
"pending 只是处理中,结果随后会作为消息送达;不要重复发起。"
))
async def make_report(background: bool = True) -> dict:
async def work() -> dict:
await asyncio.sleep(3) # 模拟耗时计算。
return {"orders": 12, "total_yuan": 360}
return await jobs.submit(
work(), wait_seconds=0 if background else None,
)
await conversation.run(
runtime,
"请调用 make_report 后台生成报告,先说明报告可用于什么,"
"收到实际结果后再总结数字。不要重复调用。",
jobs,
)
if __name__ == "__main__":
asyncio.run(main())
make_report 用三秒延迟模拟计算。background=True 时立即返回 pending 和任务标识;False 时直接等待报告结果。
运行过程类似:
调用 make_report → 工具返回 pending
Agent 先说明报告的用途
后台计算完成 → 发送包含任务标识的结果
Agent 总结:12 笔订单,总金额 360
如果报告很快完成,结果也可能在主 Agent 给出首次回答前到达。
设置等待时间¶
将工具中的 submit() 改为:
return await jobs.submit(work(), wait_seconds=1)
三秒任务超过一秒等待,会返回 pending。改为 5 则直接返回报告。关键实现只有:
task = asyncio.ensure_future(work)
done, _ = await asyncio.wait({task}, timeout=wait_seconds)
if done:
return task.result()
# task 仍在运行,登记完成后的投递,再返回 pending。
这里使用 asyncio.wait(),超时不会取消 task。工具只返回一次;晚到结果通过新消息送达,不会修改之前的工具结果。
结果如何回到 Agent¶
共用辅助代码做两件事:
BackgroundTools管理等待及完成通知,失败也会作为结果通知。Conversation在运行中调用steer();本轮结束后则更新历史,收到晚到结果再开始下一轮。
查看完整辅助代码
"""教学示例共用的等待与结果投递逻辑;不属于 SDK 公共 API。"""
import asyncio
import json
from collections.abc import Awaitable, Callable
from typing import Any
from uuid import uuid4
import bumblehive
class BackgroundTools:
"""共用等待逻辑;调用方决定怎样投递晚到结果。"""
def __init__(self, notify: Callable[[str, Any], None]) -> None:
self.notify = notify
self.tasks: list[asyncio.Task] = []
self.deliveries: list[asyncio.Task] = []
async def submit(self, work: Awaitable, *, wait_seconds: float | None) -> Any:
task = asyncio.ensure_future(work)
self.tasks.append(task)
if wait_seconds is None:
return await task
if wait_seconds > 0:
# 只结束等待,不取消仍在执行的工作。
done, _ = await asyncio.wait({task}, timeout=wait_seconds)
if done:
return task.result()
task_id = uuid4().hex
self.deliveries.append(asyncio.create_task(self._deliver(task_id, task)))
return {"status": "pending", "task_id": task_id}
async def _deliver(self, task_id: str, task: asyncio.Future) -> None:
try:
value = await task
except Exception as exc:
value = {"status": "error", "message": str(exc)}
self.notify(task_id, value)
async def drain(self) -> None:
await asyncio.gather(*self.deliveries)
async def close(self) -> None:
for task in self.tasks + self.deliveries:
if not task.done():
task.cancel()
await asyncio.gather(*self.tasks, *self.deliveries, return_exceptions=True)
class Conversation:
"""运行中用 steer 投递;运行结束后用同一份历史继续。"""
def __init__(self) -> None:
self.current: bumblehive.AgentRun | None = None
self.inbox: list[str] = []
def notify(self, task_id: str, value: Any) -> None:
content = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
message = f"[Background tool result]\nTask ID: {task_id}\n\n{content}"
if self.current is not None:
try:
self.current.steer(message)
return
except RuntimeError:
pass # 当前运行已结束,交给下一轮。
self.inbox.append(message)
async def run(self, runtime, prompt: str, background: BackgroundTools):
history = bumblehive.MessageHistory()
try:
while True:
self.current = runtime.run(prompt, history=history)
result = await self.current
history.replace_run_messages(result.messages)
self.inbox.extend(
message["content"] for message in self.current.take_pending()
)
print(result.error.message if result.error else result.final_content)
if result.error:
return result
# 保持应用存活,等待后台结果;Agent 的这一轮已经结束。
await background.drain()
if not self.inbox:
return result
prompt = "\n\n".join(self.inbox)
self.inbox.clear()
finally:
await background.close()
应用需要保持运行才能接收结果。本例等待所有后台任务完成后退出,任务保存在当前进程中,不提供重启恢复。
同一机制可以用于子 Agent 协作和主动追问。工具描述应明确告诉模型:pending 不是最终答案,不要重复发起同一任务;还有独立工作就继续,必须依赖结果时就等待后续消息。