Skip to content

Latest commit

 

History

History
205 lines (163 loc) · 6.62 KB

File metadata and controls

205 lines (163 loc) · 6.62 KB

该系列文章基于 github.com/shareAI-lab/learn-claude-code 写就,该仓库以大道至简的风格剖析了Claude Code的核心原理,值得大家学习。由于该仓库是基于Python语言,为方便.NET开发者学习,我已经将代码基于.NET 10的dotnet file 重写,源码已上传至github,源码地址见文末。

v10: 自治智能体 - WORK/IDLE 生命周期

本文是 Learn Claude Code (C# 版) 系列的第十一篇,对应代码文件 v10_autonomous_agents.cs。

问题:被动的队友

v9 实现了结构化协议,但队友仍然是被动的——它们只在收到消息时才工作:

Lead: "fe-dev, implement the login page"
fe-dev: (完成后) "Done."
  ... fe-dev 空转, 不主动做任何事 ...

Lead: "fe-dev, now implement the dashboard"
fe-dev: (完成后) "Done."
  ... 再次空转 ...

这就像一个员工做完手头的事就坐着发呆,不会主动看看任务板上还有什么待办事项。

问题分两层:

  1. 无自驱力:队友完成一个任务后不会主动寻找下一个
  2. 身份丢失:上下文压缩后,队友忘记自己是谁、擅长什么

解决方案:WORK/IDLE 双阶段循环

带任务看板轮询的空闲循环让队友能自己发现和认领工作,上下文压缩后通过身份重注入保持角色认知。

              ┌──────────────────────────┐
              │         WORK 阶段         │
              │  正常 agent loop          │
              │  执行工具 / 处理消息       │
              │                          │
              │  调用 idle 工具 ──────────┼──→ 转入 IDLE
              └──────────────────────────┘
                           ↑
                           │ (发现新任务)
                           │
              ┌──────────────────────────┐
              │         IDLE 阶段         │
              │  每 5 秒轮询:             │
              │    1. 检查收件箱           │
              │    2. 扫描任务看板         │
              │                          │
              │  找到 → claim → WORK      │
              │  60秒超时 → 上报 Lead     │
              └──────────────────────────┘

idle 工具

让队友显式声明"我现在没事做":

"idle" => {
    phase = Phase.IDLE;
    return "Entering IDLE phase. Will poll for new tasks.";
}

IDLE 阶段轮询

async Task IdleLoopAsync(string name)
{
    var maxPolls = 12;  // 12 × 5s = 60s 超时
    var polls = 0;

    while (polls < maxPolls)
    {
        await Task.Delay(5000);
        polls++;

        // 1. 检查收件箱
        var inbox = messageBus.Read(name);
        if (inbox.Count > 0)
        {
            // 有消息 → 注入到消息流, 回到 WORK
            messages.Add(new() { Role = Role.User,
                Content = FormatInboxMessages(inbox) });
            phase = Phase.WORK;
            return;
        }

        // 2. 扫描任务看板
        var unclaimed = taskManager.ScanUnclaimed();
        if (unclaimed.Count > 0)
        {
            var task = unclaimed.First();
            if (taskManager.ClaimTask(task.Id, name))
            {
                // 成功认领 → 回到 WORK
                messages.Add(new() { Role = Role.User,
                    Content = $"[Claimed task {task.Id}: {task.Title}]" });
                phase = Phase.WORK;
                return;
            }
        }
    }

    // 超时 → 通知 Lead
    messageBus.Send(name, "lead", "IDLE timeout, no tasks found.", "message");
}

任务认领的线程安全

多个队友可能同时看到同一个未认领的任务,需要原子操作:

class TaskManager
{
    object claimLock = new();

    public bool ClaimTask(string taskId, string owner)
    {
        lock (claimLock)
        {
            var task = Load(taskId);
            if (task["status"] != "pending" || task["owner"] != null)
                return false;  // 已被认领

            task["status"] = "in-progress";
            task["owner"] = owner;
            Save(taskId, task);
            return true;
        }
    }
}

身份重注入

上下文压缩会丢失 system prompt 和角色认知。解决方案:压缩后重新注入身份信息:

static ContentBlockParam MakeIdentityBlock(string name, string role)
{
    return new TextBlockParam
    {
        Text = $"[Identity Reminder] You are {name}, role: {role}. " +
               $"Use your specialized skills. Check inbox and task board regularly."
    };
}

在自动压缩后调用:

if (EstimateTokens(messages) > THRESHOLD)
{
    messages = await AutoCompactAsync(messages);

    // 重注入身份
    messages.Add(new()
    {
        Role = Role.User,
        Content = MakeIdentityBlock(name, role)
    });
}

工具集扩展

Agent v9 工具数 v10 新增 v10 总计
Lead 12 +2 (task_create, task_list) 14
Teammate 8 +2 (idle, claim_task) 10

相对 v9 的变更

组件 之前 (v9) 之后 (v10)
队友行为 被动 (等待消息) 主动 (轮询任务看板)
生命周期 单一循环 WORK/IDLE 双阶段
任务看板 无 集成 TaskManager
身份管理 无 压缩后重注入
自治能力 无 自动认领 + 超时上报

设计原理

WORK/IDLE 循环是基于拉取 (pull-based) 的任务分配模型:

  1. Push vs Pull:v8-v9 是 push(Lead 逐个分配),v10 增加了 pull(队友自己认领)
  2. 超时兜底:60 秒找不到工作就上报,避免无限空转
  3. 身份重注入:系统提示在压缩后被清除,显式注入保持角色一致性
  4. 乐观锁:ClaimTask 用 lock 保证只有一个 agent 能认领同一任务

这个模式在分布式系统中很常见:Kubernetes 的 Pod 调度、Celery 的 worker 都使用类似的"工作循环 + 空闲轮询"模式。

运行

dotnet run v10_autonomous_agents.cs

可以尝试的提示:

  1. Create 3 tasks on the board, then spawn alice and bob. Watch them auto-claim.
  2. Spawn a coder teammate and let it find work from the task board itself
  3. Create tasks with dependencies. Watch teammates respect the blocked order.
  4. 输入 /tasks 查看带 owner 的任务看板
  5. 输入 /team 监控谁在工作、谁在空闲