Spring AI Alibaba A2A 使用指南
目录
- [1. A2A 概述](#1. A2A 概述)
- [1.1 什么是 A2A](#1.1 什么是 A2A)
- [1.2 为什么需要 A2A](#1.2 为什么需要 A2A)
- [1.3 应用场景](#1.3 应用场景)
- [1.4 核心特性](#1.4 核心特性)
- [2. A2A 架构](#2. A2A 架构)
- [2.1 整体架构](#2.1 整体架构)
- [2.2 核心组件](#2.2 核心组件)
- [2.3 工作原理](#2.3 工作原理)
- [2.4 通信协议](#2.4 通信协议)
- [3. 快速开始](#3. 快速开始)
- [3.1 环境准备](#3.1 环境准备)
- [3.2 创建服务提供者](#3.2 创建服务提供者)
- [3.3 创建服务消费者](#3.3 创建服务消费者)
- [3.4 运行示例](#3.4 运行示例)
- [4. 核心概念详解](#4. 核心概念详解)
- [4.1 AgentCard](#4.1 AgentCard)
- [4.2 AgentCardProvider](#4.2 AgentCardProvider)
- [4.3 A2aRemoteAgent](#4.3 A2aRemoteAgent)
- [4.4 注册中心](#4.4 注册中心)
- [5. Nacos 集成](#5. Nacos 集成)
- [5.1 Nacos 介绍](#5.1 Nacos 介绍)
- [5.2 Nacos 安装与配置](#5.2 Nacos 安装与配置)
- [5.3 服务注册配置](#5.3 服务注册配置)
- [5.4 服务发现配置](#5.4 服务发现配置)
- [6. 配置详解](#6. 配置详解)
- [6.1 完整配置示例](#6.1 完整配置示例)
- [6.2 注册配置参数](#6.2 注册配置参数)
- [6.3 发现配置参数](#6.3 发现配置参数)
- [6.4 网络配置](#6.4 网络配置)
- [7. 服务注册](#7. 服务注册)
- [7.1 自动注册(推荐)](#7.1 自动注册(推荐))
- [7.2 手动注册](#7.2 手动注册)
- [7.3 AgentCard 详解](#7.3 AgentCard 详解)
- [7.4 注册生命周期](#7.4 注册生命周期)
- [8. 服务发现](#8. 服务发现)
- [8.1 发现机制](#8.1 发现机制)
- [8.2 查询服务](#8.2 查询服务)
- [8.3 负载均衡](#8.3 负载均衡)
- [8.4 健康检查](#8.4 健康检查)
- [9. 远程智能体调用](#9. 远程智能体调用)
- [9.1 创建远程智能体代理](#9.1 创建远程智能体代理)
- [9.2 同步调用](#9.2 同步调用)
- [9.3 异步调用](#9.3 异步调用)
- [9.4 流式调用](#9.4 流式调用)
- [10. 多智能体协作模式](#10. 多智能体协作模式)
- [10.1 点对点通信](#10.1 点对点通信)
- [10.2 发布订阅模式](#10.2 发布订阅模式)
- [10.3 请求响应模式](#10.3 请求响应模式)
- [10.4 工作流编排](#10.4 工作流编排)
- [11. 高级特性](#11. 高级特性)
- [11.1 安全认证](#11.1 安全认证)
- [11.2 限流与熔断](#11.2 限流与熔断)
- [11.3 链路追踪](#11.3 链路追踪)
- [11.4 灰度发布](#11.4 灰度发布)
- [12. 实战案例](#12. 实战案例)
- [12.1 分布式客服系统](#12.1 分布式客服系统)
- [12.2 多专家咨询系统](#12.2 多专家咨询系统)
- [12.3 智能工作流引擎](#12.3 智能工作流引擎)
- [12.4 跨部门协作平台](#12.4 跨部门协作平台)
- [13. REST API 规范](#13. REST API 规范)
- [13.1 接口定义](#13.1 接口定义)
- [13.2 请求格式](#13.2 请求格式)
- [13.3 响应格式](#13.3 响应格式)
- [13.4 错误处理](#13.4 错误处理)
- [14. 最佳实践](#14. 最佳实践)
- [14.1 服务拆分策略](#14.1 服务拆分策略)
- [14.2 性能优化](#14.2 性能优化)
- [14.3 故障处理](#14.3 故障处理)
- [14.4 监控告警](#14.4 监控告警)
- [15. 常见问题](#15. 常见问题)
- [16. 附录](#16. 附录)
1. A2A 概述
1.1 什么是 A2A
A2A (Agent-to-Agent) 是 Spring AI Alibaba 提供的智能体间通信协议和框架,允许不同的智能体通过网络进行发现、通信和协作。
A2A 解决了以下核心问题:
- 服务发现:智能体如何找到其他智能体?
- 远程调用:如何像调用本地智能体一样调用远程智能体?
- 负载均衡:如何在多个智能体实例间分配请求?
- 故障恢复:如何处理网络故障和服务不可用?
核心理念:让分布式智能体系统的开发像单体应用一样简单。
1.2 为什么需要 A2A
在现代 AI 应用中,单个智能体往往无法解决所有问题。A2A 提供了构建分布式智能体系统的能力:
1.2.1 专业化分工
不同的智能体可以专注于不同的领域:
用户请求 → 路由智能体 → ┬→ 技术专家智能体
├→ 商业专家智能体
├→ 法律专家智能体
└→ 金融专家智能体
每个专家智能体可以:
- 使用专门的模型
- 配置特定的工具
- 优化特定的提示词
- 独立部署和扩展
1.2.2 资源隔离
不同的智能体服务可以:
- 部署在不同的服务器上
- 使用不同的计算资源
- 设置不同的 API 配额
- 独立扩容和缩容
1.2.3 团队协作
大型 AI 应用可以由多个团队并行开发:
- 每个团队负责特定的智能体服务
- 通过统一的 A2A 协议进行集成
- 独立测试和发布
1.2.4 可复用性
通用智能体可以被多个应用复用:
应用 A ─┐
应用 B ─┼→ 通用翻译智能体
应用 C ─┘
应用 D ─┐
应用 E ─┼→ 通用计算智能体
应用 F ─┘
1.3 应用场景
1.3.1 企业级智能客服
┌─────────────────────────────────────────────┐
│ 客户请求 │
└─────────────────┬───────────────────────────┘
↓
┌────────────────┐
│ 路由智能体 │ (识别问题类型)
└───────┬────────┘
│
┌──────────┼──────────┐
↓ ↓ ↓
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 订单服务 │ │ 技术支持 │ │ 售后服务 │
│ 智能体 │ │ 智能体 │ │ 智能体 │
└─────────┘ └─────────┘ └─────────┘
1.3.2 多专家咨询系统
用户问题:"我应该如何投资这笔钱?"
↓
协调智能体
↓
├→ 金融专家:分析投资方案
├→ 税务专家:评估税务影响
├→ 法律专家:审查合规性
└→ 风险专家:评估风险等级
↓
汇总智能体 → 综合建议
1.3.3 智能研究助手
研究主题
↓
┌─────────────┐
│ 规划智能体 │ (制定研究计划)
└─────┬───────┘
│
├→ 文献检索智能体 (查找相关论文)
├→ 数据收集智能体 (收集数据)
├→ 分析智能体 (数据分析)
├→ 撰写智能体 (撰写报告)
└→ 审阅智能体 (审查修改)
↓
研究报告
1.3.4 跨部门协作
项目需求
↓
项目管理智能体
↓
├→ 开发部门智能体 (技术方案)
├→ 设计部门智能体 (UI/UX 设计)
├→ 测试部门智能体 (测试计划)
├→ 运维部门智能体 (部署方案)
└→ 产品部门智能体 (需求分析)
1.4 核心特性
1.4.1 透明的远程调用
调用远程智能体就像调用本地智能体一样简单:
java
// 本地智能体
ReactAgent localAgent = ...;
localAgent.invoke("你好");
// 远程智能体 - 完全相同的 API
A2aRemoteAgent remoteAgent = ...;
remoteAgent.invoke("你好");
1.4.2 自动服务发现
智能体自动注册和发现,无需手动配置:
java
// 服务提供者:智能体自动注册
@Bean
public ReactAgent myAgent() {
return ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.build();
}
// 服务消费者:自动发现并调用
A2aRemoteAgent remote = A2aRemoteAgent.builder()
.name("my_agent") // 通过名称发现
.agentCardProvider(provider)
.build();
1.4.3 负载均衡
自动在多个智能体实例间分配请求:
请求 → Nacos → ┬→ 实例 1 (权重 60%)
├→ 实例 2 (权重 30%)
└→ 实例 3 (权重 10%)
1.4.4 健康检查
自动检测智能体服务的健康状态:
Nacos
├→ agent-1: ✓ 健康 (响应时间: 100ms)
├→ agent-2: ✓ 健康 (响应时间: 120ms)
└→ agent-3: ✗ 不健康 (超时) → 自动摘除
1.4.5 版本管理
支持多版本智能体共存:
my_agent:1.0.0 → 稳定版本
my_agent:1.1.0 → 新功能版本
my_agent:2.0.0 → 重大升级版本
1.4.6 灰度发布
逐步将流量切换到新版本:
旧版本 (90%) ──┐
├→ 用户流量
新版本 (10%) ──┘
2. A2A 架构
2.1 整体架构
┌─────────────────────────────────────────────────────────┐
│ 应用层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │智能体 A │ │智能体 B │ │智能体 C │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼─────────────┼─────────────┼──────────────────────┘
│ │ │
┌───────┼─────────────┼─────────────┼──────────────────────┐
│ │ A2A 框架层 │ │
│ ↓ ↓ │
│ ┌─────────┐ ┌─────────┐ │
│ │ A2A │ │ A2A │ │
│ │ Server │ │ Client │ │
│ └────┬────┘ └────┬────┘ │
│ │ (注册) │ (发现) │
└───────┼──────────────────────────┼──────────────────────┘
│ │
│ ┌───────────────────┤
↓ ↓ ↓
┌───────────────────────────────────────────────────────┐
│ 注册中心 (Nacos) │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ 服务注册表 │ │ 配置中心 │ │
│ │ │ │ │ │
│ │ - agent_a:8081 │ │ - A2A 配置 │ │
│ │ - agent_b:8082 │ │ - 路由规则 │ │
│ │ - agent_c:8083 │ │ - 限流策略 │ │
│ └─────────────────┘ └──────────────────┘ │
└───────────────────────────────────────────────────────┘
↑ ↑
│ (HTTP/REST) │
│ │
┌───────┴──────────────────────────┴──────────────────────┐
│ 网络传输层 │
│ (HTTP, gRPC, WebSocket) │
└─────────────────────────────────────────────────────────┘
2.2 核心组件
2.2.1 A2A Server(服务端)
职责:
- 暴露本地智能体为 REST API
- 向注册中心注册服务
- 发送心跳保持在线状态
- 处理远程调用请求
核心类:
A2aServerAutoConfiguration- 自动配置A2aAgentEndpoint- REST 端点NacosAgentRegistry- Nacos 注册器
工作流程:
应用启动
↓
1. 扫描 ReactAgent Bean
↓
2. 为每个 Agent 创建 REST 端点
↓
3. 构造 AgentCard (服务元数据)
↓
4. 向 Nacos 注册服务
↓
5. 启动心跳线程
↓
服务就绪,等待请求
2.2.2 A2A Client(客户端)
职责:
- 从注册中心发现服务
- 创建远程智能体代理
- 发起 HTTP 调用
- 处理响应和异常
核心类:
A2aRemoteAgent- 远程智能体代理AgentCardProvider- 服务发现提供者NacosAgentDiscovery- Nacos 服务发现
工作流程:
创建 A2aRemoteAgent
↓
1. 根据名称查询 AgentCard
↓
2. 获取服务端点地址
↓
3. 构造 HTTP 请求
↓
4. 发送到远程服务
↓
5. 接收并解析响应
↓
返回结果
2.2.3 AgentCard(服务元数据)
AgentCard 包含智能体的所有元信息:
java
public class AgentCard {
private String name; // 智能体名称
private String description; // 描述
private String version; // 版本号
private String endpoint; // 服务端点 (http://host:port)
private List<String> capabilities; // 能力列表
private Map<String, String> metadata; // 扩展元数据
}
示例:
json
{
"name": "customer_service",
"description": "智能客服机器人",
"version": "1.0.0",
"endpoint": "http://192.168.1.10:8081",
"capabilities": ["tool_calling", "streaming", "chinese"],
"metadata": {
"team": "customer-support",
"owner": "zhangsan@example.com",
"max_concurrent": "100"
}
}
2.2.4 AgentCardProvider(服务发现)
提供服务发现接口:
java
public interface AgentCardProvider {
// 获取指定名称的智能体卡片
Optional<AgentCard> getAgentCard(String agentName);
// 列出所有可用的智能体
List<AgentCard> listAgentCards();
// 按条件过滤智能体
List<AgentCard> findAgentCards(Predicate<AgentCard> filter);
}
实现类:
NacosAgentCardProvider- 基于 Nacos 的服务发现StaticAgentCardProvider- 静态配置的服务列表(用于测试)
2.3 工作原理
2.3.1 服务注册流程
┌──────────────┐
│ 服务启动 │
└──────┬───────┘
↓
┌──────────────────────────────────┐
│ 1. Spring Boot 应用启动 │
│ - 加载配置 │
│ - 初始化 Bean │
└──────┬───────────────────────────┘
↓
┌──────────────────────────────────┐
│ 2. A2aServerAutoConfiguration │
│ - 检查是否启用 A2A Server │
│ - 扫描 ReactAgent Bean │
└──────┬───────────────────────────┘
↓
┌──────────────────────────────────┐
│ 3. 为每个 ReactAgent 创建端点 │
│ POST /api/a2a/{agentName} │
└──────┬───────────────────────────┘
↓
┌──────────────────────────────────┐
│ 4. 构造 AgentCard │
│ - name: Agent.name() │
│ - endpoint: http://host:port │
│ - description, version 等 │
└──────┬───────────────────────────┘
↓
┌──────────────────────────────────┐
│ 5. 注册到 Nacos │
│ NacosNamingService.register() │
│ - namespace: 配置的命名空间 │
│ - group: 配置的分组 │
│ - serviceName: agent 名称 │
│ - instance: IP + Port │
└──────┬───────────────────────────┘
↓
┌──────────────────────────────────┐
│ 6. 启动心跳线程 │
│ 每 5 秒发送一次心跳 │
└──────┬───────────────────────────┘
↓
┌──────────────┐
│ 服务就绪 │
└──────────────┘
2.3.2 服务发现流程
┌──────────────────────┐
│ 创建 A2aRemoteAgent │
└──────┬───────────────┘
↓
┌──────────────────────────────────────┐
│ 1. 调用 provider.getAgentCard(name) │
└──────┬───────────────────────────────┘
↓
┌──────────────────────────────────────┐
│ 2. NacosAgentCardProvider 查询 │
│ NacosNamingService.selectInstance │
│ - 获取服务实例列表 │
│ - 应用负载均衡策略 │
│ - 选择一个健康实例 │
└──────┬───────────────────────────────┘
↓
┌──────────────────────────────────────┐
│ 3. 构造 AgentCard │
│ 从 Nacos 元数据构造 │
└──────┬───────────────────────────────┘
↓
┌──────────────────────────────────────┐
│ 4. 缓存 AgentCard │
│ 缓存 30 秒,减少 Nacos 查询 │
└──────┬───────────────────────────────┘
↓
┌──────────────┐
│ 返回 AgentCard│
└──────────────┘
2.3.3 远程调用流程
┌───────────────────┐
│ remoteAgent.invoke│
└─────┬─────────────┘
↓
┌─────────────────────────────────────┐
│ 1. 获取 AgentCard (从缓存或 Nacos) │
└─────┬───────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 2. 构造 HTTP 请求 │
│ POST {endpoint}/api/a2a/{name} │
│ Content-Type: application/json │
│ Body: { │
│ "messages": [...], │
│ "config": {...} │
│ } │
└─────┬───────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 3. 发送请求到远程服务 │
│ RestTemplate.postForObject() │
└─────┬───────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 4. 远程服务处理 │
│ - 查找本地 ReactAgent │
│ - 调用 agent.invoke() │
│ - 返回 OverAllState │
└─────┬───────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 5. 接收响应 │
│ 解析 JSON → OverAllState │
└─────┬───────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 6. 错误处理 │
│ - 连接超时: 重试 │
│ - 服务不可用: 切换实例 │
│ - 业务错误: 返回异常 │
└─────┬───────────────────────────────┘
↓
┌───────────────┐
│ 返回结果 │
└───────────────┘
2.4 通信协议
2.4.1 REST API
A2A 使用标准的 REST API 进行通信:
端点 :POST /api/a2a/{agentName}
请求格式:
json
{
"messages": [
{
"role": "user",
"content": "你好,请帮我查询订单"
}
],
"config": {
"threadId": "session_123",
"metadata": {
"userId": "user_001"
}
}
}
响应格式:
json
{
"messages": [
{
"role": "user",
"content": "你好,请帮我查询订单"
},
{
"role": "assistant",
"content": "好的,请提供您的订单号。"
}
],
"metadata": {
"nodeId": "agent_llm_node",
"duration": 523
}
}
2.4.2 流式响应
对于流式调用,使用 Server-Sent Events (SSE):
端点 :POST /api/a2a/{agentName}/stream
响应:
data: {"nodeName":"agent_llm_node","state":{"messages":[...]}}
data: {"nodeName":"agent_tool_node","state":{"messages":[...]}}
data: {"nodeName":"agent_llm_node","state":{"messages":[...]}}
data: [DONE]
3. 快速开始
3.1 环境准备
3.1.1 系统要求
- JDK 17 或更高版本
- Maven 3.6+ 或 Gradle 7+
- Nacos 2.0+(用于服务注册与发现)
3.1.2 安装 Nacos
方式 1:使用 Docker(推荐)
bash
# 启动 Nacos Server
docker run -d \
--name nacos \
-e MODE=standalone \
-p 8848:8848 \
-p 9848:9848 \
nacos/nacos-server:latest
方式 2:下载独立版本
bash
# 下载
wget https://github.com/alibaba/nacos/releases/download/2.4.3/nacos-server-2.4.3.tar.gz
# 解压
tar -xzf nacos-server-2.4.3.tar.gz
# 启动(单机模式)
cd nacos/bin
./startup.sh -m standalone
验证 Nacos 启动:
访问 http://localhost:8848/nacos
默认用户名密码:nacos / nacos
3.2 创建服务提供者
3.2.1 创建 Maven 项目
bash
mkdir a2a-provider
cd a2a-provider
pom.xml:
xml
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.8</version>
</parent>
<groupId>com.example</groupId>
<artifactId>a2a-provider</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
<spring-ai-alibaba.version>1.1.0.0-RC1</spring-ai-alibaba.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Agent Framework -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- DashScope -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- A2A with Nacos -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-a2a-nacos</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2.2 配置文件
application.yml:
yaml
server:
port: 8081
spring:
application:
name: math-agent-service
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
options:
model: qwen-max
alibaba:
a2a:
nacos:
server-addr: 127.0.0.1:8848
registry:
enabled: true
namespace: dev
group: DEFAULT_GROUP
discovery:
enabled: true
namespace: dev
group: DEFAULT_GROUP
server:
enabled: true
card:
name: math_agent
description: "数学计算智能体"
version: "1.0.0"
logging:
level:
com.alibaba.cloud.ai: DEBUG
3.2.3 创建智能体
MathAgentConfig.java:
java
package com.example.provider;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
@Configuration
public class MathAgentConfig {
@Bean
public ReactAgent mathAgent(ChatModel chatModel, MathTools mathTools) {
return ReactAgent.builder()
.name("math_agent")
.description("专业的数学计算智能体,可以进行加减乘除和复杂计算")
.model(chatModel)
.instruction("""
你是一个专业的数学助手。
职责:
1. 解答数学问题
2. 使用计算工具进行精确计算
3. 解释计算过程
回答要求:
- 简洁明了
- 给出计算步骤
- 验证结果的正确性
""")
.tools(mathTools)
.build();
}
}
@Component
class MathTools {
@Tool(description = "计算两个数的和")
public double add(
@ToolParam(description = "第一个数") double a,
@ToolParam(description = "第二个数") double b
) {
return a + b;
}
@Tool(description = "计算两个数的差")
public double subtract(
@ToolParam(description = "被减数") double a,
@ToolParam(description = "减数") double b
) {
return a - b;
}
@Tool(description = "计算两个数的积")
public double multiply(
@ToolParam(description = "第一个数") double a,
@ToolParam(description = "第二个数") double b
) {
return a * b;
}
@Tool(description = "计算两个数的商")
public double divide(
@ToolParam(description = "被除数") double a,
@ToolParam(description = "除数") double b
) {
if (b == 0) {
throw new IllegalArgumentException("除数不能为零");
}
return a / b;
}
@Tool(description = "计算数的平方")
public double square(@ToolParam(description = "数值") double x) {
return x * x;
}
@Tool(description = "计算数的平方根")
public double sqrt(@ToolParam(description = "数值") double x) {
if (x < 0) {
throw new IllegalArgumentException("不能计算负数的平方根");
}
return Math.sqrt(x);
}
}
3.2.4 启动类
ProviderApplication.java:
java
package com.example.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
3.2.5 启动服务
bash
# 设置 API Key
export AI_DASHSCOPE_API_KEY=your-api-key
# 启动
mvn spring-boot:run
检查注册状态:
访问 Nacos 控制台:http://localhost:8848/nacos
在"服务管理 → 服务列表"中应该看到 math_agent 服务。
3.3 创建服务消费者
3.3.1 创建项目
bash
mkdir a2a-consumer
cd a2a-consumer
pom.xml:(与 provider 类似,只需要 A2A 客户端依赖)
xml
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.8</version>
</parent>
<groupId>com.example</groupId>
<artifactId>a2a-consumer</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
<spring-ai-alibaba.version>1.1.0.0-RC1</spring-ai-alibaba.version>
</properties>
<dependencies>
<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>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-a2a-nacos</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.3.2 配置文件
application.yml:
yaml
server:
port: 8082
spring:
application:
name: consumer-service
ai:
alibaba:
a2a:
nacos:
server-addr: 127.0.0.1:8848
discovery:
enabled: true
namespace: dev
group: DEFAULT_GROUP
logging:
level:
com.alibaba.cloud.ai: DEBUG
3.3.3 创建消费者服务
MathConsumerService.java:
java
package com.example.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.agent.a2a.A2aRemoteAgent;
import com.alibaba.cloud.ai.graph.agent.a2a.AgentCardProvider;
import java.util.Optional;
@Service
public class MathConsumerService {
@Autowired
private AgentCardProvider agentCardProvider;
public String calculate(String question) {
// 创建远程数学智能体代理
A2aRemoteAgent mathAgent = A2aRemoteAgent.builder()
.name("math_agent")
.agentCardProvider(agentCardProvider)
.build();
// 调用远程智能体
Optional<OverAllState> result = mathAgent.invoke(question);
// 获取回复
return result.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("未获取到响应");
}
}
3.3.4 创建 REST 控制器
MathController.java:
java
package com.example.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/math")
public class MathController {
@Autowired
private MathConsumerService mathService;
@PostMapping("/calculate")
public CalculateResponse calculate(@RequestBody CalculateRequest request) {
String answer = mathService.calculate(request.question());
return new CalculateResponse(request.question(), answer);
}
}
record CalculateRequest(String question) {}
record CalculateResponse(String question, String answer) {}
3.3.5 启动类
ConsumerApplication.java:
java
package com.example.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
3.4 运行示例
3.4.1 启动服务
终端 1 - 启动 Nacos:
bash
# 如果使用 Docker
docker start nacos
# 或者使用独立版本
cd nacos/bin
./startup.sh -m standalone
终端 2 - 启动 Provider:
bash
cd a2a-provider
export AI_DASHSCOPE_API_KEY=your-api-key
mvn spring-boot:run
终端 3 - 启动 Consumer:
bash
cd a2a-consumer
mvn spring-boot:run
3.4.2 测试调用
bash
# 测试计算
curl -X POST http://localhost:8082/api/math/calculate \
-H "Content-Type: application/json" \
-d '{
"question": "计算 123 + 456 的结果"
}'
响应:
json
{
"question": "计算 123 + 456 的结果",
"answer": "好的,我来帮你计算。使用加法工具:123 + 456 = 579。所以结果是 579。"
}
更复杂的测试:
bash
curl -X POST http://localhost:8082/api/math/calculate \
-H "Content-Type: application/json" \
-d '{
"question": "先计算 100 的平方,然后除以 50"
}'
响应:
json
{
"question": "先计算 100 的平方,然后除以 50",
"answer": "好的,让我分步计算:\n1. 首先计算 100 的平方:100² = 10000\n2. 然后用 10000 除以 50:10000 ÷ 50 = 200\n\n最终结果是 200。"
}
3.4.3 查看调用链路
Provider 日志:
2024-01-20 10:30:15.123 DEBUG [a2a-provider] Received A2A request for agent: math_agent
2024-01-20 10:30:15.125 DEBUG [a2a-provider] Invoking ReactAgent with message: 计算 123 + 456 的结果
2024-01-20 10:30:15.456 DEBUG [a2a-provider] Agent called tool: add(123.0, 456.0)
2024-01-20 10:30:15.457 DEBUG [a2a-provider] Tool returned: 579.0
2024-01-20 10:30:15.789 DEBUG [a2a-provider] Agent response completed
Consumer 日志:
2024-01-20 10:30:15.100 DEBUG [a2a-consumer] Discovering agent: math_agent
2024-01-20 10:30:15.110 DEBUG [a2a-consumer] Found agent at: http://192.168.1.10:8081
2024-01-20 10:30:15.120 DEBUG [a2a-consumer] Sending request to remote agent
2024-01-20 10:30:15.790 DEBUG [a2a-consumer] Received response from remote agent
4. 核心概念详解
4.1 AgentCard
AgentCard 是智能体的"名片",包含了智能体的所有元信息。
4.1.1 字段说明
java
public class AgentCard {
// 必需字段
private String name; // 智能体唯一标识
private String endpoint; // 服务端点地址
// 可选字段
private String description; // 智能体描述
private String version; // 版本号
private List<String> capabilities; // 能力列表
private Map<String, String> metadata; // 自定义元数据
}
字段详解:
| 字段 | 类型 | 必需 | 说明 | 示例 |
|---|---|---|---|---|
| name | String | 是 | 智能体名称,必须唯一 | "customer_service" |
| endpoint | String | 是 | REST API 端点 | "http://192.168.1.10:8081" |
| description | String | 否 | 智能体功能描述 | "智能客服机器人" |
| version | String | 否 | 版本号,支持语义化版本 | "1.0.0", "2.1.3-beta" |
| capabilities | List | 否 | 支持的能力列表 | ["tool_calling", "streaming"] |
| metadata | Map | 否 | 扩展元数据 | {"team": "support", "max_qps": "100"} |
4.1.2 创建 AgentCard
方式 1:通过配置自动生成(推荐)
yaml
spring:
ai:
alibaba:
a2a:
server:
card:
name: my_agent
description: "我的智能体"
version: "1.0.0"
框架会自动创建 AgentCard 并注册到 Nacos。
方式 2:手动创建 Bean
java
@Configuration
public class AgentCardConfig {
@Bean
public AgentCard myAgentCard() {
return AgentCard.builder()
.name("my_agent")
.description("我的智能体")
.version("1.0.0")
.endpoint("http://localhost:8081")
.capabilities(List.of("tool_calling", "streaming", "chinese"))
.metadata(Map.of(
"team", "ai-team",
"owner", "zhangsan@example.com",
"max_concurrent", "50",
"timeout", "30s"
))
.build();
}
}
4.1.3 AgentCard 最佳实践
1. 命名规范
java
// 好的命名
"customer_service" // 清晰的业务含义
"code_reviewer" // 功能明确
"data_analyzer_v2" // 包含版本信息
// 不好的命名
"agent1" // 无意义
"my-agent" // 使用连字符(推荐使用下划线)
"CustomerService" // 使用驼峰(推荐使用蛇形命名)
2. 版本管理
使用语义化版本:主版本.次版本.修订号
java
"1.0.0" // 初始版本
"1.1.0" // 新增功能,向后兼容
"1.1.1" // 修复 bug
"2.0.0" // 重大变更,可能不向后兼容
"2.1.0-beta" // 测试版本
3. 能力声明
在 capabilities 中声明智能体支持的特性:
java
capabilities = List.of(
"tool_calling", // 支持工具调用
"streaming", // 支持流式响应
"chinese", // 支持中文
"english", // 支持英文
"multimodal", // 支持多模态(图片、音频等)
"long_context" // 支持长上下文
)
消费者可以根据能力筛选智能体:
java
List<AgentCard> chineseAgents = agentCardProvider.findAgentCards(
card -> card.getCapabilities().contains("chinese")
);
4. 元数据扩展
使用 metadata 存储额外信息:
java
metadata = Map.of(
// 团队信息
"team", "customer-support",
"owner", "zhangsan@example.com",
"contact", "support@example.com",
// 性能指标
"max_qps", "100", // 最大 QPS
"avg_latency", "500ms", // 平均延迟
"timeout", "30s", // 超时时间
// 业务信息
"business_domain", "ecommerce",
"supported_languages", "zh-CN,en-US",
// 部署信息
"region", "cn-hangzhou",
"environment", "production",
"deploy_time", "2024-01-20T10:00:00Z"
)
4.2 AgentCardProvider
AgentCardProvider 是服务发现的核心接口,负责查询和管理 AgentCard。
4.2.1 接口定义
java
public interface AgentCardProvider {
/**
* 根据名称获取智能体卡片
* @param agentName 智能体名称
* @return AgentCard,如果不存在返回 empty
*/
Optional<AgentCard> getAgentCard(String agentName);
/**
* 列出所有可用的智能体
* @return 智能体列表
*/
List<AgentCard> listAgentCards();
/**
* 按条件查找智能体
* @param filter 过滤条件
* @return 匹配的智能体列表
*/
default List<AgentCard> findAgentCards(Predicate<AgentCard> filter) {
return listAgentCards().stream()
.filter(filter)
.collect(Collectors.toList());
}
}
4.2.2 实现类
NacosAgentCardProvider
基于 Nacos 的动态服务发现:
java
@Component
public class NacosAgentCardProvider implements AgentCardProvider {
@Autowired
private NamingService nacosNamingService;
@Override
public Optional<AgentCard> getAgentCard(String agentName) {
try {
// 从 Nacos 查询服务实例
Instance instance = nacosNamingService.selectOneHealthyInstance(agentName);
// 构造 AgentCard
return Optional.of(buildAgentCard(instance));
} catch (NacosException e) {
return Optional.empty();
}
}
@Override
public List<AgentCard> listAgentCards() {
try {
// 获取所有服务列表
ListView<String> services = nacosNamingService.getServicesOfServer(1, 100);
return services.getData().stream()
.map(this::getAgentCard)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
} catch (NacosException e) {
return List.of();
}
}
private AgentCard buildAgentCard(Instance instance) {
Map<String, String> metadata = instance.getMetadata();
return AgentCard.builder()
.name(instance.getServiceName())
.endpoint("http://" + instance.getIp() + ":" + instance.getPort())
.description(metadata.get("description"))
.version(metadata.get("version"))
.capabilities(parseCapabilities(metadata.get("capabilities")))
.metadata(metadata)
.build();
}
}
StaticAgentCardProvider
基于静态配置的服务发现(用于测试):
java
@Component
public class StaticAgentCardProvider implements AgentCardProvider {
private final Map<String, AgentCard> cards = new HashMap<>();
public StaticAgentCardProvider() {
// 配置静态服务列表
cards.put("math_agent", AgentCard.builder()
.name("math_agent")
.endpoint("http://localhost:8081")
.description("数学计算智能体")
.version("1.0.0")
.build());
cards.put("weather_agent", AgentCard.builder()
.name("weather_agent")
.endpoint("http://localhost:8082")
.description("天气查询智能体")
.version("1.0.0")
.build());
}
@Override
public Optional<AgentCard> getAgentCard(String agentName) {
return Optional.ofNullable(cards.get(agentName));
}
@Override
public List<AgentCard> listAgentCards() {
return new ArrayList<>(cards.values());
}
}
4.2.3 使用示例
基础用法:
java
@Service
public class AgentDiscoveryService {
@Autowired
private AgentCardProvider agentCardProvider;
public void discoverAgents() {
// 1. 获取特定智能体
Optional<AgentCard> mathAgent = agentCardProvider.getAgentCard("math_agent");
mathAgent.ifPresent(card -> {
System.out.println("找到智能体: " + card.getName());
System.out.println("端点: " + card.getEndpoint());
System.out.println("描述: " + card.getDescription());
});
// 2. 列出所有智能体
List<AgentCard> allAgents = agentCardProvider.listAgentCards();
System.out.println("共发现 " + allAgents.size() + " 个智能体");
allAgents.forEach(card -> {
System.out.println("- " + card.getName() + ": " + card.getDescription());
});
// 3. 按条件查找
List<AgentCard> chineseAgents = agentCardProvider.findAgentCards(
card -> card.getCapabilities() != null &&
card.getCapabilities().contains("chinese")
);
// 4. 按版本查找
List<AgentCard> v2Agents = agentCardProvider.findAgentCards(
card -> card.getVersion() != null &&
card.getVersion().startsWith("2.")
);
}
}
高级用法 - 智能路由:
java
@Service
public class IntelligentRouter {
@Autowired
private AgentCardProvider agentCardProvider;
public A2aRemoteAgent selectBestAgent(String task) {
// 获取所有客服智能体
List<AgentCard> customerServiceAgents = agentCardProvider.findAgentCards(
card -> card.getName().startsWith("customer_service_")
);
// 按负载排序(从 metadata 中获取当前 QPS)
AgentCard bestAgent = customerServiceAgents.stream()
.min(Comparator.comparingInt(card -> {
String qps = card.getMetadata().get("current_qps");
return qps != null ? Integer.parseInt(qps) : 0;
}))
.orElseThrow(() -> new RuntimeException("没有可用的客服智能体"));
// 创建远程代理
return A2aRemoteAgent.builder()
.name(bestAgent.getName())
.agentCardProvider(agentCardProvider)
.build();
}
}
4.3 A2aRemoteAgent
A2aRemoteAgent 是远程智能体的本地代理,提供与 ReactAgent 相同的接口。
4.3.1 创建远程智能体
基础用法:
java
@Service
public class RemoteAgentService {
@Autowired
private AgentCardProvider agentCardProvider;
public A2aRemoteAgent createRemoteAgent(String agentName) {
return A2aRemoteAgent.builder()
.name(agentName)
.agentCardProvider(agentCardProvider)
.build();
}
}
完整配置:
java
A2aRemoteAgent remoteAgent = A2aRemoteAgent.builder()
.name("math_agent") // 远程智能体名称
.agentCardProvider(agentCardProvider) // 服务发现提供者
.connectTimeout(Duration.ofSeconds(10)) // 连接超时
.readTimeout(Duration.ofSeconds(30)) // 读取超时
.maxRetries(3) // 最大重试次数
.retryDelay(Duration.ofSeconds(1)) // 重试延迟
.build();
4.3.2 调用方法
同步调用:
java
// 简单调用
Optional<OverAllState> result = remoteAgent.invoke("你好");
// 带配置的调用
RunnableConfig config = RunnableConfig.builder()
.threadId("session_123")
.metadata(Map.of("userId", "user_001"))
.build();
Optional<OverAllState> result = remoteAgent.invoke("计算 100 + 200", config);
// 获取响应
String response = result
.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("未获取到响应");
异步调用:
java
CompletableFuture<Optional<OverAllState>> future =
CompletableFuture.supplyAsync(() -> remoteAgent.invoke("你好"));
future.thenAccept(result -> {
result.flatMap(state -> state.getLastMessage())
.ifPresent(msg -> System.out.println(msg.getContent()));
});
流式调用:
java
Flux<NodeOutput> stream = remoteAgent.stream("讲一个故事");
stream.subscribe(
output -> {
output.state().getLastMessage().ifPresent(msg -> {
System.out.print(msg.getContent());
});
},
error -> System.err.println("错误: " + error.getMessage()),
() -> System.out.println("\n完成")
);
4.3.3 错误处理
java
public String callRemoteAgent(String message) {
try {
Optional<OverAllState> result = remoteAgent.invoke(message);
return result.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("未获取到响应");
} catch (A2aException e) {
// A2A 特定异常
if (e instanceof AgentNotFoundException) {
return "智能体未找到";
} else if (e instanceof AgentUnavailableException) {
return "智能体暂时不可用,请稍后重试";
} else if (e instanceof A2aTimeoutException) {
return "请求超时";
} else {
return "调用失败: " + e.getMessage();
}
} catch (Exception e) {
// 其他异常
return "系统错误: " + e.getMessage();
}
}
4.4 注册中心
4.4.1 为什么需要注册中心?
注册中心解决了分布式系统中的核心问题:
- 服务发现:服务消费者如何找到服务提供者?
- 负载均衡:如何在多个服务实例间分配请求?
- 健康检查:如何及时发现并摘除故障实例?
- 动态扩缩容:如何自动适应服务实例的增减?
没有注册中心的问题:
java
// 硬编码服务地址
String endpoint = "http://192.168.1.10:8081";
// 问题:
// 1. IP 地址变更需要修改代码
// 2. 无法实现负载均衡
// 3. 实例下线无法感知
// 4. 无法动态扩容
使用注册中心:
java
// 通过名称发现服务
AgentCard card = agentCardProvider.getAgentCard("math_agent");
// 优势:
// 1. 自动发现可用实例
// 2. 自动负载均衡
// 3. 自动健康检查
// 4. 支持动态扩缩容
4.4.2 Nacos 核心概念
服务(Service)
服务是一组提供相同功能的实例的逻辑集合:
math_agent 服务
├─ 实例 1: 192.168.1.10:8081
├─ 实例 2: 192.168.1.11:8081
└─ 实例 3: 192.168.1.12:8081
实例(Instance)
实例是服务的具体运行单元:
java
Instance {
ip: "192.168.1.10",
port: 8081,
healthy: true,
weight: 1.0,
metadata: {
"version": "1.0.0",
"description": "数学计算智能体"
}
}
命名空间(Namespace)
用于隔离不同环境的服务:
├─ dev 命名空间
│ ├─ math_agent (开发环境)
│ └─ weather_agent (开发环境)
├─ test 命名空间
│ ├─ math_agent (测试环境)
│ └─ weather_agent (测试环境)
└─ prod 命名空间
├─ math_agent (生产环境)
└─ weather_agent (生产环境)
分组(Group)
在同一命名空间内进一步分组:
prod 命名空间
├─ DEFAULT_GROUP
│ ├─ math_agent
│ └─ weather_agent
├─ PAYMENT_GROUP
│ ├─ payment_agent
│ └─ refund_agent
└─ ORDER_GROUP
├─ order_agent
└─ inventory_agent
4.4.3 服务注册流程
应用启动
↓
1. 读取配置
- Nacos 地址
- 命名空间
- 分组
↓
2. 创建 NamingService
NamingFactory.createNamingService(properties)
↓
3. 构造实例信息
Instance instance = new Instance();
instance.setIp(getLocalIp());
instance.setPort(serverPort);
instance.setHealthy(true);
instance.setMetadata(buildMetadata());
↓
4. 注册到 Nacos
namingService.registerInstance(serviceName, instance);
↓
5. 启动心跳线程
每 5 秒发送一次心跳
↓
服务注册成功
4.4.4 服务发现流程
查询服务
↓
1. 调用 getAgentCard(name)
↓
2. 从 Nacos 获取实例列表
List<Instance> instances =
namingService.getAllInstances(name);
↓
3. 过滤健康实例
instances.stream()
.filter(Instance::isHealthy)
↓
4. 应用负载均衡
- 加权随机
- 轮询
- 一致性哈希
↓
5. 选择一个实例
Instance selected = loadBalancer.select(instances);
↓
6. 构造 AgentCard
AgentCard card = buildCard(selected);
↓
7. 缓存结果(30 秒)
↓
返回 AgentCard
4.4.5 健康检查机制
心跳检查:
服务实例每 5 秒向 Nacos 发送一次心跳:
实例 ──(每 5秒)──> Nacos
如果 15 秒未收到心跳:
- 标记为不健康
- 但不删除
如果 30 秒未收到心跳:
- 删除实例
主动健康检查:
Nacos 也会主动检查实例:
Nacos ──(HTTP GET)──> 实例的健康检查端点
如果响应 200:
- 标记为健康
如果超时或错误:
- 标记为不健康
在 Spring Boot 中配置健康检查端点:
java
@RestController
public class HealthController {
@GetMapping("/actuator/health")
public Map<String, Object> health() {
return Map.of(
"status", "UP",
"timestamp", System.currentTimeMillis()
);
}
}
(继续编写后续章节...由于内容非常长,我会分多次生成)
5. Nacos 集成
5.1 Nacos 介绍
Nacos (Dynamic Naming and Configuration Service) 是阿里巴巴开源的动态服务发现、配置管理和服务管理平台。
核心功能:
- 服务注册与发现
- 动态配置管理
- 服务健康监测
- 动态 DNS 服务
为什么选择 Nacos?
- 阿里巴巴背书:经过双 11 大促验证
- 功能全面:集成注册中心和配置中心
- 高可用:支持集群部署
- 易于使用:提供 Web 控制台
- Spring Cloud 集成:与 Spring 生态无缝集成
5.2 Nacos 安装与配置
5.2.1 Docker 部署(单机模式)
bash
# 拉取镜像
docker pull nacos/nacos-server:latest
# 启动容器
docker run -d \
--name nacos \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
nacos/nacos-server:latest
5.2.2 Docker Compose 部署(集群模式)
docker-compose.yml:
yaml
version: '3'
services:
nacos1:
image: nacos/nacos-server:latest
container_name: nacos1
environment:
- MODE=cluster
- NACOS_SERVERS=nacos1:8848 nacos2:8848 nacos3:8848
- MYSQL_SERVICE_HOST=mysql
- MYSQL_SERVICE_DB_NAME=nacos
- MYSQL_SERVICE_USER=nacos
- MYSQL_SERVICE_PASSWORD=nacos
ports:
- "8848:8848"
- "9848:9848"
- "9849:9849"
depends_on:
- mysql
nacos2:
image: nacos/nacos-server:latest
container_name: nacos2
environment:
- MODE=cluster
- NACOS_SERVERS=nacos1:8848 nacos2:8848 nacos3:8848
- MYSQL_SERVICE_HOST=mysql
- MYSQL_SERVICE_DB_NAME=nacos
- MYSQL_SERVICE_USER=nacos
- MYSQL_SERVICE_PASSWORD=nacos
ports:
- "8849:8848"
depends_on:
- mysql
nacos3:
image: nacos/nacos-server:latest
container_name: nacos3
environment:
- MODE=cluster
- NACOS_SERVERS=nacos1:8848 nacos2:8848 nacos3:8848
- MYSQL_SERVICE_HOST=mysql
- MYSQL_SERVICE_DB_NAME=nacos
- MYSQL_SERVICE_USER=nacos
- MYSQL_SERVICE_PASSWORD=nacos
ports:
- "8850:8848"
depends_on:
- mysql
mysql:
image: mysql:8.0
container_name: nacos-mysql
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=nacos
- MYSQL_USER=nacos
- MYSQL_PASSWORD=nacos
volumes:
- ./mysql:/var/lib/mysql
- ./nacos-mysql.sql:/docker-entrypoint-initdb.d/nacos-mysql.sql
ports:
- "3306:3306"
启动集群:
bash
docker-compose up -d
5.2.3 独立部署
bash
# 下载
wget https://github.com/alibaba/nacos/releases/download/2.4.3/nacos-server-2.4.3.tar.gz
# 解压
tar -xzf nacos-server-2.4.3.tar.gz
cd nacos
# 配置(可选)
# 编辑 conf/application.properties
# 修改数据库连接等配置
# 启动(单机模式)
sh bin/startup.sh -m standalone
# 启动(集群模式)
sh bin/startup.sh
# 停止
sh bin/shutdown.sh
5.2.4 访问控制台
访问:http://localhost:8848/nacos
默认账号:nacos / nacos
5.3 服务注册配置
5.3.1 基础配置
yaml
spring:
ai:
alibaba:
a2a:
nacos:
# Nacos 服务器地址
server-addr: 127.0.0.1:8848
# 服务注册配置
registry:
enabled: true # 启用注册
namespace: dev # 命名空间
group: DEFAULT_GROUP # 分组
cluster-name: DEFAULT # 集群名称
weight: 1.0 # 权重(0.0-1.0)
# A2A Server 配置
server:
enabled: true
card:
name: my_agent
description: "我的智能体"
version: "1.0.0"
5.3.2 完整配置示例
yaml
spring:
application:
name: agent-service
ai:
alibaba:
a2a:
nacos:
server-addr: 192.168.1.100:8848,192.168.1.101:8848,192.168.1.102:8848
username: nacos # Nacos 用户名
password: nacos # Nacos 密码
registry:
enabled: true
namespace: prod # 生产环境命名空间
group: AI_AGENT_GROUP # 自定义分组
cluster-name: hz-cluster # 杭州集群
weight: 1.0
# 元数据
metadata:
team: "ai-team"
owner: "zhangsan@example.com"
version: "1.0.0"
region: "cn-hangzhou"
server:
enabled: true
port: ${server.port} # 使用 Spring Boot 端口
card:
name: ${spring.application.name}
description: "生产环境智能体服务"
version: "1.0.0"
capabilities:
- tool_calling
- streaming
- chinese
- english
server:
port: 8081
5.3.3 多环境配置
application.yml(公共配置):
yaml
spring:
application:
name: agent-service
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
ai:
alibaba:
a2a:
server:
enabled: true
card:
name: ${spring.application.name}
application-dev.yml(开发环境):
yaml
spring:
ai:
alibaba:
a2a:
nacos:
server-addr: 127.0.0.1:8848
registry:
enabled: true
namespace: dev
group: DEFAULT_GROUP
weight: 1.0
server:
card:
description: "开发环境智能体"
version: "1.0.0-dev"
server:
port: 8081
application-test.yml(测试环境):
yaml
spring:
ai:
alibaba:
a2a:
nacos:
server-addr: nacos-test.example.com:8848
registry:
enabled: true
namespace: test
group: DEFAULT_GROUP
weight: 1.0
server:
card:
description: "测试环境智能体"
version: "1.0.0-test"
server:
port: 8081
application-prod.yml(生产环境):
yaml
spring:
ai:
alibaba:
a2a:
nacos:
server-addr: nacos-1.prod.example.com:8848,nacos-2.prod.example.com:8848,nacos-3.prod.example.com:8848
username: ${NACOS_USERNAME}
password: ${NACOS_PASSWORD}
registry:
enabled: true
namespace: prod
group: AI_AGENT_GROUP
cluster-name: ${CLUSTER_NAME:default}
weight: 1.0
server:
card:
description: "生产环境智能体"
version: "1.0.0"
server:
port: 8081
5.4 服务发现配置
5.4.1 基础配置
yaml
spring:
ai:
alibaba:
a2a:
nacos:
server-addr: 127.0.0.1:8848
# 服务发现配置
discovery:
enabled: true
namespace: dev
group: DEFAULT_GROUP
5.4.2 高级配置
yaml
spring:
ai:
alibaba:
a2a:
nacos:
server-addr: 192.168.1.100:8848
username: nacos
password: nacos
discovery:
enabled: true
namespace: prod
group: AI_AGENT_GROUP
# 负载均衡策略
load-balance-strategy: weighted-random # weighted-random, round-robin, random
# 缓存配置
cache-enabled: true
cache-ttl: 30s # 缓存时间
# 健康检查
healthy-only: true # 只返回健康实例
# 监听配置变更
watch-delay: 5s # 监听延迟
6. 配置详解
6.1 完整配置示例
yaml
server:
port: 8081
spring:
application:
name: intelligent-agent-service
ai:
# DashScope 配置
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
options:
model: qwen-max
temperature: 0.7
alibaba:
# A2A 配置
a2a:
# Nacos 配置
nacos:
# 服务器地址(支持多个地址,逗号分隔)
server-addr: nacos-1.prod.com:8848,nacos-2.prod.com:8848,nacos-3.prod.com:8848
# 认证信息
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
# 命名空间
namespace: ${NACOS_NAMESPACE:prod}
# 服务注册配置
registry:
enabled: true
group: AI_AGENT_GROUP
cluster-name: ${CLUSTER_NAME:default}
weight: 1.0
# 心跳配置
heart-beat-interval: 5s
heart-beat-timeout: 15s
# IP 配置
ip: ${SERVICE_IP:} # 为空则自动检测
prefer-ip-address: true
# 元数据
metadata:
team: "ai-team"
owner: "admin@example.com"
version: "${project.version}"
environment: "production"
max_concurrent_requests: "100"
avg_response_time: "500ms"
# 服务发现配置
discovery:
enabled: true
group: AI_AGENT_GROUP
# 负载均衡
load-balance-strategy: weighted-random
# 缓存
cache-enabled: true
cache-ttl: 30s
cache-max-size: 1000
# 健康检查
healthy-only: true
# 监听配置变更
watch-enabled: true
watch-delay: 5s
# A2A Server 配置
server:
enabled: true
port: ${server.port}
context-path: /api/a2a
# 服务卡片
card:
name: ${spring.application.name}
description: "智能代理服务"
version: "1.0.0"
capabilities:
- tool_calling
- streaming
- chinese
- english
- long_context
# 线程池配置
executor:
core-pool-size: 10
max-pool-size: 50
queue-capacity: 100
thread-name-prefix: "a2a-server-"
# 超时配置
timeout:
connect: 10s
read: 30s
write: 30s
# A2A Client 配置
client:
# 连接池配置
connection-pool:
max-total: 200
max-per-route: 20
connection-timeout: 10s
socket-timeout: 30s
# 重试配置
retry:
enabled: true
max-attempts: 3
backoff:
initial-interval: 1s
multiplier: 2
max-interval: 10s
# 熔断配置
circuit-breaker:
enabled: true
failure-rate-threshold: 50
slow-call-rate-threshold: 50
slow-call-duration-threshold: 5s
sliding-window-size: 100
minimum-number-of-calls: 10
wait-duration-in-open-state: 60s
# 日志配置
logging:
level:
com.alibaba.cloud.ai: DEBUG
com.alibaba.nacos: INFO
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
# 监控配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
6.2 注册配置参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
spring.ai.alibaba.a2a.nacos.registry.enabled |
Boolean | false | 是否启用服务注册 |
spring.ai.alibaba.a2a.nacos.registry.namespace |
String | public | 命名空间 ID |
spring.ai.alibaba.a2a.nacos.registry.group |
String | DEFAULT_GROUP | 服务分组 |
spring.ai.alibaba.a2a.nacos.registry.cluster-name |
String | DEFAULT | 集群名称 |
spring.ai.alibaba.a2a.nacos.registry.weight |
Double | 1.0 | 权重(0.0-1.0) |
spring.ai.alibaba.a2a.nacos.registry.ip |
String | 自动检测 | 服务 IP 地址 |
spring.ai.alibaba.a2a.nacos.registry.port |
Integer | server.port | 服务端口 |
spring.ai.alibaba.a2a.nacos.registry.prefer-ip-address |
Boolean | true | 优先使用 IP 注册 |
spring.ai.alibaba.a2a.nacos.registry.heart-beat-interval |
Duration | 5s | 心跳间隔 |
spring.ai.alibaba.a2a.nacos.registry.heart-beat-timeout |
Duration | 15s | 心跳超时 |
spring.ai.alibaba.a2a.nacos.registry.metadata |
Map | {} | 元数据 |
6.3 发现配置参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
spring.ai.alibaba.a2a.nacos.discovery.enabled |
Boolean | false | 是否启用服务发现 |
spring.ai.alibaba.a2a.nacos.discovery.namespace |
String | public | 命名空间 ID |
spring.ai.alibaba.a2a.nacos.discovery.group |
String | DEFAULT_GROUP | 服务分组 |
spring.ai.alibaba.a2a.nacos.discovery.healthy-only |
Boolean | true | 只返回健康实例 |
spring.ai.alibaba.a2a.nacos.discovery.load-balance-strategy |
String | weighted-random | 负载均衡策略 |
spring.ai.alibaba.a2a.nacos.discovery.cache-enabled |
Boolean | true | 启用缓存 |
spring.ai.alibaba.a2a.nacos.discovery.cache-ttl |
Duration | 30s | 缓存过期时间 |
spring.ai.alibaba.a2a.nacos.discovery.cache-max-size |
Integer | 1000 | 缓存最大条目数 |
spring.ai.alibaba.a2a.nacos.discovery.watch-enabled |
Boolean | true | 监听配置变更 |
spring.ai.alibaba.a2a.nacos.discovery.watch-delay |
Duration | 5s | 监听延迟 |
6.4 网络配置
6.4.1 超时配置
yaml
spring:
ai:
alibaba:
a2a:
client:
# 连接超时
connection-timeout: 10s
# 读取超时
read-timeout: 30s
# 写入超时
write-timeout: 30s
# 总体超时(连接 + 读取)
request-timeout: 40s
6.4.2 连接池配置
yaml
spring:
ai:
alibaba:
a2a:
client:
connection-pool:
# 最大连接数
max-total: 200
# 每个路由的最大连接数
max-per-route: 20
# 连接空闲时间(超过则关闭)
idle-timeout: 30s
# 连接存活时间
time-to-live: 5m
6.4.3 重试配置
yaml
spring:
ai:
alibaba:
a2a:
client:
retry:
enabled: true
max-attempts: 3
# 可重试的异常
retryable-exceptions:
- java.net.ConnectException
- java.net.SocketTimeoutException
- org.springframework.web.client.ResourceAccessException
# 退避策略
backoff:
initial-interval: 1s
multiplier: 2
max-interval: 10s
由于文档非常长,我会继续生成剩余章节。让我继续完成这个详细的 A2A 文档...
7. 服务注册
7.1 自动注册(推荐)
Spring AI Alibaba 提供了自动注册机制,只需配置即可自动注册智能体服务。
7.1.1 配置步骤
步骤 1:添加依赖
xml
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-a2a-nacos</artifactId>
<version>1.1.0.0-RC1</version>
</dependency>
步骤 2:配置文件
yaml
spring:
ai:
alibaba:
a2a:
nacos:
server-addr: 127.0.0.1:8848
registry:
enabled: true
server:
enabled: true
card:
name: my_agent
description: "我的智能体"
version: "1.0.0"
步骤 3:定义智能体
java
@Bean
public ReactAgent myAgent(ChatModel chatModel) {
return ReactAgent.builder()
.name("my_agent") // 名称必须与配置中的 card.name 一致
.model(chatModel)
.build();
}
应用启动后,智能体会自动注册到 Nacos。
7.1.2 自动注册原理
应用启动
↓
Spring Boot 启动完成事件
↓
A2aServerAutoConfiguration 初始化
↓
扫描所有 ReactAgent Bean
↓
为每个 Agent 创建 REST 端点
POST /api/a2a/{agentName}
POST /api/a2a/{agentName}/stream
↓
构造 AgentCard
- name: 从配置或 Bean 获取
- endpoint: http://{ip}:{port}
- description, version 等
↓
注册到 Nacos
NamingService.registerInstance()
↓
启动心跳线程
每 5 秒发送一次心跳
↓
注册完成
7.1.3 验证注册
方式 1:通过 Nacos 控制台
- 访问 http://localhost:8848/nacos
- 进入"服务管理" → "服务列表"
- 查找你的服务名称(如
my_agent) - 查看实例列表和健康状态
方式 2:通过 Nacos Open API
bash
curl "http://localhost:8848/nacos/v1/ns/instance/list?serviceName=my_agent"
响应:
json
{
"name": "DEFAULT_GROUP@@my_agent",
"hosts": [
{
"ip": "192.168.1.10",
"port": 8081,
"healthy": true,
"weight": 1.0,
"metadata": {
"name": "my_agent",
"description": "我的智能体",
"version": "1.0.0"
}
}
]
}
方式 3:通过应用日志
2024-01-20 10:00:15.123 INFO c.a.c.a.a2a.registry.NacosAgentRegistry : Registering agent: my_agent
2024-01-20 10:00:15.456 INFO c.a.c.a.a2a.registry.NacosAgentRegistry : Agent registered successfully: my_agent at http://192.168.1.10:8081
2024-01-20 10:00:15.457 INFO c.a.c.a.a2a.server.A2aServerEndpoint : A2A endpoint created: POST /api/a2a/my_agent
7.2 手动注册
在某些场景下,你可能需要手动控制注册过程。
7.2.1 使用 AgentRegistry
java
@Service
public class ManualRegistrationService {
@Autowired
private AgentRegistry agentRegistry;
@Autowired
private ReactAgent myAgent;
@PostConstruct
public void registerAgent() {
// 构造 AgentCard
AgentCard card = AgentCard.builder()
.name(myAgent.name())
.description("手动注册的智能体")
.version("1.0.0")
.endpoint("http://localhost:8081")
.capabilities(List.of("tool_calling", "streaming"))
.metadata(Map.of(
"team", "ai-team",
"environment", "production"
))
.build();
// 注册
agentRegistry.register(card);
System.out.println("智能体已注册: " + card.getName());
}
@PreDestroy
public void unregisterAgent() {
// 注销
agentRegistry.unregister(myAgent.name());
System.out.println("智能体已注销: " + myAgent.name());
}
}
7.2.2 动态注册和注销
java
@RestController
@RequestMapping("/api/registry")
public class RegistryController {
@Autowired
private AgentRegistry agentRegistry;
@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody AgentCard card) {
try {
agentRegistry.register(card);
return ResponseEntity.ok("注册成功: " + card.getName());
} catch (Exception e) {
return ResponseEntity.status(500).body("注册失败: " + e.getMessage());
}
}
@PostMapping("/unregister/{agentName}")
public ResponseEntity<String> unregister(@PathVariable String agentName) {
try {
agentRegistry.unregister(agentName);
return ResponseEntity.ok("注销成功: " + agentName);
} catch (Exception e) {
return ResponseEntity.status(500).body("注销失败: " + e.getMessage());
}
}
@GetMapping("/status/{agentName}")
public ResponseEntity<Map<String, Object>> getStatus(@PathVariable String agentName) {
boolean registered = agentRegistry.isRegistered(agentName);
return ResponseEntity.ok(Map.of(
"agentName", agentName,
"registered", registered
));
}
}
7.3 AgentCard 详解
7.3.1 完整示例
java
AgentCard card = AgentCard.builder()
// 必需字段
.name("customer_service")
.endpoint("http://192.168.1.10:8081")
// 基本信息
.description("智能客服机器人,提供7x24小时服务")
.version("2.1.0")
// 能力列表
.capabilities(List.of(
"tool_calling", // 支持工具调用
"streaming", // 支持流式响应
"chinese", // 支持中文
"english", // 支持英文
"multimodal", // 支持多模态
"long_context" // 支持长上下文
))
// 扩展元数据
.metadata(Map.of(
// 团队信息
"team", "customer-support",
"owner", "zhangsan@example.com",
"contact", "support@example.com",
// 性能指标
"max_qps", "200",
"avg_latency_ms", "500",
"p99_latency_ms", "2000",
"timeout_seconds", "30",
// 业务信息
"business_domain", "ecommerce",
"supported_channels", "web,mobile,wechat",
// 部署信息
"region", "cn-hangzhou",
"availability_zone", "cn-hangzhou-b",
"environment", "production",
"cluster", "prod-cluster-1",
"instance_type", "8core-16gb",
// 版本信息
"git_commit", "a1b2c3d4",
"build_time", "2024-01-20T10:00:00Z",
"deploy_time", "2024-01-20T12:00:00Z"
))
.build();
7.3.2 Capabilities 使用场景
场景 1:按能力筛选智能体
java
// 查找支持中文的智能体
List<AgentCard> chineseAgents = agentCardProvider.findAgentCards(
card -> card.getCapabilities() != null &&
card.getCapabilities().contains("chinese")
);
// 查找支持流式响应的智能体
List<AgentCard> streamingAgents = agentCardProvider.findAgentCards(
card -> card.getCapabilities() != null &&
card.getCapabilities().contains("streaming")
);
// 查找同时支持中文和工具调用的智能体
List<AgentCard> advancedAgents = agentCardProvider.findAgentCards(
card -> {
List<String> caps = card.getCapabilities();
return caps != null &&
caps.contains("chinese") &&
caps.contains("tool_calling");
}
);
场景 2:客户端能力协商
java
public class CapabilityNegotiationService {
public A2aRemoteAgent selectAgent(List<String> requiredCapabilities) {
// 查找满足所有要求的智能体
List<AgentCard> candidates = agentCardProvider.findAgentCards(
card -> {
List<String> caps = card.getCapabilities();
return caps != null &&
caps.containsAll(requiredCapabilities);
}
);
if (candidates.isEmpty()) {
throw new RuntimeException("没有满足要求的智能体");
}
// 选择第一个候选
AgentCard selected = candidates.get(0);
return A2aRemoteAgent.builder()
.name(selected.getName())
.agentCardProvider(agentCardProvider)
.build();
}
}
// 使用
A2aRemoteAgent agent = service.selectAgent(
List.of("chinese", "tool_calling", "streaming")
);
7.3.3 Metadata 最佳实践
类别 1:性能指标
java
metadata.put("max_qps", "200"); // 最大 QPS
metadata.put("avg_latency_ms", "500"); // 平均延迟
metadata.put("p95_latency_ms", "1000"); // P95 延迟
metadata.put("p99_latency_ms", "2000"); // P99 延迟
metadata.put("timeout_seconds", "30"); // 超时时间
metadata.put("max_concurrent", "100"); // 最大并发数
类别 2:业务信息
java
metadata.put("business_domain", "finance"); // 业务领域
metadata.put("supported_languages", "zh,en"); // 支持的语言
metadata.put("data_classification", "internal"); // 数据分类
metadata.put("sla_level", "gold"); // SLA 等级
类别 3:路由信息
java
metadata.put("region", "cn-hangzhou"); // 地域
metadata.put("availability_zone", "zone-a"); // 可用区
metadata.put("isp", "aliyun"); // 云服务商
metadata.put("weight", "1.0"); // 权重
类别 4:监控和调试
java
metadata.put("log_level", "INFO"); // 日志级别
metadata.put("trace_enabled", "true"); // 是否启用追踪
metadata.put("metrics_endpoint", "/metrics"); // 监控端点
metadata.put("health_endpoint", "/health"); // 健康检查端点
7.4 注册生命周期
7.4.1 生命周期事件
应用启动
↓
┌─────────────────────────────┐
│ BEFORE_REGISTER │ 注册前
│ - 构造 AgentCard │
│ - 验证配置 │
└──────────┬──────────────────┘
↓
┌─────────────────────────────┐
│ REGISTERING │ 注册中
│ - 连接 Nacos │
│ - 注册实例 │
└──────────┬──────────────────┘
↓
┌─────────────────────────────┐
│ REGISTERED │ 已注册
│ - 启动心跳线程 │
│ - 开始接收请求 │
└──────────┬──────────────────┘
│
│ (运行中)
│
↓
┌─────────────────────────────┐
│ BEFORE_UNREGISTER │ 注销前
│ - 停止接收新请求 │
│ - 等待现有请求完成 │
└──────────┬──────────────────┘
↓
┌─────────────────────────────┐
│ UNREGISTERING │ 注销中
│ - 停止心跳线程 │
│ - 从 Nacos 注销实例 │
└──────────┬──────────────────┘
↓
┌─────────────────────────────┐
│ UNREGISTERED │ 已注销
│ - 清理资源 │
│ - 关闭连接 │
└─────────────────────────────┘
7.4.2 监听生命周期事件
java
@Component
public class AgentLifecycleListener {
@EventListener
public void onRegistered(AgentRegisteredEvent event) {
AgentCard card = event.getAgentCard();
System.out.println("智能体已注册: " + card.getName());
System.out.println("端点: " + card.getEndpoint());
// 执行注册后的操作
// 例如:发送通知、记录日志、启动监控等
}
@EventListener
public void onUnregistered(AgentUnregisteredEvent event) {
String agentName = event.getAgentName();
System.out.println("智能体已注销: " + agentName);
// 执行注销后的操作
// 例如:清理缓存、关闭连接、发送通知等
}
@EventListener
public void onRegistrationFailed(AgentRegistrationFailedEvent event) {
System.err.println("注册失败: " + event.getAgentName());
System.err.println("原因: " + event.getCause().getMessage());
// 执行失败处理
// 例如:重试注册、发送告警、记录错误等
}
}
7.4.3 优雅关闭
确保应用关闭时正确注销服务:
java
@Component
public class GracefulShutdown {
@Autowired
private AgentRegistry agentRegistry;
@Autowired
private List<ReactAgent> agents;
@PreDestroy
public void shutdown() {
System.out.println("开始优雅关闭...");
// 1. 停止接收新请求
stopAcceptingNewRequests();
// 2. 等待现有请求完成(最多等待 30 秒)
waitForActiveRequests(30);
// 3. 注销所有智能体
agents.forEach(agent -> {
try {
agentRegistry.unregister(agent.name());
System.out.println("已注销: " + agent.name());
} catch (Exception e) {
System.err.println("注销失败: " + agent.name() + ", " + e.getMessage());
}
});
System.out.println("优雅关闭完成");
}
private void stopAcceptingNewRequests() {
// 实现停止接收新请求的逻辑
}
private void waitForActiveRequests(int maxWaitSeconds) {
// 实现等待现有请求完成的逻辑
}
}
8. 服务发现
8.1 发现机制
8.1.1 发现流程
客户端请求 getAgentCard("math_agent")
↓
┌─────────────────────────────────┐
│ 1. 检查本地缓存 │
│ if (cache.contains(name)) │
│ return cache.get(name) │
└──────────┬──────────────────────┘
↓ (未命中)
┌─────────────────────────────────┐
│ 2. 查询 Nacos │
│ instances = │
│ nacos.getAllInstances(name)│
└──────────┬──────────────────────┘
↓
┌─────────────────────────────────┐
│ 3. 过滤健康实例 │
│ healthy = instances.stream() │
│ .filter(i -> i.isHealthy())│
└──────────┬──────────────────────┘
↓
┌─────────────────────────────────┐
│ 4. 应用负载均衡 │
│ selected = │
│ loadBalancer.select(healthy)│
└──────────┬──────────────────────┘
↓
┌─────────────────────────────────┐
│ 5. 构造 AgentCard │
│ card = buildCard(selected) │
└──────────┬──────────────────────┘
↓
┌─────────────────────────────────┐
│ 6. 缓存结果 │
│ cache.put(name, card, 30s) │
└──────────┬──────────────────────┘
↓
返回 AgentCard
8.1.2 缓存机制
为什么需要缓存?
- 减少 Nacos 查询:避免每次调用都查询 Nacos
- 提高性能:本地缓存查询速度远快于网络请求
- 降低延迟:减少服务发现的延迟
- 减轻 Nacos 压力:降低 Nacos 服务器的负载
缓存配置:
yaml
spring:
ai:
alibaba:
a2a:
nacos:
discovery:
cache-enabled: true
cache-ttl: 30s # 缓存过期时间
cache-max-size: 1000 # 最大缓存条目数
缓存失效策略:
- TTL 过期:缓存条目在 30 秒后自动过期
- 主动刷新:收到 Nacos 推送的变更通知时刷新
- 失败重试:调用失败时尝试刷新缓存
8.2 查询服务
8.2.1 基本查询
java
@Service
public class AgentQueryService {
@Autowired
private AgentCardProvider agentCardProvider;
// 查询单个智能体
public Optional<AgentCard> getAgent(String agentName) {
return agentCardProvider.getAgentCard(agentName);
}
// 列出所有智能体
public List<AgentCard> listAllAgents() {
return agentCardProvider.listAgentCards();
}
// 按条件查找
public List<AgentCard> findAgents(Predicate<AgentCard> filter) {
return agentCardProvider.findAgentCards(filter);
}
}
8.2.2 高级查询
按版本查询:
java
// 查找特定版本
public List<AgentCard> findByVersion(String version) {
return agentCardProvider.findAgentCards(
card -> version.equals(card.getVersion())
);
}
// 查找主版本号匹配的所有版本
public List<AgentCard> findByMajorVersion(String majorVersion) {
return agentCardProvider.findAgentCards(
card -> card.getVersion() != null &&
card.getVersion().startsWith(majorVersion + ".")
);
}
// 查找最新版本
public Optional<AgentCard> findLatestVersion(String agentBaseName) {
return agentCardProvider.listAgentCards().stream()
.filter(card -> card.getName().startsWith(agentBaseName))
.max(Comparator.comparing(card -> parseVersion(card.getVersion())));
}
按能力查询:
java
// 查找具有特定能力的智能体
public List<AgentCard> findByCapability(String capability) {
return agentCardProvider.findAgentCards(
card -> {
List<String> caps = card.getCapabilities();
return caps != null && caps.contains(capability);
}
);
}
// 查找具有多个能力的智能体
public List<AgentCard> findByCapabilities(List<String> requiredCapabilities) {
return agentCardProvider.findAgentCards(
card -> {
List<String> caps = card.getCapabilities();
return caps != null && caps.containsAll(requiredCapabilities);
}
);
}
按元数据查询:
java
// 按团队查找
public List<AgentCard> findByTeam(String team) {
return agentCardProvider.findAgentCards(
card -> team.equals(card.getMetadata().get("team"))
);
}
// 按地域查找
public List<AgentCard> findByRegion(String region) {
return agentCardProvider.findAgentCards(
card -> region.equals(card.getMetadata().get("region"))
);
}
// 按 SLA 等级查找
public List<AgentCard> findBySLA(String slaLevel) {
return agentCardProvider.findAgentCards(
card -> slaLevel.equals(card.getMetadata().get("sla_level"))
);
}
8.2.3 查询优化
批量查询:
java
public Map<String, AgentCard> batchGetAgents(List<String> agentNames) {
return agentNames.stream()
.collect(Collectors.toMap(
name -> name,
name -> agentCardProvider.getAgentCard(name).orElse(null),
(a, b) -> a
));
}
预加载缓存:
java
@Component
public class AgentCacheWarmer {
@Autowired
private AgentCardProvider agentCardProvider;
@EventListener(ApplicationReadyEvent.class)
public void warmupCache() {
System.out.println("预热智能体缓存...");
// 预加载常用智能体
List<String> commonAgents = List.of(
"customer_service",
"math_agent",
"weather_agent"
);
commonAgents.forEach(name -> {
agentCardProvider.getAgentCard(name);
System.out.println("已预加载: " + name);
});
System.out.println("缓存预热完成");
}
}
8.3 负载均衡
8.3.1 负载均衡策略
加权随机(Weighted Random):
根据权重随机选择实例,权重越高被选中的概率越大。
实例 A (权重 3) ───┐
实例 B (权重 2) ───┼→ 选择概率: A=60%, B=40%
│
轮询(Round Robin):
依次选择每个实例,确保流量均匀分配。
请求 1 → 实例 A
请求 2 → 实例 B
请求 3 → 实例 C
请求 4 → 实例 A (循环)
一致性哈希(Consistent Hash):
根据请求的某个属性(如用户 ID)选择实例,确保同一属性的请求总是路由到同一实例。
user_001 → 实例 A
user_002 → 实例 B
user_001 → 实例 A (相同用户)
8.3.2 配置负载均衡
yaml
spring:
ai:
alibaba:
a2a:
nacos:
discovery:
# 负载均衡策略
load-balance-strategy: weighted-random # weighted-random, round-robin, consistent-hash
8.3.3 自定义负载均衡
java
public interface LoadBalancer {
/**
* 从实例列表中选择一个
* @param instances 可用实例列表
* @param context 上下文信息
* @return 选中的实例
*/
Instance select(List<Instance> instances, LoadBalanceContext context);
}
实现自定义负载均衡器:
java
@Component
public class LatencyBasedLoadBalancer implements LoadBalancer {
@Override
public Instance select(List<Instance> instances, LoadBalanceContext context) {
// 选择延迟最低的实例
return instances.stream()
.min(Comparator.comparingInt(this::getLatency))
.orElseThrow(() -> new RuntimeException("没有可用实例"));
}
private int getLatency(Instance instance) {
// 从元数据中获取延迟信息
String latency = instance.getMetadata().get("avg_latency_ms");
return latency != null ? Integer.parseInt(latency) : Integer.MAX_VALUE;
}
}
注册自定义负载均衡器:
java
@Configuration
public class LoadBalancerConfig {
@Bean
public LoadBalancer customLoadBalancer() {
return new LatencyBasedLoadBalancer();
}
}
8.4 健康检查
8.4.1 健康检查机制
心跳检查:
服务实例定期发送心跳给 Nacos:
实例 ──(每 5秒)──> Nacos
Nacos 判断:
- 收到心跳: 标记为健康
- 15 秒未收到: 标记为不健康
- 30 秒未收到: 删除实例
主动健康检查:
Nacos 定期检查实例的健康端点:
Nacos ──(HTTP GET)──> 实例的 /actuator/health
如果返回 200 且 status=UP:
- 标记为健康
否则:
- 标记为不健康
8.4.2 配置健康检查
Spring Boot Actuator 配置:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
yaml
management:
endpoints:
web:
exposure:
include: health,info
endpoint:
health:
show-details: always
health:
nacos:
enabled: true
8.4.3 自定义健康指标
java
@Component
public class AgentHealthIndicator implements HealthIndicator {
@Autowired
private ChatModel chatModel;
@Autowired
private List<ReactAgent> agents;
@Override
public Health health() {
try {
// 检查模型连接
boolean modelHealthy = checkModelConnection();
// 检查智能体状态
Map<String, String> agentStatus = agents.stream()
.collect(Collectors.toMap(
ReactAgent::name,
agent -> "UP"
));
if (modelHealthy) {
return Health.up()
.withDetail("model", "connected")
.withDetail("agents", agentStatus)
.build();
} else {
return Health.down()
.withDetail("model", "disconnected")
.withDetail("agents", agentStatus)
.build();
}
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
private boolean checkModelConnection() {
try {
// 发送一个简单的测试请求
chatModel.call("test");
return true;
} catch (Exception e) {
return false;
}
}
}
8.4.4 故障实例处理
自动摘除不健康实例:
yaml
spring:
ai:
alibaba:
a2a:
nacos:
discovery:
healthy-only: true # 只返回健康实例
手动摘除实例:
java
@RestController
@RequestMapping("/api/management")
public class InstanceManagementController {
@Autowired
private NamingService nacosNamingService;
@PostMapping("/instance/offline")
public ResponseEntity<String> offlineInstance(
@RequestParam String serviceName,
@RequestParam String ip,
@RequestParam int port
) {
try {
// 将实例标记为不健康
Instance instance = nacosNamingService.getInstance(serviceName, ip, port);
instance.setHealthy(false);
nacosNamingService.registerInstance(serviceName, instance);
return ResponseEntity.ok("实例已下线");
} catch (Exception e) {
return ResponseEntity.status(500).body("下线失败: " + e.getMessage());
}
}
@PostMapping("/instance/online")
public ResponseEntity<String> onlineInstance(
@RequestParam String serviceName,
@RequestParam String ip,
@RequestParam int port
) {
try {
// 将实例标记为健康
Instance instance = nacosNamingService.getInstance(serviceName, ip, port);
instance.setHealthy(true);
nacosNamingService.registerInstance(serviceName, instance);
return ResponseEntity.ok("实例已上线");
} catch (Exception e) {
return ResponseEntity.status(500).body("上线失败: " + e.getMessage());
}
}
}
9. 远程智能体调用
9.1 创建远程智能体代理
9.1.1 基础创建
java
@Service
public class RemoteAgentFactory {
@Autowired
private AgentCardProvider agentCardProvider;
public A2aRemoteAgent createRemoteAgent(String agentName) {
return A2aRemoteAgent.builder()
.name(agentName)
.agentCardProvider(agentCardProvider)
.build();
}
}
9.1.2 完整配置
java
A2aRemoteAgent remoteAgent = A2aRemoteAgent.builder()
// 必需配置
.name("math_agent")
.agentCardProvider(agentCardProvider)
// 超时配置
.connectTimeout(Duration.ofSeconds(10)) // 连接超时
.readTimeout(Duration.ofSeconds(30)) // 读取超时
.writeTimeout(Duration.ofSeconds(30)) // 写入超时
// 重试配置
.maxRetries(3) // 最大重试次数
.retryDelay(Duration.ofSeconds(1)) // 重试延迟
// 熔断配置
.circuitBreakerEnabled(true) // 启用熔断
.failureRateThreshold(50) // 失败率阈值 50%
.slowCallDurationThreshold(Duration.ofSeconds(5)) // 慢调用阈值
// 其他配置
.followRedirects(true) // 跟随重定向
.bufferRequestBody(false) // 不缓冲请求体(流式传输)
.build();
9.1.3 使用 Bean 注入
java
@Configuration
public class RemoteAgentConfig {
@Autowired
private AgentCardProvider agentCardProvider;
@Bean
public A2aRemoteAgent mathAgent() {
return A2aRemoteAgent.builder()
.name("math_agent")
.agentCardProvider(agentCardProvider)
.connectTimeout(Duration.ofSeconds(10))
.readTimeout(Duration.ofSeconds(30))
.build();
}
@Bean
public A2aRemoteAgent weatherAgent() {
return A2aRemoteAgent.builder()
.name("weather_agent")
.agentCardProvider(agentCardProvider)
.build();
}
}
// 使用
@Service
public class MyService {
@Autowired
@Qualifier("mathAgent")
private A2aRemoteAgent mathAgent;
@Autowired
@Qualifier("weatherAgent")
private A2aRemoteAgent weatherAgent;
public void useAgents() {
mathAgent.invoke("计算 1 + 1");
weatherAgent.invoke("北京天气");
}
}
9.2 同步调用
9.2.1 简单调用
java
// 最简单的调用
Optional<OverAllState> result = remoteAgent.invoke("你好");
// 获取响应内容
String response = result
.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("未获取到响应");
9.2.2 带配置的调用
java
// 创建配置
RunnableConfig config = RunnableConfig.builder()
.threadId("session_123") // 会话 ID
.metadata(Map.of( // 元数据
"userId", "user_001",
"clientType", "web",
"timestamp", System.currentTimeMillis()
))
.build();
// 调用
Optional<OverAllState> result = remoteAgent.invoke("计算 100 + 200", config);
// 获取完整状态
result.ifPresent(state -> {
// 消息列表
List<Message> messages = state.getMessages();
// 自定义数据
Object customData = state.getValue("custom_key");
// 最后一条消息
Optional<Message> lastMsg = state.getLastMessage();
});
9.2.3 错误处理
java
public String callRemoteAgentSafely(String message) {
try {
Optional<OverAllState> result = remoteAgent.invoke(message);
return result
.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("未获取到响应");
} catch (AgentNotFoundException e) {
// 智能体未找到
return "错误:智能体不存在";
} catch (AgentUnavailableException e) {
// 智能体不可用
return "错误:智能体暂时不可用,请稍后重试";
} catch (A2aTimeoutException e) {
// 超时
return "错误:请求超时";
} catch (A2aException e) {
// 其他 A2A 异常
return "错误:" + e.getMessage();
} catch (Exception e) {
// 未知异常
log.error("调用远程智能体失败", e);
return "系统错误,请联系管理员";
}
}
9.2.4 超时控制
java
// 方式 1:创建时配置
A2aRemoteAgent agent = A2aRemoteAgent.builder()
.name("math_agent")
.agentCardProvider(provider)
.readTimeout(Duration.ofSeconds(30))
.build();
// 方式 2:使用 CompletableFuture 控制总体超时
CompletableFuture<Optional<OverAllState>> future =
CompletableFuture.supplyAsync(() -> agent.invoke(message));
try {
Optional<OverAllState> result = future.get(30, TimeUnit.SECONDS);
// 处理结果
} catch (TimeoutException e) {
future.cancel(true);
System.err.println("调用超时");
}
9.3 异步调用
9.3.1 使用 CompletableFuture
java
// 异步调用
CompletableFuture<Optional<OverAllState>> future =
CompletableFuture.supplyAsync(() ->
remoteAgent.invoke("计算 1 + 1")
);
// 处理结果
future.thenAccept(result -> {
result.flatMap(state -> state.getLastMessage())
.ifPresent(msg -> {
System.out.println("结果: " + msg.getContent());
});
});
// 或者阻塞等待
Optional<OverAllState> result = future.get();
9.3.2 并发调用多个智能体
java
public Map<String, String> callMultipleAgents(String message) {
List<CompletableFuture<Map.Entry<String, String>>> futures = List.of(
CompletableFuture.supplyAsync(() -> {
String response = mathAgent.invoke(message)
.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("");
return Map.entry("math", response);
}),
CompletableFuture.supplyAsync(() -> {
String response = weatherAgent.invoke(message)
.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("");
return Map.entry("weather", response);
}),
CompletableFuture.supplyAsync(() -> {
String response = codeAgent.invoke(message)
.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("");
return Map.entry("code", response);
})
);
// 等待所有完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// 收集结果
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue
));
}
9.3.3 异步回调
java
public void callWithCallback(String message, Consumer<String> callback) {
CompletableFuture.supplyAsync(() -> remoteAgent.invoke(message))
.thenApply(result -> result
.flatMap(state -> state.getLastMessage())
.map(msg -> msg.getContent())
.orElse("未获取到响应"))
.thenAccept(callback)
.exceptionally(throwable -> {
System.err.println("调用失败: " + throwable.getMessage());
callback.accept("错误: " + throwable.getMessage());
return null;
});
}
// 使用
callWithCallback("你好", response -> {
System.out.println("收到响应: " + response);
});
9.4 流式调用
9.4.1 基础流式调用
java
// 创建流
Flux<NodeOutput> stream = remoteAgent.stream("讲一个故事");
// 订阅流
stream.subscribe(
output -> {
// 处理每个输出
output.state().getLastMessage().ifPresent(msg -> {
System.out.print(msg.getContent());
});
},
error -> {
// 处理错误
System.err.println("错误: " + error.getMessage());
},
() -> {
// 完成
System.out.println("\n完成");
}
);
9.4.2 带配置的流式调用
java
RunnableConfig config = RunnableConfig.builder()
.threadId("session_123")
.build();
Flux<NodeOutput> stream = remoteAgent.stream("讲一个故事", config);
stream.subscribe(output -> {
System.out.println("节点: " + output.nodeName());
System.out.println("内容: " + output.state().getLastMessage()
.map(msg -> msg.getContent())
.orElse(""));
});
9.4.3 流式输出到前端
后端代码:
java
@RestController
@RequestMapping("/api/agent")
public class StreamingController {
@Autowired
private A2aRemoteAgent remoteAgent;
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamResponse(@RequestParam String message) {
return remoteAgent.stream(message)
.mapNotNull(output -> output.state().getLastMessage().orElse(null))
.map(msg -> "data: " + msg.getContent() + "\n\n");
}
}
前端代码(JavaScript):
javascript
const eventSource = new EventSource('/api/agent/stream?message=' + encodeURIComponent('讲一个故事'));
eventSource.onmessage = (event) => {
const content = event.data;
// 追加到页面
document.getElementById('output').innerHTML += content;
};
eventSource.onerror = (error) => {
console.error('流式传输错误:', error);
eventSource.close();
};
// 完成后关闭连接
eventSource.addEventListener('done', () => {
eventSource.close();
});
9.4.4 流式调用高级用法
限流:
java
// 限制每秒最多 10 个事件
Flux<NodeOutput> stream = remoteAgent.stream(message)
.limitRate(10);
超时:
java
// 30 秒超时
Flux<NodeOutput> stream = remoteAgent.stream(message)
.timeout(Duration.ofSeconds(30));
重试:
java
// 失败时重试 3 次
Flux<NodeOutput> stream = remoteAgent.stream(message)
.retry(3);
组合多个流:
java
Flux<String> combined = Flux.merge(
agent1.stream("问题 1").map(out -> "Agent1: " + out),
agent2.stream("问题 2").map(out -> "Agent2: " + out),
agent3.stream("问题 3").map(out -> "Agent3: " + out)
);
combined.subscribe(System.out::println);