从 Background Tasks 到 Cron Scheduler 再到 Agent Teams:Agent Harness 的异步化与多 Agent 协作演进

在前面的章节中,Agent 已经具备:

  • Tool Calling
  • Hooks
  • Todo Planning
  • Subagent
  • Skill Loading
  • Context Compact
  • Memory
  • Error Recovery
  • Task System

此时的 Agent 已经能够独立完成复杂任务。

但还有三个明显瓶颈:

问题一:等待

Agent 执行:

1
npm install

可能需要 5 分钟。

执行:

1
pip install torch

可能需要十几分钟。

这期间 Agent 什么都做不了。


问题二:只能手动触发

Agent 只能这样工作:

1
2
3
用户说一句

Agent 做一次

无法做到:

1
2
每天 9 点跑测试
每隔 5 分钟检查服务

问题三:单 Agent 注意力有限

当任务变成:

1
2
3
4
5
6
重构整个后端
重构数据库
修改鉴权
修改 API
补测试
写文档

一个 Agent 的上下文根本装不下。


因此:

1
2
3
s13 解决等待
s14 解决定时
s15 解决协作

最终把 Harness 从同步单体结构推进到异步协同结构。


第一部分:s13 Background Tasks

为什么需要 Background

s12 的执行模型是:

1
2
3
4
5
6
7
Agent

调用工具

等待工具完成

继续下一轮

例如:

1
run_bash("npm install")

Agent 会一直阻塞。

问题在于:

1
2
3
4
安装依赖期间
Agent 本来还能看代码
还能分析文件
还能拆任务

却被迫等待。

所以 s13 引入:

1
Background Task

思想非常简单:

慢任务丢后台,Agent 继续工作。


如何判断后台执行

核心函数:

1
should_run_background()

优先看模型是否明确要求:

1
run_in_background=True

例如:

1
2
3
4
bash(
command="npm install",
run_in_background=True
)

如果模型没有指定,则退化为启发式判断:

1
2
3
4
5
install
build
test
deploy
compile

出现这些关键词就认为可能是慢任务。


后台任务注册表

s13 引入三个全局对象:

1
2
3
background_tasks
background_results
background_lock

结构:

1
2
3
4
5
6
background_tasks = {
"bg_0001": {
"status": "running",
"command": "npm install"
}
}

类似:

1
ConcurrentHashMap<String, Task>

只是教学版使用 Python dict + Lock。


start_background_task

核心流程:

1
def start_background_task(block):

生成:

1
2
3
bg_0001
bg_0002
bg_0003

然后启动线程:

1
2
3
4
threading.Thread(
target=worker,
daemon=True
)

线程执行:

1
execute_tool(block)

完成后:

1
background_results[bg_id]

保存结果。


collect_background_results

后台完成后不会自动打断 Agent。

而是在下一轮收集:

1
collect_background_results()

生成:

1
2
3
4
<task_notification>
<task_id>bg_0001</task_id>
<status>completed</status>
</task_notification>

然后注入消息。

于是 Agent 看到:

1
npm install 完成了

再决定下一步。


本质

s13 的本质:

1
2
3
同步调用

异步调用

从:

1
调用 → 等待 → 返回

变成:

1
2
3
4
5
调用 → 占位返回

后台执行

完成通知

第二部分:s14 Cron Scheduler

Background 解决不了定时问题

虽然有后台任务:

1
Agent 不用等待

但仍然需要:

1
人工触发

例如:

1
帮我每天9点跑测试

Background 做不到。

所以引入:

1
Scheduler

CronJob

s14 增加:

1
2
3
4
5
6
7
@dataclass
class CronJob:
id
cron
prompt
recurring
durable

例如:

1
2
3
4
CronJob(
cron="0 9 * * *",
prompt="Run tests"
)

表示:

1
2
每天上午9点
执行 Run tests

Cron 表达式

经典 Unix 格式:

1
min hour day month week

例如:

1
0 9 * * *

每天9点

1
*/5 * * * *

每5分钟


Scheduler Thread

核心循环:

1
cron_scheduler_loop()

独立线程:

1
2
while True:
sleep(1)

每秒检查一次。

它与 Agent Loop 完全解耦。

即使:

1
2
3
Agent 正在工作
Agent 空闲
Agent 等待用户

Scheduler 都持续运行。


Cron Queue

Scheduler 不直接执行任务。

只负责:

1
cron_queue.append(job)

因此形成:

1
2
3
4
5
Scheduler

Queue

Agent

生产者消费者模型。


Queue Processor

负责检测:

1
cron_queue

是否有内容。

如果:

1
2
Agent 空闲
队列不为空

自动启动:

1
agent_loop

无需用户输入。


Durable

Cron 支持:

1
durable=True

写入:

1
.scheduled_tasks.json

例如:

1
2
3
4
{
"cron":"0 9 * * *",
"prompt":"Run tests"
}

下次启动:

1
load_durable_jobs()

恢复。


本质

s14 本质上实现:

1
事件驱动

过去:

1
2
3
用户输入

Agent行动

现在:

1
2
3
4
5
时间到

Scheduler触发

Agent行动

Agent 开始拥有:

1
主动性

第三部分:s15 Agent Teams

单 Agent 的极限

即使有:

1
2
3
Task System
Background
Scheduler

仍然只有一个 Agent。

问题:

1
2
3
4
数据库改造
鉴权改造
接口重构
测试补充

全部塞进一个上下文。

迟早:

1
上下文爆炸

为什么 Subagent 不够

s06 已经有:

1
task()

生成:

1
spawn_subagent()

但 Subagent:

1
干完活就销毁

不能持续协作。


Team 的核心思想

从:

1
Lead

变成:

1
2
3
4
Lead
├── Alice
├── Bob
└── Charlie

多个 Agent 同时工作。


MessageBus

核心组件:

1
class MessageBus

每个 Agent 一个收件箱:

1
2
3
4
.mailboxes/
lead.jsonl
alice.jsonl
bob.jsonl

发送消息:

1
2
3
4
5
BUS.send(
"alice",
"lead",
"schema completed"
)

本质:

1
append json line

收件箱机制

读取:

1
read_inbox()

然后:

1
unlink()

删除文件。

即:

1
读取即消费

类似:

1
2
RabbitMQ
Kafka Consumer

的简化版。


spawn_teammate

Lead 调用:

1
2
3
4
spawn_teammate(
name="alice",
role="backend engineer"
)

启动线程:

1
2
3
Thread(
target=run
)

然后:

1
Alice Agent Loop

开始运行。


Teammate 的上下文

每个 Agent:

1
2
3
messages
system prompt
tool set

完全独立。

例如:

1
2
3
Alice 专注数据库
Bob 专注测试
Charlie 专注文档

互不干扰。


Lead Inbox Injection

Lead 每轮检查:

1
BUS.read_inbox("lead")

如果收到:

1
2
Alice:
Schema completed

则自动注入:

1
2
3
history.append(
"[Inbox] ..."
)

让 Lead 知道结果。


一个完整协作流程

Lead:

1
重构整个系统

创建:

1
2
3
Alice → 数据库
Bob → API
Charlie → 测试

三个 Agent 并行运行。

Alice:

1
schema.sql 完成

发送:

1
lead mailbox

Lead 收到。

继续调度。


Team 与 Task System

此时真正形成:

1
2
3
4
5
6
7
Task System

Task 分发

Agent Team

Inbox 通信

架构。

任务系统负责:

1
做什么

团队系统负责:

1
谁来做

第四部分:Harness 架构升级

s13 之前:

1
2
3
4
5
User

Agent

Tool

加入 Background:

1
2
3
4
5
6
7
User

Agent

Tool

Background Thread

加入 Scheduler:

1
2
3
4
5
Scheduler

Queue

Agent

加入 Teams:

1
2
3
4
Lead
├── Alice
├── Bob
└── Charlie

最终结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
                Scheduler

Cron Queue

Lead Agent

Task System

MessageBus
┌────┼────┐
Alice Bob Charlie

Background Tasks

总结

s13 解决:

Agent 必须等待吗?

答案:

1
2
不用等
后台执行

s14 解决:

Agent 必须人工触发吗?

答案:

1
Scheduler 自动触发

s15 解决:

一个 Agent 怎么减轻负担?

答案:

1
组建团队

因此这三个章节实际上代表 Harness 的三个重大升级:

1
2
3
4
5
6
7
8
9
10
11
同步执行

异步执行

人工触发

自动触发

单 Agent

多 Agent

到这里,一个简单的 ReAct Agent 已经演化成具备:

  • 后台任务
  • 定时任务
  • 持久化任务图
  • 多 Agent 协作
  • 消息总线

能力的完整 Agent Harness。

而这也正是 Claude Code 后半部分架构开始向 Swarm(Agent Team)演化的起点。