
前几篇我们一直在打磨单个 Agent01 篇搭好统一的 LLM 底座与重试策略02 篇补上对话记忆03 篇让模型学会调工具04 篇用检索增强让它有据可答。但真实任务往往一句话里塞了三件事——“调研竞品输出对比报告给出落地建议”。让一个 Agent 一口气全做完中间步骤会被压缩错了也不知道错在哪。这一篇把镜头拉远在 HarmonyOS 7 上用三个各司其职的角色——规划、执行、评审——把任务分解 自我反思跑成一条可测试的流水线。先把边界划清楚本篇讲的是单设备上的多角色任务分解与反思不是跨设备的多智能体协商。系列后面讲 A2A 时智能体之间会对等交换意图、互相协商这里的三种角色只是同一个应用里的三段纯逻辑彼此不通信、不协商只有编排器单向调度它们。这个区分在架构评审里经常被追问值得先讲明白。一、为什么分解 反思是 Agent 的基本功大模型直接面对复杂目标的典型症状是一步到位想一次性给出最终答案过程不可见结果不可纠。把目标拆成子任务再逐个执行有两个直接收益可观测每一步的输入输出都是独立字符串哪个子任务出了问题一目了然页面可以逐条渲染可纠错评审角色对结果打分不满意就把反馈回灌给规划器重新拆解——这就是反思也是本篇标题里反思二字的来源。拿开头那个目标举例。直连模式下模型被要求一次输出竞品调研 对比报告 落地建议往往前两部分写得认真第三部分开始敷衍而且你没法只重做第三部分。分解之后三个子任务各自独立执行、独立留痕评审发现落地建议太薄可以精准地只对它重规划。粒度带来的不只是质量还有可运维性——这是把大任务变小任务之后最容易被低估的收益。本篇的三个角色全部是确定性纯函数不联网、不依赖设备、没有隐藏状态。这带来一个很实际的工程好处——同样的逻辑可以在 Node 里镜像跑断言做预检也可以在 DevEco 里用 Hypium 做回归两边结论互为印证。这是纯逻辑分层从 01 篇延续下来的红利。二、三个角色各管一段PlannerAgent规划decompose(goal)把目标按中英文标点。、;,切成子任务整句里一个分隔符都没有时回退到固定三步模板保证任何输入都有一条可执行的计划。exportinterfaceSubTask{id:number;description:string;}constFALLBACK_TEMPLATE:string[][明确目标的边界与成功标准,拆解为核心可执行步骤,执行并汇总自检结果];exportclassPlannerAgent{decompose(goal:string):SubTask[]{constdescs:string[]this.hasDelimiter(goal)?this.splitGoal(goal):FALLBACK_TEMPLATE.slice();constfinalDescs:string[]descs.length0?descs:FALLBACK_TEMPLATE.slice();consttasks:SubTask[][];for(leti0;ifinalDescs.length;i){tasks.push({id:i1,description:finalDescs[i]});}returntasks;}// hasDelimiter目标中是否出现过任意分隔符// splitGoal逐字符扫描遇分隔符收尾一段空段丢弃首尾空白去除}注意hasDelimiter的判断维度目标里是否出现过分隔符而不是切完剩几段。一句没有标点的话切完只有一段但它需要的不是单步执行而是模板兜底的三步走。这个细节曾让首版实现把做个年度计划拆成了一步单测立刻抓住了。WorkerAgent执行execute(subtask, context)对单个子任务产出一段结果。演示里返回确定性的完成[编号]: 描述 - 产出编号真实项目把这一行换成工具调用或模型推理即可编排层完全不用改。CriticAgent评审review(goal, results)用启发式打分——每条结果非空且长度达到MIN_LEN本例 8 字符记为达标全部达标给满分 1.0否则按达标比例给分低于 0.6 触发needsReplan。完整实现如下exportclassCriticAgent{review(goal:string,results:string[]):CriticVerdict{if(results.length0){return{score:0,needsReplan:true,feedback:没有任何执行结果需要重规划};}letokCount:number0;for(constrofresults){if(r.trim().lengthMIN_LEN){okCount;}}if(okCountresults.length){return{score:1.0,needsReplan:false,feedback:全部子任务产出达标};}constscore:numberokCount/results.length;return{score:score,needsReplan:scoreREPLAN_THRESHOLD,feedback:scoreREPLAN_THRESHOLD?部分产出不足建议重规划:大部分达标可交付};}}分数不是重点feedback才是它会被回灌给规划器成为下一轮拆解的输入。评审器的判决结构分数、是否重规划、反馈文本是一个纯数据接口今天用启发式实现明天换成大模型评审编排器一行代码都不用动。三、Orchestrator把三段串成有界循环exportclassOrchestrator{run(goal:string,maxReplans:number1):OrchestratorResult{letplan:SubTask[]this.planner.decompose(goal);letresults:string[]this.worker.executeAll(plan,);letverdict:CriticVerdictthis.critic.review(goal,results);letreplanned:booleanfalse;letreplanCount:number0;while(verdict.needsReplanreplanCountmaxReplans){replannedtrue;replanCount;constnewGoal:stringgoal反思反馈verdict.feedback;planthis.planner.decompose(newGoal);resultsthis.worker.executeAll(plan,verdict.feedback);verdictthis.critic.review(goal,results);}return{plan,results,score:verdict.score,replanned};}}三个关键设计终止性replanCount maxReplans是循环唯一的继续条件评审器再怎么打低分循环也最多转maxReplans圈。只要这一条在编排器就不可能死循环反馈回灌重规划时把feedback追加到目标字符串里规划器下一轮看得见上次差在哪这就是反思闭环的物理载体依赖注入三个角色都从构造参数传入缺省才用默认实现。单测时可以注入一个永远打低分的 mock 评审器精确验证恰好重规划一次后收敛也能验证maxReplans3时评审恰好被调用 4 次。四、在 DevEco 里跑起来演示页只做一件事输入目标点运行编排看到计划、逐条产出、评审分、是否发生过重规划。页面同时接了一个MockLlmClient——继承 01 篇的LlmClient底座超时、重试与响应解析照常复用只是不联网exportclassMockLlmClientextendsLlmClient{protectedasyncsendOnce(_req:ChatRequest):Promisestring{returnJSON.stringify({choices:[{message:{content:演示已基于编排给出结论},finish_reason:stop}]});}// buildPayload / buildHeaders / classifyError 按底座契约同样实现}它把编排结论转成一句LLM 旁注证明大模型通道随时可以接进来。真实项目里换成LlmClientFactory.create(openai, {...})即可页面与编排层的代码零改动。constorch:OrchestratornewOrchestrator();constresorch.run(this.goal,1);this.planLinesres.plan.map((t):stringt.id. t.description);this.resultLinesres.results;this.replanTextres.replanned?本次发生了重规划:一次通过未触发重规划;验证分两层。Node 镜像层跑 22 条断言分隔符切分计数、无分隔符走模板、空目标兜底、好评分不重规划、低分恰好一次重规划、maxReplans3时评审恰好 4 次后终止、聚合结果与计划一一对应。Hypium 层共 29 个用例覆盖同一批场景外加底座回归RetryPolicy 分类与退避、ResponseParser 解析、ConversationStore 裁剪、Agent 编排、ChatUIMessage 映射。两层都绿才敢说这条流水线是稳的。恰好一次重规划这条最值得展开它靠注入 mock 评审器实现classLowScoreCriticextendsCriticAgent{calls:number0;review(goal:string,results:string[]):CriticVerdict{this.calls;return{score:0.3,needsReplan:true,feedback:mock 不达标};}}it(低分恰好发生一次重规划后终止,0,(){constcriticnewLowScoreCritic();constresnewOrchestrator({critic}).run(目标一目标二。目标三,1);expect(critic.calls).assertEqual(2);// 初评 重规划后复评恰好一轮expect(res.replanned).assertTrue();});评审器被调用两次意味着初评一次、重规划后复评一次然后循环条件replanCount maxReplans失败退出。终止性不是靠希望它停下来而是靠一个可以被断言的结构性质——这是编排器区别于随手写个 while的地方。运行效果截图五、从演示到生产把 Worker 换成真活演示版的 Worker 返回占位字符串迁到生产只需三步方法签名一律不动接入真实执行在execute里调用LlmClient.chat或某个工具把子任务描述组装进提示词串起上下文context参数此时派上用场——把前序子任务的产出拼接进去子任务之间就有了依赖链不再是孤岛升级评审把启发式的MIN_LEN换成大模型评审让它输出结构化的分数与反馈判决接口CriticVerdict保持不变。唯一要留意的结构性变化Worker 变成异步后run也随之变成async但循环上界maxReplans的写法原样保留。换句话说无论三个角色内部多复杂有界反思循环这个骨架是整个编排器的不变量——改血肉别改骨架。六、避坑指南结合源码四个最容易踩的雷重规划死循环忘了maxReplans上界评审器持续打低分时页面直接卡死。循环条件必须是replanCount maxReplans且计数器在进入循环体时自增别放在循环末尾反馈空泛feedback 写结果不好重新想想规划器收到的等于没有。哪怕启发式评审也要输出哪几条不达标、差在哪级别的具体描述LLM 评审时同理子任务爆炸用户目标里有十个逗号就会拆出十步。工程上应给单条计划设上限比如 8 步超出就合并相邻步骤否则执行成本和 UI 都撑不住评审无标准MIN_LEN与 0.6 阈值要和业务对齐。阈值定 0.9两步里错一步就永远重规划定 0.3评审形同虚设。标准先于代码定下来写进测试。还有一条工程习惯值得坚持三个角色保持纯函数。别在 Planner 里偷偷发请求别在 Critic 里读数据库。纯函数才能在 Node 里秒级镜像、在 Hypium 里稳定回归——这也是本系列一以贯之的分层原则底座、编排、UI 各管各的谁也不越界。六、和多智能体协商的区别再强调一次维度本篇任务分解与反思系列后续A2A 多智能体协商角色关系编排器单向调度三段逻辑智能体对等协商通信方式同进程函数调用跨设备、跨进程消息终止条件maxReplans 数值上界协商轮次或共识达成典型场景拆解、执行、自检意图交换、能力发现一句话概括本篇的三个角色是同一张流程图上的三站协商场景里的多个智能体是各自流程图的主人。搜多 Agent资料时两者经常混在一起讲动手前先问自己要的是哪一种。参考与出处HarmonyOS 开发者官网主页https://developer.huawei.com/consumer/cn/doc/harmonyos-homeArkTS 语言介绍官方指南https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-overviewUIAbility 与页面生命周期官方参考https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-app-ability-uiabilityArkUI 声明式开发范式官方指南https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-ui-development应用测试框架 Hypium官方指南https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/application-test-framework最后一句让规划的有章法、执行的有产出、评审的有标准Agent 才算真正学会了想清楚再做做完再想。