RSS

第 17 章 / 共 21 章

自治代理:从被指派到自己认领

约 5 分钟 更新于

上一章留下的问题:现在的队友是被动的——Lead 派一件,它做一件。任务板上躺着十个待办,Lead 就得发十次指派消息。而且队友做完当前工作就退出了,新任务解锁时它已经不在了。

Lead 变成了瓶颈。它把大部分 token 花在了调度上,而不是判断上。

17.1 三个状态

给队友一个状态机:

状态在做什么退出条件
WORK读收件箱 → 调模型 → 执行工具,循环模型不再调工具(stop_reason != "tool_use")
IDLE每 5 秒轮询一次:先看收件箱,再扫任务板有消息 / 认领到任务 → 回 WORK;60 秒无事 → SHUTDOWN
SHUTDOWN汇报总结,退出—

关键改动是:**做完一轮工作后进入 IDLE,而不是直接退出。**这一个改动同时解决了三件事——队友能接住后续解锁的任务、能收到关机请求(第16章的遗留问题)、能响应临时指派。

第17章:WORK / IDLE / SHUTDOWN 三态循环
图 17.1:自治队友的三态循环。要看的是 IDLE 指回 WORK 的那两条入边——收件箱和任务板是两个独立的唤醒源,且收件箱优先。理解了这个优先级,就理解了”自治”为什么没有牺牲”可指挥”。

17.2 空闲轮询

IDLE_POLL_INTERVAL = 5      # 秒
IDLE_TIMEOUT = 60           # 秒

def idle_poll(agent_name: str) -> str:
    """返回 'work' / 'shutdown' / 'timeout'"""
    for _ in range(IDLE_TIMEOUT // IDLE_POLL_INTERVAL):
        time.sleep(IDLE_POLL_INTERVAL)

        inbox = BUS.read_inbox(agent_name)              # ① 收件箱优先
        if inbox:
            for msg in inbox:
                if msg.get("type") == "shutdown_request":
                    return "shutdown"
            requeue(agent_name, inbox)                  # 其余消息留给 WORK 处理
            return "work"

        unclaimed = scan_unclaimed_tasks()              # ② 再扫任务板
        if unclaimed:
            task = unclaimed[0]
            if "Claimed" in claim_task(task["id"], agent_name):
                return "work"
    return "timeout"

def scan_unclaimed_tasks() -> list[dict]:
    out = []
    for p in TASKS_DIR.glob("*.json"):
        t = json.loads(p.read_text())
        if t["status"] == "pending" and not t["owner"] and can_start(t["id"]):
            out.append(t)
    return out

收件箱优先于任务板,理由很实际:来自 Lead 或同伴的消息通常带有更高的时效性(“停下来”、“先做这个”、“我改了接口”)。如果先扫任务板,队友可能刚认领了一个新任务,才发现 Lead 十秒前让它停下。

scan_unclaimed_tasks 里三个条件缺一不可:状态是 pending(没在做也没做完)、没有 owner(没被别人认领)、can_start 为真(上游依赖都完成了)。少任何一条,你都会得到重复劳动或者提前开工。

17.3 走一遍:一次自治协作

任务板:A(无依赖)、B(依赖 A)、C(依赖 A)、D(依赖 B、C)。队友:alice、bob。

时刻alicebob任务板
T0扫到 A,认领,进入 WORK扫描:B/C/D 都被阻塞 → IDLEA 进行中
T1完成 A → IDLE仍在 IDLE 轮询A 完成,B/C 解锁
T2轮询扫到 B,认领轮询扫到 C,认领B、C 并行进行
T3完成 B → IDLE完成 C → IDLED 解锁
T4先轮询到,认领 D扫不到可认领的任务D 进行中
T5完成 D → IDLE 60 秒 → 汇报退出IDLE 60 秒 → 汇报退出全部完成

**Lead 在这段过程里一次指派都没发。**它只需要在开头把任务图建好,之后队友自己按依赖顺序推进。Lead 从”调度员”变成了”规划者”——这才是它该干的事。

17.4 工程现实

自治带来了两个新问题,教学版都没解决:

认领竞态。 alice 和 bob 可能在同一毫秒读到同一个任务的 status == "pending",然后都写入认领。真实系统会对任务文件加锁并配短退避重试。教学版靠”轮询间隔 5 秒 + 队友数量少”降低概率,但没有消除它。判断你的系统需不需要锁,看的是”同时扫描的 Agent 数量”和”任务数量”的比值。

身份丢失。 队友的上下文也会被压缩(第8章)。压缩之后,“我是 alice,负责认证模块”这句话可能被摘要掉,队友开始不知道自己是谁、该认领什么。解法是在每次压缩后重新注入身份——把角色说明当作和系统提示词同级的常驻内容,而不是普通对话历史。

另外两个可以改进的点:

  • **固定 60 秒超时太死板。**长跑任务的队友可能需要等更久。可以做成指数增长的轮询间隔。
  • **认领策略是”第一个”。**没有优先级概念。真实场景里应该按任务优先级、依赖深度(先做阻塞了最多下游的任务)或者队友专长来挑。

17.5 常见坑

17.6 让两个队友零指派完成四个任务

17.7 说全认领任务的三个条件