豆包 Agent Harness 工程师入门 | 第 8 章 后台任务

S08 后台任务 Background Tasks

前面几章我们学习了 子 Agenttask 任务系统Compact 上下文压缩 这些工具都只解决了一个问题,那就是上下文不够长的问题。

我们在使用 AI 时,还会遇到另外一个问题,那就是速度不够快的问题------目前我们都是单线程在跑 AI 交互,如果有某个任务需要去下载一个大文件怎么办?如果要下载半个小时,难道要等半个小时下载完成后再继续工作吗?

所以我们这一章引入多线程任务,如果任务被分解后,可以多线程执行,那么哪些耗时比较长的都放到后台去做,做完了再拿结果返回给 AI 即可。

代码

完整代码:gitee.com/sanqiushu/D...

python 复制代码
# 系统提示词
SYSTEM = f"You are a coding agent at {WORKDIR}. Use background_run for long-running commands."

首先是系统提示词:你是位于 {WORKDIR} 的编码代理。对于长时间运行的命令,请使用 background_run。

然后是子线程的控制器类

python 复制代码
# BackgroundManager: 子线程执行 + 通知队列
class BackgroundManager:
    # 初始化:读取 dir 目录,创建目录,然后计算有几个 task_*.json 文件,假设有 5 个,将 _next_id 设为下一个:6
    def __init__(self):
        self.tasks = {}  # task_id -> {status, result, command}
        self._notification_queue = []  # completed task results
        self._lock = threading.Lock()
    def run(self, command: str) -> str:
        """开始一个后台线程, 立即返回任务ID"""
        task_id = str(uuid.uuid4())[:8]
        self.tasks[task_id] = {"status": "running", "result": None, "command": command}
        thread = threading.Thread(
            target=self._execute, args=(task_id, command), daemon=True
        )
        thread.start()
        return f"Background task {task_id} started: {command[:80]}"
    def _execute(self, task_id: str, command: str):
        """线程目标:运行子进程,捕获输出,推送到队列"""
        try:
            # ⚠️ 风险:可能引发 shell注入攻击(如果 command 包含用户输入)
            r = subprocess.run(
                command, shell=True, cwd=WORKDIR,
                capture_output=True, text=True, timeout=300
            )
            output = (r.stdout + r.stderr).strip()[:50000]
            status = "completed"
        except subprocess.TimeoutExpired:
            output = "Error: Timeout (300s)"
            status = "timeout"
        except Exception as e:
            output = f"Error: {e}"
            status = "error"
        self.tasks[task_id]["status"] = status
        self.tasks[task_id]["result"] = output or "(no output)"
        with self._lock:
            self._notification_queue.append({
                "task_id": task_id,
                "status": status,
                "command": command[:80],
                "result": (output or "(no output)")[:500],
            })
    def check(self, task_id: str = None) -> str:
        """检查一个任务的状态或列出所有任务"""
        if task_id:
            t = self.tasks.get(task_id)
            if not t:
                return f"Error: Unknown task {task_id}"
            return f"[{t['status']}] {t['command'][:60]}\n{t.get('result') or '(running)'}"
        # 如果没有找到 task_id 那就把存在的任务都打印出来
        lines = []
        for tid, t in self.tasks.items():
            lines.append(f"{tid}: [{t['status']}] {t['command'][:60]}")
        return "\n".join(lines) if lines else "No background tasks."
    def drain_notifications(self) -> list:
        """返回并清除所有待处理的完成通知。"""
        with self._lock:
            notifs = list(self._notification_queue)
            self._notification_queue.clear()
        return notifs

后台任务结果通过 threading.Queue 传递,而非直接回调。后台线程在工作完成时向队列放入通知,主 agent 循环在每次 LLM 调用前轮询队列。这种解耦很重要:后台线程无需了解主循环的状态或时序,只需往队列放入消息然后继续。主循环按自己的节奏取出消息------永远不会在 API 调用中途或工具执行中途。没有竞争条件,没有回调地狱。

后台任务线程以 daemon=True 创建。在 Python 中,守护线程在主线程退出时自动被终止。这防止了一个常见问题:如果主 agent 完成工作并退出,但后台线程仍在运行(等待一个长时间 API 调用或陷入循环),进程会无限挂起。使用守护线程,退出是干净的------主线程结束,所有守护线程自动终止,进程退出。没有僵尸进程,不需要清理代码。

后台任务的通知使用结构化格式:{"type": "attachment", "attachment": {status, result, ...}},而非纯文本字符串。类型标签让主循环可以区别处理不同通知类型:attachment 可能作为 tool_result 注入对话,而 status_update 可能只更新进度指示器。机器可读的通知还支持程序化过滤(只显示错误、抑制进度更新)和 UI 渲染(将状态显示为进度条而非原始文本)。

其他的基本都没有什么变化:

运行

下面是我给 AI 的任务

js 复制代码
帮我运行'加密.py'文件,给我结果即可不需要知道文件内容(这是一个长耗时任务),并且等待的同时帮我修改'解密.py'的错误。

虽然能正常完成,但是如果只剩下一个等待中的任务了,那么 AI 会反复调用 check_background 工具来查看后台任务,会浪费我非常多的 token ,刚才火山引擎给我发消息,我的体验包只剩下 7 万 token 了,只剩下 20% 了。😱😱😱

相关推荐
Hello-Mr.Wang2 小时前
【保姆级教程】MasterGo MCP + Cursor 一键实现 UI 设计稿还原
前端·javascript·vue.js·ai编程
Peter·Pan爱编程2 小时前
第四篇:Cursor 深度评测 —— Composer 模式下的全栈 vibe 体验
人工智能·ai编程·composer
好运的阿财2 小时前
OpenClaw工具拆解之memory_search+memory_get
人工智能·python·ai编程·openclaw·openclaw工具
奔跑吧树袋熊3 小时前
Opus 4.7 + GPT-5.5“双核驱动”——2026最强AI编程工作流实测
gpt·ai编程
_Evan_Yao3 小时前
一文搞懂:AI编程辅助工具——从GitHub Copilot到通义灵码,不同人群如何驾驭AI编程助手?
人工智能·后端·copilot·ai编程
jimy15 小时前
进入codex后,如何检验codex是否在bubblewrap沙箱运行
ai编程
icestone20005 小时前
智能客服如何按客户类型切换话术?一套支持“渠道标签 + 用户自选 + 对话推断“的分类架构设计
大数据·人工智能·ai编程
豆包MarsCode5 小时前
TRAE SOLO 移动端上线!我们请你喝星巴克
ai编程·trae
Flynt6 小时前
谷歌75%代码由AI生成?我扒了扒Claude Code的实现原理和真实效果
ai编程·claude·cursor