news 2026/2/22 6:01:55

使用 Java、Spring Boot 和 Spring AI 开发符合 A2A 标准的 AI 智能体

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Java、Spring Boot 和 Spring AI 开发符合 A2A 标准的 AI 智能体

I 智能体指的是一种软件实体,它能够利用自然语言处理、机器学习或推理系统等人工智能技术,自主感知、推理和行动,以实现特定目标。

我为 Telex 开发了一个 AI 智能体,该智能体接收一个正则表达式模式,并就该模式所匹配的字符串类型提供易于理解的解释。开发此智能体的灵感源于我在此之前开发的一个 API(您可以在此处查看该项目),在该 API 中我必须使用正则表达式进行一些自然语言处理。尽管我之前学习过正则表达式,但感觉像是第一次见到它。正则表达式就是这样。因此,当 Telex 为其平台寻求更多 AI 智能体时,我决定开发这个智能体。

以下是我使用 Java、Spring AI 和 Spring Boot 实现它的过程。

初始设置

1. Spring Boot 项目初始化

我使用 Spring 提供的初始化工具来初始化项目。请注意,我在依赖项中包含了 Spring Web 和 Open AI。

初始化 Spring 项目

2. 设置 API 凭证

在我的 application.properties 文件中,我设置了 Spring AI 以使用我的 API 凭证(我的 API 密钥)。我通过 Google AI Studio 获得了一个免费的 Google Gemini API 密钥。我的 application.properties 文件设置如下:

spring.config.import=classpath:AI.properties

spring.application.name=regexplain

spring.ai.openai.api-key = ${GEMINI_API_KEY}

spring.ai.openai.base-url https://generativelanguage.googleapis.com/v1beta/openai

spring.ai.openai.chat.completions-path = /chat/completions

spring.ai.openai.chat.options.model = gemini-2.5-pro

第一行导入了包含我 API 密钥的文件。重要的是不要将您的 API 密钥暴露给公众。该文件与 application.properties 位于同一文件夹中。

3. 首次项目运行

使用我的包管理器(Maven),我安装了所需的依赖项。然后我运行了我的主类,以确保一切正常。如果您到目前为止一切都做对了,您的项目应该可以无错误运行。如果遇到任何错误,请在 Google 上查找解决方法。

A2A 请求和响应模型

在深入实现之前,让我们先谈谈符合 A2A 标准的请求和响应的结构。A2A 协议遵循标准的 JSON-RPC 2.0 结构来处理请求和响应。

所有方法调用都封装在一个请求对象中,其结构如下:

{

"jsonrpc": "2.0",

"method": "String",

"id": "String | Integer",

"params": "Message"

}

响应对象有些类似:

{

"jsonrpc": "2.0",

"id": "String | Integer | null",

"result?": "Task | Message | null",

"error?": "JSONRPCError"

}

响应中的 ID 必须与请求中的 ID 相同。

有关 A2A 协议的更多信息,请查阅 A2A 协议文档。

以上就是请求和响应的通用结构。我开发这个智能体是为了在 Telex 平台上使用,因此我的部分实现可能特定于 Telex。

现在进入实现部分。我创建了一个名为 model 的文件夹,用于存储我的模型。请求模型类 A2ARequest 如下所示:

public class A2ARequest {

private String id;

private RequestParamsProperty params;

public A2ARequest(String id, RequestParamsProperty params) {

this.id = id;

this.params = params;

}

// getters and setters

}

RequestParamsProperty 类代表了 params 中包含信息的结构。它如下所示:

public class RequestParamsProperty {

private HistoryMessage message;

private String messageId;

public RequestParamsProperty(HistoryMessage message, String messageId) {

this.message = message;

this.messageId = messageId;

}

// getters and setter

}

HistoryMessage 类如下所示:

@JsonIgnoreProperties(ignoreUnknown = true)

@JsonInclude(JsonInclude.Include.NON_NULL)

public class HistoryMessage {

private String kind;

private String role;

private List<MessagePart> parts;

private String messageId;

private String taskId;

public HistoryMessage() {}

public HistoryMessage(String role, List<MessagePart> parts, String messageId, String taskId) {

this.kind = "message";

this.role = role;

this.parts = parts;

this.messageId = messageId;

this.taskId = taskId;

}

// getters and setters

}

注解的作用是让 Spring 知道在请求和响应的 JSON 表示中包含什么。如果请求中不存在某个属性,它应该忽略它并在类中将其设置为 null。如果某个属性设置为 null,则不应将其包含在响应中。

MessagePart 类如下所示:

@JsonIgnoreProperties(ignoreUnknown = true)

@JsonInclude(JsonInclude.Include.NON_NULL)

public class MessagePart {

private String kind;

private String text;

private List<MessagePart> data;

public MessagePart(String kind, String text, List<MessagePart> data) {

this.kind = kind;

this.text = text;

this.data = data;

}

// getters and setters

}

以上就是表示从 Telex 接收的请求结构所需的所有类。现在需要为我的响应创建一个模型,以及表示响应所需的所有支持类。

A2AResponse 类:

@JsonInclude(JsonInclude.Include.NON_NULL)

public class A2AResponse {

private final String jsonrpc;

@JsonInclude(JsonInclude.Include.ALWAYS)

private String id;

private Result result;

private CustomError error;

public A2AResponse() {

this.jsonrpc = "2.0";

}

public A2AResponse(String id, Result result, CustomError error) {

this.jsonrpc = "2.0";

this.id = id;

this.result = result;

this.error = error;

}

//getters and setters

}

Result 类:

public class Result {

private String id;

private String contextId;

private TaskStatus status;

private List<Artifact> artifacts;

private List<HistoryMessage> history;

private String kind;

public Result() {}

public Result(String id, String contextId, TaskStatus status, List<Artifact> artifacts, List<HistoryMessage> history, String task) {

this.id = id;

this.contextId = contextId;

this.status = status;

this.artifacts = artifacts;

this.history = history;

this.kind = task;

}

// getters and setters

}

CustomError 类:

public class CustomError {

private int code;

private String message;

private Map<String, String> data;

public CustomError(int code, String message, Map<String, String> data) {

this.code = code;

this.message = message;

this.data = data;

}

// getters and setters

}

TaskStatus 类:

@JsonInclude(JsonInclude.Include.NON_NULL)

public class TaskStatus {

private String state;

private Instant timestamp;

private HistoryMessage message;

public TaskStatus() {}

public TaskStatus(String state, Instant timestamp, HistoryMessage message) {

this.state = state;

this.timestamp = timestamp;

this.message = message;

}

// getters and setters

}

Artifact 类:

public class Artifact {

private String artifactId;

private String name;

private List<MessagePart> parts; // 稍后复查此类型

public Artifact() {}

public Artifact(String artifactId, String name, List<MessagePart> parts) {

this.artifactId = artifactId;

this.name = name;

this.parts = parts;

}

// getters and setters

}

A2A 协议还包含一个称为"智能体卡片"的东西。我也为它创建了一个模型。

public class AgentCard {

private String name;

private String description;

private String url;

private Map<String, String> provider;

private String version;

private Map<String, Boolean> capabilities;

private List<String> defaultInputModes;

private List<String> defaultOutputModes;

private List<Map<String, Object>> skills;

public AgentCard() {

this.provider = new HashMap<>();

this.capabilities = new HashMap<>();

this.skills = new ArrayList<>();

}

// getters and setters

}

模型部分就这些了。继续...

服务类

我的智能体的作用是获取一个正则表达式字符串,然后使用预定义的提示词将其发送到 OpenAI 的 API。服务类负责与 OpenAI 通信,发送提示词并接收响应。我创建了另一个名为 service 的文件夹,我的服务类就放在这里。我是这样编写我的服务类的:

@Service

public class RegExPlainService {

private ChatClient chatClient;

RegExPlainService(ChatClient.Builder chatClientBuilder) {

this.chatClient = chatClientBuilder.build();

}

@Tool(name = "regexplain", description = "An agent that explains what type of string a regex pattern matches")

public String generateResponse(String regex) {

return chatClient

.prompt("Give me a simple explanation of the type of string matched by this regex pattern: %s. No validating statements from you. Just straight to the point".formatted(regex))

.call()

.content();

}

}

@Service 注解允许 Spring Boot 将服务注入到您的控制器中。@Tool 注解将该方法标记为一个智能体工具,如果将来要扩展该智能体以包含该功能,它可以被自主调用。不过目前并不需要它。

控制器

控制器通过 REST API 暴露该智能体。在这个案例中,我有两个端点,一个 GET 端点和一个 POST 端点。我在一个名为 controller 的文件夹中创建了我的控制器。实现如下:

@RestController

public class RegExPlainController {

private final RegExPlainService regexplainService;

@Autowired

RegExPlainController (RegExPlainService regexplainService) {

this.regexplainService = regexplainService;

}

@GetMapping("/a2a/agent/regexplain/.well-known/agent.json")

public ResponseEntity<AgentCard> getAgentCard () {

AgentCard agentCard = new AgentCard();

agentCard.setName("regexplain");

agentCard.setDescription("An agent that provides a simple explanation of the type of string a regex pattern matches");

agentCard.setUrl("regexplain-production.up.railway.app/api");

agentCard.setProvider("Bituan", null); // 假设 setProvider 处理 Map 的填充

agentCard.setVersion("1.0");

agentCard.setCapabilities(false, false, false); // 假设 setCapabilities 处理 Map 的填充

agentCard.setDefaultInputModes(List.of("text/plain"));

agentCard.setDefaultOutputModes(List.of("application/json", "text/plain"));

agentCard.setSkill("skill-001", "Explain Regex", "Provides a simple explanation of the type of string a regex pattern matches",

List.of("text/plain"), List.of("text/plain"), List.of());

return ResponseEntity.ok(agentCard);

}

@PostMapping("/a2a/agent/regexplain")

public ResponseEntity<A2AResponse> explainRegex (@RequestBody A2ARequest request) {

String regexRequest;

String responseText;

// 如果参数无效,返回 403

try {

regexRequest = request.getParams().getMessage().getParts().get(0).getText();

} catch (Exception e) {

CustomError error = new CustomError(-32603, "Invalid Parameter", Map.of("details", e.getMessage()));

A2AResponse errorResponse = new A2AResponse(null, null, error);

return ResponseEntity.status(HttpStatusCode.valueOf(403)).body(errorResponse);

}

// 如果调用服务失败,返回错误 500

try {

responseText = regexplainService.generateResponse(regexRequest);

} catch (Exception e) {

CustomError error = new CustomError(-32603, "Internal Error", Map.of("details", e.getMessage()));

A2AResponse errorResponse = new A2AResponse(null, null, error);

return ResponseEntity.internalServerError().body(errorResponse);

}

// 构建响应

A2AResponse response = new A2AResponse();

response.setId(request.getId());

// 构建响应 -> 构建结果

Result result = new Result();

result.setId(UUID.randomUUID().toString());

result.setContextId(UUID.randomUUID().toString());

result.setKind("task");

// 构建响应 -> 构建结果 -> 构建状态

TaskStatus status = new TaskStatus();

status.setState("completed");

status.setTimestamp(Instant.now());

// 构建响应 -> 构建结果 -> 构建状态 -> 构建消息

HistoryMessage message = new HistoryMessage();

message.setRole("agent");

message.setParts(List.of(new MessagePart("text", responseText, null)));

message.setKind("message");

message.setMessageId(UUID.randomUUID().toString());

// 构建响应 -> 构建结果 -> 构建状态 (续)

status.setMessage(message);

// 构建响应 -> 构建结果 -> 构建工件

List<Artifact> artifacts = new ArrayList<>();

Artifact artifact = new Artifact();

artifact.setArtifactId(UUID.randomUUID().toString());

artifact.setName("regexplainerResponse");

artifact.setParts(List.of(new MessagePart("text", responseText, null)));

artifacts.add(artifact);

// 构建响应 -> 构建结果 -> 构建历史记录

List<HistoryMessage> history = new ArrayList<>();

// 构建响应 -> 构建结果 (续)

result.setStatus(status);

result.setArtifacts(artifacts);

result.setHistory(history);

// 构建响应 (续)

response.setResult(result);

return ResponseEntity.ok(response);

}

}

GET 端点使用的路由路径是 A2A 协议标准中用于获取智能体卡片的部分。智能体卡片是对智能体及其功能的描述。

POST 端点接收一个符合 A2A 标准的请求,执行智能体,然后返回适当的响应。

结论

就是这样。这就是我编写 Regexplain 的过程。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/16 8:34:01

突破性音频AI技术:Step-Audio 2系列重塑智能语音交互新范式

突破性音频AI技术&#xff1a;Step-Audio 2系列重塑智能语音交互新范式 【免费下载链接】Step-Audio-2-mini-Think 项目地址: https://ai.gitcode.com/StepFun/Step-Audio-2-mini-Think 在人工智能浪潮席卷全球的今天&#xff0c;语音交互正成为人机沟通的核心桥梁。St…

作者头像 李华
网站建设 2026/2/19 3:17:20

基于vue的家政服务管理系统_37cw9ju0_springboot php python nodejs

目录具体实现截图项目介绍论文大纲核心代码部分展示项目运行指导结论源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作具体实现截图 本系统&#xff08;程序源码数据库调试部署讲解&#xff09;同时还支持java、ThinkPHP、Node.js、Spring B…

作者头像 李华
网站建设 2026/2/18 6:11:24

《零基础学 PHP:从入门到实战》·PHP编程精进之路:掌握高级特性与实战技巧-1

第1章:面向对象编程进阶 章节介绍 学习目标: 深入掌握PHP面向对象编程(OOP)的核心与高级机制.你将不再满足于创建简单的类,而是学会运用静态成员、继承、多态、抽象与接口来设计松耦合、高复用的架构.本章将解锁"魔术方法"的奥秘,让你能够优雅地处理对象生命周期与动…

作者头像 李华
网站建设 2026/2/20 9:54:37

Step-Audio 2:重新定义人机语音交互的技术革命

当语音助手仍停留在简单问答阶段&#xff0c;当智能设备只能机械执行指令&#xff0c;当跨语言交流仍充满障碍&#xff0c;我们是否在期待一个真正能"听懂"人类声音的AI伙伴&#xff1f;Step-Audio 2系列模型的诞生&#xff0c;正在为这个期待给出肯定答案。 【免费下…

作者头像 李华
网站建设 2026/2/21 22:30:39

AutoGPT与Stable Diffusion联用:图文内容协同生成新玩法

AutoGPT与Stable Diffusion联用&#xff1a;图文内容协同生成新玩法 在内容创作的战场上&#xff0c;效率就是生命线。一条社交媒体推文从构思到发布&#xff0c;往往需要文案、设计师、审核三轮协作&#xff0c;耗时数小时甚至数天。而今天&#xff0c;一个AI系统可以在几分钟…

作者头像 李华
网站建设 2026/2/20 19:14:56

NetSonar:3分钟快速掌握的网络诊断终极方案

NetSonar&#xff1a;3分钟快速掌握的网络诊断终极方案 【免费下载链接】NetSonar Network pings and other utilities 项目地址: https://gitcode.com/gh_mirrors/ne/NetSonar 你是否曾经遇到过这样的困扰&#xff1a;网络突然变慢&#xff0c;却不知道问题出在哪里&am…

作者头像 李华