Eino学习笔记 —— 附录1
Eino学习笔记 —— 附录一:Flow 流程模块 , ReAct Agent 与 MultiAgent
本笔记通过ai辅助生成,但对于代码阅读顺序应当没有问题(均使用eino官方的例子进行,个人认为官方的教程感觉有点太难懂了)
理论上个人感觉附录只做了解即可,需要时可进行查询
附录一:Flow 流程模块 — ReAct Agent 与 MultiAgent
📂 源码仓库:github.com/cloudwego/eino-examples↗
📖 系列文档:入门笔记 | Agentic 进阶 | 附录一 Flow | 附录二 组件 | 附录三 工具
📁 代码根目录:
flow/agent/📖 前置知识:本附录假设你已掌握 入门笔记(一~七节)和 Agentic 进阶 的内容。
速览:Flow vs ADK — 两套体系
Eino 提供了两套并行的 Agent 构建方式:
| 维度 | ADK(入门笔记 / Agentic 进阶) | Flow(本附录) |
|---|---|---|
| 入口 | adk.NewChatModelAgent / adk.NewTypedChatModelAgent | react.NewAgent / host.NewMultiAgent / Compose Graph |
| 抽象层级 | 高层封装,开箱即用 | 中低层,精细控制每个节点 |
| 编排方式 | Agent + Runner + Middleware(声明式) | Graph + Node + Branch + State(命令式) |
| 适用场景 | 快速构建标准 Agent 应用 | 自定义控制流、多 Agent 协作、精细状态管理 |
🧠 怎么选:ADK 能覆盖的场景优先用 ADK。需要自定义 ReAct 循环行为、运行时动态修改模型参数、多 Agent 状态图流转时用 Flow。
技术选型决策树
graph TD
Q["你要做什么?"] --> Q1{"标准单 Agent
问答+工具+记忆?"}
Q1 -->|是| ADK["用 ADK
入门笔记 §3 / Agentic 进阶 §3"]
Q1 -->|否| Q2{"需要自定义
ReAct 循环行为?"}
Q2 -->|是| REACT["用 react.NewAgent
本附录 §1/§2/§4"]
Q2 -->|否| Q3{"需要多 Agent 协作?"}
Q3 -->|调度模式| SUP["快速: ADK Supervisor
精细: Host MultiAgent
本附录 §6"]
Q3 -->|流水线| PE["Plan-Execute
本附录 §7"]
Q3 -->|复杂状态图| CUSTOM["自定义 Graph+State
本附录 §9"]
Q3 -->|否| Q4{"需要浏览器/沙箱工具?"}
Q4 -->|是| MANUS["参考 Manus Agent
本附录 §8"]📇 本附录速记卡片
react.NewAgent({ToolCallingModel, ToolsConfig}) → Stream/Generatereact.WithMessageFuture() → 观察中间步骤(无需 Callback)host.NewMultiAgent({Host, Specialists}) → 调度型多 Agent自定义 Graph + State → Plan-Execute / Deer-Go一、ReAct Agent 基础
📁
flow/agent/react/📖 官方文档:https://www.cloudwego.io/zh/docs/eino/core_modules/flow_integration_components/react_agent_manual/↗
1.1 ReAct 是什么
ReAct(Reasoning + Acting)是 Agent 的核心循环模式:
用户输入 → ChatModel 思考 → 需要调 Tool? ├─ 是 → 执行 Tool → Tool 结果交还 ChatModel → 继续思考 └─ 否 → 输出最终答案Eino 将这套循环封装为 react.NewAgent,一行创建,开箱即用。
1.2 最简示例:餐厅推荐
import ( "github.com/cloudwego/eino/flow/agent/react" "github.com/cloudwego/eino/compose")
rAgent, err := react.NewAgent(ctx, &react.AgentConfig{ ToolCallingModel: arkModel, ToolsConfig: compose.ToolsNodeConfig{ Tools: []tool.BaseTool{restaurantTool, dishTool}, },})
// 流式调用(与 ChatModel.Stream 一样的 Recv+EOF 模式)sr, _ := rAgent.Stream(ctx, []*schema.Message{ schema.SystemMessage("你是一个帮助用户推荐餐厅的助手..."), schema.UserMessage("我在北京,推荐口味辣的菜,至少2家餐厅"),})defer sr.Close()
for { msg, err := sr.Recv() if errors.Is(err, io.EOF) { break } fmt.Print(msg.Content)}💡 这与 入门笔记 第三节的 ChatModelAgent + Runner 功能等效,但
react.NewAgent暴露了更多底层控制点。
1.3 两个重要扩展点
MessageModifier:在每轮 ReAct 循环中注入 System Prompt:
react.NewAgent(ctx, &react.AgentConfig{ MessageModifier: func(_ context.Context, input []*schema.Message) []*schema.Message { return append([]*schema.Message{schema.SystemMessage(sys)}, input...) },})StreamToolCallChecker:自定义 Tool 调用判断(某些模型不在第一个 chunk 返回 ToolCall):
toolCallChecker := func(ctx context.Context, sr *schema.StreamReader[*schema.Message]) (bool, error) { defer sr.Close() for { msg, err := sr.Recv() if errors.Is(err, io.EOF) { break } if len(msg.ToolCalls) > 0 { return true, nil } } return false, nil}1.4 可视化调试:ExportGraph + Mermaid
anyG, opts := rAgent.ExportGraph()gen := visualize.NewMermaidGenerator("flow/agent/react")g := compose.NewGraph[[]*schema.Message, *schema.Message]()g.AddGraphNode("react_agent", anyG, opts...)g.AddEdge(compose.START, "react_agent")g.AddEdge("react_agent", compose.END)g.Compile(context.Background(), compose.WithGraphCompileCallbacks(gen))二、Agentic ReAct Agent(Responses API 版)
📁
flow/agent/react/agentic/
2.1 核心差异点
| 维度 | 基础 ReAct | Agentic ReAct |
|---|---|---|
| 模型 | model.BaseChatModel | model.AgenticModel |
| 消息 | *schema.Message | *schema.AgenticMessage |
| 工具节点 | compose.NewToolNode | compose.NewAgenticToolsNode |
| 分支判断 | 检查 ToolCalls | 检查 ContentBlocks 中的 FunctionToolCall |
| 服务端工具 | ❌ | ✅ |
2.2 关键创新:ToolReturnDirectly
config := &AgentConfig{ Model: am, ToolsConfig: compose.ToolsNodeConfig{ Tools: []tool.BaseTool{summarizeTool, locationTool}, }, ToolReturnDirectly: map[string]struct{}{ "summarize_news": {}, // ← summarize_news 执行完直接结束 },}2.3 按 ContentBlock 类型分别处理
msg, _ := schema.ConcatAgenticMessages(msgs)
for _, block := range msg.ContentBlocks { switch block.Type { case schema.ContentBlockTypeReasoning: fmt.Printf("[推理] %s\n", block.Reasoning.Text) case schema.ContentBlockTypeServerToolCall: fmt.Printf("[服务端工具] %s\n", block.ServerToolCall.Name) case schema.ContentBlockTypeAssistantGenText: fmt.Printf("[输出] %s\n", block.AssistantGenText.Text) }}三、短期记忆(Memory)
📁
flow/agent/react/memory_example/
3.1 MemoryStore 接口
type MemoryStore interface { Write(ctx, sessionID string, msgs []*schema.Message) error Read(ctx, sessionID string) ([]*schema.Message, error) Query(ctx, sessionID, text string, limit int) ([]*schema.Message, error)}| 实现 | 存储介质 | 适用场景 |
|---|---|---|
InMemoryStore | 进程内存 | 开发/测试 |
RedisStore | Redis | 生产环境 |
3.2 多轮对话模式
store := memory.NewInMemoryStore()sessionID := "session:demo"
// 每轮对话prev, _ := store.Read(ctx, sessionID) // 恢复历史eff := append(prev, schema.UserMessage(turn)) // 拼接新输入sr, _ := agent.Stream(ctx, eff, msgFutureOpt) // 执行_ = store.Write(ctx, sessionID, append(eff, produced...)) // 持久化3.3 用 MessageFuture 观察中间步骤
msgFutureOpt, msgFuture := react.WithMessageFuture()
// 消费中间消息流iter := msgFuture.GetMessageStreams()for { sr, ok, _ := iter.Next() if !ok { break } full, _ := schema.ConcatMessages(readAllChunks(sr)) fmt.Printf("中间消息: role=%s content=%s\n", full.Role, full.Content)}四、动态选项(Dynamic Option)
📁
flow/agent/react/dynamic_option_example/
4.1 痛点
ReAct Agent 在每轮迭代中使用相同的模型参数。但你可能希望第一轮开启思考模式,后续轮关闭思考。
4.2 核心机制:包装 ChatModel + ProcessState
type ChatModel struct { Model model.BaseChatModel GetOptionFunc OptionFunc // ← 根据 State 返回动态选项}
func (d *ChatModel) Generate(ctx context.Context, input []*schema.Message, opts ...model.Option) (*schema.Message, error) { var dynamicOpts []model.Option
compose.ProcessState[*State](ctx, func(_ context.Context, state *State) error { dynamicOpts = d.GetOptionFunc(ctx, input, state) state.Iteration++ return nil })
return d.Model.Generate(ctx, input, append(dynamicOpts, opts...)...)}4.3 动态选项函数示例
func getDynamicOptions(ctx context.Context, input []*schema.Message, state *State) []model.Option { if state.Iteration >= 1 { // 第二轮开始:关闭思考,禁止 Tool 调用 return []model.Option{ ark.WithThinking(&arkModel.Thinking{Type: arkModel.ThinkingTypeDisabled}), model.WithToolChoice(schema.ToolChoiceForbidden), } } // 第一轮:允许 Tool 调用 return []model.Option{ model.WithToolChoice(schema.ToolChoiceAllowed), model.WithTools(toolInfos), }}五、未知工具处理(Unknown Tool Handler)
📁
flow/agent/react/unknown_tool_handler_example/
rAgent, _ := react.NewAgent(ctx, &react.AgentConfig{ ToolsConfig: compose.ToolsNodeConfig{ Tools: []tool.BaseTool{sumTool}, UnknownToolsHandler: func(ctx context.Context, name, input string) (string, error) { return fmt.Sprintf( "unknown tool: %s; try again with the correct tool name", name, ), nil }, },})模型幻觉调用不存在的工具 → 不崩溃,返回提示 → 模型自纠错。
六、Multi-Agent:Host 模式
📁
flow/agent/multiagent/host/journal/📖 官方文档:https://www.cloudwego.io/zh/docs/eino/core_modules/flow_integration_components/multi_agent_hosting/↗
6.1 概念
一个 Host(调度者) + 多个 Specialist(专家):
用户 → Host(分析意图) ├─ "写日记" → WriteJournalSpecialist ├─ "读日记" → ReadJournalSpecialist └─ "查日记" → AnswerWithJournalSpecialist6.2 完整实现
import "github.com/cloudwego/eino/flow/agent/multiagent/host"
h := &host.Host{ChatModel: chatModel, SystemPrompt: "..."}
hostMA, _ := host.NewMultiAgent(ctx, &host.MultiAgentConfig{ Host: *h, Specialists: []*host.Specialist{writer, reader, answerer},})
// 多轮对话out, _ := hostMA.Stream(ctx, []*schema.Message{schema.UserMessage(input)}, host.WithAgentCallbacks(cb), // 可监听 HandOff 事件)6.3 与 ADK Supervisor 的对比
| 维度 | Host MultiAgent(Flow) | Supervisor(ADK) |
|---|---|---|
| 位置 | flow/agent/multiagent/host | adk/prebuilt/supervisor |
| 调度方式 | Host ChatModel 决定派给谁 | Supervisor Agent 决定派给谁 |
| 抽象层级 | 中低层(Compose Graph) | 高层(ADK 封装) |
| 适用 | 需要自定义 Specialist 结构 | 快速构建多 Agent 系统 |
七、Multi-Agent:Plan-Execute 模式
📁
flow/agent/multiagent/plan_execute/
7.1 概念
三阶段协作模式:
Planner(规划者)→ Executor(执行者)⇄ Reviser(审查者) ↓ 输出"最终答案" → END7.2 核心配置
config := &Config{ PlannerModel: deepSeekModel, // 规划者模型 ExecutorModel: arkModel, // 执行者模型(需支持 ToolCalling) ReviserModel: deepSeekModel, // 审查者模型 ToolsConfig: compose.ToolsNodeConfig{Tools: toolsConfig}, MaxStep: 100,}💡 不同角色可以用不同的模型——Planner/Reviser 用便宜的推理模型,Executor 用支持 Tool Calling 的模型。
八、完整应用:Manus Agent
📁
flow/agent/manus/
8.1 概述
Manus Agent 展示了如何用 Compose Graph API 构建一个具备以下能力的 Agent:
- 🖥️ 命令行执行:Docker 沙箱中运行 Python 代码
- 🌐 浏览器操作:基于 Playwright 的网页浏览和交互
- 🔍 搜索引擎:DuckDuckGo 集成
- 👤 人类反馈:执行中断,等待用户确认后继续
- 📊 可观测性:Langfuse + CozeLoop 双追踪
8.2 Interrupt 驱动的人类反馈循环
for { result, err := agent.Invoke(ctx, input, compose.WithCheckPointID("1"), compose.WithRuntimeMaxSteps(20), )
info, ok := compose.ExtractInterruptInfo(err) if ok { // 中断了——展示结果,等待用户确认 fmt.Print("Do you want to continue? (y/n): ") // ... 读用户输入 ... continue // 自动 Resume } fmt.Printf("[FinalResult]: %s", result) break}8.3 工具生态
// 命令行工具(Docker 沙箱)sandbox.NewDockerSandbox(ctx, &sandbox.Config{...})
// 浏览器工具browseruse.NewBrowserUseTool(ctx, &browseruse.Config{...})
// 搜索引擎duckduckgo.NewSearch(ctx, &duckduckgo.Config{...})九、完整应用:Deer-Go(研究团队协作)
📁
flow/agent/deer-go/
9.1 概述
Deer-Go 是一个基于状态图的多 Agent 研究团队,模拟真实的研究协作流程。
9.2 核心机制:State 驱动的子图流转
每个子图执行完毕后,改写 state.Goto 指定下一个要执行的 Agent:
func agentHandOff(ctx context.Context, input string) (next string, err error) { compose.ProcessState[*model.State](ctx, func(_ context.Context, state *model.State) error { next = state.Goto return nil }) return next, nil}
// 所有 Agent 节点共享同一个 Branch 函数g.AddBranch(consts.Coordinator, compose.NewGraphBranch(agentHandOff, outMap))g.AddBranch(consts.Planner, compose.NewGraphBranch(agentHandOff, outMap))9.3 团队组成
Coordinator → BackgroundInvestigator → Planner → ResearchTeam ├─→ Researcher ├─→ Coder └─→ Planner(迭代) └─→ Reporter → Human → Coordinator十、示例导航
| 目录 | 主题 | 核心知识点 | 复杂度 |
|---|---|---|---|
react/ | ReAct Agent 基础 | react.NewAgent, Stream, ExportGraph | ⭐ |
react/agentic/ | Agentic ReAct | Graph 自定义 ReAct, ToolReturnDirectly | ⭐⭐⭐ |
react/memory_example/ | 短期记忆 | MemoryStore 接口, MessageFuture | ⭐⭐ |
react/dynamic_option_example/ | 动态选项 | ChatModel 包装, ProcessState | ⭐⭐⭐ |
react/unknown_tool_handler_example/ | 未知工具 | UnknownToolsHandler | ⭐ |
multiagent/host/journal/ | Host Multi-Agent | Host + Specialist, HandOff 回调 | ⭐⭐ |
multiagent/plan_execute/ | Plan-Execute | Planner→Executor⇄Reviser | ⭐⭐⭐ |
manus/ | Manus Agent | Docker 沙箱, 浏览器, Interrupt+Resume | ⭐⭐⭐⭐ |
deer-go/ | Deer-Go 研究团队 | 状态图流转, 多子图协作, Goto 驱动 | ⭐⭐⭐⭐ |
常用 import 路径
// ReAct Agent"github.com/cloudwego/eino/flow/agent/react" // react.NewAgent, WithMessageFuture
// Multi-Agent"github.com/cloudwego/eino/flow/agent/multiagent/host" // host.NewMultiAgent, Host, Specialist
// Compose 底层"github.com/cloudwego/eino/compose" // Graph, Branch, ProcessState, ToolNode"github.com/cloudwego/eino/schema" // Message, AgenticMessage, ToolInfo
// Callbacks"github.com/cloudwego/eino/callbacks" // Handler, HandlerBuilder"github.com/cloudwego/eino/utils/callbacks" // NewHandlerHelper📇 本附录速记卡片
Flow 模块核心方法: react.NewAgent({ToolCallingModel, ToolsConfig}) host.NewMultiAgent({Host, Specialists}) compose.NewGraphBranch(fn, outMap) compose.ProcessState[T](ctx, fn) — 读写 Graph State
选型:标准→ADK | 自定义循环→react | 多Agent调度→host | 状态图→Graph+State📂 源码仓库:github.com/cloudwego/eino-examples↗
📖 继续阅读:
- 入门笔记 — Eino ADK 从 Hello World 到 Compose 编排
- Agentic 进阶 — Responses API / AgenticMessage / Typed 泛型
- 附录一:Flow 流程模块 — ReAct Agent / Multi-Agent / 状态图 ← 你在这里
- 附录二:Components 组件模块 — A/B 路由 / HTTP 日志 / 检索增强 / 文档解析
- 附录三:Lambda 与调试工具 — Lambda 写法 / Devops / Mermaid 可视化