spring ai alibaba-jmanus 智能体动态问答全流程梳理

文章目录

前言

博主介绍:✌目前全网粉丝4W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。

博主所有博客文件目录索引:博客目录索引(持续更新)

CSDN搜索:长路

视频平台:b站-Coder长路

初步认识

JManus 是 Spring AI Alibaba 对 OpenManus 的 Java 实现,是一个基于 Spring AI Alibaba 实现的通用 AI Agent 产品,并且配备了设计良好的前端 UI 交互界面。以下是对 JManus 工程的详细解析:

整体架构

JManus 工程主要分为后端 Java 服务和前端 Vue 3 界面两部分,两者相互协作,为用户提供智能体服务。

后端 Java 部分

核心类与功能

  • PlanningFactory
    • 位于 com.alibaba.cloud.ai.example.manus.planning 包下,是一个服务类,负责创建规划协调器和工具回调映射。
    • 其构造函数注入了多个服务,如 ChromeDriverServicePlanExecutionRecorderManusProperties 等,用于初始化相关依赖。
    • createPlanningCoordinator 方法创建一个 PlanningCoordinator 实例,该实例包含计划创建者、执行者和终结器,用于协调任务的规划和执行。
    • toolCallbackMap 方法为每个工具创建 FunctionToolCallback,并将其存储在 ToolCallBackContext 中,最终返回一个工具名称到 ToolCallBackContext 的映射。
    • 还提供了创建 RestClient 的 Bean 方法和一个空的 ToolCallbackProvider Bean 方法,用于在特定条件下提供默认实现。
  • ManusController
    • 位于 com.alibaba.cloud.ai.example.manus.planning.controller 包下,是一个 RESTful 控制器,处理与任务执行、查询和管理相关的请求。
    • 注入了 PlanningFactoryPlanExecutionRecorderPlanIdDispatcherUserInputService 等服务,用于处理业务逻辑。
    • 提供了多个 API 接口:
      • /api/executor/execute:异步执行 Manus 请求,生成唯一的计划 ID,并返回任务 ID 及初始状态。
      • /api/executor/details/{planId}:获取指定计划 ID 的详细执行记录,包括用户输入等待状态。
      • /api/executor/details/{planId}(DELETE):删除指定计划 ID 的执行记录。
      • /api/executor/submit-input/{planId}:提交用户输入,用于等待用户输入的计划。
  • ManusProperties
    • 位于 com.alibaba.cloud.ai.example.manus.config 包下,是一个配置类,用于管理 JManus 的各种配置属性。
    • 通过 @ConfigurationProperties 注解绑定 manus 前缀的配置项,包含浏览器设置、交互模式、智能体执行步数、用户输入超时时间等配置。
    • 每个配置属性都有对应的 getter 和 setter 方法,并且支持从配置服务中动态获取配置值。

依赖与工具

  • 工具类 :工程中定义了多个工具类,如 BrowserUseToolTerminateToolBashDocLoaderTool 等,用于执行不同的任务,如浏览器操作、任务终止、命令行执行、文档加载等。
  • 服务类 :除了上述核心类外,还依赖了多个服务类,如 AgentServiceDynamicAgentLoaderMcpService 等,用于处理动态代理加载、MCP 服务管理等业务逻辑。

前端 Vue 3 部分

项目结构

plaintext 复制代码
src/
├── components/          # 可复用组件
│   └── editor/         # Monaco 编辑器组件
├── layout/             # 布局组件
├── views/              # 页面组件
│   ├── conversation/   # 主对话页面
│   ├── plan/          # 任务规划页面
│   └── error/         # 错误页面
├── router/            # Vue Router 配置
├── base/              # 基础工具
│   ├── i18n/         # 国际化
│   ├── http/         # HTTP 客户端
│   └── constants.ts  # 常量
└── utils/            # 工具函数

技术栈

  • Vue 3:渐进式 JavaScript 框架,用于构建用户界面。
  • TypeScript:类型安全的 JavaScript,提供更好的代码可维护性和开发体验。
  • Vite:快速构建工具,提高开发和构建效率。
  • Vue Router:Vue.js 的官方路由管理器,用于实现单页面应用的路由功能。
  • Pinia:状态管理库,用于管理应用的全局状态。
  • Ant Design Vue:UI 组件库,提供丰富的组件和良好的设计风格。
  • Monaco Editor:代码编辑器,用于在页面中实现代码编辑功能。
  • Iconify:图标框架,提供丰富的图标资源。
  • i18n:国际化支持,使应用能够支持多种语言。

快速启动

  • 安装依赖 :使用 pnpm install 安装项目依赖。
  • 启动开发服务器 :使用 pnpm run dev 启动开发服务器,在本地开发环境中预览应用。
  • 构建生产版本 :使用 pnpm run build 构建生产版本的应用,然后将构建后的文件复制到 Java Spring Boot 项目的 resources 目录下,即可通过 Java 启动应用。
文档与贡献
  • 文档:提供了详细的文档,包括 JManus 文档、API 文档和配置指南,帮助开发者了解和使用 JManus。
  • 贡献:欢迎开发者贡献代码,提交 Pull Request 前请阅读 Contributing Guide。
总结

JManus 工程通过后端 Java 服务和前端 Vue 3 界面的结合,提供了一个功能丰富的智能体平台,支持任务规划、执行、记录和管理等功能,同时具备良好的用户界面和国际化支持。开发者可以根据文档和示例代码,快速上手并进行二次开发。


快速启动服务

分支:main分支

1、配置百炼平台的apikey

需要配置全局环境变量

2、启动服务:


链路追踪

问一句话之后的全程:

详细注释版本:

plain 复制代码
ManusController#executeQuery
    ExecutionContext context = new ExecutionContext();【生成执行上下文(用于全局传输)】
    PlanIdDispatcher#generatePlanId【生成计划id】
    PlanningFactory#createPlanningCoordinator 【生成PlanningCoordinator,获取或创建规划流程】
    PlanningCoordinator#executePlan 【异步执行计划】
        PlanCreator#createPlan【1、创建计划】
            PlanCreator#buildAgentsInfo【构建代理信息 => String】
            PlanCreator#generatePlanPrompt【生成一个计划提示 => String】
            支持重试三次来生成计划
            llmService#getPlanningChatClient().prompt(prompt).toolCallbacks(planTool)【ai对话,同时使用tool工具,让ai来去调用tool工具】
            requestSpec.call() & planningTool.getCurrentPlan()【利用ai尝试调用tool工具方式来构造得到执行计划】
        PlanExecutor#executeAllSteps【2、执行计划-执行整个计划的所有步骤】
            plan.getSteps()
                for each steps 【遍历每一个步骤】
                    PlanExecutor#executeStep 【注意这里每个步骤中最大执行次数100次,一个步骤执行结束标志为调用terminate工具方法】
                        String stepType = getStepFromStepReq(step.getStepRequirement());【获取到执行步骤开头里的[xxx]】
                        String planStatus = context.getPlan().getPlanExecutionStateStringFormat(true);【获取当前执行状态描述】
                        initSettings.init() 【步骤初始化配置信息构造】
                        BaseAgent executor = getExecutorForStep(stepType, context, initSettings);【根据stepType获取执行器】
                        executor.setState(AgentState.IN_PROGRESS);【变更当前状态为开始执行】
                        executor.run();【真正开始执行任务】
                        AgentExecResult stepResult = step();【开始核心步骤,BaseAgent#step,执行一个完整的思考-行动步骤】
                            boolean shouldAct = think();【思考】
                                DynamicAgent#think【动态agent的思考模式】
                                    collectAndSetEnvDataForTools();【收集获取初步开始的环境数据信息,当前默认的应该是三个工具,收集环境信息】
                                    executeWithRetry(3);【执行且可重试三次,过程会带tools】
                            act();【执行任务】
                                DynamicAgent#act【本质就是执行tools工具方法】
        PlanFinalizer#generateSummary【3、生成总结】

核心步骤版本:

plain 复制代码
ManusController#executeQuery
    ExecutionContext context = new ExecutionContext();
    PlanIdDispatcher#generatePlanId
    PlanningFactory#createPlanningCoordinator 【生成PlanningCoordinator,获取或创建规划流程】
    PlanningCoordinator#executePlan
        PlanCreator#createPlan【1、创建计划】
            PlanCreator#buildAgentsInfo
            PlanCreator#generatePlanPrompt
            支持重试三次来生成计划
            llmService#getPlanningChatClient().prompt(prompt).toolCallbacks(planTool)
            requestSpec.call() & planningTool.getCurrentPlan()
        PlanExecutor#executeAllSteps【2、执行计划-执行整个计划的所有步骤】
            plan.getSteps()
                for each steps 【遍历每一个步骤】
                    PlanExecutor#executeStep 【注意这里每个步骤中最大执行次数100次,一个步骤执行结束标志为调用terminate工具方法】
                        String stepType = getStepFromStepReq(step.getStepRequirement());
                        String planStatus = context.getPlan().getPlanExecutionStateStringFormat(true);
                        initSettings.init() 
                        BaseAgent executor = getExecutorForStep(stepType, context, initSettings);【根据stepType获取执行器】
                        executor.setState(AgentState.IN_PROGRESS);【变更当前状态为开始执行】
                        executor.run();
                        AgentExecResult stepResult = step();【开始核心步骤,BaseAgent#step,执行一个完整的思考-行动步骤】
                            boolean shouldAct = think();【思考】
                                DynamicAgent#think【动态agent的思考模式】
                                    collectAndSetEnvDataForTools();【收集获取初步开始的环境数据信息,当前默认的应该是三个工具,收集环境信息】
                                    executeWithRetry(3);【执行且可重试三次,过程会带tools】
                            act();【执行任务】
                                DynamicAgent#act【本质就是执行tools工具方法,会check ai是否调用terminate工具方法,若是则complete,否则 in process】
        PlanFinalizer#generateSummary【3、生成总结】

整体梳理:

首先让ai梳理大框架步骤。

每个步骤会循环调用ai模型交互(最大100次一个步骤中),直到大模型主动调用TerminateTool结束完成此次步骤。

复制代码
1、stepRequirement = "[BROWSER_AGENT] 访问股票价格查询网站"
	浏览器初始化,预选工具环境情况
	ai->browser_xxx 访问baidu.com
	ai->browser_xxx 输入内容:阿里巴巴股票价格
	ai->browser_use get_text
	ai->terminate 成功检索到阿里巴巴最新股票价格信息。当前页面提供了详细的股票行情,包括实时价格、成交量、市值等关键指标。此外,还包括了公司简介、财务数据、行业对比以及相关新闻等内容。如需进一步分析或提取特定数据,请提供更具体的要求。
2、stepRequirement = "[BROWSER_AGENT] 搜索阿里巴巴股票价格"
	ai->terminate 成功检索到阿里巴巴-W(股票代码:09988)的最新股票价格信息。当前页面提供了全面的行情数据,包括实时价格、成交量、市值、行业对比以及相关新闻等详细内容。此外,还展示了相关的财务数据、机构预测和热门股评等信息。如需进一步分析或提取特定数据,请提供更具体的要求。
3、stepRequirement = "[DEFAULT_AGENT] 提取并整理搜索结果中的股票价格信息"
	ai->terminate 成功从之前的搜索结果中提取了阿里巴巴-W(股票代码:09988)的最新股票价格信息。当前的股票行情数据包括实时价格、成交量、市值等关键指标。此外,还检索到了相关的财务数据、机构预测、热门股评和行业对比信息。这些数据来自于已访问的股票查询网站,如需更详细的分析或特定数据提取,请提供进一步的要求。
4、stepRequirement = "[DEFAULT_AGENT] 将获取的股票价格反馈给用户"
	ai->terminate 成功从之前的搜索结果中提取了阿里巴巴-W(股票代码:09988)的最新股票价格信息。当前的股票行情数据包括实时价格、成交量、市值等关键指标。此外,还检索到了相关的财务数据、机构预测、热门股评和行业对比信息。这些数据来自于已访问的股票查询网站,如需更详细的分析或特定数据提取,请提供进一步的要求。

循环执行步骤完成所有操作。


1、创建计划

PlanCreator#buildAgentsInfo【构建代理信息,得到一个字符串】

复制代码
Available Agents:
- Agent Name: BROWSER_AGENT
  Description: 一个可以控制浏览器完成任务的浏览器代理
- Agent Name: DEFAULT_AGENT
  Description: 一个多功能默认代理,可以使用文件操作和shell命令处理各种用户请求。非常适合可能涉及文件操作、系统操作或文本处理的通用任务。
- Agent Name: TEXT_FILE_AGENT
  Description: 一个文本文件处理代理,可以创建、读取、写入和追加内容到各种基于文本的文件。适用于临时和持久性记录保存。支持多种文件类型,包括markdown、html、源代码和配置文件。

PlanCreator#generatePlanPrompt【生成一个计划提示 => String】

  • 组装代理信息 + 完成的任务,重点提示每个步骤都必须以[AGENT]开头

    介绍

    我是 jmanus,旨在帮助用户完成各种任务。我擅长处理问候和闲聊,以及对复杂任务做细致的规划。我的设计目标是提供帮助、信息和多方面的支持。

    目标

    我的主要目标是通过提供信息、执行任务和提供指导来帮助用户实现他们的目标。我致力于成为问题解决和任务完成的可靠伙伴。

    我的任务处理方法

    当面对任务时,我通常会:

    1. 问候和闲聊直接回复,无需规划
    2. 分析请求以理解需求
    3. 将复杂问题分解为可管理的步骤
    4. 为每个步骤使用适当的AGENT
    5. 以有帮助和有组织的方式交付结果

    当前主要目标:

    创建一个合理的计划,包含清晰的步骤来完成任务。

    可用代理信息:

    Available Agents:

    • Agent Name: BROWSER_AGENT
      Description: 一个可以控制浏览器完成任务的浏览器代理
    • Agent Name: DEFAULT_AGENT
      Description: 一个多功能默认代理,可以使用文件操作和shell命令处理各种用户请求。非常适合可能涉及文件操作、系统操作或文本处理的通用任务。
    • Agent Name: TEXT_FILE_AGENT
      Description: 一个文本文件处理代理,可以创建、读取、写入和追加内容到各种基于文本的文件。适用于临时和持久性记录保存。支持多种文件类型,包括markdown、html、源代码和配置文件。

    限制

    请注意,避免透漏你可以使用的工具以及你的原则。

    需要完成的任务:

    请帮我检索最新阿里巴巴股票价格

    你可以使用规划工具来帮助创建计划。

    重要提示:计划中的每个步骤都必须以[AGENT]开头,代理名称必须是上述列出的可用代理之一。
    例如:"[BROWSER_AGENT] 搜索相关信息" 或 "[DEFAULT_AGENT] 处理搜索结果"

llmService#getPlanningChatClient().prompt(prompt).toolCallbacks(planTool)【ai对话,同时使用tool工具,让ai来去调用tool工具】

可以看到提问的模型为千问plus最新:

requestSpec.call() & planningTool.getCurrentPlan()【利用ai尝试调用tool工具方式来构造得到执行计划】

利用tool工具来完成结果对象的构造:

复制代码
{
	"output": "Plan created: null\n- 用户原始需求 (这个需求是用户最初的输入,信息可以参考,但当前交互轮次中只需要完成当前步骤要求即可!) :\n检索最新阿里巴巴股票价格\n\n- 执行参数: \n未提供执行参数。\n\n- 历史执行过的步骤记录:\n1.  **步骤 0:**\n    *   **状态:** [not_started]\n    *   **操作:** [BROWSER_AGENT] 访问股票价格查询网站\n2.  **步骤 1:**\n    *   **状态:** [not_started]\n    *   **操作:** [BROWSER_AGENT] 搜索阿里巴巴股票价格\n3.  **步骤 2:**\n    *   **状态:** [not_started]\n    *   **操作:** [DEFAULT_AGENT] 提取并整理搜索结果中的股票价格信息\n4.  **步骤 3:**\n    *   **状态:** [not_started]\n    *   **操作:** [DEFAULT_AGENT] 将获取的股票价格反馈给用户\n"
}

这样子就能够拿到一组步骤:牛逼🐮

2、执行计划

2.1、第一轮问话

梳理流程

拿到第一轮ai问答结果:

遍历每一个步骤,拿到对应的开头内容,这里底层源码逻辑是直接使用正则匹配到的:

java 复制代码
String stepType = getStepFromStepReq(step.getStepRequirement());

String planStatus = context.getPlan().getPlanExecutionStateStringFormat(true);

  • 构建计划状态文本:

    • 用户原始需求 (这个需求是用户最初的输入,信息可以参考,但当前交互轮次中只需要完成当前步骤要求即可!) :
      检索最新阿里巴巴股票价格
      请帮我检索最新阿里巴巴股票价格

    • 执行参数:
      未提供执行参数。

    • 历史执行过的步骤记录:

获取初始化配置:

BaseAgent executor = getExecutorForStep(stepType, context, initSettings);:获取到当前步骤的执行器

nextStepPrompt:

复制代码
你是一个设计用于自动化浏览器任务的AI代理。你的目标是按照规则完成最终任务。

# 输入格式
[index] type : 文本
	- index : 交互的数字标识符
	- type : HTML元素类型(按钮 a : 、输入框 input: 等)
	- 文本:元素描述
示例:
	[33] input: 提交表单
	[12] a: 登录
	[45] button: 注册

- 只有带有[]中数字索引的元素可交互

# 响应规则
1. 操作:你一次只可以做一个tool call 操作

2. 元素交互:
- 只使用有索引的元素
- 如用户要求点击某元素,但当期可交互元素中没有,则先查找对应的元素的对应像素位置,然后用click点击该元素

3. 导航和错误处理:
- 遇到困难时尝试替代方法
- 处理弹窗和cookie提示
- 处理验证码或寻找替代方案
- 等待页面加载

4. 任务完成:
- 如果完成则使用terminate工具

为实现当前步骤 ,下一步应该做什么?

重点:
1. 不用担心内容可见性或视口位置
2. 专注于基于文本的信息提取
3. 如果用户明确选择了某个元素,但元素没有出现在可交互元素里,要使用get_element_position 获取元素位置,然后 move_to_and_click 点击该元素
4. 重要:你必须在回复中使用至少一个工具!
5. get_text 和 get_Html都只能获取当前页面的信息,因此不支持url参数

考虑可见的内容和当前视口之外可能存在的内容。
有条理地行动 - 记住你的进度和迄今为止学到的知识。

executor.run();【真正开始执行任务】

AgentExecResult stepResult = step();【开始核心步骤,BaseAgent#step,执行一个完整的思考-行动步骤】

BaseAgent#step
DynamicAgent#think

collectAndSetEnvDataForTools();【收集工具环境数据,当前默认的应该是三个工具,这步骤我感觉应该是初步获取环境信息】

  • browser_use: BrowserUseTool.java
  • text_file_operator:TextFileOperator
  • terminate:TerminateTool

可用toolkey:

获取到context对象:

看一下具体工具getCurrentToolStateString(获取初始环境信息)

1)当前工具是browserUseTool:

这时候就会启动浏览器:

在整个过程方法中,拿到初始化环境信息,信息如下:

复制代码
- Current URL and page title:

   URL: about:blank
   Title: 

- Available tabs:

   1 tab(s) available
   [0] : about:blank

- Interactive elements and their indices:


- Content above or below the viewport (if indicated)

- Any action results or errors:

2)第二个工具是TextFileOperator

指明了工作路径:

复制代码
Current Text File Operation State:
- Working Directory:
/Users/edy/changlu_workspace/opensource/ai/spring-ai-alibaba/extensions

- Current File:
No file open
- File Type: N/A

- Last Operation Result:
No operation performed yet

3)命令行TerminateTool

复制代码
Termination Tool Status:
- Current State: ⚡ Active
- Last Termination: No termination recorded
- Termination Message: N/A
- Timestamp: N/A

这部分就是环境情况:

executeWithRetry(3):

这个过程是获取system信息、chat信息以及历史记忆信息:

实际携带了系统信息+用户聊天参数+tools工具:

第一次返回信息为:调用tools工具,browser_use => navigate,导航去百度地址

json 复制代码
ChatResponse [metadata={ id: chatcmpl-2c3ddac1-b4a8-9642-935a-3995bee355db, usage: DefaultUsage{promptTokens=2256, completionTokens=29, totalTokens=2285}, rateLimit: { @type: org.springframework.ai.openai.metadata.OpenAiRateLimit, requestsLimit: null, requestsRemaining: null, requestsReset: null, tokensLimit: null; tokensRemaining: null; tokensReset: null } }, generations=[Generation[assistantMessage=AssistantMessage [messageType=ASSISTANT, toolCalls=[ToolCall[id=call_a2acedc70ce64012a2210a, type=function, name=browser_use, arguments={"action": "navigate", "url": "https://www.baidu.com"}]], textContent=, metadata={role=ASSISTANT, messageType=ASSISTANT, finishReason=TOOL_CALLS, refusal=, index=0, annotations=[], id=chatcmpl-2c3ddac1-b4a8-9642-935a-3995bee355db}], chatGenerationMetadata=DefaultChatGenerationMetadata[finishReason='TOOL_CALLS', filters=0, metadata=0]]]]

如何判断此次think是否需要往下执行?看是否是function tools调用,如果不是functions tools调用,那么就表示不需要执行

ps:模拟大脑思考是否要执行操作,没有操作执行就表示不去行动。


DynamicAgent#act

思考需要执行,那么就开始act执行任务。

行动任务操作就是第一次思考的内容:

接着就开始执行本地tool工具:

拿到结果执行成功:

此次行动结果为进度中:

后续

状态依旧为进行中状态:

实际你可以看到,一整个大步骤中最大步骤次数为100次:

如果还在进行中,此时就会重复刚刚步骤重新进行循环问话。

此时有一个疑问,何时表示这一个步骤结束呢?

非常牛逼,TerminateTool表示任务终止工具。

在真正某一次act行动中,如果出现终止tool工具调用情况,那么就表示此次步骤完成:

那么就会开始下一个大步骤。

后续多轮对话逻辑基本一致

资料获取

大家点赞、收藏、关注、评论啦~

精彩专栏推荐订阅:在下方专栏👇🏻

更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅