自主 Agent 设计:任务认领与自组织执行 @ Lin | 2026-01-14T14:21:26+08:00 | 3 分钟阅读 | 更新于 2026-01-14T14:21:26+08:00

1、问题

s09-s10 里的队友依然高度依赖领导调度。

如果任务板上有 10 个待办任务,领导必须一个个指定谁来做,这种模式很难扩展。

这一节的目标,是让队友自己扫描任务板,发现可做任务后主动认领。

阅读前提

建议先掌握任务系统、团队邮箱和协议层三节内容。因为自主 Agent 不是凭空出现的,它依赖共享任务板、稳定通信机制和明确的认领规则。

2、生命周期升级

Agent 的循环被分成两个阶段:

  • WORK
  • IDLE

当模型不再调用工具,或者显式请求进入空闲态后,队友会进入 IDLE,并周期性轮询:

  • 自己的收件箱
  • 任务板中的可认领任务

本节架构图

flowchart TB
    subgraph L1["运行层"]
        WORK[WORK 阶段]
        IDLE[IDLE 阶段]
    end

    subgraph L2["感知层"]
        Inbox[扫描收件箱]
        Board[扫描任务板]
    end

    subgraph L3["协调层"]
        Claim[自动认领任务]
        Resume[恢复执行]
    end

    subgraph L4["状态层"]
        Shutdown[超时退出]
    end

    WORK --> IDLE
    IDLE --> Inbox
    IDLE --> Board
    Inbox --> Resume
    Board --> Claim
    Claim --> Resume
    Resume --> WORK
    IDLE --> Shutdown

3、WORK / IDLE 双阶段

主循环的结构大致如下:

def _loop(self, name, role, prompt):
    while True:
        # WORK
        messages = [{"role": "user", "content": prompt}]
        for _ in range(50):
            response = client.messages.create(...)
            if response.stop_reason != "tool_use":
                break
            # execute tools...
            if idle_requested:
                break

        # IDLE
        self._set_status(name, "idle")
        resume = self._idle_poll(name, messages)
        if not resume:
            self._set_status(name, "shutdown")
            return
        self._set_status(name, "working")

4、空闲阶段轮询

IDLE 阶段会每隔几秒检查一次邮箱和任务板:

def _idle_poll(self, name, messages):
    for _ in range(IDLE_TIMEOUT // POLL_INTERVAL):
        time.sleep(POLL_INTERVAL)

        inbox = BUS.read_inbox(name)
        if inbox:
            messages.append({"role": "user", "content": f"<inbox>{inbox}</inbox>"})
            return True

        unclaimed = scan_unclaimed_tasks()
        if unclaimed:
            claim_task(unclaimed[0]["id"], name)
            messages.append({
                "role": "user",
                "content": f"<auto-claimed>Task #{unclaimed[0]['id']}</auto-claimed>",
            })
            return True

    return False

如果在超时时间内没有消息也没有新任务,Agent 就会自动关机。

5、扫描未认领任务

可认领任务的筛选条件很清晰:

  • status == pending
  • 没有 owner
  • 没有 blockedBy

代码类似下面这样:

def scan_unclaimed_tasks() -> list:
    unclaimed = []
    for f in sorted(TASKS_DIR.glob("task_*.json")):
        task = json.loads(f.read_text())
        if task.get("status") == "pending" and not task.get("owner") and not task.get("blockedBy"):
            unclaimed.append(task)
    return unclaimed

6、身份重注入

教程里还提到了一个很细的点:上下文压缩后,Agent 可能会忘记自己的身份。

因此在消息长度很短时,要重新插入身份块:

if len(messages) <= 3:
    messages.insert(0, {
        "role": "user",
        "content": f"<identity>You are '{name}', role: {role}, team: {team_name}. Continue your work.</identity>",
    })
    messages.insert(1, {
        "role": "assistant",
        "content": f"I am {name}. Continuing.",
    })

更完整的可运行示例

下面这个版本把空闲轮询、扫描任务板和自动认领三个关键动作串起来了。

import json
import time
from pathlib import Path

TASKS_DIR = Path(".tasks")
IDLE_TIMEOUT = 30
POLL_INTERVAL = 3

def scan_unclaimed_tasks() -> list[dict]:
    items = []
    for f in sorted(TASKS_DIR.glob("task_*.json")):
        task = json.loads(f.read_text(encoding="utf-8"))
        if (
            task.get("status") == "pending"
            and not task.get("owner")
            and not task.get("blockedBy")
        ):
            items.append(task)
    return items

def claim_task(task_id: int, owner: str) -> bool:
    path = TASKS_DIR / f"task_{task_id}.json"
    task = json.loads(path.read_text(encoding="utf-8"))
    if task.get("owner") or task.get("status") != "pending" or task.get("blockedBy"):
        return False
    task["owner"] = owner
    task["status"] = "in_progress"
    path.write_text(json.dumps(task, ensure_ascii=False, indent=2), encoding="utf-8")
    return True

def idle_poll(name: str) -> str | None:
    for _ in range(IDLE_TIMEOUT // POLL_INTERVAL):
        time.sleep(POLL_INTERVAL)
        unclaimed = scan_unclaimed_tasks()
        if unclaimed and claim_task(unclaimed[0]["id"], name):
            return f"{name} claimed task #{unclaimed[0]['id']}"
    return None

本节完整 demo 目录结构

自治执行依赖任务板、轮询逻辑和 Agent 主体三部分:

demo-s11/
├── autonomous_agent.py
├── task_board.py
└── .tasks/
    ├── task_1.json
    └── task_2.json

autonomous_agent.py 负责 WORK / IDLE 双阶段循环,task_board.py 负责扫描和认领任务,.tasks/ 存放共享任务状态。

7、补充说明

自主 Agent 的难点不在“会不会轮询”,而在“如何避免多个队友同时抢同一个任务”。

一个比较稳妥的实现方式是在认领任务时做原子更新,例如先重新读取任务文件,再校验 ownerstatus 是否仍满足认领条件,最后一次性写回。否则两个 Agent 在同一时间窗口里看到同一个空任务,就可能同时认领成功。

另外,自治并不等于完全放任。任务认领规则、超时退出、身份重注入这些机制,本质上都是为了让队友在自主执行时仍然保持角色边界和系统一致性。

与下一节的衔接

当多个 Agent 都能主动认领任务以后,新的冲突会从“谁做什么”转到“大家在哪里做”。最后一节会把工作区隔离补上。

8、小结

这一节的关键是“自组织”。

队友不再只是被动等待指令,而是能够在空闲时主动查看任务板、认领任务、恢复工作。到这里,多 Agent 协作已经开始具备自治特征。

© 2019 - 2026 Lin 的博客

Powered by Hugo with theme Dream.

avatar
关于我

Lin 的 ❤️ 博客

记录一些 🌈 生活上,技术上的事

  • 🛠️ 技术阵地: 深耕 Java 开发 ,目前向 AI 应用领域探索与拓展。这里主要记录我从后端架构到 AI 赋能的进阶之路、技术思考以及日常的 debug 血泪史。始终致力于写出优雅且没有 bug 的代码(尽量)。
  • 🌈 生活侧写: 偶尔脱下极客的外衣,我也会在这里分享生活中的灵光一现。不管是好用的效率工具,还是周末的一场骑行,都是构成我真实生活的一部分。