基于 A2A 协议的生产实践

一、什么是 A2A 协议?

Agent2Agent(A2A)协议是由 Google 于 2025 年发布的一项开放标准,旨在解决 AI 智能体生态中的核心挑战:如何让不同团队、不同技术栈、不同组织开发的 AI 智能体之间进行高效通信与协作。

1.1 A2A 解决的核心问题

假设用户让 AI 助手规划一次国际旅行,这个请求可能需要多个专业智能体协同完成:

  • 航班预订智能体 — 搜索和预订航班
  • 酒店预订智能体 — 管理住宿安排
  • 本地旅游智能体 — 提供景点推荐
  • 财务服务智能体 — 处理货币兑换和理财建议

如果没有统一的通信协议,集成这些多样化的智能体需要大量定制的点对点方案,系统将难以扩展和维护。A2A 正是为此而生。

1.2 A2A 与 MCP 的区别

对比维度 A2A 协议 MCP 协议
核心定位 智能体之间的对等协作 AI 模型到工具/资源的连接
交互特点 有状态、多轮对话、协商式 无状态、单次调用、事务式
应用场景 智能体委托、协作项目管理 函数调用、API 查询、数据检索

一句话总结:MCP 让智能体能用工具,A2A 让智能体之间能对话协作。两者互补而非竞争。


二、A2A 协议核心概念

2.1 协议架构

用户 → A2A Client(客户端智能体)→ A2A Server(远程智能体)→ 执行结果 → 客户端 → 用户
  • A2A Client:代表用户向远程智能体发起请求的应用或智能体
  • A2A Server:实现 A2A 协议 HTTP 端点的 AI 智能体或系统

2.2 核心通信要素

(1)Agent Card — 智能体名片

Agent Card 是一个 JSON 元数据文档,通常发布在 /.well-known/agent.json,描述智能体的完整信息:

{
  "name": "智能翻译助手",
  "description": "专业的多语言翻译服务",
  "url": "https://api.translator.com/a2a",
  "version": "1.0.0",
  "capabilities": {
    "streaming": true,
    "pushNotifications": false
  },
  "skills": [
    {
      "id": "translate",
      "name": "文本翻译",
      "description": "支持中英日韩等多语言互译",
      "inputModes": ["text"],
      "outputModes": ["text"]
    }
  ]
}
(2)Task — 任务

任务是 A2A 协议的核心工作单元,具有完整的生命周期:

submitted → working → completed
                   ↘ failed
            working → input-required → working
(3)Message — 消息
  • 角色区分user(客户端发送)或 agent(服务端发送)
  • 内容载体:包含一个或多个 Part 对象
(4)Part — 内容部件
Part 类型 用途 示例
TextPart 纯文本 指令、问答
FilePart 文件传输 文档、图片
DataPart 结构化数据 JSON 表单、参数
(5)Artifact — 产物

智能体完成任务后生成的输出结果,包含名称、内容部件等信息。

2.3 通信机制

机制 适用场景 技术实现
Request/Response 简单查询、快速任务 HTTP 请求 + 轮询
Streaming 实时更新、增量结果 Server-Sent Events
Push Notifications 长时任务、异步处理 Webhook 回调

三、Java 生产实践

下面通过一个完整的示例,演示如何用 Java + Spring Boot 构建符合 A2A 协议的智能体服务。

3.1 项目结构

a2a-demo/
├── pom.xml
└── src/main/java/com/example/a2a/
    ├── A2aApplication.java
    ├── model/
    │   ├── AgentCard.java
    │   ├── AgentSkill.java
    │   ├── JSONRPCRequest.java
    │   ├── JSONRPCResponse.java
    │   ├── JSONRPCError.java
    │   ├── Message.java
    │   ├── TextPart.java
    │   ├── Task.java
    │   ├── TaskStatus.java
    │   ├── TaskState.java
    │   ├── TaskSendParams.java
    │   └── Artifact.java
    ├── controller/
    │   └── A2AController.java
    ├── client/
    │   └── A2AClient.java
    └── service/
        └── TaskHandlerService.java

3.2 Maven 依赖

<?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.3.0</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>a2a-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

3.3 数据模型定义

TaskState — 任务状态枚举
package com.example.a2a.model;

public enum TaskState {
    SUBMITTED,
    WORKING,
    INPUT_REQUIRED,
    COMPLETED,
    FAILED,
    CANCELED
}
TextPart — 文本内容部件
package com.example.a2a.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TextPart {
    private final String type = "text";
    private String text;

    public TextPart() {}

    public TextPart(String text) {
        this.text = text;
    }
}
Message — 消息
package com.example.a2a.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.List;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Message {
    private String role;        // "user" 或 "agent"
    private List<TextPart> parts;
    private String messageId;

    public Message() {}

    public Message(String role, List<TextPart> parts, String messageId) {
        this.role = role;
        this.parts = parts;
        this.messageId = messageId;
    }
}
TaskStatus — 任务状态
package com.example.a2a.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.time.Instant;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TaskStatus {
    private TaskState state;
    private Instant timestamp;
    private Message message;

    public TaskStatus() {}

    public TaskStatus(TaskState state, Message message) {
        this.state = state;
        this.timestamp = Instant.now();
        this.message = message;
    }
}
Artifact — 产物
package com.example.a2a.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.List;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Artifact {
    private String artifactId;
    private String name;
    private List<TextPart> parts;

    public Artifact() {}

    public Artifact(String artifactId, String name, List<TextPart> parts) {
        this.artifactId = artifactId;
        this.name = name;
        this.parts = parts;
    }
}
Task — 任务
package com.example.a2a.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.List;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Task {
    private String id;
    private String contextId;
    private TaskStatus status;
    private List<Artifact> artifacts;
    private List<Message> history;

    public Task() {}

    public Task(String id, TaskStatus status) {
        this.id = id;
        this.status = status;
    }
}
TaskSendParams — 任务发送参数
package com.example.a2a.model;

import lombok.Data;

@Data
public class TaskSendParams {
    private String id;
    private String contextId;
    private Message message;
}
JSON-RPC 请求与响应
package com.example.a2a.model;

import lombok.Data;

@Data
public class JSONRPCRequest {
    private String jsonrpc = "2.0";
    private String method;
    private Object id;
    private TaskSendParams params;
}
package com.example.a2a.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class JSONRPCResponse {
    private final String jsonrpc = "2.0";
    private Object id;
    private Object result;
    private JSONRPCError error;

    public JSONRPCResponse() {}

    public JSONRPCResponse(Object id, Object result, JSONRPCError error) {
        this.id = id;
        this.result = result;
        this.error = error;
    }
}
package com.example.a2a.model;

import lombok.Data;

@Data
public class JSONRPCError {
    private int code;
    private String message;

    public JSONRPCError() {}

    public JSONRPCError(int code, String message) {
        this.code = code;
        this.message = message;
    }
}
AgentCard & AgentSkill — 智能体名片
package com.example.a2a.model;

import lombok.Data;
import java.util.*;

@Data
public class AgentSkill {
    private String id;
    private String name;
    private String description;
    private List<String> inputModes;
    private List<String> outputModes;

    public AgentSkill(String id, String name, String description) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.inputModes = List.of("text");
        this.outputModes = List.of("text");
    }
}
package com.example.a2a.model;

import lombok.Data;
import java.util.*;

@Data
public class AgentCard {
    private String name;
    private String description;
    private String url;
    private String version;
    private Map<String, Boolean> capabilities;
    private List<AgentSkill> skills;

    public AgentCard() {
        this.capabilities = new HashMap<>();
        this.skills = new ArrayList<>();
    }
}

3.4 任务处理服务

package com.example.a2a.service;

import com.example.a2a.model.*;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class TaskHandlerService {

    /** 内存中存储任务(生产环境应使用 Redis 或数据库) */
    private final Map<String, Task> taskStore = new ConcurrentHashMap<>();

    /**
     * 处理 tasks/send 请求
     */
    public Task handleTaskSend(TaskSendParams params) {
        String taskId = params.getId();
        if (taskId == null || taskId.isEmpty()) {
            taskId = UUID.randomUUID().toString();
        }

        // 1. 提取用户消息
        Message userMessage = params.getMessage();
        String userInput = extractTextFromMessage(userMessage);

        // 2. 执行业务逻辑(这里以文本摘要为例)
        String result = processBusinessLogic(userInput);

        // 3. 构建响应消息
        Message agentMessage = new Message(
                "agent",
                List.of(new TextPart(result)),
                UUID.randomUUID().toString()
        );

        // 4. 构建任务对象
        Task task = new Task();
        task.setId(taskId);
        task.setContextId(params.getContextId());
        task.setStatus(new TaskStatus(TaskState.COMPLETED, agentMessage));
        task.setHistory(List.of(userMessage, agentMessage));
        task.setArtifacts(List.of(
                new Artifact(UUID.randomUUID().toString(), "summary",
                        List.of(new TextPart(result)))
        ));

        // 5. 存储任务
        taskStore.put(taskId, task);
        return task;
    }

    /**
     * 处理 tasks/get 请求 — 查询任务状态
     */
    public Task handleTaskGet(String taskId) {
        return taskStore.get(taskId);
    }

    /**
     * 处理 tasks/cancel 请求 — 取消任务
     */
    public Task handleTaskCancel(String taskId) {
        Task task = taskStore.get(taskId);
        if (task != null) {
            task.setStatus(new TaskStatus(TaskState.CANCELED, null));
        }
        return task;
    }

    /**
     * 业务逻辑处理(示例:文本摘要)
     * 生产环境中可替换为调用 LLM API(如 OpenAI、Gemini 等)
     */
    private String processBusinessLogic(String input) {
        if (input == null || input.isEmpty()) {
            return "未收到有效输入。";
        }
        if (input.length() > 100) {
            return "【摘要】" + input.substring(0, 100) + "...(共 " + input.length() + " 字)";
        }
        return "【处理结果】已收到您的消息:" + input;
    }

    private String extractTextFromMessage(Message message) {
        if (message == null || message.getParts() == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (TextPart part : message.getParts()) {
            if (part.getText() != null) {
                sb.append(part.getText());
            }
        }
        return sb.toString();
    }
}

3.5 A2A 控制器(核心端点)

package com.example.a2a.controller;

import com.example.a2a.model.*;
import com.example.a2a.service.TaskHandlerService;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
public class A2AController {

    private final TaskHandlerService taskHandler;

    public A2AController(TaskHandlerService taskHandler) {
        this.taskHandler = taskHandler;
    }

    /**
     * Agent Card 发现端点
     * 客户端通过 GET /.well-known/agent.json 获取智能体信息
     */
    @GetMapping("/.well-known/agent.json")
    public AgentCard getAgentCard() {
        AgentCard card = new AgentCard();
        card.setName("文本摘要智能体");
        card.setDescription("接收文本内容并生成摘要的 AI 智能体");
        card.setUrl("http://localhost:8080/a2a");
        card.setVersion("1.0.0");
        card.setCapabilities(Map.of(
                "streaming", false,
                "pushNotifications", false
        ));
        card.setSkills(List.of(
                new AgentSkill("summarize", "文本摘要",
                        "接收长文本并生成简洁摘要")
        ));
        return card;
    }

    /**
     * A2A 协议主端点 — 处理所有 JSON-RPC 2.0 请求
     */
    @PostMapping("/a2a")
    public JSONRPCResponse handleA2ARequest(@RequestBody JSONRPCRequest request) {
        String method = request.getMethod();

        try {
            switch (method) {
                case "tasks/send": {
                    Task task = taskHandler.handleTaskSend(request.getParams());
                    return new JSONRPCResponse(request.getId(), task, null);
                }
                case "tasks/get": {
                    String taskId = request.getParams().getId();
                    Task task = taskHandler.handleTaskGet(taskId);
                    if (task == null) {
                        return new JSONRPCResponse(request.getId(), null,
                                new JSONRPCError(-32001, "Task not found: " + taskId));
                    }
                    return new JSONRPCResponse(request.getId(), task, null);
                }
                case "tasks/cancel": {
                    String taskId = request.getParams().getId();
                    Task task = taskHandler.handleTaskCancel(taskId);
                    if (task == null) {
                        return new JSONRPCResponse(request.getId(), null,
                                new JSONRPCError(-32001, "Task not found: " + taskId));
                    }
                    return new JSONRPCResponse(request.getId(), task, null);
                }
                default:
                    return new JSONRPCResponse(request.getId(), null,
                            new JSONRPCError(-32601, "Method not found: " + method));
            }
        } catch (Exception e) {
            return new JSONRPCResponse(request.getId(), null,
                    new JSONRPCError(-32603, "Internal error: " + e.getMessage()));
        }
    }
}

3.6 A2A 客户端实现

package com.example.a2a.client;

import com.example.a2a.model.*;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.net.URI;
import java.net.http.*;
import java.util.*;

/**
 * A2A 协议客户端
 * 用于发现远程智能体、发送任务、查询状态
 */
public class A2AClient {

    private final String serverBaseUrl;
    private final HttpClient httpClient;
    private final ObjectMapper objectMapper;

    public A2AClient(String serverBaseUrl) {
        this.serverBaseUrl = serverBaseUrl;
        this.httpClient = HttpClient.newHttpClient();
        this.objectMapper = new ObjectMapper();
        this.objectMapper.findAndRegisterModules();
    }

    /**
     * 发现远程智能体 — 获取 Agent Card
     */
    public AgentCard discoverAgent() throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(serverBaseUrl + "/.well-known/agent.json"))
                .GET()
                .build();
        HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());
        return objectMapper.readValue(response.body(), AgentCard.class);
    }

    /**
     * 发送任务给远程智能体
     */
    public JSONRPCResponse sendTask(String text) throws Exception {
        // 1. 构建任务参数
        TaskSendParams params = new TaskSendParams();
        params.setId(UUID.randomUUID().toString());
        params.setMessage(new Message(
                "user",
                List.of(new TextPart(text)),
                UUID.randomUUID().toString()
        ));

        // 2. 构建 JSON-RPC 请求
        JSONRPCRequest rpcRequest = new JSONRPCRequest();
        rpcRequest.setMethod("tasks/send");
        rpcRequest.setId(UUID.randomUUID().toString());
        rpcRequest.setParams(params);

        // 3. 发送 HTTP 请求
        String body = objectMapper.writeValueAsString(rpcRequest);
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(serverBaseUrl + "/a2a"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .build();
        HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());

        return objectMapper.readValue(response.body(), JSONRPCResponse.class);
    }

    /**
     * 使用示例
     */
    public static void main(String[] args) throws Exception {
        A2AClient client = new A2AClient("http://localhost:8080");

        // 1. 发现智能体
        AgentCard card = client.discoverAgent();
        System.out.println("发现智能体: " + card.getName());
        System.out.println("描述: " + card.getDescription());
        System.out.println("技能列表: " + card.getSkills());
        System.out.println("---");

        // 2. 发送任务
        JSONRPCResponse response = client.sendTask(
                "A2A协议是Google发布的开放标准,用于实现不同AI智能体之间的通信与协作。"
                + "它基于JSON-RPC 2.0和HTTP(S),支持流式传输和推送通知等企业级特性。"
        );
        System.out.println("任务结果: " + response.getResult());
    }
}

四、生产环境实践要点

4.1 Agent Card 的版本管理

在生产环境中,Agent Card 需要进行版本化管理:

@GetMapping("/.well-known/agent.json")
public AgentCard getAgentCard(@RequestParam(defaultValue = "latest") String version) {
    // 从配置中心动态加载对应版本的 Agent Card
    return agentCardRegistry.getCard(version);
}

4.2 任务持久化

示例中使用了 ConcurrentHashMap 存储任务,生产环境应切换为持久化存储:

@Service
public class PersistentTaskStore {

    @Autowired
    private RedisTemplate<String, Task> redisTemplate;

    private static final String TASK_KEY_PREFIX = "a2a:task:";
    private static final long TASK_TTL_HOURS = 24;

    public void storeTask(Task task) {
        String key = TASK_KEY_PREFIX + task.getId();
        redisTemplate.opsForValue().set(key, task, TASK_TTL_HOURS, TimeUnit.HOURS);
    }

    public Task getTask(String taskId) {
        return redisTemplate.opsForValue().get(TASK_KEY_PREFIX + taskId);
    }
}

4.3 安全认证

生产环境必须加入认证机制,推荐使用 OAuth 2.0 / Bearer Token:

@Component
public class A2AAuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"error\":\"Missing or invalid token\"}");
            return false;
        }
        String token = authHeader.substring(7);
        if (!tokenValidator.validate(token)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return false;
        }
        return true;
    }
}

4.4 Streaming 支持(SSE)

对于长时间运行的任务,应使用 Server-Sent Events 实现流式响应:

@PostMapping(value = "/a2a/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handleStreamingRequest(@RequestBody JSONRPCRequest request) {
    SseEmitter emitter = new SseEmitter(300_000L); // 5 分钟超时

    CompletableFuture.runAsync(() -> {
        try {
            // 发送状态更新:working
            emitter.send(SseEmitter.event()
                    .name("status")
                    .data(new TaskStatus(TaskState.WORKING, null)));

            // 模拟分步处理
            String result = processLongRunningTask(request.getParams());

            // 发送最终结果
            Message agentMsg = new Message("agent",
                    List.of(new TextPart(result)),
                    UUID.randomUUID().toString());
            emitter.send(SseEmitter.event()
                    .name("status")
                    .data(new TaskStatus(TaskState.COMPLETED, agentMsg)));

            emitter.complete();
        } catch (Exception e) {
            emitter.completeWithError(e);
        }
    });

    return emitter;
}

4.5 多智能体编排模式

在真实生产场景中,通常需要一个编排智能体来协调多个专业智能体:

@Service
public class OrchestratorService {

    private final Map<String, A2AClient> agentClients = new HashMap<>();

    @PostConstruct
    public void init() {
        // 注册多个远程智能体
        agentClients.put("translator", new A2AClient("http://translator-agent:8080"));
        agentClients.put("summarizer", new A2AClient("http://summarizer-agent:8081"));
        agentClients.put("analyzer",   new A2AClient("http://analyzer-agent:8082"));
    }

    /**
     * 编排多个智能体完成复杂任务
     * 例如:先翻译 → 再摘要 → 再分析
     */
    public String orchestrate(String input) throws Exception {
        // Step 1: 翻译
        JSONRPCResponse translateResult = agentClients.get("translator").sendTask(input);
        String translated = extractResult(translateResult);

        // Step 2: 摘要
        JSONRPCResponse summaryResult = agentClients.get("summarizer").sendTask(translated);
        String summary = extractResult(summaryResult);

        // Step 3: 分析
        JSONRPCResponse analysisResult = agentClients.get("analyzer").sendTask(summary);
        return extractResult(analysisResult);
    }

    private String extractResult(JSONRPCResponse response) {
        // 从 JSON-RPC 响应中提取文本结果(省略具体实现)
        return response.getResult().toString();
    }
}

五、完整交互流程示例

以下展示一次完整的 A2A 通信过程:

请求:发送任务

curl -X POST http://localhost:8080/a2a \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/send",
    "id": "req-001",
    "params": {
      "id": "task-001",
      "message": {
        "role": "user",
        "messageId": "msg-001",
        "parts": [
          {
            "type": "text",
            "text": "请帮我总结一下A2A协议的核心要点"
          }
        ]
      }
    }
  }'

响应:任务结果

{
  "jsonrpc": "2.0",
  "id": "req-001",
  "result": {
    "id": "task-001",
    "status": {
      "state": "COMPLETED",
      "timestamp": "2025-04-20T10:30:00Z",
      "message": {
        "role": "agent",
        "messageId": "agent-msg-001",
        "parts": [
          {
            "type": "text",
            "text": "【处理结果】已收到您的消息:请帮我总结一下A2A协议的核心要点"
          }
        ]
      }
    },
    "artifacts": [
      {
        "artifactId": "artifact-001",
        "name": "summary",
        "parts": [
          {
            "type": "text",
            "text": "【处理结果】已收到您的消息:请帮我总结一下A2A协议的核心要点"
          }
        ]
      }
    ]
  }
}

六、总结

实践要点 说明
Agent Card 作为智能体的"名片",是服务发现的基础,需版本化管理
JSON-RPC 2.0 统一的通信格式,简化客户端和服务端的集成成本
Task 生命周期 支持有状态的多轮交互,适用于复杂业务场景
Streaming(SSE) 长时任务的实时反馈机制,提升用户体验
安全认证 生产环境必备,推荐 OAuth 2.0 + Bearer Token
多智能体编排 通过编排模式让多个专业智能体协作完成复杂任务

A2A 协议为构建可互操作的 AI 智能体生态提供了坚实基础。结合 Java + Spring Boot 的技术栈,我们可以快速构建生产级别的 A2A 服务,实现智能体之间的无缝协作。


参考资料

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐