第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:什么时候用哪个?
| Chain | Agent(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调用拉开差距的地方:
- ReAct:想一步、做一步、看结果、再调整。Eino的
react.NewAgent一行搞定。 - 思维树:当"一条路走到黑"不行时,同时想好几个方案再挑。Go的goroutine让并行探索天然快速。
- Eino Chain:流程固定的场景,用Chain把步骤串成流水线——更快、更稳、更省token。
- Eino Graph:流程不是一条直线时,用Graph做分支、循环、并行——对应Python生态的LangGraph。
- 反思机制:在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 天生适合这个场景。"

