
25Plan-Execute-Replan让复杂任务拥有可调整的计划《Eino 实战用 Go 构建 AI 应用与智能体》· 第 25 篇关键词Planner、Executor、Replanner、动态规划、执行预算前置条件已完成 24Supervisor 模式 与 23Workflow Agents。复杂任务的麻烦在于一开始就把计划想全往往想不全。比如“排查打印机离线并恢复”可能第一步检查网线就发现是网段问题后续步骤全都要改。一次性规划面对这种情况会很僵硬。Plan-Execute-Replan 的思路是接受这个现实先制定计划执行中发现不符就重新规划。计划是可调整的不是一次性的。一、三个角色角色职责说明Planner规划者把目标拆成有序步骤输出结构化的planExecutor执行者逐步执行计划可以调用工具Replanner重规划者判断是否需要调整输出新计划或最终答复流程是目标 ↓ Planner 制定计划 ↓ Executor 执行步骤 ↓ Replanner 判断 ├─ 需要继续 → 更新计划 → 回到 Executor └─ 可以收尾 → 输出最终答复关键在 Replanner它决定“继续”还是“收尾”。这是与一次性规划的本质区别。二、两个内置工具planexecute包预置了两个工具定义用来规范模型的输出格式plan生成步骤列表。ParamsOneOf:{steps:数组每项是字符串要求按顺序}respond生成最终答复。ParamsOneOf:{response:字符串完整的最终答复}这两个工具都是内置常量PlanToolInfo、RespondToolInfo文档和参数说明已经写好。不要自己重新发明格式——用内置的能保证 Planner、Executor、Replanner 三方对齐。Replanner会同时配置plan和respond两个工具由模型决定调用哪个调用plan表示要调整计划调用respond表示任务完成。三、构建方式三个角色各自创建再组装planner,err:planexecute.NewPlanner(ctx,planexecute.PlannerConfig{ToolCallingChatModel:cm,// 需要支持工具调用的模型// ChatModelWithFormattedOutput: cm, // 或者用支持结构化输出的模型})executor,err:planexecute.NewExecutor(ctx,planexecute.ExecutorConfig{Model:cm,ToolsConfig:adk.ToolsConfig{...},// 执行者可用的工具})replanner,err:planexecute.NewReplanner(ctx,planexecute.ReplannerConfig{ChatModel:cm,// 需要支持工具调用})pe,err:planexecute.New(ctx,planexecute.Config{Planner:planner,Executor:executor,Replanner:replanner,MaxIterations:10,// 默认 10})几个配置要点配置说明Planner的模型需要支持工具调用或结构化输出二选一Replanner的模型需要支持工具调用它要用plan/respond两个工具MaxIterations“执行—重规划”循环的上限默认 10Executor的ToolsConfig执行者能用的工具这才是真正干活的部分MaxIterations不可忽略。它约束的是“执行—重规划”的循环次数是防止无限改进的保护。四、完整示例下面这段程序用脚本化模型走完“规划 → 执行 → 重规划 → 收尾”的完整流程不访问外部服务packagemainimport(contextfmtloggithub.com/cloudwego/eino/adkgithub.com/cloudwego/eino/adk/prebuilt/planexecutegithub.com/cloudwego/eino/components/modelgithub.com/cloudwego/eino/schema)// scriptedModel 按脚本返回响应。typescriptedModelstruct{responses[]*schema.Message idxint}func(m*scriptedModel)Generate(_context.Context,_[]*schema.Message,_...model.Option)(*schema.Message,error){ifm.idxlen(m.responses){returnschema.AssistantMessage(脚本结束,nil),nil}r:m.responses[m.idx]m.idxreturnr,nil}func(m*scriptedModel)Stream(ctx context.Context,in[]*schema.Message,opts...model.Option)(*schema.StreamReader[*schema.Message],error){msg,err:m.Generate(ctx,in,opts...)iferr!nil{returnnil,err}sr,sw:schema.Pipe[*schema.Message](1)gofunc(){defersw.Close();sw.Send(msg,nil)}()returnsr,nil}func(m*scriptedModel)WithTools([]*schema.ToolInfo)(model.ToolCallingChatModel,error){returnm,nil}funcmain(){iferr:run();err!nil{log.Fatal(err)}}funcrun()error{ctx:context.Background()// 规划者输出 plan 工具调用给出步骤列表。planner,err:planexecute.NewPlanner(ctx,planexecute.PlannerConfig{ToolCallingChatModel:scriptedModel{responses:[]*schema.Message{schema.AssistantMessage(,[]schema.ToolCall{{ID:plan1,Type:function,Function:schema.FunctionCall{Name:plan,Arguments:{steps:[检查网线连接,确认所在网段,提交工单给网络支持组]},},}}),}},})iferr!nil{returnerr}// 执行者逐步执行计划这里是模拟执行。executor,err:planexecute.NewExecutor(ctx,planexecute.ExecutorConfig{Model:scriptedModel{responses:[]*schema.Message{schema.AssistantMessage(步骤执行完成网线与网段均已检查。,nil),schema.AssistantMessage(步骤执行完成已提交工单。,nil),schema.AssistantMessage(步骤执行完成全部完成。,nil),schema.AssistantMessage(步骤执行完成,nil),}},})iferr!nil{returnerr}// 重规划者判断可以给出最终答复调用 respond 工具。replanner,err:planexecute.NewReplanner(ctx,planexecute.ReplannerConfig{ChatModel:scriptedModel{responses:[]*schema.Message{schema.AssistantMessage(,[]schema.ToolCall{{ID:resp1,Type:function,Function:schema.FunctionCall{Name:respond,Arguments:{response:已完成排查网线与网段正常工单已提交网络支持组。},},}}),}},})iferr!nil{returnerr}pe,err:planexecute.New(ctx,planexecute.Config{Planner:planner,Executor:executor,Replanner:replanner,})iferr!nil{returnfmt.Errorf(创建 Plan-Execute-Replan 失败: %w,err)}runner:adk.NewRunner(ctx,adk.RunnerConfig{Agent:pe})iter:runner.Query(ctx,帮我排查三楼打印机离线问题)n:0for{event,ok:iter.Next()if!ok{break}ifevent.Err!nil{fmt.Println( [事件错误],event.Err)continue}nifmv:messageOutput(event);mv!nilmv.Message!nilmv.Message.Content!{fmt.Printf( agent%-12s content%q\n,event.AgentName,trim(mv.Message.Content))}}fmt.Println(事件总数:,n)returnnil}functrim(sstring)string{r:[]rune(s)iflen(r)44{returnstring(r[:44])…}returns}funcmessageOutput(event*adk.AgentEvent)*adk.MessageVariant{ifeventnil||event.Outputnil{returnnil}returnevent.Output.MessageOutput}实机输出agentplanner content{\steps\:[\检查网线连接\,\确认所在网段\,\提交工单给网络支持组\]} agentexecutor content步骤执行完成网线与网段均已检查。 agentreplanner content{\response\:\已完成排查网线与网段正常工单已提交网络支持组。\} 事件总数: 4三个角色的分工在输出里一目了然planner输出了结构化的步骤数组。executor执行了步骤并报告结果。replanner判定可以收尾输出最终答复调用respond。如果 Replanner 改为调用plan就会带上新的步骤回到 Executor形成“执行—重规划”的循环——这正是名字里 “Replan” 的含义。五、什么时候值得用Plan-Execute-Replan 适合步骤无法一次确定的任务场景是否适合排查类任务下一步取决于上一步结果适合需要多轮信息收集才能定方案适合步骤明确且固定不适合用 Workflow Agents单个操作查询、生成不适合用普通 Agent需要人工审批的关键操作可以结合但要用 HITL第 20 篇判断依据如果“计划本身可能需要改”这个模式才带来价值。计划根本不会变那就只是多绕了一圈。六、代价与现实问题1. 调用次数明显增加。每轮“执行—重规划”都包含多次模型调用。一个 5 步任务加上若干轮重规划实际调用次数可能是简单 Agent 的数倍。2. Replanner 是效果的关键也最容易出问题。它可能过早收尾步骤没做完就说完成。反复调整永远在重规划触及MaxIterations。计划越改越偏偏离原始目标。改善方向在提示词中明确“什么条件下才算完成”并约束重规划的理由。3. 计划与执行的语义要一致。如果 Executor 没按计划执行Replanner 的判断就会失真。内置的plan/respond工具保证了格式一致但语义一致性仍依赖提示词。4. 与人工审批结合。关键步骤可以引入 HITL执行前确认、计划变更时告知。这比事后发现更安全。5.MaxIterations要按成本设。默认 10 意味着最多 10 轮“执行—重规划”。上生产前应结合延迟与费用预算确认这个数字。七、常见问题Replanner 一直重规划跑满上限提示词没有明确“完成条件”或 Executor 每次返回的结果都不让 Replanner 满意。检查两者的提示词一致性必要时收紧重规划标准。Replanner 过早收尾同样是指示不清。明确“所有步骤都执行成功才能收尾”。Planner 报错说模型不支持PlannerConfig需要支持工具调用或结构化输出的模型。二选一必须提供。Executor 看不到工具工具是配在ExecutorConfig.ToolsConfig里的不是配在顶层Config。计划改了但执行结果没变检查 Executor 的输入是否真的拿到新计划。脚本化模型演示时尤其要注意固定响应会掩盖这个问题。流程很长、费用很高统计“执行—重规划”的实际轮数调低MaxIterations或简化计划粒度。八、小结Plan-Execute-Replan 由三个角色组成Planner 规划、Executor 执行、Replanner 决定继续或收尾。核心价值是计划可调整适合步骤无法一次确定的排查类任务。使用内置的plan和respond工具定义保证三方格式对齐。MaxIterations默认 10限制“执行—重规划”轮数必须按成本预算确认。效果关键在 Replanner 的判断质量需要用提示词明确完成条件。计划不会变的任务用这个模式只是多绕一圈。下一篇《DeepAgents 实战任务列表、子任务委派与文件协作》看官方预置的组合模式如何一次提供规划与委派能力。参考资料与版本说明资料核对日期为2026 年 10 月 9 日Eino ADK 概述预置模式五分钟上手 Eino ADKEino 官方示例plan-execute-replanEino 主仓库本文示例按Eino v0.9.21编写使用脚本化模型不访问外部服务已实机运行正文输出为真实结果。