Skip to content

第8章 会规划才值钱

前几章我们给Agent装了脑子、手脚、记性。现在Agent会思考、会干活、会查资料——但如果一个任务有10个步骤,它该先做哪个?做错了能不能自己发现并改正?这一章,我们讲Agent的"规划力"——它值钱的地方就在这。而且用Eino的Chain和Graph,你不用从头写循环逻辑。


8.1 让Agent学会"想一步、做一步"

8.1.1 光想不做和光做不想都不行

你可以让LLM直接回答:"帮我规划一个三天两夜的北京旅行"。LLM会给你一份看起来很合理的行程。

但你也可以让LLM真的去查天气、比价格、看评价——做一步,看一眼结果,再决定下一步

第二种方式叫 ReAct(Reasoning + Acting,推理+行动)

举个例子:用户问"北京和上海今天哪个更热?"

纯LLM回答(只有推理,没有行动):

LLM: 北京和上海的气温我没法实时获取,建议你自己查一下天气App。

ReAct Agent回答(推理+行动循环):

思考:我需要分别查北京和上海的天气。
行动:调用 get_weather("北京")
观察:北京今天28度。
思考:还需要上海的天气。
行动:调用 get_weather("上海")
观察:上海今天32度。
思考:上海比北京热4度。
回答:上海今天更热,32度,比北京高4度。

8.1.2 Eino 中的 ReAct Agent

Eino 内置了 ReAct 模式,你不需要手写思考-行动-观察的循环:

go
import (
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/flow/agent/react"
)

// 用 Eino 的 ReAct Agent——框架帮你管理整个循环
reactAgent, err := react.NewAgent(ctx, &react.AgentConfig{
    Model:       chatModel,  // 思考的大脑
    ToolsConfig: react.ToolsConfig{
        Tools: tools,         // 能干活的工具
    },
    MaxStep: 10,             // 最多思考10步,防止死循环
})

// 调用——Eino 自动管理 ReAct 循环
result, err := reactAgent.Generate(ctx, []*schema.Message{
    schema.UserMessage("北京和上海今天哪个更热?"),
})

关键MaxStep: 10 不是建议,是保命符。没有它,Agent可能在某个循环里永远出不来。


8.2 当"一条路走到黑"不够用的时候

8.2.1 ReAct 的局限性

ReAct 很好用,但它有一个问题:一旦走错方向,就很难回头

比如 Agent 帮你想了一个旅行方案,执行到一半发现"第一天去的博物馆周一闭馆"。ReAct 只能回头看一步——它可能改成去别的地方,但它不会想"也许我该从第一天开始重新规划"。

8.2.2 思维树:同时想多条路

思维树(Tree of Thoughts,ToT) 是一种更高级的推理方式——它不只走一条路,而是同时想好几个方案,然后挑最好的

和ReAct的区别

ReAct思维树
思考方式一条路走到底同时想多条路
适合任务步骤明确的(查天气→比价→下单)需要创意的(写作、规划、策略)
计算成本高(需要多次调用LLM)
纠错能力看到上一步的结果调整能从根上换一条思路

实际使用建议:大部分Agent用ReAct就够了。只有当任务本身"没有标准答案"时(比如写文章、做方案),才考虑思维树。


8.3 Eino Chain:把多个步骤串成流水线

8.3.1 不是所有场景都需要ReAct

ReAct 很强大,但有些场景不需要Agent"自己决定做什么"——你明确知道要做哪些步骤,只是想让它们自动按顺序执行

比如一个"文档总结"流程:

读文件 → 提取关键段落 → 翻译成中文 → 生成摘要

每一步的输出是下一步的输入,流程固定。这时候用 Chain 比 Agent 更合适——更快、更稳定、token消耗更低。

go
import (
    "github.com/cloudwego/eino/compose"
    "github.com/cloudwego/eino/schema"
)

// 构建 Chain:把几个步骤串成流水线
chain := compose.NewChain[map[string]any, *schema.Message]().
    // 步骤1:读取并提取关键段落
    AppendLambda(extractKeyParagraphs).
    // 步骤2:翻译成中文
    AppendChatModel(translateModel).
    // 步骤3:生成摘要
    AppendChatModel(summarizeModel)

// 编译
runnable, err := chain.Compile(ctx)

// 一行调用,自动按顺序执行
result, err := runnable.Invoke(ctx, map[string]any{
    "file_path": "/docs/report.txt",
})

8.3.2 Chain vs Agent:什么时候用哪个?

ChainAgent(ReAct)
流程固定顺序,预先定义LLM动态决定下一步
稳定性高,不会跑偏可能走弯路
灵活性低,改需求要改代码高,靠LLM自适应
Token消耗高(多次思考+工具调用)
适用场景翻译管线、数据清洗客服、搜索、复杂任务

原则:流程确定的用Chain,流程不确定的用Agent。不要什么都上ReAct——杀鸡不用牛刀。


8.4 Eino Graph:当流程不再是一条直线

8.4.1 Chain 的局限性

Chain 只能处理"一条直线"的流程。但真实场景里,你经常需要:

  • 分支:根据上一步的结果,走不同的处理路径
  • 循环:结果不满意,回去重新做
  • 并行:几个步骤同时执行,用 goroutine 加速

这就是 Graph 的用武之地。Eino Graph 对应 Python 生态里的 LangGraph——用有向图来编排 Agent 的行为。

8.4.2 一个带分支的 Graph

场景:客服意图识别。根据用户输入分流到不同处理逻辑。

go
import "github.com/cloudwego/eino/compose"

// 创建 Graph
graph := compose.NewGraph[[]*schema.Message, *schema.Message]()

// 添加节点
graph.AddNode("classifier", intentClassifier)  // 意图识别
graph.AddNode("handle_order", orderHandler)     // 处理订单查询
graph.AddNode("handle_refund", refundHandler)   // 处理退款
graph.AddNode("handle_other", otherHandler)     // 处理其他

// 添加边:classifier 根据结果分流
graph.AddEdge("classifier", func(ctx context.Context, 
    input *schema.Message) (string, error) {
    intent := extractIntent(input.Content)
    switch intent {
    case "order":
        return "handle_order", nil
    case "refund":
        return "handle_refund", nil
    default:
        return "handle_other", nil
    }
})

8.4.3 带循环的 Graph:用 ReAct 实现搜索验证

Graph 也可以实现 ReAct 的循环——而且比纯代码循环更清晰:

go
graph := compose.NewGraph[[]*schema.Message, *schema.Message]()

graph.AddNode("think", thinkNode)     // 思考下一步
graph.AddNode("act", actNode)         // 执行工具
graph.AddNode("decide", decideNode)   // 判断是否完成

// 思考 → 行动 → 判断
graph.AddEdge("think", func(ctx context.Context, 
    input *schema.Message) (string, error) {
    if needsTool(input) {
        return "act", nil    // 需要工具 → 去执行
    }
    return "decide", nil     // 不需要 → 判断是否完成
})

graph.AddEdge("act", func(ctx context.Context, 
    input *schema.Message) (string, error) {
    return "think", nil      // 工具执行完 → 回去再想想
})

graph.AddEdge("decide", func(ctx context.Context, 
    input *schema.Message) (string, error) {
    if isDone(input) {
        return compose.END, nil  // 任务完成,结束
    }
    return "think", nil           // 没完成,继续想
})

这和 react.NewAgent 本质是一样的——只不过 Graph 让你能看到每一步怎么走、为什么这么走。


8.5 Agent的"反思"能力

8.5.1 做错了要能改

Agent 不是神,它也会犯错。调了错误的工具、理解错了用户意思、生成了不准确的数据——都很常见。

好的 Agent 不是不犯错,而是犯了错能发现并改正。

这个过程叫 Self-Reflection(自我反思)

8.5.2 三个常见的反思场景

场景一:工具返回异常。

Agent调了 get_weather("北京"),返回 {"temp": 999}
反思模块:温度不可能999度,API可能出错了。
Agent:用备用工具 retry,或者告诉用户"暂时查不到"。

场景二:LLM回答自相矛盾。

LLM回答:"北京今天晴天,适合户外运动。不过出门记得带伞。"
反思模块:晴天为什么带伞?回答有矛盾。
Agent:重新生成,或者标注"可能有误"。

场景三:用户反馈。

用户:"你推荐的餐厅已经关门了。"
反思模块:用户明确指出了错误。
Agent:记录这家餐厅已关闭,下次不会再推。同时道歉并重新推荐。

8.5.3 Eino 中的反思实现

最简单的实现方式:在 Chain 末尾加一个检查节点——用另一个 LLM 调用做审查。

go
// 反思器:检查 Agent 的输出是否合理
reflector := func(ctx context.Context, 
    input map[string]any) (map[string]any, error) {
    
    checkPrompt := fmt.Sprintf(`
    请检查以下输出是否存在问题:
    1. 是否完成了任务?
    2. 数据是否合理?
    3. 是否自相矛盾?
    
    任务:%v
    输出:%v
    
    如果发现问题,回复"FAIL: 问题描述"。
    如果没有问题,回复"PASS"。
    `, input["task"], input["result"])
    
    checkResult, _ := verifierModel.Generate(ctx, 
        []*schema.Message{schema.UserMessage(checkPrompt)})
    
    if strings.HasPrefix(checkResult.Content, "FAIL") {
        return nil, fmt.Errorf("反思未通过:%s", checkResult.Content)
    }
    return input, nil
}

// 把反思节点加到 Chain 或 Graph 里
chain := compose.NewChain[map[string]any, map[string]any]().
    AppendLambda(agentLogic).   // Agent 的核心逻辑
    AppendLambda(reflector)      // 审查输出

8.6 本章小结

这一章我们讲了Agent的"规划力"——它和普通LLM调用拉开差距的地方:

  1. ReAct:想一步、做一步、看结果、再调整。Eino的 react.NewAgent 一行搞定。
  2. 思维树:当"一条路走到黑"不行时,同时想好几个方案再挑。Go的goroutine让并行探索天然快速。
  3. Eino Chain:流程固定的场景,用Chain把步骤串成流水线——更快、更稳、更省token。
  4. Eino Graph:流程不是一条直线时,用Graph做分支、循环、并行——对应Python生态的LangGraph。
  5. 反思机制:在Chain/Graph末尾加检查节点。犯错不可怕,错了不能改才可怕。

✅ 知识点检查

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

  • [ ] ReAct和纯LLM推理的核心区别是什么?
  • [ ] Eino Chain和ReAct Agent分别适合什么场景?
  • [ ] Eino Graph和Chain的本质区别是什么?
  • [ ] 反思模块主要检查哪三个方面?
  • [ ] MaxStep 参数为什么重要?

📚 延伸阅读

  • 《ReAct: Synergizing Reasoning and Acting in Language Models》
  • 《Tree of Thoughts: Deliberate Problem Solving with Large Language Models》
  • Eino Graph 文档:https://www.cloudwego.io/zh/docs/eino/
  • 本书配套源码:关注公众号「图解AI系列」免费领取

🎯 下一章预告

第9章,一个Agent搞不定的时候——

"一个人不行?那就组个团。多Agent协作,goroutine + channel 天生适合这个场景。"