【AgentScope Java新手村系列】(12)计划模式:用 enablePlanMode 给 HarnessAgent 装上任务拆解大脑

发布时间:2026/9/27 15:34:32
【AgentScope Java新手村系列】(12)计划模式:用 enablePlanMode 给 HarnessAgent 装上任务拆解大脑 1. 为什么 HarnessAgent 需要计划模式如果你用过 AgentScope Java 的 HarnessAgent 跑多步任务大概率遇到过这种场景你让它帮我搭一个用户认证模块它上来就开始写代码写到一半发现数据库表结构没设计回头改改完发现接口定义又对不上来回折腾四五轮。更糟的是有些操作是破坏性的——文件已经覆盖了命令已经执行了你想喊停都来不及。这就是 ReAct 循环的天然缺陷思考、调工具、看结果、再思考每一步都是临场判断没有全局视角。任务越复杂跑偏的概率越高。计划模式Plan Mode要解决的就是这个问题。开启enablePlanMode(true)之后HarnessAgent 会把一次复杂任务拆成两个阶段先进入只读的 PLAN 阶段agent 只能看文件、调研现状然后把计划写进workspace/plans/PLAN.md你审完这份计划说行它才进入 BUILD 阶段真正动手。这就像装修队先出图纸给你审审过了再砸墙布线而不是一进门就开始砸。这篇面向 AgentScope Java 新手从零讲清楚enablePlanMode开启后 HarnessAgent 怎么把复杂指令拆成可执行步骤给出可复制的配置骨架并用一次多步任务让你直观看到计划模式前后的行为差异。适合正在用 HarnessAgent 做多步工作流、又不想让 agent 跑偏的开发者。2. 前置准备模型接入与依赖在写代码之前先把模型接入这块理清楚。AgentScope Java 的 HarnessAgent 需要一个 ChatModel 实例你可以用 DashScope、OpenAI 兼容接口或者通过 TaoToken 这类聚合网关来统一管理模型调用。TaoToken 的定位是模型 API 聚合平台把不同厂商的模型接口统一成一套 OpenAI 兼容协议你换模型时不用改代码只换 modelName 就行。对于新手来说好处是不用同时维护好几套 SDK 和 Key。注册和拿 Key 的流程不复杂进官网注册账号在控制台创建一个 API Key然后把它配到环境变量里。接入文档里有各语言的调用示例Java 这边直接用 OpenAI 兼容的 base_url 即可。# 把 Key 配到环境变量避免硬编码进代码 export TAOTOKEN_API_KEYsk-你的key如果你用的是 DashScope 原生接口那就配DASHSCOPE_API_KEY两者选其一即可。下面示例里我用 TaoToken 的兼容端点方便你换模型。依赖方面AgentScope Java 的核心包和 harness 包都要引进来。Maven 里大致是这样dependency groupIdio.agentscope/groupId artifactIdagentscope-core/artifactId version2.0.0/version /dependency dependency groupIdio.agentscope/groupId artifactIdagentscope-harness/artifactId version2.0.0/version /dependency版本号以你实际拉到的为准2.0 之后enablePlanMode才下沉到 HarnessAgent1.x 时代用的是 PlanNotebook这个后面会讲迁移。3. 可复制的 HarnessAgent 配置骨架先看一个最小的计划模式配置。核心就一行.enablePlanMode(true)但周边几个参数决定了 agent 的行为边界。import io.agentscope.core.agent.RuntimeContext; import io.agentscope.core.message.UserMessage; import io.agentscope.core.model.OpenAIChatModel; import io.agentscope.harness.HarnessAgent; import java.nio.file.Path; import java.util.List; public class PlanModeDemo { public static void main(String[] args) { OpenAIChatModel model OpenAIChatModel.builder() .apiKey(System.getenv(TAOTOKEN_API_KEY)) .baseUrl(https://taotoken.net/api) .modelName(qwen-plus) .build(); HarnessAgent agent HarnessAgent.builder() .name(project_planner) .sysPrompt(你是一个项目经理遇到多步任务先用 plan mode 写计划。) .model(model) .workspace(Path.of(./workspace)) .enablePlanMode(true) // 关键开启计划模式 .build(); agent.call( List.of(new UserMessage(user, 我下周要办一场 200 人技术大会请帮我做一份执行计划 - 场地 - 议程 - 嘉宾 - 报名 - 现场 )), RuntimeContext.empty()) .block(); } }几个参数值得单独说workspace是 agent 的工作目录计划文件会落在workspace/plans/PLAN.mdtodo 状态会持久化到workspace/state/session-*.json。这个目录建议纳入 git 管理计划变更就有版本记录。sysPrompt里最好明确告诉 agent多步任务先写计划虽然 plan mode 本身有强制门控但系统提示词能减少它误判单步任务的情况。enablePlanMode(true)是开关。关掉它agent 就是普通 ReAct 循环打开它agent 在 PLAN 阶段会被拦截所有非只读工具调用。跑完这段代码你去workspace/plans/PLAN.md看大致会是这样# 技术大会执行计划 ## Step 1 — 锁定场地 - 目标确定可容纳 200 人的宴会厅 - 负责行政 - 完成标准拿到合同 付款凭证 ## Step 2 — 公布议程 - 目标议程在官网公开 - 依赖Step 1 ## Step 3 — 确认嘉宾 ...注意这时候 agent 还没真正去订场地、发议程它只是把计划写下来了。这就是 PLAN 阶段和 BUILD 阶段的分界。4. 四个内置工具到底做什么开启计划模式后HarnessAgent 会注入四个内置工具理解它们的分工是用好这个功能的前提。工具何时被调用副作用plan_enteragent 判断这是多步任务写入 PLAN.md 头部进入只读计划模式plan_write写或修订计划步骤修改 workspace/plans/PLAN.mdplan_exit所有计划步骤完成收尾 PLAN.md状态置为 DONEtodo_write任何时候记录子任务写入 AgentState 的 todo 列表plan_enter是进入计划模式的入口。LLM 调了它PlanModeMiddleware 就开始拦截所有非只读工具返回 DENIED。这时候 agent 只能读文件、查资料不能写文件、不能调部署接口。plan_write负责写和改计划。你中途想调整方向比如把第三步的嘉宾改成都用远程连线agent 会读当前 PLAN.md调plan_write改对应步骤然后输出确认。plan_exit是退出信号。所有步骤做完agent 调它PLAN.md 收尾执行能力恢复。todo_write严格说不是计划工具而是待办工具。plan 管长期大计划todo 管短期可勾掉的清单。两者在计划模式下是配对的agent 一边按 PLAN.md 推进一边用 todo 跟踪每个子任务的完成状态。这里有个容易踩的坑plan_enter和plan_exit是成对的如果 agent 调了plan_enter但任务中途失败没调plan_exit下次会话可能还停在只读模式。排查时先看 PLAN.md 的状态字段。5. 验证一次多步任务的行为差异光看配置不够直观我们跑一个对比实验。任务用调研三个城市的咖啡店数量这是典型的多步任务每步都要调搜索工具。先跑不开计划模式的版本HarnessAgent agent HarnessAgent.builder() .name(researcher) .model(model) .workspace(Path.of(./workspace)) // .enablePlanMode(true) // 注释掉 .build();你会看到 agent 直接开始搜第一个城市搜完搜第二个中间没有任何计划输出。如果它搜到一半理解偏了比如把咖啡店数量理解成咖啡品牌数量你只能等它全跑完才发现。再跑开启计划模式的版本这次加上 subagent 协作import io.agentscope.harness.SubagentDeclaration; SubagentDeclaration research SubagentDeclaration.builder() .name(research) .description(做单点调研输入主题输出 200 字摘要) .inlineAgentsBody(你是一个研究员每主题输出 200 字摘要。) .build(); HarnessAgent agent HarnessAgent.builder() .name(research_lead) .sysPrompt( 你是一个研究主管。接到多主题调研任务时 1. 先 plan_enter 写出计划 2. 对每个主题 async spawn research subagent 3. 用 todo_write 跟踪每个 subagent 的状态 4. 等所有 subagent 回来后 plan_exit ) .model(model) .workspace(Path.of(./workspace)) .subagent(research) .enablePlanMode(true) .build(); agent.call( List.of(new UserMessage(user, 请调研以下 3 个主题 1. 杭州咖啡店数量 2. 上海咖啡店数量 3. 成都咖啡店数量 )), RuntimeContext.empty()) .block();跑完看目录结构workspace/ ├── plans/ │ └── PLAN.md # 3 步计划 └── state/ └── session-*.json # 包含 todo 列表PLAN.md 里会列出三个调研步骤session 文件里能看到 todo 的勾选状态。行为差异很明显不开计划模式agent 边想边做开了计划模式agent 先把三步写清楚你审完再放行。提示计划模式下 subagent 调度不受影响agent_spawn、agent_send、agent_list照常可用。主 agent 可以一边写计划一边 spawn 子 agent这是 2.0 推荐的中型工作流模式。6. 本篇常见错排查新手用计划模式报错和困惑集中在几个地方我按出现频率排一下。问题一agent 不进入计划模式直接开始执行。先确认enablePlanMode(true)真的加上了再看 sysPrompt 有没有引导它识别多步任务。有些模型对多步的判断偏保守你可以在提示词里明确超过两步的任务必须先 plan_enter。问题二PLAN.md 没生成。检查 workspace 目录是否有写权限以及路径是不是相对路径导致的定位问题。建议用绝对路径或确认工作目录。问题三agent 卡在只读模式出不来。大概率是plan_enter调了但plan_exit没调。打开 PLAN.md 看状态字段如果是 PLAN 状态手动编辑或重新发起一轮让它收尾。问题四想让人工介入放行。给plan_enter配一条 Permission 规则 ASKimport io.agentscope.core.permission.*; PermissionContextState perms PermissionContextState.builder() .mode(PermissionMode.ACCEPT_EDITS) .addAskRule(plan_enter, new PermissionRule(plan_enter, null, PermissionBehavior.ASK, userSettings)) .build();这样每次 agent 调plan_enter前端会弹出agent 写了如下 plan是否放行这就是 HITLHuman In The Loop模式。问题五从 1.x 迁移过来找不到 PlanNotebook。2.0 移除了io.agentscope.core.plan.PlanNotebook对应关系是PlanNotebook.createPlan(...)换成enablePlanMode(true)加 LLM 自己调plan_enternotebook.addStep(...)换成plan_writenotebook.finishStep(idx)换成plan_write改对应步骤状态notebook.getCurrentPlan()换成直接读workspace/plans/PLAN.md。注意PLAN.md 是普通 Markdown你可以直接编辑它下一轮 agent 推理时会读到人编辑后的版本。这个特性让人改计划和agent 改计划能无缝衔接。7. 继续深入的方向计划模式跑通之后下一步可以往两个方向走。一是把 Permission 规则配细让不同工具走不同审批策略比如plan_enter走 ASK、只读工具走 ALLOW、写文件走 ASK。二是把计划模式和 subagent 编排结合主 agent 负责写计划和汇总子 agent 负责并行执行适合调研、批量重构这类可以拆分的任务。如果你还没配好模型接入可以先去 TaoToken 控制台创建一个 API Key接入文档里有 Java 的完整示例。想先直观感受计划模式的行为差异用模型对话跑一轮多步任务看它会不会先写计划再动手比读文档快得多。长期做编码类 Agent 的话Coding Plan 那边有更完整的工程化配置可以参考。