Skip to content

第9章 一个人不行?组个团

前面几章我们一直在打造一个全能Agent——脑子好、手脚快、记性好、会规划。但现实中,一个Agent再强也有天花板。就像你不会让一个人同时当销售、财务、程序员和客服——你得组团队。这一章,我们讲Multi-Agent协作。Go的goroutine + channel在这个场景里是天然优势。


9.1 一个Agent忙不过来?那就多找几个

9.1.1 一个全栈Agent的困境

假设你要做一个"智能软件开发助手",它需要:

  • 理解需求(产品经理的活)
  • 写代码(程序员的活)
  • 审查代码(Code Review的活)
  • 写测试(测试工程师的活)
  • 写文档(技术写作的活)

全塞给一个Agent?它的Prompt会变得又长又乱,工具列表爆炸,一不留神就搞混角色——明明在写代码,突然跳到审查模式。

每人只干一件事?更靠谱。五个Agent,各司其职。

9.1.2 什么时候该用Multi-Agent

简单判断标准:如果一个人能干的活,用一个Agent;如果需要不同角色配合的活,用Multi-Agent。


9.2 多Agent怎么配合?

9.2.1 三种经典架构

层级模式(Supervisor):有一个"总监"Agent,负责分配任务、检查结果、做最终决策。

总监Agent:用户想做一个登录功能。
Agent A(前端):你负责写登录表单。
Agent B(后端):你负责写登录接口。
Agent C(测试):等A和B完成后,你负责写测试用例。

流水线模式(Pipeline):每个Agent处理完交给下一个。适合有明确步骤的任务。

Agent A(数据清洗)→ Agent B(数据分析)→ Agent C(报告生成)

平行模式(Parallel):多个Agent同时干活,互不依赖。这是Go最擅长的场景。

Agent A 查天气 → 
Agent B 查新闻 →  同时进行,结果汇总
Agent C 查股价 →

9.3 Eino多Agent:goroutine让它们真的并行

9.3.1 平行模式:goroutine同时跑

Python 做平行模式靠 asyncio 或线程池,Go 一个 go 关键字就搞定——而且是真正的并行:

go
// 三个 Agent 同时执行,互不等待
func parallelQuery(ctx context.Context, question string) map[string]string {
    results := make(chan QueryResult, 3)
    
    // 同时启动三个 Agent
    go func() {
        results <- QueryResult{"天气", weatherAgent.Ask(ctx, question)}
    }()
    go func() {
        results <- QueryResult{"新闻", newsAgent.Ask(ctx, question)}
    }()
    go func() {
        results <- QueryResult{"股价", stockAgent.Ask(ctx, question)}
    }()
    
    // 收集结果
    output := make(map[string]string)
    for i := 0; i < 3; i++ {
        r := <-results
        output[r.Source] = r.Content
    }
    return output
}

Python 的 asyncio.gather 也能并发,但在多核 CPU 上 Go 的 goroutine 能做到真正的并行——Python 的 GIL 决定了 asyncio 只是"看起来同时"。


9.3.2 层级模式:Eino Graph 实现 Supervisor

用 Eino Graph 实现层级模式——Supervisor节点 + 条件路由:

go
graph := compose.NewGraph[AgentState, AgentState]()

// 添加节点
graph.AddNode("supervisor", supervisorNode)    // 总监:决定下一步找谁
graph.AddNode("researcher", researcherNode)    // 研究员:查资料
graph.AddNode("coder", coderNode)              // 程序员:写代码
graph.AddNode("reviewer", reviewerNode)        // 审查员:检查代码

// 总监根据结果路由
graph.AddConditionalEdge("supervisor", func(ctx context.Context, 
    state AgentState) (string, error) {
    if state.ResearchDone && state.CodeDone && state.ReviewDone {
        return compose.END, nil
    }
    if !state.ResearchDone {
        return "researcher", nil
    }
    if !state.CodeDone {
        return "coder", nil
    }
    return "reviewer", nil
})

// 每个 Worker 干完活回总监
graph.AddEdge("researcher", "supervisor")
graph.AddEdge("coder", "supervisor")
graph.AddEdge("reviewer", "supervisor")

9.3.3 流水线模式:Chain 天然支持

流水线不需要 Graph,Chain 就是天然的流水线:

go
pipeline := compose.NewChain[TaskInput, FinalReport]().
    AppendLambda(dataCleaner).     // 数据清洗
    AppendLambda(dataAnalyzer).    // 数据分析
    AppendLambda(reportGenerator)  // 报告生成

9.4 Agent之间怎么"说话"?

9.4.1 在Go里,Agent说话就是channel通信

Python 多Agent通信靠消息字典、共享黑板、事件总线。Go 有更简单的方式——channel

go
// Agent 之间的消息
type AgentMessage struct {
    From    string
    To      string
    Type    string // request / response / notify
    Content string
}

// 前端 Agent 给后端 Agent 发消息
frontendChan := make(chan AgentMessage, 10)
backendChan := make(chan AgentMessage, 10)

// 前端 Agent
go func() {
    backendChan <- AgentMessage{
        From:    "前端Agent",
        To:      "后端Agent",
        Type:    "request",
        Content: "需要你提供一个 /api/login 接口",
    }
}()

// 后端 Agent 收到消息后处理
msg := <-backendChan

9.4.2 三种通信方式

通信方式Go 实现适合场景
点对点两个 goroutine 之间的 channel固定搭档
广播sync.Cond 或 多个 channel 写入通知所有人
共享状态sync.Map 或带锁的结构体进度追踪
go
// 共享状态——所有 Agent 往同一个地方读写
type Blackboard struct {
    mu       sync.RWMutex
    Status   map[string]string
}

func (b *Blackboard) Update(agent, status string) {
    b.mu.Lock()
    defer b.mu.Unlock()
    b.Status[agent] = status
}

func (b *Blackboard) Read() map[string]string {
    b.mu.RLock()
    defer b.mu.RUnlock()
    return b.Status
}

9.5 意见不合怎么办?

9.5.1 多Agent一定会吵架

两个Agent给出矛盾的建议很正常:

Agent A(成本优先):建议用SQLite,零配置,够用。
Agent B(性能优先):建议用PostgreSQL,并发强,可扩展。

这不是bug,这是多Agent系统的特性。不同Agent有不同的"视角"和"偏好"。

9.5.2 解决冲突的三种方式

方式一:投票。 三个以上的Agent,少数服从多数。

go
func vote(opinions []Opinion) string {
    tally := make(map[string]int)
    for _, o := range opinions {
        tally[o.Choice]++
    }
    // 找最高票
    var winner string
    max := 0
    for choice, count := range tally {
        if count > max {
            winner, max = choice, count
        }
    }
    return winner
}

方式二:总监裁决。 层级模式下,总监Agent做最终决定。

总监Agent:A说的有道理,项目初期SQLite够了。但我们预期用户量会快速增长,
          B的考虑也很重要。折中方案:先用SQLite开发,但数据层加抽象接口。

方式三:辩论后共识。 让不同意见的Agent各自陈述理由,再综合出最优解。


9.6 本章小结

这一章我们突破了"单Agent"的思维:

  1. 什么时候用Multi-Agent:任务可分拆、需要不同能力时。全栈Agent不如专业Agent团队。
  2. 三种协作模式:层级(有总监)、流水线(接力棒)、平行(各干各的)。Go的goroutine让平行模式真正并行。
  3. Eino 实现:平行模式用goroutine+channel,层级模式用Graph条件路由,流水线用Chain。
  4. Agent通信:点对点用channel,广播用sync.Cond,共享状态用sync.Map——Go的标准库就够用了。
  5. 意见不合是正常的:投票、总监裁决、辩论后共识——和人类团队解决问题的方式一模一样。

✅ 知识点检查

学完这一章,试试回答这几个问题:

  • [ ] 什么情况下该用Multi-Agent而不是单Agent?
  • [ ] 层级、流水线、平行三种模式各适合什么场景?Go在哪一种上有天然优势?
  • [ ] Eino Graph 如何实现 Superisor 模式?
  • [ ] Agent之间有哪三种通信方式?Go用什么实现?
  • [ ] 两个Agent意见不合时,有哪三种解决方法?

📚 延伸阅读


🎯 下一章预告

第10章,Agent框架那么多,到底该学哪个——

"巨人的肩膀就在那,挑哪个站?Eino、LangChainGo、自己撸——一次讲清楚,帮你做选择。"