——执行逻辑:从 ReAct 到 Agent 的配置骨架与验证)
1. 从一次「查询股价」说起Jmanus 的执行逻辑到底长什么样如果你正在看 spring-ai-alibaba 里的 Jmanus大概率会被它那套「点一下按钮浏览器自己打开、搜索、翻页、抓文本、最后吐出一段结论」的流程吸引。它定位是 Java 版的 manus 实现核心卖点不是某个单点工具而是把 ReActReasoning Acting循环真正跑成了一个可配置、可观测、可复现的 Agent 执行链路。很多同学第一次跑通OpenManusSpringBootApplication.main之后看到http://localhost:18080/弹出页面就以为结束了其实真正的学习才刚开始页面背后那条从 PlanTemplate 到 Plan、再到 Step、再到 think/act 的调度链才是 Jmanus 值得拆开看的部分。这篇聚焦的是「执行逻辑」这一层不铺开讲前端交互也不重复官方 README 的启动步骤。我会把 Jmanus 的调度链路按 ReAct 的视角拆成可验证的动作并给出可复制的config.toml/settings.json骨架以及用 TaoToken 统一 Key 接入的配置方式。适合已经在本地把 Jmanus 跑起来、想搞清楚「为什么大模型会先 navigate 再 input_text 再 click 再 get_text 最后 terminate」的开发者。读完你应该能自己判断某一步没触发是 Plan 组装的问题还是 think 阶段提示词的问题还是 act 阶段工具没挂上。2. 前置准备TaoToken 统一 Key 与 Jmanus 的接入位置Jmanus 默认走的是 DashScope 的DASHSCOPE_API_KEY这在本地试跑没问题但一旦你要在多个模型、多个 Agent 项目之间切换Key 管理就会变得很碎。我的做法是统一走 TaoToken 的 API 入口把模型调用收敛到一个 Key 上Jmanus 这边只需要改 base-url 和 api-key 两个字段。TaoToken 的 API 地址是https://taotoken.net/api注意这个地址不带任何查询参数直接作为 OpenAI 兼容协议的 base-url 使用。官网入口在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册和拿 Key 都在控制台里完成。如果你还没建 Key可以直接去 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite。这里要强调一点Jmanus 本身是 spring-ai-alibaba 生态里的项目它用的是 Spring AI 的 ChatClient 抽象所以只要你的模型服务兼容 OpenAI 的/v1/chat/completions就能接进来。TaoToken 的/api路径就是干这个的。你不需要改 Jmanus 的源码只需要在配置文件里把base-url指向 TaoToken把api-key换成 TaoToken 控制台里生成的那一串。如果你更习惯先验证模型通不通可以先用模型对话页面发一条消息https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite。确认返回正常之后再回到 Jmanus 里改配置这样排障的时候能少绕一圈。3. 可复制配置config.toml 与 settings.json 骨架Jmanus 的配置分两块一块是 Spring Boot 侧的application.yml或者application-h2.yml管数据库、端口、模型连接另一块是 Agent 侧的config.toml和settings.json管工具挂载、Agent 初始化、ReAct 循环的行为。下面这份骨架是我实测能跑通「查询股价」这条链路的版本你可以直接抄然后按自己的环境改路径和 Key。先看application-h2.yml里跟模型相关的部分。Jmanus 默认用 DashScope我们把它换成 TaoToken 的 OpenAI 兼容端点spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-sonnet-4-20250514 temperature: 0.2这里TAOTOKEN_API_KEY建议用环境变量注入不要硬编码在文件里。model字段填你在 TaoToken 控制台里能用的模型名不同模型在 ReAct 循环里的工具调用稳定性差异挺大建议先用一个工具调用能力强的模型跑通链路再换便宜的做批量。然后是 Agent 侧的config.toml。Jmanus 的ConfigurableDynaAgent会读这个文件来决定挂哪些工具、是否开启无限上下文、是否初始化工具[agent] name ConfigurableDynaAgent init true max_steps 20 infinite_context false [tools] browser true database true terminal true bash true text_file true form_input true [mcp] enabled falseinit true对应源码里那个判断如果为 true就把浏览器、数据库、终端、Bash、Text 文件操作器、表单输入工具都加到 Agent 上。max_steps是 ReAct 循环的上限超过就强制退出防止大模型在某个页面里反复 click 出不来。infinite_context默认关闭开了之后会额外挂定时工具和 Markdown 转换器但实测在长链路里容易把上下文撑爆建议先关着。settings.json这边主要管工具的具体参数比如浏览器用哪个 driver、超时多少、是否 headless{ browser: { headless: false, timeout: 30000, driver: chrome }, terminal: { timeout: 60000 }, text_file: { base_dir: ./workspace } }headless设成 false 是为了让你能肉眼看到浏览器在动学习阶段非常有用。等你确认链路没问题了再改成 true 跑后台。base_dir是 Text 文件操作器的根目录Jmanus 读写文件都会限制在这个目录下别指向系统盘根目录。4. 逐步验证从 executeByToolNameAsync 到 terminate配置改完之后重启OpenManusSpringBootApplication打开http://localhost:18080/点「查询股价」。这时候打开开发者工具你会看到前端调了/api/executor/executeByToolNameAsync请求体长这样{ toolName: default-plan-id-001000222, replacementParams: { userRequirement: 用浏览器基于百度查询今天阿里巴巴的股价并返回最新股价 }, isVueRequest: true }这个接口是异步的结果要通过/api/executor/details/{planId}去轮询。planId是后端生成的前缀是plan-。你可以在 Network 面板里找到这个 planId然后手动请求 details 接口看每一步的状态变化。后端拿到toolName之后会先把它当planTemplateId用因为请求里没传 planTemplateId然后去数据库里查这个模板的最大版本号取出 JSON 格式的 PlanTemplate。这个模板里有一个关键字段planType值是dynamic_agent对应生成DynamicToolPlanExecutor。如果planType是simple就生成PlanExecutor是advanced就生成MapReducePlanExecutordirectResponse为 true 则生成DirectResponseExecutor。这一步决定了后面走哪条执行器分支是排障时第一个要看的地方。接着进入AbstractPlanExecutor.executeAllStepsAsync它会先保存一条PlanExecutionRecordcompletedfalse再保存一条AgentExecutionRecord状态 IDLE。然后遍历 Plan 里的每一个 Step根据 Step 的属性组装BaseAgent这里就是ConfigurableDynaAgent。组装的时候会按config.toml里的init判断是否挂工具挂完之后把AgentExecutionRecord状态改成 RUNNING进入ReactAgent.step。step方法分 think 和 act 两段。think 阶段先收集上一步的工具执行结果第一步为空然后构造 SystemMessage里面包含系统信息、用户原始需求、当前 Step 需求、操作指令。注意指令里明确写了「调用工具时不需要额外解释」「不要在工具调用前给推理描述」「只做当前 Step 要求的事」「如果当前 Step 完成就调用 terminate 工具」。这几条直接决定了大模型会不会在第一次就返回browser_use的 navigate 动作。think 阶段调 ChatClient 的时候internalToolExecutionEnabled被设成 false意思是禁止 Spring AI 自动执行工具工具调用必须由应用层显式处理。所以你会看到 ChatResponse 里返回的是一个ToolCallname 是browser_usearguments 是{action: navigate, url: https://www.baidu.com}。应用层拿到这个 ToolCall 之后记录一条ThinkActRecord然后进入 act 阶段真正去执行BrowserUseTool。执行完之后BaseAgent会继续循环 step。第二次 think 时上一步的环境信息页面链接、元素索引等会被塞进消息里大模型这次返回的 ToolCall 变成{action: input_text, index: 13, text: 阿里巴巴 股价}。第三次是{action: click, index: 45}第四次是{action: get_text}第五次是terminatearguments 里带着最终结论。terminate一执行shouldTerminate置为 true状态变 completed退出循环AgentExecutionRecord更新为 FINISHED最后再根据所有 Step 的结果生成总结。整个链路里PlanTemplate → Plan → Step → think/act → toolCall 是五个层次。PlanTemplate 是带占位符的模板Plan 是替换占位符后的可执行计划Step 是计划里的一环think/act 是 Step 内的一次迭代toolCall 是一次具体的工具调用。think/act 和 toolCall 的次数不确定取决于大模型决策其他层次的次数取决于 PlanTemplate 配置。这就是 ReAct 模式在 Jmanus 里的落地方式Thought 对应 think 阶段的推理Act 对应 act 阶段的工具执行Obs 对应下一次 think 时收集到的环境信息。5. 本篇常见错排查第一个高频问题点了「查询股价」之后浏览器没反应。先看config.toml里browser true有没有写对再看settings.json里driver是不是你本机装了的浏览器。如果 driver 写的是 chrome 但本机只有 edgeBrowserUseTool 初始化就会失败但错误可能被吞在异步线程里前端只显示「执行中」。这时候去/api/executor/details/{planId}看 AgentExecutionRecord 的状态如果一直是 RUNNING 没变成 FINISHED基本就是工具初始化挂了。第二个问题大模型返回的 ToolCall 里 name 不是browser_use而是一段纯文本。这通常是模型不支持 function calling或者internalToolExecutionEnabled没设成 false 导致 Spring AI 尝试自动执行但失败了。检查application-h2.yml里的 model 字段换一个工具调用能力强的模型。另外确认 base-url 是https://taotoken.net/api不要多加/v1或者结尾斜杠路径拼接错了会返回 404但错误信息可能被包装成模型调用失败。第三个问题ReAct 循环停不下来一直 click 同一个 index。这是max_steps设太大加上提示词里「只做当前 Step 要求的事」没生效。把max_steps降到 10 左右同时在config.toml里确认infinite_context false。如果开了无限上下文历史消息会不断累积大模型容易迷失在当前页面里。第四个问题/api/executor/details/{planId}返回的 result 是 null。这通常是 terminate 工具没被调用或者调用之后总结生成失败。先看 AgentExecutionRecord 的 status 是不是 FINISHED如果是但 result 为 null检查总结阶段的模型调用有没有报错。总结阶段用的是同一个 ChatClient如果 Key 额度不够或者模型名写错这里会静默失败。第五个问题H2 数据库连不上http://localhost:18080/h2-console打不开。用户名和密码在src/main/resources/application-h2.yml里默认可能是sa和空密码。如果你改过application.yml里的数据源配置确认 H2 的 console 路径没被覆盖。这个跟执行逻辑无关但会挡住你看 PlanExecutionRecord 和 AgentExecutionRecord 的表数据。6. 把 Key 和链路分开管后续接入与编码场景的分流跑通这条链路之后你会发现 Jmanus 的配置其实分两层一层是模型接入一层是 Agent 行为。模型接入这层我建议统一走 TaoToken 的 API Key这样你在 Jmanus、Claude Code、其他 Agent 项目之间切换时只需要维护一个 Key。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有 OpenAI 兼容协议的完整说明。如果你要新建或轮换 Key直接去https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite。Agent 行为这层Jmanus 的config.toml和settings.json是核心。我实测下来把max_steps、infinite_context、工具开关这三个参数调明白基本就能控制住大部分 ReAct 循环的行为。如果你后面要接 Claude Code 做长期编码或者 Agent 编排可以考虑 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite。它跟 Jmanus 这种本地 Agent 的定位不太一样前者偏编码工作流后者偏浏览器自动化但底层都是模型调用Key 可以复用。最后留一个我踩过的坑Jmanus 的ConfigurableDynaAgent在挂工具的时候源码里注释掉了一批工具包括谷歌搜索和 Python 执行器。如果你在config.toml里写了google_search true但发现没生效不是配置写错了是源码里根本没注册。这种情况要么改源码要么换用已经注册的browser_use走浏览器搜索。学习阶段建议先用默认工具集跑通再按需扩展。