多Agent系统

SubAgent 实现对比

Aster SubAgent vs Claude Codex SubAgent 深度对比

SubAgent 实现对比

对比 Aster 的 SubAgent 实现与 Claude Extended Thinking (Codex) 的 SubAgent 机制。

🎯 Claude Codex SubAgent 核心特性

Claude 的 Extended Thinking 模式中,SubAgent 有以下关键特性:

1. 自动状态持久化

- SubAgent 执行过程中自动保存状态
- 支持中断后恢复(Resume)
- 状态包括:执行进度、中间结果、工具调用历史
- 持久化到磁盘/数据库

2. 长时间运行支持

- SubAgent 可以运行数小时甚至数天
- 主 Agent 不需要等待 SubAgent 完成
- 异步轮询机制检查 SubAgent 状态
- 支持后台执行

3. 资源监控和限制

- CPU、内存、磁盘使用监控
- 超时控制
- 资源配额限制
- 自动清理机制

4. 丰富的生命周期管理

- Start: 启动 SubAgent
- Pause: 暂停执行
- Resume: 恢复执行
- Stop: 停止执行
- Query: 查询状态和输出
- Cleanup: 清理资源

5. 进程级隔离

- 每个 SubAgent 运行在独立进程中
- 完全的内存隔离
- 独立的文件系统视图(可选)
- 沙箱执行环境

📋 Aster SubAgent 当前实现

✅ 已实现的特性

特性实现状态说明
基本任务委派✅ 完整通过 task 工具调用
上下文隔离✅ 完整每个 SubAgent 独立上下文
并行执行✅ 完整支持同时启动多个 SubAgent
专业化配置✅ 完整SubAgentSpec 定义不同类型
工具集配置✅ 完整可继承或自定义工具
中间件继承✅ 完整灵活的中间件栈配置
结果返回✅ 完整返回摘要结果给主 Agent

⚠️ 部分实现的特性

特性实现状态说明
状态持久化⚠️ 部分FileSubagentManager 有基础实现,但未完全集成
生命周期管理⚠️ 部分有 Start/Stop,缺少 Pause/Resume
资源监控⚠️ 部分有基础的 CPU/内存监控,但不完善
进程隔离⚠️ 部分FileSubagentManager 支持进程级隔离,但未作为默认

❌ 缺失的特性

特性实现状态影响
Resume 机制❌ 缺失无法恢复中断的 SubAgent
异步轮询❌ 缺失主 Agent 必须等待 SubAgent 完成
后台执行❌ 缺失SubAgent 无法在后台长时间运行
状态查询 API❌ 缺失无法查询正在运行的 SubAgent 状态
自动清理❌ 缺失需要手动清理 SubAgent 资源
超时控制❌ 缺失没有自动超时机制

🔍 详细对比

1. 生命周期管理

Claude Codex:

# 启动 SubAgent
task_id = start_subagent(
    type="researcher",
    prompt="研究 AI 最新进展",
    timeout=3600  # 1小时超时
)

# 主 Agent 继续其他工作
# ...

# 稍后查询状态
status = query_subagent(task_id)
if status.state == "running":
    print(f"进度: {status.progress}%")
elif status.state == "completed":
    result = get_subagent_output(task_id)

# 如果需要,可以暂停
pause_subagent(task_id)

# 稍后恢复
resume_subagent(task_id)

# 清理
cleanup_subagent(task_id)

Aster 当前实现:

// 启动 SubAgent(同步等待)
result, err := subagent.Execute(ctx, description, parentContext)
// 主 Agent 阻塞等待结果

// ❌ 无法查询状态
// ❌ 无法暂停/恢复
// ❌ 无法异步执行

Aster 的 FileSubagentManager(未完全集成):

// 有基础的生命周期管理,但未与 SubAgentMiddleware 集成
manager := builtin.NewFileSubagentManager()

// 启动
instance, _ := manager.StartSubagent(ctx, config)

// 查询状态
status, _ := manager.GetSubagent(instance.ID)

// 停止
manager.StopSubagent(instance.ID)

// ⚠️ 但这些功能未暴露给 task 工具

2. 状态持久化

Claude Codex:

# 自动持久化
task_id = start_subagent(...)

# 即使主程序崩溃,SubAgent 状态也被保存
# 重启后可以恢复
task_ids = list_subagents()
for task_id in task_ids:
    if get_status(task_id) == "running":
        # 可以选择恢复或清理
        resume_subagent(task_id)

Aster 当前实现:

// ❌ SubAgent 状态不持久化
// ❌ 主 Agent 关闭后,SubAgent 状态丢失
// ❌ 无法恢复中断的任务

// FileSubagentManager 有持久化,但未集成

3. 异步执行

Claude Codex:

# 启动多个长时间运行的 SubAgent
task1 = start_subagent("researcher", "深度研究主题A")
task2 = start_subagent("researcher", "深度研究主题B")
task3 = start_subagent("researcher", "深度研究主题C")

# 主 Agent 立即返回,继续处理其他任务
# SubAgent 在后台运行

# 稍后轮询检查
while True:
    statuses = [query_subagent(t) for t in [task1, task2, task3]]
    if all(s.state == "completed" for s in statuses):
        break
    time.sleep(10)  # 每10秒检查一次

# 收集结果
results = [get_output(t) for t in [task1, task2, task3]]

Aster 当前实现:

// ✅ 支持并行启动
// ❌ 但主 Agent 必须等待所有 SubAgent 完成
// ❌ 无法异步轮询
// ❌ 无法在后台长时间运行

// 并行执行(但仍然是同步等待)
result1, _ := subagent1.Execute(ctx, desc1, nil)  // 阻塞
result2, _ := subagent2.Execute(ctx, desc2, nil)  // 阻塞
result3, _ := subagent3.Execute(ctx, desc3, nil)  // 阻塞

4. 资源监控

Claude Codex:

status = query_subagent(task_id)
print(f"CPU: {status.cpu_percent}%")
print(f"Memory: {status.memory_mb} MB")
print(f"Runtime: {status.runtime_seconds}s")
print(f"Token usage: {status.tokens_used}")

# 自动超时
if status.runtime > timeout:
    stop_subagent(task_id)

Aster 当前实现:

// FileSubagentManager 有基础监控
instance, _ := manager.GetSubagent(taskID)
if instance.ResourceUsage != nil {
    fmt.Printf("Memory: %.2f MB\n", instance.ResourceUsage.MemoryMB)
    fmt.Printf("CPU: %.2f%%\n", instance.ResourceUsage.CPUPercent)
}

// ⚠️ 但未集成到 SubAgentMiddleware
// ❌ 没有自动超时机制
// ❌ 没有资源配额限制

5. 进程隔离

Claude Codex:

# 每个 SubAgent 运行在独立进程中
# 完全的内存隔离
# 崩溃不影响主 Agent
task_id = start_subagent(
    type="code-executor",
    prompt="执行用户代码",
    sandbox=True,  # 沙箱隔离
    resource_limits={
        "max_memory_mb": 512,
        "max_cpu_percent": 50,
        "max_runtime_seconds": 300
    }
)

Aster 当前实现:

// ❌ SubAgent 运行在同一进程中(goroutine)
// ❌ 共享内存空间
// ⚠️ 崩溃可能影响主 Agent

// FileSubagentManager 支持进程隔离,但未作为默认

🎯 改进建议

优先级 1:集成 FileSubagentManager

问题FileSubagentManager 已经实现了很多高级特性,但未与 SubAgentMiddleware 集成。

建议

// 1. 修改 SubAgentMiddleware 使用 FileSubagentManager
type SubAgentMiddleware struct {
    *BaseMiddleware
    manager        SubagentManager  // 使用 SubagentManager 接口
    enableParallel bool
    mu             sync.RWMutex
}

// 2. 修改 task 工具支持异步执行
func (t *TaskTool) Execute(ctx context.Context, input map[string]interface{}, tc *tools.ToolContext) (interface{}, error) {
    // 启动 SubAgent(异步)
    instance, err := t.middleware.manager.StartSubagent(ctx, config)

    // 返回 task_id,而不是等待结果
    return map[string]interface{}{
        "ok":      true,
        "task_id": instance.ID,
        "status":  "started",
    }, nil
}

// 3. 添加新工具:query_subagent
type QuerySubagentTool struct {
    middleware *SubAgentMiddleware
}

func (t *QuerySubagentTool) Execute(ctx context.Context, input map[string]interface{}, tc *tools.ToolContext) (interface{}, error) {
    taskID := input["task_id"].(string)
    instance, err := t.middleware.manager.GetSubagent(taskID)

    return map[string]interface{}{
        "task_id": taskID,
        "status":  instance.Status,
        "output":  instance.Output,
        "progress": calculateProgress(instance),
    }, nil
}

优先级 2:添加 Resume 机制

问题:无法恢复中断的 SubAgent。

建议

// 1. 在 SubagentManager 中添加 Resume 方法(已有)
// 2. 添加 resume_subagent 工具
type ResumeSubagentTool struct {
    middleware *SubAgentMiddleware
}

func (t *ResumeSubagentTool) Execute(ctx context.Context, input map[string]interface{}, tc *tools.ToolContext) (interface{}, error) {
    taskID := input["task_id"].(string)
    instance, err := t.middleware.manager.ResumeSubagent(taskID)

    return map[string]interface{}{
        "ok":      true,
        "task_id": instance.ID,
        "status":  instance.Status,
    }, nil
}

// 3. 在主 Agent 启动时自动恢复未完成的 SubAgent
func (m *SubAgentMiddleware) OnAgentStart(ctx context.Context, agentID string) error {
    // 列出所有 SubAgent
    instances, _ := m.manager.ListSubagents()

    // 恢复未完成的
    for _, instance := range instances {
        if instance.Status == "running" || instance.Status == "starting" {
            log.Printf("Resuming subagent: %s", instance.ID)
            _, _ = m.manager.ResumeSubagent(instance.ID)
        }
    }

    return nil
}

优先级 3:异步轮询机制

问题:主 Agent 必须等待 SubAgent 完成。

建议

// 1. 修改 task 工具支持 async 参数
func (t *TaskTool) InputSchema() map[string]interface{} {
    return map[string]interface{}{
        "type": "object",
        "properties": map[string]interface{}{
            "description": map[string]interface{}{
                "type": "string",
                "description": "Task description",
            },
            "subagent_type": map[string]interface{}{
                "type": "string",
                "description": "SubAgent type",
            },
            "async": map[string]interface{}{
                "type": "boolean",
                "description": "Run asynchronously (default: false)",
                "default": false,
            },
        },
        "required": []string{"description", "subagent_type"},
    }
}

// 2. 异步执行逻辑
func (t *TaskTool) Execute(ctx context.Context, input map[string]interface{}, tc *tools.ToolContext) (interface{}, error) {
    async := input["async"].(bool)

    if async {
        // 异步启动
        instance, _ := t.middleware.manager.StartSubagent(ctx, config)
        return map[string]interface{}{
            "ok":      true,
            "task_id": instance.ID,
            "status":  "started",
            "message": "SubAgent started in background. Use query_subagent to check status.",
        }, nil
    } else {
        // 同步等待(当前行为)
        // ...
    }
}

优先级 4:超时和资源限制

问题:没有自动超时和资源配额控制。

建议

// 1. 在 SubagentConfig 中添加限制
type SubagentConfig struct {
    // ... 现有字段

    // 资源限制
    Timeout         time.Duration     `json:"timeout,omitempty"`
    MaxMemoryMB     int               `json:"max_memory_mb,omitempty"`
    MaxCPUPercent   float64           `json:"max_cpu_percent,omitempty"`
    MaxTokens       int               `json:"max_tokens,omitempty"`
}

// 2. 在 monitorSubagent 中实现限制
func (sm *FileSubagentManager) monitorSubagent(ctx context.Context, instance *SubagentInstance, cmd *exec.Cmd, outFile *os.File) {
    ticker := time.NewTicker(5 * time.Second)
    defer ticker.Stop()

    timeout := instance.Config.Timeout
    if timeout == 0 {
        timeout = 1 * time.Hour // 默认1小时
    }

    timeoutTimer := time.NewTimer(timeout)
    defer timeoutTimer.Stop()

    for {
        select {
        case <-timeoutTimer.C:
            // 超时,终止进程
            log.Printf("SubAgent %s timeout, killing process", instance.ID)
            _ = cmd.Process.Kill()
            instance.Status = "timeout"
            instance.Error = "execution timeout"
            return

        case <-ticker.C:
            // 检查资源使用
            sm.updateResourceUsage(instance)

            if instance.ResourceUsage != nil {
                // 检查内存限制
                if instance.Config.MaxMemoryMB > 0 &&
                   instance.ResourceUsage.MemoryMB > float64(instance.Config.MaxMemoryMB) {
                    log.Printf("SubAgent %s exceeded memory limit", instance.ID)
                    _ = cmd.Process.Kill()
                    instance.Status = "resource_limit_exceeded"
                    instance.Error = "memory limit exceeded"
                    return
                }

                // 检查 CPU 限制
                if instance.Config.MaxCPUPercent > 0 &&
                   instance.ResourceUsage.CPUPercent > instance.Config.MaxCPUPercent {
                    log.Printf("SubAgent %s exceeded CPU limit", instance.ID)
                    _ = cmd.Process.Kill()
                    instance.Status = "resource_limit_exceeded"
                    instance.Error = "CPU limit exceeded"
                    return
                }
            }

        case <-ctx.Done():
            return
        }
    }
}

优先级 5:进程级隔离(可选)

问题:SubAgent 运行在同一进程中,缺乏隔离。

建议

// 1. 添加配置选项
type SubAgentMiddlewareConfig struct {
    // ... 现有字段

    // 隔离模式
    IsolationMode string // "goroutine" (默认) 或 "process"
}

// 2. 根据模式选择实现
func NewSubAgentMiddleware(config *SubAgentMiddlewareConfig) (*SubAgentMiddleware, error) {
    var manager SubagentManager

    if config.IsolationMode == "process" {
        // 使用进程级隔离
        manager = builtin.NewFileSubagentManager()
    } else {
        // 使用 goroutine(当前实现)
        manager = NewInProcessSubagentManager()
    }

    // ...
}

📊 功能完整度评分

功能类别Claude CodexAster 当前Aster 潜力
基础任务委派⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
上下文隔离⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
并行执行⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
状态持久化⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Resume 机制⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
异步执行⭐⭐⭐⭐⭐⭐⭐⭐⭐
资源监控⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
超时控制⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
进程隔离⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
生命周期管理⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

总体评分

  • Claude Codex: 50/50 ⭐ (100%)
  • Aster 当前: 28/50 ⭐ (56%)
  • Aster 潜力: 48/50 ⭐ (96%)

🎯 总结

优势

  1. 基础功能完整:任务委派、上下文隔离、并行执行都很好
  2. 设计灵活:SubAgentSpec、中间件继承等设计优秀
  3. 已有基础设施:FileSubagentManager 已实现很多高级特性

不足

  1. 未完全集成:FileSubagentManager 的高级特性未暴露给用户
  2. 缺少异步支持:无法后台运行长时间任务
  3. 缺少 Resume:无法恢复中断的任务
  4. 资源控制不足:没有超时和配额限制

建议

  1. 短期(1-2周):集成 FileSubagentManager,添加 query_subagent 工具
  2. 中期(1个月):实现异步执行和 Resume 机制
  3. 长期(2-3个月):完善资源监控、超时控制、进程隔离

好消息:你们已经有了 FileSubagentManager,它实现了很多 Claude Codex 的特性!只需要将它与 SubAgentMiddleware 集成,就能大幅提升功能完整度。

📚 参考资源