第26章 奶茶店多智能体系统
本章深入分析AgentScope-Java中的奶茶店(Boba Tea Shop)多智能体系统案例。这是一个企业级分布式系统,展示了如何使用A2A协议、MCP工具服务、Nacos服务发现等技术构建生产级的多Agent协作系统。
26.1 项目概述
26.1.1 系统特性
| 特性 | 描述 |
|---|---|
| 分布式架构 | 多个独立微服务组成的Agent系统 |
| A2A协议 | Agent到Agent的标准通信协议 |
| MCP集成 | 通过MCP协议调用外部工具服务 |
| Nacos服务发现 | 使用Nacos进行服务注册与发现 |
| 长期记忆 | 集成Mem0实现用户偏好记忆 |
| RAG支持 | 知识库检索增强咨询能力 |
| 业务工具 | 订单管理、库存查询、用户反馈 |
26.1.2 业务场景
┌──────────────────────────────────────────────────────────────────┐
│ 云原生奶茶店业务场景 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 客户端 │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐│
│ │ Supervisor Agent ││
│ │ (主管Agent) ││
│ │ 职责: ││
│ │ - 接收用户请求 ││
│ │ - 路由到专业Agent ││
│ │ - 生成日报/周报 ││
│ │ - 发送钉钉通知 ││
│ └──────────────────────────────┬───────────────────────────────┘│
│ │ │
│ ┌───────────────────┴───────────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐│
│ │ Consult Agent │ │ Business Agent ││
│ │ (咨询Agent) │ │ (业务Agent) ││
│ │ 职责: │ │ 职责: ││
│ │ - 产品咨询 │ │ - 订单创建 ││
│ │ - 菜单推荐 │ │ - 订单查询 ││
│ │ - RAG知识检索 │ │ - 库存检查 ││
│ │ - 用户偏好记忆 │ │ - 用户反馈 ││
│ └─────────────────────────┘ └──────────┬──────────────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────┐│
│ │ MCP Tool Server ││
│ │ (工具服务) ││
│ │ - 订单API ││
│ │ - 库存API ││
│ │ - 反馈API ││
│ └─────────────────────────┘│
└──────────────────────────────────────────────────────────────────┘
26.1.3 项目结构
boba-tea-shop/
├── supervisor-agent/ # 主管Agent服务
│ ├── src/main/java/.../
│ │ ├── tools/
│ │ │ └── ScheduleAgentTools.java # 报表工具
│ │ ├── config/
│ │ │ └── AgentScopeModelConfig.java
│ │ ├── entity/
│ │ │ ├── Order.java
│ │ │ ├── Product.java
│ │ │ └── Feedback.java
│ │ └── mapper/
│ │ ├── OrderMapper.java
│ │ └── FeedbackMapper.java
│ └── pom.xml
│
├── consult-sub-agent/ # 咨询Agent服务
│ ├── src/main/java/.../
│ │ ├── config/
│ │ │ └── AgentScopeRunner.java
│ │ ├── tools/
│ │ │ └── ConsultTools.java
│ │ └── utils/
│ │ └── MonitoringHook.java
│ └── pom.xml
│
├── business-sub-agent/ # 业务Agent服务
│ ├── src/main/java/.../
│ │ ├── config/
│ │ │ └── AgentScopeRunner.java
│ │ └── utils/
│ │ └── MonitoringHook.java
│ └── pom.xml
│
├── business-mcp-server/ # MCP工具服务
│ ├── src/main/java/.../
│ │ └── config/
│ │ └── McpToolDefinitions.java
│ └── pom.xml
│
└── docker-compose.yml # 容器编排
26.2 系统架构
26.2.1 服务架构图
┌──────────────────────────────────────────────────────────────────────┐
│ Boba Tea Shop Architecture │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 用户入口层 │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Web Client │ │ Mobile App │ │ 钉钉Bot │ │ │
│ │ └──────┬─────┘ └──────┬─────┘ └──────┬─────┘ │ │
│ └─────────┼───────────────┼───────────────┼───────────────────────┘ │
│ │ │ │ │
│ └───────────────┼───────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Supervisor Agent │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ ReActAgent + A2A Client │ │ │
│ │ │ - ScheduleAgentTools (报表生成) │ │ │
│ │ │ - DingTalkNotification (通知) │ │ │
│ │ │ - A2A调用子Agent │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌───────────────┴───────────────┐ │ │
│ │ │ A2A Protocol │ │ │
│ └────────────┼───────────────────────────────┼────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ Consult Sub-Agent │ │ Business Sub-Agent │ │
│ │ (咨询服务) │ │ (业务服务) │ │
│ │ ┌───────────────────┐ │ │ ┌───────────────────┐ │ │
│ │ │ AgentRunner │ │ │ │ AgentRunner │ │ │
│ │ │ - NacosToolkit │ │ │ │ - NacosToolkit │ │ │
│ │ │ - RAG Knowledge │ │ │ │ - MCP Client │ │ │
│ │ │ - Mem0 Memory │ │ │ │ - Mem0 Memory │ │ │
│ │ └───────────────────┘ │ │ └─────────┬─────────┘ │ │
│ └─────────────────────────┘ └────────────┼────────────┘ │
│ │ │
│ │ MCP Protocol │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ Business MCP Server │ │
│ │ (工具服务) │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ McpToolDefinitions │ │ │
│ │ │ - order-create-order │ │ │
│ │ │ - order-get-order │ │ │
│ │ │ - order-check-stock │ │ │
│ │ │ - feedback-create │ │ │
│ │ └────────────────────────┘ │ │
│ └─────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ 数据层 ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ MySQL │ │ Nacos │ │ Mem0 Cloud │ ││
│ │ │ (业务数据) │ │ (服务发现) │ │ (长期记忆) │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────┘ ││
│ └─────────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────┘
26.2.2 技术栈
| 层级 | 技术 | 用途 |
|---|---|---|
| Agent框架 | AgentScope-Java | 核心Agent实现 |
| 通信协议 | A2A (Agent-to-Agent) | Agent间通信 |
| 工具协议 | MCP (Model Context Protocol) | 工具调用 |
| 服务发现 | Nacos | 服务注册与发现 |
| 长期记忆 | Mem0 | 用户偏好持久化 |
| 知识库 | RAG + 向量存储 | 产品知识检索 |
| 数据库 | MySQL | 订单、产品数据 |
| 容器化 | Docker Compose | 服务编排 |
26.3 咨询Agent实现
26.3.1 AgentRunner配置
@Configuration
public class AgentScopeRunner {
@Value("${agentscope.dashscope.api-key}")
String apiKey;
@Value("${agentscope.dashscope.model-name}")
String modelName;
@Bean
public AgentRunner agentRunner(
AgentPromptConfig promptConfig,
ConsultTools consultTools,
Knowledge knowledge,
Model model) {
// 使用Nacos工具包(支持服务发现)
Toolkit toolkit = new NacosToolkit();
toolkit.registerTool(consultTools);
// 自动上下文压缩配置
AutoContextConfig autoContextConfig = AutoContextConfig.builder()
.tokenRatio(0.4) // 40%用于上下文
.lastKeep(10) // 保留最近10条
.build();
// 支持自动压缩的记忆
AutoContextMemory memory = new AutoContextMemory(autoContextConfig, model);
// 构建Agent
ReActAgent.Builder builder = ReActAgent.builder()
.name("consult_agent")
.sysPrompt(promptConfig.getConsultAgentInstruction())
.memory(memory)
.hooks(List.of(new MonitoringHook()))
.model(model)
.toolkit(toolkit)
.knowledge(knowledge) // RAG知识库
.ragMode(RAGMode.AGENTIC); // Agent控制的RAG
return new CustomAgentRunner(builder);
}
}
26.3.2 自定义AgentRunner
/**
* 自定义AgentRunner
* 支持用户级长期记忆和任务管理
*/
private static class CustomAgentRunner implements AgentRunner {
private static final Pattern USER_ID_PATTERN =
Pattern.compile("<userId>(.+?)</userId>");
private final ReActAgent.Builder agentBuilder;
private final Map<String, ReActAgent> agentCache;
private CustomAgentRunner(ReActAgent.Builder agentBuilder) {
this.agentBuilder = agentBuilder;
this.agentCache = new ConcurrentHashMap<>();
}
/**
* 构建带长期记忆的Agent
* 每个用户有独立的记忆空间
*/
private ReActAgent buildReActAgent(String userId) {
Mem0LongTermMemory longTermMemory = Mem0LongTermMemory.builder()
.agentName("ConsultAgent")
.userId(userId)
.apiBaseUrl("https://api.mem0.ai")
.apiKey(System.getenv("MEM0_API_KEY"))
.build();
return agentBuilder
.longTermMemory(longTermMemory)
.build();
}
@Override
public String getAgentName() {
return "consult_agent";
}
@Override
public String getAgentDescription() {
return "Boba tea shop consultation agent";
}
/**
* 处理A2A请求
* 为每个任务创建独立的Agent实例
*/
@Override
public Flux<Event> stream(List<Msg> requestMessages, AgentRequestOptions options) {
if (agentCache.containsKey(options.getTaskId())) {
throw new IllegalStateException(
"Agent already exists for taskId: " + options.getTaskId());
}
// 从消息中解析用户ID
String userId = parseUserIdFromMessages(requestMessages);
// 创建用户专属Agent
ReActAgent agent = buildReActAgent(userId);
agentCache.put(options.getTaskId(), agent);
// 将用户ID添加到记忆
agent.getMemory().addMessage(
Msg.builder()
.role(MsgRole.USER)
.content(TextBlock.builder()
.text("<userId>" + userId + "</userId>")
.build())
.build());
// 执行并在完成后清理
return agent.stream(requestMessages)
.doFinally(signal -> agentCache.remove(options.getTaskId()));
}
/**
* 从消息中解析用户ID
*/
private String parseUserIdFromMessages(List<Msg> requestMessages) {
for (Msg msg : requestMessages) {
if (msg.getContent() == null) continue;
for (var block : msg.getContent()) {
if (block instanceof TextBlock textBlock) {
String text = textBlock.getText();
if (text != null) {
Matcher matcher = USER_ID_PATTERN.matcher(text);
if (matcher.find()) {
return matcher.group(1).trim();
}
}
}
}
}
return "default_userId";
}
/**
* 停止任务
*/
@Override
public void stop(String taskId) {
ReActAgent agent = agentCache.remove(taskId);
if (agent != null) {
agent.interrupt();
}
}
}
26.4 业务Agent与MCP集成
26.4.1 MCP客户端初始化
@Configuration
public class AgentScopeRunner {
@Bean
public AgentRunner agentRunner(
AgentPromptConfig promptConfig,
AiService aiService,
Model model) {
Toolkit toolkit = new NacosToolkit();
AutoContextConfig autoContextConfig = AutoContextConfig.builder()
.tokenRatio(0.4)
.lastKeep(10)
.build();
AutoContextMemory memory = new AutoContextMemory(autoContextConfig, model);
ReActAgent.Builder builder = ReActAgent.builder()
.name("business_agent")
.sysPrompt(promptConfig.getBusinessAgentInstruction())
.memory(memory)
.hooks(List.of(new MonitoringHook()))
.model(model)
.toolkit(toolkit);
return new CustomAgentRunner(builder, aiService, toolkit);
}
}
26.4.2 MCP工具注册
private static class CustomAgentRunner implements AgentRunner {
private final ReActAgent.Builder agentBuilder;
private final AiService aiService;
private final Toolkit toolkit;
private final Map<String, ReActAgent> agentCache;
private volatile boolean mcpInitialized = false;
/**
* 延迟初始化MCP客户端
* 确保MCP服务可用后才注册工具
*/
private void initializeMcpOnce() {
if (!mcpInitialized) {
synchronized (this) {
if (!mcpInitialized) {
try {
// 创建Nacos MCP服务管理器
NacosMcpServerManager mcpServerManager =
new NacosMcpServerManager(aiService);
// 通过Nacos发现并连接MCP服务
NacosMcpClientWrapper mcpClientWrapper =
NacosMcpClientBuilder.create(
"business-mcp-server", // 服务名
mcpServerManager
).build();
// 注册MCP客户端到工具包
toolkit.registerMcpClient(mcpClientWrapper).block();
mcpInitialized = true;
} catch (Exception e) {
logger.warn("Failed to initialize MCP client: "
+ e.getMessage() + ", will try later.");
}
}
}
}
}
private ReActAgent buildReActAgent(String userId) {
// 初始化MCP
initializeMcpOnce();
// 创建长期记忆
Mem0LongTermMemory longTermMemory = Mem0LongTermMemory.builder()
.agentName("BusinessAgent")
.userId(userId)
.apiBaseUrl("https://api.mem0.ai")
.apiKey(System.getenv("MEM0_API_KEY"))
.build();
return agentBuilder
.longTermMemory(longTermMemory)
.build();
}
}
26.5 MCP工具服务
26.5.1 工具定义
/**
* MCP工具定义
* 定义所有可通过MCP协议调用的工具
*/
public class McpToolDefinitions {
/**
* 工具定义记录
*/
public record ToolDefinition(
String name,
String description,
Map<String, Object> schema) {
/**
* 转换为MCP JSON Schema格式
*/
public JsonSchema jsonSchema() {
String type = (String) schema.getOrDefault("type", "object");
Map<String, Object> properties = (Map<String, Object>) schema.get("properties");
List<String> required = (List<String>) schema.get("required");
return new JsonSchema(type, properties, required, null, null, null);
}
}
// ===================== 订单工具 =====================
/**
* 创建订单工具
*/
public static final ToolDefinition ORDER_CREATE_ORDER_WITH_USER =
new ToolDefinition(
"order-create-order-with-user",
"Create a new boba tea order for user. Supports all products "
+ "from Cloud Edge Boba Tea Shop.",
createOrderWithUserSchema());
/**
* 查询订单工具
*/
public static final ToolDefinition ORDER_GET_ORDER =
new ToolDefinition(
"order-get-order",
"Query order details by order ID, including product name, "
+ "sweetness, ice level, quantity, price and creation time.",
createGetOrderSchema());
/**
* 按用户查询订单
*/
public static final ToolDefinition ORDER_GET_ORDER_BY_USER =
new ToolDefinition(
"order-get-order-by-user",
"Query order details by user ID and order ID.",
createGetOrderByUserSchema());
/**
* 库存检查工具
*/
public static final ToolDefinition ORDER_CHECK_STOCK =
new ToolDefinition(
"order-check-stock",
"Check if the specified product has sufficient stock.",
createCheckStockSchema());
// ===================== 反馈工具 =====================
/**
* 创建反馈工具
*/
public static final ToolDefinition FEEDBACK_CREATE_FEEDBACK =
new ToolDefinition(
"feedback-create-feedback",
"Create user feedback record, userId is required",
createFeedbackSchema());
/**
* 按用户查询反馈
*/
public static final ToolDefinition FEEDBACK_GET_FEEDBACK_BY_USER =
new ToolDefinition(
"feedback-get-feedback-by-user",
"Query feedback records by user ID",
createUserIdSchema());
// Schema创建辅助方法
private static Map<String, Object> createOrderWithUserSchema() {
Map<String, Object> schema = new HashMap<>();
schema.put("type", "object");
Map<String, Object> properties = new HashMap<>();
properties.put("userId", Map.of(
"type", "string",
"description", "User ID"));
properties.put("productName", Map.of(
"type", "string",
"description", "Product name"));
properties.put("quantity", Map.of(
"type", "integer",
"description", "Order quantity"));
properties.put("sweetness", Map.of(
"type", "string",
"description", "Sweetness level: full/half/light/none"));
properties.put("iceLevel", Map.of(
"type", "string",
"description", "Ice level: full/half/light/none"));
schema.put("properties", properties);
schema.put("required", List.of("userId", "productName", "quantity"));
return schema;
}
}
26.6 主管Agent与报表生成
26.6.1 报表工具
@Component
public class ScheduleAgentTools {
@Value("${agent.dingtalk.access-token}")
private String accessToken;
@Autowired
private FeedbackMapper feedbackMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductMapper productMapper;
/**
* 获取业务日报数据
*/
@Tool(description = "Get business report data information")
public Map<String, Object> getDailyReportInfo() {
// 获取时间范围
String maxMonth = orderMapper.selectMaxCreatedMonth();
Date startTime, endTime;
if (maxMonth != null && !maxMonth.isEmpty()) {
YearMonth yearMonth = YearMonth.parse(maxMonth);
LocalDate firstDayOfMonth = yearMonth.atDay(1);
startTime = Date.from(firstDayOfMonth
.atStartOfDay(ZoneId.systemDefault())
.toInstant());
} else {
startTime = new Date(System.currentTimeMillis() - 365L * 24 * 60 * 60 * 1000);
}
endTime = new Date();
Map<String, Object> templateData = new HashMap<>();
templateData.put("store_name", "Cloud Native Store #1");
// ========== 订单销售数据 ==========
List<Order> todayOrders = orderMapper.findOrdersByTimeRange(startTime, endTime);
int todayOrderCount = todayOrders.size();
BigDecimal totalRevenue = todayOrders.stream()
.map(Order::getTotalPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 昨日数据对比
Date yesterdayStartTime = new Date(startTime.getTime() - 365L * 24 * 60 * 60 * 1000);
List<Order> yesterdayOrders = orderMapper.findOrdersByTimeRange(
yesterdayStartTime, startTime);
int yesterdayOrderCount = yesterdayOrders.size();
BigDecimal yesterdayTotalRevenue = yesterdayOrders.stream()
.map(Order::getTotalPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
templateData.put("total_sales", todayOrderCount);
templateData.put("yesterday_total_sales", yesterdayOrderCount);
templateData.put("total_revenue", String.format("%.2f", totalRevenue));
templateData.put("avg_price", totalRevenue
.divide(new BigDecimal(todayOrderCount), 2, RoundingMode.HALF_UP)
.doubleValue());
// 增长率计算
double salesGrowth = (totalRevenue.doubleValue() - yesterdayTotalRevenue.doubleValue())
/ yesterdayTotalRevenue.doubleValue() * 100;
templateData.put("sales_growth",
String.format("%s %.2f%%", salesGrowth >= 0 ? "📈" : "📉", Math.abs(salesGrowth)));
// ========== 用户反馈数据 ==========
List<Feedback> validFeedbacks = feedbackMapper.selectByTimeRange(startTime, endTime);
templateData.put("feedbacks", validFeedbacks.stream()
.map(Feedback::toFormattedString)
.toList());
// 评分统计
int totalValidFeedbacks = validFeedbacks.size();
long positiveCount = validFeedbacks.stream().filter(f -> f.getRating() == 5).count();
long neutralCount = validFeedbacks.stream()
.filter(f -> f.getRating() >= 3 && f.getRating() <= 4).count();
long negativeCount = validFeedbacks.stream().filter(f -> f.getRating() < 3).count();
templateData.put("positive_rate",
totalValidFeedbacks > 0 ? positiveCount * 100.0 / totalValidFeedbacks : 0);
templateData.put("neutral_rate",
totalValidFeedbacks > 0 ? neutralCount * 100.0 / totalValidFeedbacks : 0);
templateData.put("negative_rate",
totalValidFeedbacks > 0 ? negativeCount * 100.0 / totalValidFeedbacks : 0);
return templateData;
}
/**
* 发送钉钉通知
*/
@Tool(description = "Send notification to DingTalk")
public String sendDingTalkNotification(
@ToolParam(name = "content", description = "Notification content")
String content) {
String webhookUrl = String.format(DEFAULT_WEBHOOK_URL_TEMPLATE, accessToken);
Map<String, Object> payload = new HashMap<>();
payload.put("msgtype", "markdown");
Map<String, String> markdown = new HashMap<>();
markdown.put("title", "Daily Report");
markdown.put("text", content);
payload.put("markdown", markdown);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>(
JsonUtils.toJsonString(payload), headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(
webhookUrl, request, String.class);
return response.getBody();
}
}
26.7 服务发现与A2A通信
26.7.1 Nacos集成
/**
* Nacos工具包
* 支持通过Nacos发现MCP服务
*/
public class NacosToolkit extends Toolkit {
private final AiService aiService;
public NacosToolkit() {
this.aiService = NacosFactory.createAiService();
}
/**
* 注册通过Nacos发现的MCP客户端
*/
public Mono<Void> registerNacosMcpClient(String serviceName) {
NacosMcpServerManager mcpServerManager = new NacosMcpServerManager(aiService);
NacosMcpClientWrapper mcpClientWrapper = NacosMcpClientBuilder
.create(serviceName, mcpServerManager)
.build();
return registerMcpClient(mcpClientWrapper);
}
}
26.7.2 A2A配置
/**
* A2A Agent配置
*/
@Configuration
public class A2aAgentConfiguration {
@Bean
public A2aClientAgent consultAgent(A2aClientConfig config) {
return A2aClientAgent.builder()
.name("consult_agent")
.serverUrl(config.getConsultAgentUrl())
.build();
}
@Bean
public A2aClientAgent businessAgent(A2aClientConfig config) {
return A2aClientAgent.builder()
.name("business_agent")
.serverUrl(config.getBusinessAgentUrl())
.build();
}
}
26.8 监控Hook
26.8.1 监控Hook实现
/**
* 监控Hook
* 记录Agent执行的各个阶段
*/
public class MonitoringHook implements Hook {
private static final Logger logger = LoggerFactory.getLogger(MonitoringHook.class);
@Override
public <T extends HookEvent> Mono<T> onEvent(T event) {
if (event instanceof PreReasoningEvent pre) {
logger.info("[Pre-Reasoning] Agent: {}, Messages: {}",
pre.getAgentName(),
pre.getMessages().size());
} else if (event instanceof PostReasoningEvent post) {
logger.info("[Post-Reasoning] Agent: {}, Response: {}",
post.getAgentName(),
truncate(post.getReasoningMessage().getTextContent(), 100));
} else if (event instanceof PreActingEvent preAct) {
logger.info("[Pre-Acting] Agent: {}, Tools: {}",
preAct.getAgentName(),
preAct.getToolUseBlocks().stream()
.map(ToolUseBlock::getName)
.collect(Collectors.joining(", ")));
} else if (event instanceof PostActingEvent postAct) {
logger.info("[Post-Acting] Agent: {}, Results: {}",
postAct.getAgentName(),
postAct.getToolResults().size());
}
return Mono.just(event);
}
private String truncate(String text, int maxLength) {
if (text == null) return null;
return text.length() > maxLength
? text.substring(0, maxLength) + "..."
: text;
}
}
26.9 部署配置
26.9.1 Docker Compose
version: '3.8'
services:
# Nacos服务发现
nacos:
image: nacos/nacos-server:v2.3.0
ports:
- "8848:8848"
environment:
- MODE=standalone
# MySQL数据库
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=bobatea
# 主管Agent
supervisor-agent:
build: ./supervisor-agent
ports:
- "8080:8080"
depends_on:
- nacos
- mysql
environment:
- NACOS_SERVER_ADDR=nacos:8848
- DASHSCOPE_API_KEY=${DASHSCOPE_API_KEY}
- MEM0_API_KEY=${MEM0_API_KEY}
# 咨询Agent
consult-sub-agent:
build: ./consult-sub-agent
ports:
- "8081:8081"
depends_on:
- nacos
environment:
- NACOS_SERVER_ADDR=nacos:8848
- DASHSCOPE_API_KEY=${DASHSCOPE_API_KEY}
- MEM0_API_KEY=${MEM0_API_KEY}
# 业务Agent
business-sub-agent:
build: ./business-sub-agent
ports:
- "8082:8082"
depends_on:
- nacos
- business-mcp-server
environment:
- NACOS_SERVER_ADDR=nacos:8848
- DASHSCOPE_API_KEY=${DASHSCOPE_API_KEY}
- MEM0_API_KEY=${MEM0_API_KEY}
# MCP工具服务
business-mcp-server:
build: ./business-mcp-server
ports:
- "8083:8083"
depends_on:
- nacos
- mysql
environment:
- NACOS_SERVER_ADDR=nacos:8848
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/bobatea
26.10 本章小结
26.10.1 技术要点
| 技术 | 应用场景 |
|---|---|
| A2A协议 | Agent间通信 |
| MCP协议 | 工具服务调用 |
| Nacos | 服务发现与注册 |
| NacosToolkit | Nacos集成工具包 |
| Mem0 | 用户级长期记忆 |
| AutoContextMemory | 上下文自动压缩 |
| AgentRunner | A2A服务端实现 |
| MonitoringHook | 执行监控 |
26.10.2 架构模式
- 微服务架构:每个Agent作为独立服务部署
- 服务发现:通过Nacos实现动态服务发现
- 协议标准化:使用A2A和MCP标准协议
- 用户隔离:每个用户有独立的记忆空间
- 懒加载:MCP客户端按需初始化
- 监控集成:Hook系统实现执行追踪
26.10.3 扩展能力
奶茶店案例展示了企业级多Agent系统的构建方法:
- 新增Agent:注册到Nacos,实现AgentRunner接口
- 新增工具:在MCP服务中定义ToolDefinition
- 新增业务:添加新的业务实体和数据访问层
- 水平扩展:通过Nacos实现多实例负载均衡
26.10.4 完整教程回顾
通过第六部分的四个实战案例,我们学习了:
- 第23章:基础示例的核心用法
- 第24章:高级特性的生产级应用
- 第25章:多智能体博弈游戏系统
- 第26章:企业级分布式多Agent架构
这些案例从简单到复杂,从单机到分布式,完整展示了AgentScope-Java在不同场景下的应用方式。