Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
<module>spring-ai-alibaba-bailian-example</module>
<module>spring-ai-alibaba-evaluation-example</module>
<module>spring-ai-alibaba-mem0-example</module>
</modules>
</modules>

<dependencyManagement>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Spring AI Alibaba SubAgent Personal Assistant Example

本示例展示如何使用 Spring AI Alibaba 的 ReactAgent 框架构建一个多智能体监督者模式系统,通过主智能体协调多个子智能体,实现日历代理与邮件发送功能。
## 功能特性

- **多智能体**: Subagent 使用 Tool Calling 模式被 Supervisor Agent 调用。
- **人工介入**: 通过在 Supervisor Agent 配置 hooks 加入中断功能。

## 快速开始

### 前置条件

- Java 21+
- Maven 3.6+
- DashScope API Key

### 运行步骤

1. **设置 API Key**

```bash
export DASHSCOPE_API_KEY=your-api-key
```

2. **构建项目**

```bash
cd subagent-personal-assistant-example
mvn clean package -DskipTests
```

3. **运行应用**

```bash
mvn spring-boot:run
```

4. **访问API**

``` shell
curl --location 'http://127.0.0.1:8080/react/agent/supervisorAgent?query=Schedule%20a%20meeting%20with%20the%20design%20team%20next%20Tuesday%20at%202pm%20for%201%20hour%2C%20and%20send%20them%20an%20email%20reminder%20about%20reviewing%20the%20new%20mockups.&threadId=user-session-124&nodeId=_AGENT_HOOK_HITL' \
--header 'Content-Type: application/json' \
--header 'Accept: text/event-stream' \
--data ''
````
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The curl example code block is closed with four backticks instead of three, which breaks Markdown rendering for the rest of the README. Please close it with the matching triple-backtick fence.

Suggested change
````

Copilot uses AI. Check for mistakes.
## Agent说明
* supervisor_agent: 监督者智能体,负责协调多个子智能体,实现日历代理与邮件发送功能。
* calendar_agent: 日历日程助理,检查可用时间和安排日历事件。
* email_agent: 创建邮件主题和邮件发送。
## 工具说明

| 工具名称 | 功能 | 输入参数 |
|---------|-------------------|-----------------------|
| `get_user_email_tool` | 获取用户邮箱 | UserInfo |
| `get_current_date_time` | 获取当前日期和时间 | 无 |
| `get_available_time_slots` | 获取可用时间 slots |AvailableTimeInfo |
| `create_calendar_event` | 创建日历事件 | CalendarInfo |
| `send_email` | 发送邮件 | EmailInfo |

## 示例对话

```
用户: Schedule a meeting with the design team next Tuesday at 2pm for 1 hour, and send them an email reminder about reviewing the new mockups.

Agent:
I'll help you schedule a meeting with the design team and send them an email reminder. Let me break this down into steps:

1. First, I need to get the email addresses of the design team members
2. Then schedule the calendar event for next Tuesday at 2pm for 1 hour
3. Finally, send an email reminder about reviewing the new mockups

Let me start by getting the design team members' information:

================================= Tool Message =================================

id: call_e06f221fa93b4e8ea77e62d0
name: get_user_email_tool
responseData: "Available user list for [{\"departmentName\":\"design(设计团队)\",\"email\":\"wangwu@agent.cn\",\"userName\":\"wangwu(王五)\"}]"
Now I have the design team member information. I'll schedule the meeting for next Tuesday at 2pm for 1 hour with Wang Wu (王五) from the design team:

检测到中断,需要人工审批
工具: calendar_agent
参数: {"input": "Schedule a meeting with wangwu@agent.cn next Tuesday at 2pm for 1 hour. Subject: Design Team Meeting - Mockup Review"}
描述: The AI is requesting to use the tool: calendar_agent.
Description: Calendar event pending approval
With the following arguments: {"input": "Schedule a meeting with wangwu@agent.cn next Tuesday at 2pm for 1 hour. Subject: Design Team Meeting - Mockup Review"}
Do you approve?
检测到中断,等待人工介入... node: _AGENT_HOOK_HITL
人工介入开始...
检测到中断,需要人工审批
工具: email_agent
参数: {"input": "Send an email to wangwu@agent.cn with subject 'Reminder: Design Team Meeting - Mockup Review' and content 'Hi Wang Wu, This is a reminder about our upcoming meeting next Tuesday (February 17, 2026) at 2:00 PM to review the new mockups. Please come prepared with your feedback. Looking forward to our discussion!'"}
描述: The AI is requesting to use the tool: email_agent.
Description: Outbound email pending approval
With the following arguments: {"input": "Send an email to wangwu@agent.cn with subject 'Reminder: Design Team Meeting - Mockup Review' and content 'Hi Wang Wu, This is a reminder about our upcoming meeting next Tuesday (February 17, 2026) at 2:00 PM to review the new mockups. Please come prepared with your feedback. Looking forward to our discussion!'"}
Do you approve?
检测到中断,等待人工介入... node: _AGENT_HOOK_HITL
人工介入开始...
Email sent to wangwu@agent.cn - Subject: Reminder: Design Team Meeting - Mockup Review
body: Hi Wang Wu, This is a reminder about our upcoming meeting next Tuesday (February 17, 2026) at 2:00 PM to review the new mockups. Please come prepared with your feedback. Looking forward to our discussion!
```
## 注意事项
1. 当检测到人工接入时,需要带着nodeId重新发起请求。
2. 当前模型使用的qwen3-max-2026-01-23,模型不同可能导致示例结果有所偏差。

## 相关链接

- [Spring AI Alibaba 文档](https://java2ai.com)
- [Spring AI Alibaba GitHub](https://github.com/alibaba/spring-ai-alibaba)
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-examples</artifactId>
<version>1.0.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>subagent-personal-assistant-example</artifactId>
<name>Spring AI Alibaba SubAgent Personal Assistant Example</name>
<description>Multi Agent Personal Assistant</description>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
Comment on lines +18 to +19
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module sets maven.compiler.source/target to 21 while the parent pom configures <java.version>17 and uses ${java.version} in maven-compiler-plugin. This mismatch can confuse builds and documentation; align the module’s compiler settings with the parent (or override java.version/release consistently if 21 is required).

Suggested change
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>

Copilot uses AI. Check for mistakes.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2026-2027 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cloud.alibaba.ai.example.agent;

import com.alibaba.cloud.ai.graph.action.InterruptionMetadata;

public class HITLHelper {
/**
* 批准所有工具调用
*/
public static InterruptionMetadata approveAll(InterruptionMetadata interruptionMetadata) {
InterruptionMetadata.Builder builder = InterruptionMetadata.builder()
.nodeId(interruptionMetadata.node())
.state(interruptionMetadata.state());

interruptionMetadata.toolFeedbacks().forEach(toolFeedback -> {
builder.addToolFeedback(
InterruptionMetadata.ToolFeedback.builder(toolFeedback)
.result(InterruptionMetadata.ToolFeedback.FeedbackResult.APPROVED)
.description("Agree to tool execution.")
.build()
);
});

return builder.build();
}

/**
* 拒绝所有工具调用
*/
public static InterruptionMetadata rejectAll(
InterruptionMetadata interruptionMetadata,
String reason) {
InterruptionMetadata.Builder builder = InterruptionMetadata.builder()
.nodeId(interruptionMetadata.node())
.state(interruptionMetadata.state());

interruptionMetadata.toolFeedbacks().forEach(toolFeedback -> {
builder.addToolFeedback(
InterruptionMetadata.ToolFeedback.builder(toolFeedback)
.result(InterruptionMetadata.ToolFeedback.FeedbackResult.REJECTED)
.description(reason)
.build()
);
});

return builder.build();
}

/**
* 编辑特定工具的参数
*/
public static InterruptionMetadata editTool(
InterruptionMetadata interruptionMetadata,
String toolName,
String newArguments) {
InterruptionMetadata.Builder builder = InterruptionMetadata.builder()
.nodeId(interruptionMetadata.node())
.state(interruptionMetadata.state());

interruptionMetadata.toolFeedbacks().forEach(toolFeedback -> {
if (toolFeedback.getName().equals(toolName)) {
builder.addToolFeedback(
InterruptionMetadata.ToolFeedback.builder(toolFeedback)
.arguments(newArguments)
.result(InterruptionMetadata.ToolFeedback.FeedbackResult.EDITED)
.build()
);
} else {
builder.addToolFeedback(
InterruptionMetadata.ToolFeedback.builder(toolFeedback)
.result(InterruptionMetadata.ToolFeedback.FeedbackResult.APPROVED)
.build()
);
}
});

return builder.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2026-2027 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cloud.alibaba.ai.example.agent;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* @author wangjx
* @since 2026-02-13
*/
@SpringBootApplication
public class SubAgentPersonalAssistantApplication {
public static void main(String[] args) {
SpringApplication.run(SubAgentPersonalAssistantApplication.class, args);
}
Comment on lines +18 to +29
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other agent example modules include at least a basic @SpringBootTest contextLoads test, but this new module has no src/test at all (despite depending on spring-boot-starter-test). Adding a minimal context-load test would help catch wiring/config regressions (e.g., bean creation for supervisorAgent).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

当前项目为使用示例,README.md中已提供使用方法,无需在提供 test用例

}
Loading