使用 Spring AI 创建 MCP 服务器

本文提供了使用 Spring AI 创建模型上下文协议服务器的分步指南,并阐述了使用 MCP 的优势。

在本篇博客中,您将学习如何使用 Spring AI 创建一个模型上下文协议服务器。您将看到创建自己的 MCP 服务器是多么简单。

引言

模型上下文协议(Model Context Protocol)为将大型语言模型(LLM)连接到各种数据源和工具提供了一种标准化的方式。这句话中的"标准化"一词非常重要。这意味着与数据源和工具的集成变得比以往容易得多。除此之外,MCP 服务器通过额外的知识或功能来增强您的 LLM,使其成为一个更强大的助手。想象一下,您可以要求 LLM 为您预订假期。

根据您的偏好,它将在互联网上搜索合适的地点、预订酒店、预订航班等等,然后您就可以出发了!当然,LLM 需要能够为您预订酒店和航班。这种附加功能可以由 MCP 服务器提供。这听起来很吸引人,但也有些令人担忧。为了预订酒店,MCP 服务器需要知道您的个人详细信息并需要访问您的信用卡。这可能不是一个好主意。建议对敏感操作使用人工介入(HITL),以便您可以批准或拒绝某项操作。然而,MCP 服务器将使您的生活变得更加轻松。

在本博客中,您将学习如何使用 Spring Boot 和 Spring AI 创建自己的 MCP 服务器。没有 MCP 客户端,服务器将毫无用处。MCP 客户端与一个或多个 MCP 服务器交互,并且是控制方。作为 MCP 客户端,您将(滥)用 IntelliJ DevoxxGenie 插件。DevoxxGenie 实际上是一个 AI 编码助手,但您也可以使用它来测试您的 MCP 服务器。在下一篇博客中,您将创建自己的 MCP 客户端。

本篇博客中使用的源代码可在 GitHubserver 目录中找到。

先决条件

阅读本博客的先决条件是:

  • 基础的 Java 知识;
  • 基础的 Spring Boot 知识;
  • 基础的 LMStudio 知识;
  • 基础的 IntelliJ 和 DevoxxGenie 知识。

构建 MCP 服务器

您将构建的 MCP 服务器具有以下功能:

  • 返回我喜爱的艺术家列表
  • 返回我喜爱的歌曲列表

LLM 不会拥有关于此信息的任何知识,而当它能够访问这些工具时,希望它会利用它们。本应用深受 Dan Vega 的《使用 Java 创建您的第一个模型上下文协议服务器》的启发。

访问 Spring Initializr 并添加依赖项 Model Context Protocol Server。这将在 pom.xml 中添加以下依赖项。

<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-mcp-server</artifactId>
</dependency>

创建 Artist 的数据模型。

public record Artist(String name) {
}

创建 Song 的数据模型。

public record Song(Artist artist, String title) {
}

创建 ArtistService。该服务将包含一个我喜爱的艺术家列表,但在实际应用中,这些信息将存储在数据库中。通过使用 @Tool 注解来定义一个名为 get_artists 的工具。为工具提供名称和描述。LLM 将使用该描述来了解工具的功能。

@Service
public class ArtistService {

    private final List<Artist> artists = new ArrayList<>();

    @Tool(name = "get_artists", description = "获取 Gunter 喜爱的艺术家完整列表")
    public List<Artist> getArtists() {
        return artists;
    }

    @PostConstruct
    public void init() {
        artists.addAll(List.of(
                new Artist("Bruce Springsteen"),
                new Artist("JJ Johnson")
        ));
    }

}

以类似的方式,创建 SongService

@Service
public class SongService {

    private final List<Song> songs = new ArrayList<>();

    @Tool(name = "get_songs", description = "获取 Gunter 喜爱的歌曲完整列表")
    public List<Song> getSongs() {
        return songs;
    }

    @PostConstruct
    public void init() {
        songs.addAll((List.of(
                new Song(new Artist("Bruce Springsteen"), "My Hometown"),
                new Song(new Artist("JJ Johnson"), "Lament")
        )));
    }

}

您通过一个 Bean 来注册这些工具。

@SpringBootApplication
public class MyMcpServerPlanetApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyMcpServerPlanetApplication.class, args);
    }

    @Bean
    public ToolCallbackProvider mcpServices(ArtistService artistService, SongService songService) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(artistService, songService)
                .build();
    }

}

最后,添加以下 application.properties 配置。

spring.main.web-application-type=none
spring.ai.mcp.server.name=mcp-server
spring.ai.mcp.server.version=0.0.1

spring.main.banner-mode=off
logging.pattern.console=

此配置做了几件重要的事情:

  • 禁用 Web 应用程序:由于 MCP 使用 STDIO 传输,因此不需要 Web 服务器。
  • 设置服务器名称和版本:这用于向客户端标识 MCP 服务器。
  • 禁用横幅和控制台日志记录:这对于 STDIO 传输正常工作至关重要。

构建 jar 文件。

mvn clean verify

这将在 target 目录中生成一个 jar 文件:target/mcp-server-0.0.1-SNAPSHOT.jar

测试 MCP 服务器

为了测试 MCP 服务器,需要一个 MCP 客户端。如前所述,将使用 DevoxxGenie 来实现此目的。

将 MCP 服务器添加到 DevoxxGenie 的 MCP 设置中。请注意,您需要对命令和参数使用完整路径。

  • 名称:MyMcpServerPlanet
  • 传输类型:STDIO
  • 命令/<java installation directory>/bin/java
  • 参数-jar /home/<project directory>/mymcpserverplanet/server/target/mymcpserverplanet-0.0.1-SNAPSHOT.jar

Test MCP Server)

单击"测试连接并获取工具"按钮。按钮的标题应变更为"连接成功!找到 2 个工具"。

与 DevoxxGenie 一起,使用 LMStudio 作为推理引擎,模型 qwen3-8b 在 GPU 上运行。

输入提示:给我一个 Gunter 喜爱的艺术家列表。

该请求被发送到 LMStudio,可用的工具也随请求一起发送。如果您在 LMStudio 中启用了调试日志,可以检查该请求。

2025-07-20 11:45:42 [DEBUG]
 Received request: POST to /v1/chat/completions with body  {
  "model": "qwen3-8b",
  "messages": [
    {
      "role": "system",
      "content": "You are a software developer IDEA plugin with expe... <日志中已截断> ...at is correct and relevant to the code or plugin.\n"
    },
    {
      "role": "user",
      "content": "<ProjectPath>\n/home/<project directory>... <日志中已截断> ... list of gunter's favorite artists\n</UserPrompt>\n\n"
    }
  ],
  "temperature": 0,
  "top_p": 0.9,
  "stream": false,
  "max_tokens": 8000,
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_songs",
        "description": "获取 Gunter 喜爱的歌曲完整列表",
        "parameters": {
          "type": "object",
          "properties": {},
          "required": []
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "get_artists",
        "description": "获取 Gunter 喜爱的艺术家完整列表",
        "parameters": {
          "type": "object",
          "properties": {},
          "required": []
        }
      }
    }
  ]
}

LLM 将意识到它自己无法回答这个问题。然而,LLM 确实识别出有一个可用的工具可以回答这个问题。因此,LLM 将请求客户端调用 MCP 工具。DevoxxGenie 内置了人工介入机制,并将请求您的批准。
点击批准按钮

点击"批准"按钮。MCP 服务器将执行 get_artists 工具,并将响应发送回 LLM。LLM 将使用此响应来给出适当的答案。

收到以下响应,并且完全正确。观察 LLM 的思维过程也很有趣。

<think> 好的,用户要求提供 Gunter 喜爱的艺术家列表。我调用了 `get_artists` 函数,返回了两位艺术家:Bruce Springsteen 和 JJ Johnson。现在他们可能想知道是否还有更多或者需要进一步帮助。

我应该确认结果,也许可以询问他们是否还需要其他什么。由于之前的响应是成功的,保持积极的语气很好。工具调用没有错误,所以这里不需要额外的函数调用。 </think>

以下是 Gunter 喜爱的艺术家:

*   **Bruce Springsteen**
*   **JJ Johnson**

如果您需要更多信息或帮助,请告诉我!

让我们使用以下提示检查 get_songs 工具:给我一个 Gunter 喜爱的歌曲列表

遵循相同的过程,响应再次正确。

<think> 好的,用户要求提供 Gunter 喜爱的歌曲列表。我使用了 get_songs 函数,它检索到了两首歌曲。现在我需要清晰地呈现这些信息。让我用艺术家和歌名来格式化每首歌曲。确保响应易于阅读,也许用项目符号。检查是否需要任何额外信息,但既然用户只想要一个列表,保持简单应该是最好的。 </think>

以下是 Gunter 喜爱的歌曲列表:

1.  **Bruce Springsteen** - *《My Hometown》*
2.  **JJ Johnson** - *《Lament》*

如果您想了解这些曲目的更多详细信息,请告诉我!

添加搜索功能

让我们向两个 MCP 工具添加一些基本的搜索功能。

ArtistService 添加一个 search_artist 工具。

@Tool(name = "search_artist", description = "从 Gunter 喜爱的艺术家中搜索单个艺术家")
public Artist getArtist(String name) {
    return artists.stream()
            .filter(artist -> artist.name().equalsIgnoreCase(name))
            .findFirst()
            .orElse(null);
}

SongService 添加一个 search_song 工具。

@Tool(name = "search_song", description = "从 Gunter 喜爱的歌曲中搜索单首歌曲")
public Song getSong(String title) {
    return songs.stream()
            .filter(song -> song.title().equalsIgnoreCase(title))
            .findFirst()
            .orElse(null);
}

构建 jar 文件并使用 DevoxxGenie 获取新工具。

输入以下提示:Bruce Springsteen 是 Gunter 喜爱的艺术家之一吗?

这次调用了 search_artist 工具,响应再次正确。

<think> 好的,用户询问 Bruce Springsteen 是否是 Gunter 喜爱的艺术家之一。我使用 search_artist 函数并传入了他的名字。响应返回了艺术家 "Bruce Springsteen"。现在我需要检查他是否在 Gunter 的喜爱列表中。

等等,可用的函数是 get_artists 和 search_artist。search_artist 函数可能检查艺术家是否存在于数据库中。既然响应返回了该艺术家,那意味着 Bruce Springsteen 确实在 Gunter 喜爱的艺术家列表中。

所以答案应该是肯定的。我应该根据函数的响应来确认这一点。既然用户的问题已经得到回答,不需要进一步的操作。只需清晰地呈现结果即可。 </think>

是的,Bruce Springsteen 是 Gunter 喜爱的艺术家之一。搜索确认了他在收藏中。

输入以下提示:滚石乐队是 Gunter 喜爱的艺术家之一吗?

调用了 search_artist 工具,由于未找到滚石乐队,LLM 断定他们不在我喜爱的艺术家列表中。

<think> 好的,让我试着弄清楚这里发生了什么。用户询问滚石乐队是否是 Gunter 喜爱的艺术家之一。我使用 search_artist 函数,传入了名称 "Rolling Stones",但响应是 null。这意味着该函数在 Gunter 的喜爱列表中没有找到该名称的艺术家。

嗯,也许数据库中艺术家的名称不完全是 "Rolling Stones"。会不会有拼写错误或不同的拼写方式?或者也许他们在系统中以另一个名称而闻名。或者,也许是因为数据不存在,所以函数没有返回任何结果。

既然用户是在询问 Gunter 的喜爱列表,我应该检查是否有一种方法可以首先列出所有喜爱的艺术家。get_artists 函数可能会有所帮助。如果我调用它,也许我可以看到滚石乐队是否在列表中。但是等等,用户可能不想要所有艺术家,只是确认这一个特定的。

或者,也许 search_artist 函数需要不同的参数。让我再次检查工具定义。search_artist 函数需要一个 "name" 参数,这是必需的。那么查询应该是大小写不敏感的?或者可能是部分匹配?用户可能指的是 "Rolling Stones",但数据库中的存储方式不同。

另一个想法:也许滚石乐队不在 Gunter 喜爱的艺术家列表中。搜索返回了 null,所以可能他们不在。但我应该通过先检查所有艺术家来确认。我应该向用户建议这一点。等等,但用户是专门询问滚石乐队。也许我应该告知他们搜索没有找到任何结果,并询问他们是否想改为检查所有艺术家。

等等,响应是 null,所以函数没有返回任何数据。这意味着该艺术家不在列表中。所以答案应该是否定的,滚石乐队不是 Gunter 喜爱的艺术家之一。但是函数调用会不会有错误?让我再检查一遍。函数是 search_artist,名称是 "Rolling Stones"。如果数据库中有确切的名称,那么它应该返回。如果没有,则返回 null。

所以结论是滚石乐队不在 Gunter 的喜爱列表中。或者,可能存在拼写错误。但既然用户是直接询问,我应该根据函数的响应来回答。 </think>

在 Gunter 喜爱的艺术家列表中,对艺术家 "Rolling Stones" 的搜索未返回任何结果。这表明滚石乐队**目前并未**列为 Gunter 喜爱的艺术家之一。您想改为查看他喜爱的艺术家的完整列表吗?

结论

在本博客中,您学习了如何使用 Spring Boot 和 Spring AI 创建一个 MCP 服务器。创建 MCP 服务器和添加工具非常容易。您可以专注于工具的功能,而这正是您想要的。


【注】本文译自:Creating an MCP Server With Spring AI

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

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

我为 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 的过程。

通过这个示例,您可以从头开始构建您的 AI 智能体并使其符合 A2A 标准。或者,至少我希望这能让您对如何使用 Java 开发符合 A2A 标准的 AI 智能体有所了解。


【注】本文译自:Developing an A2A-compliant AI Agent with Java, Spring Boot and Spring AI – DEV Community

构建可用于生产环境的AI智能体

围绕AI智能体的炒作确实存在,但让我们拨开迷雾,直面实质。在过去六个月中,我致力于构建并部署用于生产环境的AI智能体,并深刻认识到演示系统与可用于生产环境的系统之间存在着巨大差距。本指南将引导您构建真正能在现实世界中工作的AI智能体,而不仅仅是在您的本地环境中运行。

作为一位深耕AI微调大语言模型部署领域的人,我可以告诉您,构建智能体所需的心态与传统软件开发截然不同。

AI智能体究竟是什么?

在深入技术细节之前,我们先明确讨论的对象。AI智能体是一种自主系统,它能够感知环境、做出决策并采取行动以实现特定目标。与仅响应查询的传统聊天机器人不同,AI智能体能够:

  • 将复杂任务分解为子任务
  • 自主使用工具和API
  • 在多次交互中保持上下文
  • 从反馈中学习并随时间改进

可以将它们视为能够处理整个工作流程的智能工作者,而不仅仅是单个任务。这与我们一直在大语言模型中使用的传统提示工程方法有着根本的不同。

AI智能体的商业价值

根据麦肯锡2025年报告,部署AI智能体的公司实现了:

  • 运营成本降低40%
  • 任务完成速度提升3倍
  • 客户满意度得分提高60%

但问题是:只有15%的AI智能体项目能够成功进入生产环境。为什么?因为大多数团队低估了构建可靠、可扩展的智能体系统的复杂性。正如我在关于AI对劳动力动态影响的文章中所讨论的,这项技术具有变革性,但需要谨慎实施。

实践证明有效的架构

在尝试了各种方法之后,以下是经过生产环境验证最为可靠的架构:

核心组件

组件 用途 关键考量因素
编排层 管理智能体生命周期、处理重试、记录交互 必须容错、支持异步操作
规划模块 将复杂任务分解为可执行步骤 需要处理模糊性、验证可行性
执行引擎 运行单个动作、管理状态 错误处理至关重要、需实现超时机制
记忆系统 存储上下文、过往交互、学习到的模式 考虑使用向量数据库进行语义搜索
工具层 与外部API、数据库、服务交互 实施适当的身份验证、速率限制

为何选择此架构?

这种模块化方法使您能够:

  1. 独立扩展 – 每个组件可根据负载独立扩展
  2. 优雅降级 – 局部故障不会导致整个系统瘫痪
  3. 快速迭代 – 更新组件而无需重建所有内容
  4. 有效监控 – 清晰的边界使调试更容易

这类似于我在关于模型上下文协议 的指南中概述的原则,其中结构化的上下文管理是可扩展AI系统的关键。

构建您的第一个生产级智能体

让我们一步步构建一个真实的智能体,它能够分析GitHub仓库并生成技术文档。这不是一个玩具示例——它基于一个当前在生产环境中运行、每日处理超过1000个仓库的系统。

步骤1:明确界定能力范围

团队最常犯的错误是试图构建无所不能的智能体。请从聚焦开始:

class AgentCapabilities:
    """定义您的智能体能做什么"""
    name: str = "github_analyzer"
    description: str = "分析GitHub仓库并生成文档"
    tools: List[str] = [
        "fetch_repo_structure",
        "analyze_code_quality", 
        "generate_documentation"
    ]
    max_iterations: int = 10  # 防止无限循环
    memory_window: int = 2000  # 要记住的令牌数

步骤2:实施健壮的错误处理

这是大多数教程未能覆盖的地方。在生产环境中,任何可能出错的地方都终将出错。以下是您需要处理的情况:

错误类型 发生频率 影响程度 解决方案
API速率限制 每日 实现指数退避、队列管理
网络超时 每小时 设置积极的超时时间,使用断路器进行重试
无效响应 常见 验证所有响应,制定回退策略
上下文溢出 每周 实施上下文修剪、摘要
无限循环 罕见 严重 循环检测、最大迭代次数限制

步骤3:记忆与上下文管理

没有记忆的智能体只不过是花哨的API包装器。一个生产级的记忆系统需要:

  1. 短期记忆 – 当前任务上下文(Redis,内存缓存)
  2. 长期记忆 – 学习到的模式和成功策略(PostgreSQL,向量数据库)
  3. 情景记忆 – 过去的交互及其结果(时间序列数据库)

这种方法建立在我MCP架构指南中详细介绍的上下文管理策略之上。

规划模块:智能所在之处

规划模块是真正智能体与简单自动化之间的区别所在。一个好的规划器:

  • 将任务分解为具体、可实现的步骤
  • 识别步骤间的依赖关系
  • 在步骤失败时提供回退选项
  • 估算资源需求(时间、API调用、成本)

有效的规划策略

策略 适用场景 优点 缺点
线性规划 简单、顺序性任务 易于调试、可预测 无法处理复杂依赖关系
分层规划 复杂、多层次任务 能很好地处理复杂性 实现难度较大
自适应规划 不确定环境 能从经验中学习 需要更多数据
混合规划 大多数生产场景 平衡各种方法 架构更复杂

工具集成:智能体的双手

工具是智能体与世界交互的方式。常见的工具类别包括:

  • 数据检索 – API、数据库、网络爬虫
  • 数据处理 – 分析、转换、验证
  • 外部操作 – 发送邮件、创建工单、更新系统
  • 监控 – 检查状态、验证结果

工具设计最佳实践

  • 保持工具原子性 – 每个工具应专注于做好一件事
  • 优雅地处理错误 – 返回结构化的错误信息
  • 实现超时机制 – 任何操作都不应无限期运行
  • 记录一切 – 调试时将需要这些日志
  • 对工具进行版本控制 – API会变化,您的工具也应如此

部署策略

将智能体投入生产环境需要仔细考量。根据我大规模部署LLM的经验,基础设施的选择至关重要。

部署方案比较

方法 适用场景 可扩展性 成本 复杂度
无服务器 偶发性工作负载 自动扩展 按使用付费
容器 稳定工作负载 手动/自动 可预测
托管服务 快速部署 有限 较高
混合 复杂需求 灵活 可变 非常高

关键的部署考量因素

  • API密钥管理 – 使用密钥管理服务(AWS Secrets Manager, HashiCorp Vault)
  • 速率限制 – 在多个层级实施(API、用户、全局)
  • 监控 – 实时仪表板是必不可少的
  • 回滚策略 – 您将需要进行回滚,请提前规划
  • 成本控制 – 设定API支出的硬性限制

监控与可观测性

无法衡量,就无法改进。必要的指标包括:

关键绩效指标

指标 说明 告警阈值
任务成功率 整体可靠性 < 95%
平均执行时间 性能退化 > 2倍基线值
单任务成本 经济可行性 > $0.50
按工具分类的错误率 问题组件 > 5%
内存使用率 资源效率 > 80%
队列深度 容量问题 > 1000个任务

可观测性技术栈

一个生产级的智能体系统需要:

  • 指标 – Prometheus + Grafana 用于实时监控
  • 日志 – 带有关联ID的结构化日志
  • 追踪 – OpenTelemetry 用于分布式追踪
  • 告警 – PagerDuty 用于关键问题

现实世界的陷阱与解决方案

1. 上下文窗口问题

  • 挑战:随着对话增长,您会触及LLM的上下文限制。
  • 解决方案:实施智能上下文修剪:
    • 总结较早的交互
    • 仅保留相关信息
    • 对长期记忆使用高级检索模式

2. 成本爆炸

  • 挑战:一个失控的智能体在3小时内消耗了10,000美元的API积分。
  • 解决方案:实施多重保障措施:
    • 每小时/每日的硬性成本限制
    • 昂贵操作的审批流程
    • 带有自动关闭功能的实时成本监控
      这一点在我分析算法交易系统时探讨的AI经济学中尤为重要。

3. 幻觉问题

  • 挑战:智能体基于幻觉信息自信地执行错误操作。
  • 解决方案
    • 执行前验证所有智能体输出
    • 实施置信度评分
    • 关键操作需要人工批准

4. 规模化性能

  • 挑战:能为10个用户工作的系统在1000个用户时失败。
  • 解决方案
    • 实施适当的队列机制(RabbitMQ, AWS SQS)
    • 对数据库使用连接池
    • 积极但智能地进行缓存

投资回报率与业务影响

让我们谈谈数字。以下是我们跨部署观察到的情况:

典型的投资回报时间线

月份 投资 回报 累计投资回报率
1-2 $50,000 $0 -100%
3-4 $30,000 $40,000 -50%
5-6 $20,000 $80,000 +20%
7-12 $60,000 $360,000 +180%

AI智能体表现出色的领域

  • 客户支持 – 响应时间减少70%
  • 数据分析 – 洞察生成速度提升10倍
  • 内容生成 – 输出量增加5倍
  • 流程自动化 – 手动任务减少90%

这些影响与我在分析AI经济影响时所讨论的内容一致,即自动化能带来显著的生产力提升。

安全考量

安全常被事后考虑,但不该如此。正如我在黑帽SEO分析中所述,了解攻击向量对于防御至关重要。

基本安全措施

层级 威胁 缓解措施
输入 提示注入 输入验证、沙箱
处理 数据泄露 加密、访问控制
输出 有害操作 操作审批、速率限制
存储 数据泄露 静态加密、审计日志
网络 中间人攻击 全程TLS、证书固定

入门:您的30天路线图

第1周:基础

  • 精确界定您的用例
  • 设置开发环境
  • 构建一个简单的原型

第2周:核心开发

  • 实现具有2-3个工具的基本智能体
  • 添加错误处理和日志记录
  • 创建初始测试套件

第3周:生产就绪

  • 添加监控和可观测性
  • 实施安全措施
  • 对系统进行压力测试

第4周:部署

  • 部署到预生产环境
  • 与有限用户进行试点运行
  • 收集反馈并迭代

选择正确的工具

AI智能体生态系统正在蓬勃发展。以下是选择方法:

框架比较

框架 最适合 学习曲线 生产就绪 成本
LangChain 快速原型开发 免费
CrewAI 多智能体系统 新兴 免费
AutoGPT 自主智能体 免费
自定义 特定需求 非常高 视情况而定 开发成本

LLM提供商比较

提供商 优势 劣势 成本(每百万令牌)
OpenAI GPT-4 整体质量最佳 昂贵、速率限制 $30-60
Anthropic Claude 非常适合分析 可用性有限 $25-50
Google Gemini 多模态能力 较新、验证较少 $20-40
开源模型 完全控制、无限制 需要基础设施 仅基础设施成本

有关详细实施指南,请查阅我关于微调LLM使用Hugging Face托管模型的文章。

面向未来的智能体系统

AI领域每周都在变化。请以应对变化为目标进行构建:

  • 抽象化LLM提供商 – 不要硬编码到某一个提供商
  • 对提示进行版本控制 – 它们也是代码,请同样对待
  • 为多模态做准备 – 未来的智能体将能看、听、说
  • 内置学习循环 – 智能体应能随时间改进
  • 为监管做准备 – AI治理即将到来

这与我LLM引导指南中概述的策略一致,其中适应性是长期成功的关键。

结论

构建可用于生产环境的AI智能体充满挑战,但也回报丰厚。关键在于从简单开始,快速失败,并根据现实世界的反馈进行迭代。请记住:

  • 完美是优秀的敌人 – 先交付一个可用的东西,然后再改进
  • 监控一切 – 您无法修复看不见的问题
  • 为失败做好计划 – 失败终会发生,请做好准备
  • 聚焦价值 – 技术是手段,而非目的

在未来12-18个月内掌握AI智能体的公司将会获得显著的竞争优势。问题不在于是否要构建AI智能体,而在于您能以多快的速度将它们投入生产环境。


【注】本文译自:How to Build AI Agents (Complete 2025 Guide) – Superprompt.com

如何构建 AI 智能体(2025 完全指南)

🎯内容提要

AI 智能体是能够自主决策并采取行动以完成任务的系统。与聊天机器人不同,它们不遵循预定义的工作流程——它们会进行推理、规划、使用工具并动态适应。本指南将通过真实示例和代码,向你具体展示如何使用如 LangChain 和 AutoGen 等现代框架来构建可工作的智能体。


2025 年正被誉为"AI 智能体之年",其在企业中的应用正在加速。微软 CEO 萨提亚·纳德拉称其为一根本性转变:"请将智能体视为 AI 时代的应用。"但问题在于——大多数教程向你展示的都是伪装成智能体的聊天机器人,或者更糟的是,那些在演示中有效但在生产环境中失败的复杂系统。
在构建了多个生产级智能体并分析了最新框架之后,我将确切地向你展示如何创建真正有效的 AI 智能体。不掺水分,不搞噱头——只有由真实代码和经过验证的架构支持的实践实现细节。

AI 智能体与聊天机器人有何不同?

让我们立刻澄清这一点。智能体没有预定义的工作流程——它不仅仅是遵循第一步、第二步、第三步。相反,它会在不确定的步骤数量中动态做出决策,并根据需要进行调整。

特性 传统聊天机器人 AI 智能体
决策能力 遵循预定义规则 自主决策
工作流程 固定的、线性的步骤 动态的、自适应的规划
记忆 仅限于会话 跨任务持久化
工具使用 无或硬编码 动态选择和使用工具
错误处理 失败或请求帮助 尝试替代方法

真实示例: 要求一个聊天机器人"预订下周二飞往纽约的航班",它要么会失败,要么会向你询问更多信息。而一个智能体会检查你的日历、搜索航班、比较价格,甚至处理预订——根据发现的情况调整其方法。

每个 AI 智能体所需的 5 个核心组件

基于广泛的研究和生产部署,每个可工作的 AI 智能体都需要以下五个组件:

1. 大语言模型 – 大脑

LLM 充当推理引擎。在 2025 年,你有多种优秀选择(参见我们的详细比较):

  •   Claude 4 Opus: 最适合复杂推理和扩展思考
  •   GPT-4.1: 在编码和工具使用方面表现出色,拥有 100 万令牌上下文
  •   Gemini 2.5 Pro: 强大的多模态能力

💡 专业提示: 不要默认使用最昂贵的模型。对于智能体任务,每百万令牌 2 美元的 GPT-4.1-mini 通常表现不俗,尤其是在结合良好提示的情况下。

2. 记忆系统 – 上下文

由于 LLM 默认是无状态的,你需要管理它们的历史和上下文。现代框架提供几种记忆类型:

# 示例:LangChain 记忆实现
from langchain.memory import ConversationSummaryBufferMemory
memory = ConversationSummaryBufferMemory(
    llm=llm,
    max_token_limit=2000,
    return_messages=True
)
# 智能体现在可以跨多次交互进行记忆

记忆类型:

  •   缓冲区记忆: 存储原始对话历史
  •   摘要记忆: 压缩长对话
  •   实体记忆: 跟踪特定实体及其属性
  •   知识图谱记忆: 构建概念间的关系

3. 工具 – 双手

工具允许你的智能体与外部世界交互。正确的工具配置与提示工程同等重要。

# 示例:定义一个用于网络搜索的工具
from langchain.tools import Tool
def search_web(query: str) -> str:
    """搜索网络以获取最新信息。"""
    # 此处为实现代码
    return search_results
web_search_tool = Tool(
    name="WebSearch",
    func=search_web,
    description="搜索网络以获取最新信息。当你需要最新数据时使用。"
)

⚠️ 关键点: 你的工具描述直接影响智能体性能。要具体说明何时以及如何使用每个工具。模糊的描述会导致工具选择不当。

4. 规划系统 – 策略

智能体必须能够提前规划和思考。2025 年最成功的方法是 ReAct 范式(推理 + 行动)

# ReAct 风格智能体循环
while not task_complete:
    # 1. 观察当前状态
    observation = get_current_state()
   
    # 2. 思考下一步行动
    thought = llm.think(f"给定 {observation},我下一步该做什么?")
   
    # 3. 决定行动
    action = llm.decide_action(thought, available_tools)
   
    # 4. 执行行动
    result = execute_action(action)
   
    # 5. 反思结果
    reflection = llm.reflect(result)
   
    # 更新状态并继续

5. 执行循环 – 引擎

执行循环负责协调一切。现代框架以不同方式处理此问题:

  •   LangChain/LangGraph: 使用基于图的执行模型
  •   AutoGen: 实现事件驱动的参与者模型
  •   CrewAI: 专注于基于角色的智能体协作

逐步指南:构建你的第一个可工作智能体

让我们构建一个能够研究主题并撰写报告的实用智能体。此示例展示了所有五个核心组件的实际运作。

步骤 1:设置环境

# 安装所需的包
pip install langchain langchain-openai tavily-python
# 设置环境变量
export OPENAI_API_KEY="你的密钥"
export TAVILY_API_KEY="你的密钥"

步骤 2:初始化核心组件

from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain.memory import ConversationBufferMemory
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain.tools import Tool
from langchain import hub
# 1. 初始化 LLM
llm = ChatOpenAI(model="gpt-4.1-mini", temperature=0)
# 2. 设置记忆
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)
# 3. 配置工具
search = TavilySearchResults(max_results=5)
tools = [
    Tool(
        name="Search",
        func=search.run,
        description="搜索关于任何主题的最新信息。返回相关结果。"
    )
]
# 4. 加载 ReAct 提示(处理规划)
prompt = hub.pull("hwchase17/react")

步骤 3:创建智能体

# 创建 ReAct 智能体
agent = create_react_agent(
    llm=llm,
    tools=tools,
    prompt=prompt
)
# 5. 设置执行循环
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    memory=memory,
    verbose=True,  # 查看智能体的思考过程
    handle_parsing_errors=True,
    max_iterations=10  # 防止无限循环
)

步骤 4:运行你的智能体

# 示例:研究并报告 AI 智能体
result = agent_executor.invoke({
    "input": "研究 2025 年 7 月 AI 智能体的最新发展,并撰写一份简要报告,重点介绍前 3 大趋势。"
})
print(result["output"])

成功关键: 该智能体将自主搜索多次,综合信息,并生成连贯的报告。它并非遵循脚本——而是根据发现的内容动态决定搜索什么。

真正提升性能的高级技巧

在分析了数千次智能体交互后,以下是真正能提高智能体性能的技巧:

1. 问题分解优于角色扮演

  •   无效的方法: 角色提示(例如,"你是一位专家研究员……")对准确性影响甚微或没有影响。
  •   有效的方法: 要求智能体将问题分解为子任务:
decomposition_prompt = """
    将此任务分解为更小的步骤:
      1. 首先,识别关键组成部分
      2. 然后,分别处理每个组成部分
      3. 最后,综合结果
    任务:{task}
"""

2. 自我批评与反思

添加自我批评步骤能显著提高输出质量:

reflection_prompt = """
审查你之前的回应并识别:
1. 任何逻辑错误或不一致之处
2. 遗漏的重要信息
3. 可以更清晰的领域
之前的回应:{response}
"""

3. 上下文重于指令

上下文的重要性被严重低估。仅仅提供更多相关的背景信息,比复杂的提示技术更能提高性能:

# 效果较差
prompt = "写一份关于 AI 智能体的报告"
# 效果更好
prompt = """写一份关于 AI 智能体的报告。
上下文:AI 智能体是可以规划并执行任务的自主系统。
它们与聊天机器人的不同之处在于做出动态决策而非遵循脚本。
关键框架包括 LangChain、AutoGen 和 CrewAI。
该报告面向熟悉 AI 概念的技术读者。
"""

导致 AI 智能体失效的常见错误

以下是我反复看到的常见错误:

1. 无限循环且无限制

 始终设置 max_iterations: 智能体可能陷入循环。设置合理的限制并实现超时处理。

2. 工具描述不清

# 差:描述模糊
Tool(name="search", description="搜索东西")
# 好:包含用例的具体描述
Tool(
    name="WebSearch",
    description="搜索网络以获取最新信息。用于:近期新闻、时事、事实数据、公司信息。返回 5 个最相关的结果。"
)

3. 忽略错误状态

智能体会遇到错误。要为它们做好计划:

try:
    result = agent_executor.invoke({"input": user_query})
except Exception as e:
    # 不要只是失败 - 帮助智能体恢复
    recovery_prompt = f"先前的操作因错误而失败:{e}。请尝试另一种方法。"
    result = agent_executor.invoke({"input": recovery_prompt})

4. 忽视令牌成本

智能体可能快速消耗令牌。需监控并优化:

  •   尽可能使用较小的模型(GPT-4.1-mini vs GPT-4.1)
  •   对长对话实施摘要记忆
  •   缓存工具结果以避免重复调用

生产就绪的智能体架构

对于生产系统,根据你的需求选择架构:

框架 最适合 架构 关键优势
LangChain + LangGraph 复杂的单一智能体 基于图的执行 模块化,工具丰富
AutoGen 多智能体系统 事件驱动的参与者 智能体协作
CrewAI 基于团队的工作流 基于角色的智能体 自然的团队动态
自定义 特定需求 你的选择 完全控制

LangChain + LangGraph 架构

LangChain 已发展成为单智能体系统的事实标准。2025 年 LangGraph 的加入带来了复杂的状态管理:

from langgraph.graph import StateGraph, State
from typing import TypedDict
class AgentState(TypedDict):
    messages: list
    current_task: str
    completed_tasks: list
# 定义图
workflow = StateGraph(AgentState)
# 为不同的智能体能力添加节点
workflow.add_node("researcher", research_node)
workflow.add_node("writer", writing_node)
workflow.add_node("reviewer", review_node)
# 定义流程
workflow.add_edge("researcher", "writer")
workflow.add_edge("writer", "reviewer")

AutoGen 多智能体架构

微软的 AutoGen 在你需要多个专业智能体协同工作时表现出色:

import autogen
# 定义专业智能体
researcher = autogen.AssistantAgent(
    name="Researcher",
    system_message="你是一名研究专家。查找并验证信息。"
)
writer = autogen.AssistantAgent(
    name="Writer",
    system_message="你是一名技术文档工程师。创建清晰、准确的内容。"
)
critic = autogen.AssistantAgent(
    name="Critic",
    system_message="你审查工作的准确性和清晰度。要有建设性但要彻底。"
)

可工作的 AI 智能体真实案例

让我们看看当今在生产环境中实际使用的智能体(查看更多真实可工作的 AI 智能体示例):

1. 客户服务智能体(电子商务)

该智能体自主处理完整的客户交互(在我们的客户服务自动化指南中了解更多):

  •   在数据库中检查订单状态
  •   处理退货和退款
  •   更新送货地址
  •   将复杂问题升级给人工处理
  •   关键创新: 根据客户需求动态选择使用多个专业工具(数据库查询、支付处理、运输 API)。

2. 代码审查智能体(软件开发)

自动审查拉取请求:

  •   分析代码变更
  •   运行安全扫描
  •   提出改进建议
  •   检查是否符合编码标准

3. 研究助手智能体(内容创作)

进行综合研究:

  •   搜索多个来源
  •   事实核查信息
  •   综合发现
  •   生成引用

AI 智能体的安全考量

⚠️ 关键警告: 基于智能体的 AI 系统比聊天机器人更容易受到攻击。随着智能体开始预订航班、发送邮件和执行代码,风险呈指数级增长。

基本安全措施

  •   工具权限: 为每个工具实施细粒度权限
  •   操作验证: 对不可逆操作要求确认
  •   提示注入防御: 验证并清理所有输入
  •   审计日志: 记录每个操作以确保可追溯性
  •   人工监督: 维持紧急停止开关和审批工作流
# 示例:安全的工具执行
def execute_with_permission(action, requires_approval=True):
    if requires_approval and action.risk_level == "high":
        approval = request_human_approval(action)
        if not approval:
            return "操作被安全策略拒绝"
   
    # 记录操作
    audit_log.record(action, user, timestamp)
   
    # 带超时执行
    return execute_with_timeout(action, timeout=30)

测试和调试 AI 智能体

测试智能体需要不同于传统软件的方法:

1. 基于场景的测试

# 测试各种场景
test_scenarios = [
    {
        "input": "预订明天飞往纽约的航班",
        "expected_tools": ["calendar_check", "flight_search", "price_compare"],
        "expected_outcome": "flight_options"
    },
    {
        "input": "取消我的订阅并退还上个月的费用",
        "expected_tools": ["account_lookup", "subscription_cancel", "refund_process"],
        "expected_outcome": "confirmation"
    }
]
for scenario in test_scenarios:
    result = agent_executor.invoke({"input": scenario["input"]})
    assert all(tool in result["tool_calls"] for tool in scenario["expected_tools"])

2. 调试工具

启用详细日志记录以查看智能体的决策过程:

agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,  # 显示思考过程
    return_intermediate_steps=True  # 返回所有步骤
)

5 个即用型智能体系统提示

以下是为常见智能体类型准备的、经过实战检验的系统提示:

1. 研究智能体

你是一个可以访问网络搜索和文档分析工具的研究智能体。
对于每个研究任务:

  1. 将主题分解为关键问题
  2. 从多个来源搜索信息
  3. 通过交叉引用验证事实
  4. 将发现综合成连贯的摘要
  5. 为所有主张包含引用
    始终优先考虑近期信息和权威来源。

2. 客户支持智能体

你是一个客户支持智能体,帮助用户处理他们的账户和订单。

可用工具: order_lookup, refund_process, ticket_create, knowledge_base_search
指南:

  • 在访问账户信息前始终验证客户身份
  • 在升级前搜索知识库
  • 要有同理心并以解决方案为导向
  • 遇到以下情况升级至人工支持:法律问题、威胁或超出你工具范围的请求

切勿对你无法直接实现的功能做出承诺。

3. 数据分析智能体

你是一个专长于商业智能的数据分析智能体。
对于每个分析请求:

  1. 澄清业务问题
  2. 识别相关数据源
  3. 使用适当的统计方法执行分析
  4. 可视化关键发现
  5. 提供可操作的建议

始终在你的分析中注明数据局限性和置信水平。

4. 代码助手智能体

你是一个可以访问文件系统和执行工具的代码助手智能体。
能力:

  • 阅读和分析代码
  • 提出改进建议
  • 实施更改
  • 运行测试
  • 调试问题

切勿:

  • 未经明确许可删除文件
  • 修改系统文件
  • 执行可能有害的命令
  • 在代码中存储凭证

在进行重大更改前始终创建备份。

5. 内容创作智能体

你是一个专注于病毒式内容策略的内容创作智能体。

流程:

  1. 研究指定领域的趋势话题
  2. 分析成功的内容模式
  3. 生成多个内容创意
  4. 创建带有吸引点的详细内容
  5. 建议分发策略

关注真实性和价值,而非点击诱饵。

未来:AI 智能体的下一步是什么?

基于当前轨迹和内部知识,以下是将要发生的事情:

近期(未来 6 个月)

  •   视觉智能体: 能够查看并与 UI 交互的智能体
  •   语音优先智能体: 自然对话取代文本界面
  •   智能体市场: 针对特定行业的预构建智能体
  •   改进的安全性: 内置沙盒和权限系统

中期(2026 年)

  •   物理世界智能体: 与机器人技术集成
  •   监管框架: 为智能体行为设定法律边界
  •   智能体间经济: 智能体雇佣其他智能体
  •   个人 AI 操作系统: 管理整个数字生活的智能体

关键要点

构建真正有效的 AI 智能体需要理解五个核心组件:用于推理的 LLM、用于上下文的记忆、用于行动的工具、用于策略的规划以及一个健壮的执行循环。与聊天机器人的关键区别在于自主决策和动态适应。
从经过验证的框架开始,如用于单智能体的 LangChain 或用于多智能体系统的 AutoGen。专注于清晰的工具描述、适当的错误处理和全面的测试。最重要的是,记住上下文和问题分解比复杂的提示技巧更重要。

AI 智能体革命才刚刚开始。虽然炒作是真实的,但机遇也是真实的。通过遵循本指南并避免常见陷阱,你今天就可以构建出能够交付真正价值的智能体,同时为即将到来的自主未来做好准备。


【注】本文译自:How to Build AI Agents (Complete 2025 Guide) – Superprompt.com

Java智能体框架的繁荣是一种代码异味

停止构建编排框架,开始构建智能体。未来属于那些掌握生态系统的人,而不是那些被困在构建特定语言引擎中的人。

我需要坦白。我是一个框架狂热者。我的职业生涯建立在 Apache Camel 之上,我人生中的大部分成功都归功于企业集成模式的优雅。我懂。如果有一个社区值得获得诺贝尔框架奖,那就是 Java 社区。从早年在红帽公司到整个大数据生态系统,框架 15 年来一直是 JVM 世界的引擎。我们是抽象的大师。

因此,当智能体时代来临而 Java 在奋力追赶时,我的第一本能是原始的:构建一个框架。我甚至开始了一个,驱动力是这样一个想法:"AI 智能体的 Apache Camel 在哪里?"

三个月前,可能只有一个严肃的 Java 智能体框架。现在,包括 Embabel 在内,已经有了三个。竞赛开始了。但目睹这场爆炸式增长,我不得不提出一个难题:框架本身是否正在成为一种反模式?我们是否在为自己创造负担,而不是专注于真正重要的事情:构建智能体

最近 Java 智能体框架的繁荣并非一个健康、成熟生态系统的标志。它是一种症状。一种架构层面的代码坏味道,告诉我们我们的方法存在根本性问题。

我们最初为什么要构建框架?

让我们回顾一下。为什么像 Spring 和 Camel 这样的框架变得如此主流?原因清晰且合理:

  • 开发人员生产力: 我们当时淹没在样板代码中。框架将其抽象掉了。
  • 代码质量与治理: 它们提供了标准化的模式,防止每个开发人员重新发明轮子。
  • 可重用性: 它们为我们提供了经过实战检验的构造来构建,节省了大量的时间和精力。

目标是优化生产力、质量和治理。但这些是我们今天应该优化的相同参数吗?感觉我们像是在用 2010 年的方法解决 2025 年的问题,完全忽视了房间里的大象:AI 驱动的开发工具

这头大象有个名字:Cursor(及其伙伴)

在我们忙于将 LangChain 移植到 Java 时,情况发生了变化:

Cursor 和 Copilot 生成样板代码的速度比你输入 import 语句还快。你正在构建的那个复杂的链式抽象?Cursor 三秒钟就能写出来。你正在标准化的那个工具注册模式?Copilot 已经知道五种变体。

但在这里,我们需要停下来问一个更根本的问题:你的最终用户实际需要什么?

你真正需要构建什么?

让我们具体点。我们大多数人面临两种情况:

  • 场景 1: 你在未来 12 个月内构建一个关键智能体。也许它是一个每天处理 10,000 次对话的客户服务智能体。或者一个需要理解你公司特定标准的代码审查智能体。或者一个绝不能对监管要求产生幻觉的合规智能体。
  • 场景 2: 你正在构建一个智能体平台。成百上千个智能体,每个都有不同的上下文、不同的领域、不同的需求。也许你在一家咨询公司,为多个客户构建智能体。或者你正在创建一个内部平台,不同团队可以在上面启动自己的智能体。你需要可重用、适应性强、可演进的东西。一种能让你快速创建新智能体,同时保持所有智能体一致性和质量的东西。

在这两种情况下,诚实地问自己:你的用户需要一个代码框架吗?

还是他们需要完全不同的东西?

重新定义框架

在放弃我的框架并实际交付智能体之后,我学到了:我们不需要消除框架。我们需要重新定义在 AI 时代框架实际意味着什么。

  • 旧框架定义: 一种可重用的代码抽象,提供结构并处理横切关注点。你导入并在其之上构建的东西。
  • 新框架定义: 构建智能体的完整环境,一组协同工作的相互依赖的层,其中代码层只是更大拼图的一部分。

以下是现代智能体框架中真正重要的层次:

第 1 层:语言本身

Java(或你选择的语言)及其构造、类型和模式。不包装在抽象中,直接使用。语言已经是你的逻辑结构框架。你不需要在 Java 之上再加一个代码框架。Java 本身就是框架。

第 2 层:模型

一个真正好的大语言模型:GPT-5、Claude、Gemini、Grok。这不仅仅是你调用的 API。它是你技术栈的核心组件。模型的能力直接决定了你能构建什么。像选择编程语言一样仔细地选择它。

第 3 层:开发人员生产力工具

Cursor、Copilot 以及下一代 AI 驱动的开发工具。这些不是可选的。它们是关键基础设施。你的框架应设计成与这些工具无缝协作。如果 Cursor 不能轻松地按照你的模式生成代码,那么你的模式是错误的,或者你可能需要很好地描述你的模式。

第 4 层:提示词包与指南

你经过版本控制、测试、治理的提示词。你的组织语音。你的领域知识。你的合规规则。这是你的业务逻辑存在的地方——不在代码中,而在精心策划的上下文和指令中。将这些视为你的依赖构件,就像 JAR 包,但用于智能体行为。

第 5 层:生态系统 API

对新兴的专业化平台及其 API 的上下文感知。用于知识检索的向量数据库。上下文存储和内存管理系统,如 Zep 或 Cognee。工具执行平台,如 Arcade。用于智能体监控的可观测性平台,如 Langfuse。提示词管理和版本控制工具。这些大多暴露 REST API 或标准协议,大多提供 LLM.txt 用于上下文导入。你的框架需要知道这些存在,并知道如何连接到它们。

第 6 层:架构与设计模式

作为指南和模式捕获的架构思维。关于这些层如何在不同用例中组合在一起的可重用蓝图。不是代码抽象——关于路由逻辑、版本控制策略、部署模式和生态系统集成的文档化知识,这些知识成为你组织上下文的一部分。

想想看。当你构建那个关键的客户服务智能体时,真正决定其成功的是什么?

  • 调用 LLM 的 Java 代码吗?(那是 20 行代码,Cursor 写的)
  • 复杂的链式编排吗?(标准控制流)
  • 重试逻辑和错误处理吗?(Java 已经有这方面的模式)

还是:

  • 选择的模型以及它处理你领域的能力
  • 教导它你的升级规则和语气的提示词
  • 让你能快速迭代这些提示词的工具
  • 与像 Arcade(工具)和 Zep(内存)这样的平台的集成
  • 让你能够对变更进行版本控制、测试和部署的架构
  • 让你能在多个智能体中重用这种方法的设计模式

那就是你的框架。所有六层,协同工作。

实践中的框架

让我向你展示在构建智能体时的实际示例:

第 4 层(提示词包) 是版本化的构件,不是你代码中的字符串:

prompts/
  customer-service/
    v1.2/
      system-prompt.md
      escalation-rules.md
      tone-guidelines.md
      product-context.md
      examples/
        refund-scenarios.yaml
        technical-issues.yaml

第 5 层(生态系统 API) 配置在你的环境中:
你的生态系统上下文嵌入在指南中:

# 生态系统集成指南

## 工具发现
- 调用 Arcade API 列出可用工具: GET /tools
- 参考: 查看 Arcade LLM.txt 位于 https://docs.arcade.dev/llms.txt

## 内存管理
- Zep 会话 API: https://api.getzep.com/v2/sessions/{session_id}
- 参考: 查看 Zep API 文档位于 https://docs.getzep.com

## 基础设施与存储
- 用于提示词构件的对象存储: S3, GCS, 或 Azure Blob
- 用于长时间运行工作流的状态持久化

第 1 层(Java) 提供结构,干净简单:

public class CustomerServiceAgent {
    private final Model model;
    private final PromptPack prompts;
    private final ArcadeClient tools;
    private final ZepMemory memory;

    public Response handle(CustomerQuery query) {
        // 检索会话内存
        var history = memory.getSession(query.sessionId());

        // 从 Arcade 获取可用工具
        var availableTools = tools.listTools();

        // 使用上下文渲染提示词
        var context = prompts.render("main", query, history, availableTools);

        return model.complete(context);
    }
}

第 3 层(Cursor) 在几秒钟内生成这段代码。你专注于架构。

第 6 层(架构) 指南:

# 智能体架构指南

## 工作流路由
- 为多节点智能体工作流设计路由逻辑
  - 分类节点 → 路由到专家节点(支持、销售、技术)
  - 复杂性分析 → 路由到适当的模型层级(GPT-4o vs GPT-3.5)
  - 工具选择节点 → 根据用户意图路由到工具执行节点
- 通过 Arcade 网关路由工具执行:集中认证、速率限制、工具发现
- 提示词版本的 A/B 路由:10% 到 v2.0,90% 到 v1.5,监控质量

## 速率限制与节流
- 每用户令牌预算:10K 令牌/天(免费),100K(付费)
- 队列管理:最大 100 个并发请求,溢出到 SQS...
..
..

为什么这个框架能扩展

  • 对于一个关键智能体: 选择你的模型(第 2 层),编写清晰的代码(第 1 层),用 Cursor 迭代(第 3 层),优化提示词(第 4 层),集成生态系统 API(第 5 层),遵循架构模式(第 6 层)。
  • 对于一千个智能体: 相同的模型,相同的架构模式,相同的生态系统 API,但每个智能体都有自己的提示词包。Cursor 生成样板代码。你的语言提供结构。生态系统处理难题。

美妙之处何在?各层协同工作。Cursor 生成代码是因为模式简单。提示词是独立版本控制的。集成使用 REST API。架构无需抽象即可实现重用。

不需要编排框架。这就是框架。

引擎与 SDK 的问题

让我澄清一下:我并不是说所有框架都应该消失。我对 LangChain、LangGraph、Mastra、CrewAI、Autogen 等团队所构建的东西怀有极大的敬意。但我们需要理解一个在急于将所有东西移植到 Java 的过程中被忽视的关键区别。

不要混淆引擎SDK

我的意思是:我迫不及待地想用 Java 开发完整的智能体。我热爱 Java。但我不想仅仅因为我想用 Java 开发智能体就要一个 Java 引擎

考虑这些例子:

  • LangChain4J? 作为连接更广泛的 LangChain 生态系统的 SDK,这是一个很好的开始。你用 Java 编写,但你正在利用一个经过验证的引擎。
  • 带有 Java SDK 的 Crew AI? 完美。在 Python 中掌握编排模式,然后给我一个 Java 接口来使用它们。
  • 支持多语言的 Mastra? 正是正确的方向。构建一次引擎,为每种语言提供 SDK。
  • 为使用 Go 构建的 Not7 添加 Java SDK 或任何语言 SDK?

这里的模式是?用你喜欢的语言开发,而无需用该语言重建整个引擎。

编排层正在变薄

这就是为什么我认为即使是 SDK 方法也可能是暂时的,或者至少变得非常精简的原因:

  • 一方面: 模型正变得 dramatically 更好。GPT-5、Claude 4.5、Gemini 2.5 Pro、Grok 的推理能力使得复杂的编排模式过时了。它们可以用简单的提示词处理多步骤工作流,而这在六个月前需要复杂的链。
  • 另一方面: 真正的工程问题正在由专业平台解决。以 Arcade 为例:工具发现、认证、大规模执行、处理速率限制、管理工具版本。这才是艰难的工程工作所在。工具管理不再是编排问题;它是在平台层解决的基础设施问题。
  • 在中间: 编排框架正被挤压得越来越薄。

当你的模型能够推理工作流,并且平台处理复杂的工程问题(工具、内存、上下文)时,编排还剩下什么?

答案是:非常少。这就是为什么工程重点需要从编排转向更广泛的智能体开发挑战——提示词管理、生态系统集成、工具决策可审计性、成本优化。真正的问题已不在编排之中。

新现实:AI 原生框架

代码坏味道不仅仅是我们构建了太多框架。而是我们正在为一个不复存在的世界构建框架。以下是 2025 年构建框架实际意味着什么:

方面 过去的框架思维模式 (2005-2024) 下一代框架思维模式 (2025+)
定义 需要导入的代码库 跨越6个层级的完整环境
业务逻辑 位于代码抽象中 位于版本化提示词与指南中
关键构件 JAR 文件、软件包 提示词、上下文、API 知识
可重用性 代码继承与组合 架构模式与蓝图
开发工具 用于编写代码的 IDE 用于生成代码的 AI 工具(如 Cursor)
生态系统 自包含、单体式 集成专业化平台
样板代码 由框架抽象处理 由 AI 在几秒内生成
你导入/使用什么 Spring、Camel、框架 JAR 包 无需导入——你只需组合这些层级
  1. 接受 AI 驱动的开发现实 每个构建智能体的开发人员都将使用 Cursor、Copilot 或类似工具。这不是趋势——这是新的基线。设计你的框架以与 AI 代码生成无缝协作,而不是背道而驰。如果 Cursor 无法理解你的模式,那你的模式就是错的。
  2. 你的框架是纯文本英语,而不仅仅是代码 你的框架最关键部分将是精心设计的提示词、清晰的指南和结构化的上下文——而不是聪明的抽象。这些是你的版本化构件。这些决定了智能体行为。像对待代码一样严格对待它们。
  3. 当你需要 SDK 时,不要重新发明引擎 是的,Java SDK 至关重要。但你不需要仅仅为了用 Java 编写智能体就重建整个编排引擎。生态系统已经有平台在解决难题:内存(Zep, Mem0)、工具(MCPs, Arcade)、向量(Weaviate, Pinecone, Qdrant)、可观测性等。集成,不要重建。
  4. 框架仍然至关重要——但不是为了编排 如果你正在解决真正的问题——提示词版本控制、决策可审计性、生态系统集成模式、成本优化——那就构建这些。但编排?生态系统已经向前发展了。内存、工具、上下文、可观测性正由专业平台解决。将你的创新重点放在其他地方。
  5. 相信你的语言 如果你觉得你选择的语言中缺少一个框架,请退后一步。现代语言——Java、Python、TypeScript、Go——非常强大。凭借它们的最新特性加上 AI 代码生成工具,你可以用干净、简单的代码构建复杂的智能体。你的语言不是限制——试图用不必要的抽象包装它才是。

未来的框架不是你导入的代码库。它是对六个相互依赖层的掌握:你的语言、你的模型、你的开发工具、你的提示词、你的生态系统集成和你的架构。

也许我们不需要另一个智能体框架。也许我们所需要的只是一个智能体,一个能用你选择的语言创建智能体的智能体。一个开源的就更好了。


【注】本文译自:Java’s Agentic Framework Boom is a Code Smell

氛围编程:IT领导者须知

执行摘要

  • 氛围编程能加速开发与创新,但企业高管必须加强治理、安全与审查流程以保护业务。
  • 团队能快速测试想法并交付最小可行产品,从而缩短上市时间并提升对业务需求的响应能力。
  • 开发人员与非技术人员能更高效地协作,降低入门门槛并促进创新。

想象一下,您可以通过摩擦一盏神灯,用简单直白的语言向精灵描述您的需求,它就能为您生成一个功能齐全的应用程序。虽然神灯并不存在,但AI编程助手在很大程度上实现了这个愿望——无论其影响是好是坏。借助大语言模型,开发人员可以输入自然语言提示,并生成任何编程语言的代码。OpenAI联合创始人安德烈·卡帕西在2025年创造了"氛围编程"一词,用以描述"完全沉浸在感觉中,拥抱指数级增长,甚至忘记代码本身的存在"。

这种新范式标志着从深思熟虑、逐行编写代码,转向更流畅、更直观的人机意图与执行之间的协作。

氛围编程并非要取代开发人员,而是一种加速数字化转型的战略推动器,它能提高生产率,并且是打造快速上市工具的一种经济高效的选择。然而,IT高管必须将治理与赋能相结合,以最大化其价值,同时控制氛围编程带来的风险。

氛围编程如何运作?

开发人员首先选择一个AI编程助手,并描述他们想要的功能或特性。接着,AI会回应代码建议,开发人员可以审查、接受或优化这些建议。然后,开发人员继续迭代,通过向AI发出具体指令来添加新功能或进行调整,从而创建一个动态的、对话式的工作流程。

氛围编程 vs. 传统编程

传统上,编程过程非常结构化和有条不紊,而氛围编程描述的则是一种更具创造性或基于流程的方法。以下是这两种方法差异的细目分类:

氛围编程 传统编程
语言 自然语言 编程语言
焦点 宏观大局 / "感觉" 细节导向
审查流程 信任AI 同行代码审查
界面 AI代理 键入代码 / IDE
开发速度 几分钟到几小时 数天到数周甚至更长
入门门槛 无需具备代码知识 需要懂得如何编写所有代码
创作过程 探索与实验,如同即兴弹奏吉他 有计划、精确且可重复,如同创作交响乐

氛围编程的优势

氛围编程提供了若干关键优势,特别是对于那些希望快速将想法付诸实施并减少重复性任务的开发人员。

  • 更快的开发速度。 经验丰富的开发人员使用氛围编程可以在几小时内完成一个应用程序,而传统的开发时间则需要数天或数周。
  • 更低的入门门槛。 开发人员进行氛围编程所需的唯一语言是他们自己的自然口语。氛围编程使开发人员能够在不懂编码的情况下启动一个功能正常的项目。AI对于正在学习编码或理解应用程序工作原理的开发人员来说,也是一个强有力的工具。
  • 快速原型制作。 氛围编程的速度使开发团队能够快速创建功能性的最小可行产品。这使得氛围编程非常适合在争抢市场先机时向投资者展示项目。此外,它还通过实验实现了更快的功能迭代。
  • 爱好或内部项目。 如果无需考虑公共访问或安全问题,氛围编程是理想选择。其速度和易用性使开发人员能够快速解决问题并构建解决方案。
  • 多模态编程。 氛围编程将代码生成扩展到集成开发环境之外的键入方式,包括语音到文本提示。
  • 员工协作与生产力。 开发人员从编写代码转向审查和优化代码。其他员工,如分析师和产品经理,也可以对编程提供意见,从而实现业务和IT部门的跨职能协作。

氛围编程的局限性

氛围编程听起来是否好得令人难以置信?这取决于它的使用方式。使其成为小型应用程序和原型强大工具的特点,在大型代码库或安全性优先的场景中,却可能成为其负担。

  • 错误与幻觉。 生成代码的AI与任何其他流行的AI工具一样容易出现幻觉。几位计算机科学研究人员的一项研究发现,商业AI模型平均有5.2%的情况下会推荐不存在的软件包。相比之下,开源模型的这一比例跃升至21.7%。
  • 有限的技术复杂性。 提供给AI的每个提示都有一个有限的上下文窗口——类似于内存——其中包含大量关于您环境的数据,例如您打开的标签页内容。这为AI提供了上下文,使其能够做出明智的决策。然而,不同AI模型的上下文窗口大小不同,并且较大的上下文大小可能会影响AI的性能。项目越复杂,AI理解项目所需的上下文就越多。
  • 难以调试和维护。 未经审查就接受AI生成的代码,可能会导致创建一个无人理解代码作用及缘由的代码库。如果AI引入了其自身无法修复的错误,而开发人员又无法理解其输出,那么进展将完全受阻。
  • 缺乏原创性。 编码AI基于现有的代码示例进行训练,只能生成它所知道的内容。它无法完全靠自己提出革命性的过程或想法。

企业高管应将氛围编程生成的代码视为快速原型。然而,程序仍然需要经过审查。对于面向客户的服务,必须进行审查;如果涉及其他敏感数据,则必须检查其是否符合法规要求。

氛围编程的安全顾虑

一位名叫Leo的开发人员在X上宣布,他发布了一个完全通过氛围编程构建的SaaS应用程序。两天内,他的应用程序就遭到了黑客的攻击,Leo发帖称出现了各种随机问题。在整个项目中如此重度依赖AI会导致安全问题层出不穷。原因如下:

  • LLM或平台中的漏洞。 任何依赖外部组件的软件产品都会继承潜在的漏洞。AI编码平台也不例外。最近,安全研究人员在氛围编程平台Base44中发现了暴露的API端点,使得攻击者能够使用非机密的app_id值创建新账户来访问私有应用程序,从而绕过所有身份验证机制。
  • 开发人员错误。 氛围编程工具会精确地生成开发人员所要求的内容。如果开发人员在其提示中未包含安全实践,AI将不会生成遵循最佳安全实践的代码。
  • 数据隐私。 LLM通过摄取数据作为训练数据来改进模型。如果项目涉及敏感数据,例如支付信息、健康记录、专有代码或商业机密,则AI工具必须实施严格的数据隔离,以防止AI在其他应用程序中使用受保护的信息。

如何实施氛围编程

考虑到其局限性,在将氛围编程集成到项目中时最好谨慎行事,以充分利用其优势。

  1. 规划项目。 氛围编程和传统编程共有的一个特点是,两者在从一开始就有清晰计划指导时最为有效。确定您要构建什么,并将步骤分解为易于消化的小部分。牢记您希望为项目采用的安全和代码标准。
  2. 决定您的"氛围"策略。 真正的氛围编程定义为将所有决定交给AI。AI辅助编码是一种混合方法,开发人员向AI提示代码,然后在批准前仔细检查输出。找到最符合您优先事项的平衡点。
  3. 选择AI编程助手。 并非所有模型都构建得一样。有些专门用于代码生成,而其他则能解决更复杂的问题。不同模型在数据隔离、隐私以及成本方面有不同的政策。请仔细选择最适合您项目的AI代理。
  4. 使用源代码控制。 这对任何类型的编码都是个好主意,但对于氛围编程尤其重要。当您的项目处于良好工作状态时,为自己创建检查点,以便您可以根据需要轻松调整。
  5. 迭代。 一次创建一个功能,并在每个提示中提供尽可能多的细节和上下文。优化和重构您的代码,直到它符合您的设想。
  6. 测试。 确保您的项目在每个步骤中都正常工作。AI非常擅长生成自动化测试,但请确保您也执行手动测试,包括依赖项验证和自动化测试,以阻止合并未知/无效的软件包。
  7. 设定防护措施。 务必建立安全审查和编码标准。氛围编程项目仍应审查其准确性和合规意识,因此审批工作流是必要的。

IT高管可追踪的指标

这些指标应衡量交付速度、缺陷率和生产率的改进。以下是高管可用于追踪氛围编程的几个指标示例:

  • 原型制作时间(引入氛围编程工具前后对比)。
  • AI生成的拉取请求在自动化关卡失败的比例,例如测试、代码 lint 检查和软件成分分析。
  • 幻觉检测率,包括无效软件包或不良依赖项。
  • 每月归因于AI生成代码的安全事件
  • 每个正常工作的原型的成本,以客观显示投资回报率。

【注】本文译自:Vibe coding: What IT leaders need to know | TechTarget

AI智能体是加速器,而非开发者替代品

将AI集成到应用开发中的核心挑战,不在于其协助能力,而在于我们能在多大程度上放心地将控制权委托给它。

尽管AI智能体可以完美地执行那些曾被认为人类专属的任务,但它们同样可能在紧接着的下一段代码中犯下令人震惊的错误。

这些错误尖锐地提醒我们,即使是最先进的AI编程助手,仍然缺乏对世界运行方式的理解。这一根本区别将当前的生成式AI与通用人工智能(AGI)的愿景区分开来。考虑到这一点,让我们来看看AI智能体如何成为出色的开发加速器,却无法取代人类开发者。

LLM的推理并非逻辑推理

即使是复杂的智能体AI——构建于拥有日益庞大的上下文窗口和复杂工作流程的大型语言模型(LLM)之上——也依赖于语义模式匹配。它们无法对底层的因果关系和相互依赖提供真正的洞见。

让人类难以理解这一点的是,LLM在阐述其决策过程时具有令人信服的方式,常常模仿一种逻辑递进,暗示其对因果关系的理解,而实际上它们并不具备这种理解。它们通过拼凑统计上可能性高的人类推理文本片段来实现这一点。虽然这看起来像是逻辑推理,但它基于从训练数据中得出的概率计算,而非对步骤之间因果关系的直接理解。

LLM模仿逻辑推理,但无法掌握因果关系。

将这比作一位主演医疗电视剧的演员,他多年来记住了数千小时的对话、纪录片和真实咨询记录。他可以完美地进行鉴别诊断,像经验丰富的医生一样自信地用专业词汇滔滔不绝地说出症状、检测结果和治疗方案。他知道"向左臂放射的胸痛"通常出现在关于心脏病的场景中,"全血细胞计数和代谢指标组"跟在"我们来做些检查"之后,而担忧的表情伴随着关于肿瘤的讨论。

一位对医疗话题表现出表面理解的医疗剧演员,是比喻AI无法掌握因果关系的绝佳例子。

他们的表演如此令人信服,以至于任何观众都会相信他们懂医。但他们根本不知道阿司匹林为什么能稀释血液,心脏病发作时会发生什么,或者为什么一种治疗有效而另一种会致命。他们只是在背诵他们记住的各种医疗对话的变体,拼凑那些在统计上共同出现的片段,却不理解这些模式代表了真实的生物过程,其中顺序和因果关系 literally 意味着生与死。翻译到应用开发中,这通常意味着出色的结果之后紧接着灾难性的失败,反之亦然。

统计模式而非因果真相

LLM非常擅长在难以想象的大量文本中寻找并连接模式。尽管这些文本中有许多描述了世界的运作方式,但LLM并不理解这些描述的实际含义。相反,它将文本转换成数字——向量——这些数字捕获的是统计关系,而非因果真相。然后,模型将这些数字翻译回人类语言,而在这一切之下,它始终只是在跟踪和 shuffling 数字,而不是意义。例如,"charge"、"payment"和"credit card"这些词可能在向量空间中位置接近,因为它们经常在文本中共同出现,而"profile"、"lookup"和"fetch"则形成另一个集群——但模型实际上并不知道一组涉及金钱,而另一组不涉及。

LLM只处理词组之间的统计关系。

事物并非表面所见

由于编程语言是高度结构化的,这种数值上的 shuffling 可以产生优秀的代码。虽然AI模型并不像开发者那样"理解"代码,但它可以可靠地将输入模式映射到输出,将框架映射到样板代码,将语法映射到语义,其方式常常看起来与人类代码无异。例如,当被要求"用Python和Flask构建一个REST API"时,模型无法推理HTTP或数据库——它只是回忆起@app.route通常出现在函数定义之前,GET请求常常映射到返回jsonify,而错误处理经常涉及try/except块。结果往往是结构良好的Flask代码,即使它源于模式回忆而非真正的理解。

人类需要保持在循环中,以应对AI缺失的上下文和推理能力。

例如,为重试逻辑加固微服务听起来很简单——然而实际情况并非如此。要求AI助手"在失败时添加重试",你可能会得到一段在任何错误时都重试所有操作的代码。这对于幂等的(或无状态的)读取操作(例如"获取配置文件")来说没问题,因为重复调用只会返回相同的数据。
将相同的逻辑应用于非幂等操作——扣款、创建订单、发送电子邮件、查询数据库——你就会招致灾难:重复扣款、重复订单、通知风暴、数据库中的重复记录。解决方法并非魔术,而是判断力。人类首先对操作进行分类——幂等与非幂等——仅在瞬态错误时重试,并且对于任何有副作用的操作,都需要幂等性密钥和服务端去重。虽然这仍然为人类开发者节省大量时间,但他们仍然需要将其技能和专业知知融入其中,否则灾难可能并且将会随机发生。

理解模式匹配的局限性很棘手

原则上,模式匹配难道不能识别出对信用卡扣款需要采用与检索客户资料或产品信息不同的API调用重试方法吗?是的,它可以,但人类无法事先知道这一点,因为这取决于该特定模型的训练数据是否包含了执行标准POST或GET请求的重试函数。
模型未能建立操作类型与其现实后果之间的联系;它仅仅回忆统计关联。为了让模型避免这个错误,训练数据需要包含清晰、一致且重复出现的配对,将操作类型与重试策略及其后果联系起来。
理想情况下,数据会明确对比可以安全重试的代码与必须避免重试的代码。或许它还包括了事后分析或警告,描述了误用重试时发生的情况。然而,模型是否摄入了足够的训练数据来做出这种区分,我们人类无法确定。更棘手的是,由于其概率性质,模型可能在某一次做出了区分,但在接下来的三次尝试中却没有。
这个例子说明了为什么简单地添加更多训练数据通常不是答案,因为必要的数据可能并不以书面形式存在。或者更糟的是,训练数据可能包含了强化错误概括的内容。无论哪种情况,人类用户都无法知道是否如此,并且需要全面理解特定问题应如何解决。

AI的价值是真实的,开发团队可以受益

只要清楚地理解其局限性,AI智能体可以显著提高人类开发者在整个开发生命周期中的生产力。从收集需求并将其转化为用户故事,一直到检测并部署应用程序,AI智能体可以为人类提供建议、自动化验证和快速原型设计,从而显著缩短迭代周期。
AI智能体应被视为力量倍增器,可以处理开发的机械性方面,例如基于现有示例和文档生成样板代码、编写测试用例和记录API。另一方面,人类则负责真正理解业务影响、决定架构权衡,以及解决需要应用抽象逻辑能力的复杂问题。

AI对SDLC的生产力影响

下表分析了AI对软件开发生命周期(SDLC)中不同活动的生产力影响,以及AI对每项活动的能力、所需的人力参与程度和每项活动的风险水平。

AI对SDLC活动的生产力影响

活动 AI的生产力影响 AI能力描述 人力参与需求 风险水平
需求收集 低 – 中 根据笔记、会议记录、电子邮件和其他材料生成用户故事。 高 – 确保故事在成本、风险和回报方面与当前业务优先级保持一致。 高 – 被误解的需求将贯穿整个项目。
架构与设计 建议模式、识别瓶颈并生成初始图表,作为人类构建的坚实起点。 关键 – 考虑系统范围影响、做出战略权衡并监控技术趋势。 高 – 糟糕的架构决策难以逆转且成本高昂。
代码生成 构建定义良好的样板代码并解决精确定义的问题。保持文档更新。 中 – 掌控业务逻辑和边缘情况。 中 – 通常难以完全掌控AI编写的代码。
代码审查 捕获语法错误、发现安全漏洞、发现性能问题并建议优化。 高 – AI会遗漏依赖于上下文的问题和架构问题。 中 – 人类需要对审查负全责。
测试 创建单元测试、集成测试、自动化回归测试并发现边缘情况。 低 – 对于测试生成,但 高 – 对于测试策略。 中 – 人类必须对测试的完整性和相关性负责。
调试 分析堆栈跟踪并就已知错误建议修复方法。 中 – 指导调试过程。 低 – 错误的修复通常很容易发现。
文档编写 生成API文档、自述文件、内联注释、用户指南和变更日志。 低 – 对于面向用户的文档。 低 – 不正确的文档通常可以在没有重大影响的情况下得到纠正。
部署与CI/CD 创建部署清单、构建IaC模板、生成流水线配置。 高 – 生产部署需要仔细检查。 高 – 任何问题都会直接影响生产环境。
监控 添加检测、分析日志并生成警报规则。 中 – AI在没有上下文的情况下难以确定优先级。 中 – 误报会浪费时间。

来源:Torsten Volk, Omdia • 获取数据 • 使用 Datawrapper 创建

结论

宣布AI智能体正在接管开发者工作的技术领导者们,对AI当前能力产生了不切实际的期望。这导致许多企业高管认为开发者工时不再是他们所能构建内容的限制因素。金融分析师可以创建自己的投资组合再平衡工具;医疗保健管理员可以构建患者排班系统;供应链经理可以开发库存优化仪表板;或者营销总监可以构建个性化的活动自动化平台,而无需编写一行代码。虽然他们可以为许多此类业务任务实现概念验证,但架构、开发和交付企业级软件仍然极大地依赖于人类开发者的技能和经验。
然而,AI智能体可以通过为人类开发者完成大量基础性工作来显著加速SDLC。创建测试用例、用监控代理自动检测复杂软件、记录数万行主机代码以及精确定义复杂的基础设施清单,仅仅是AI智能体可以帮助人类开发者的几个例子。
人类与AI智能体之间的SDLC必须是协作的、迭代的并接受持续监督。确定如何最优地调整流程、开发工具和企业文化以满足这些要求,是智能体辅助应用开发的下一个前沿领域。弄清楚如何为人类编码者提供最佳AI支持,其回报有望带来显著的生产力提升,使人类开发团队能够更快、更高质量地交付更多功能。


[注]本文译自: AI agents are accelerators, not developer replacements

构建复合AI系统以实现可扩展工作流

了解如何利用复合AI系统架构化模块化且安全的智能体工作流,以实现可扩展的企业自动化。

生成式AI、大语言模型和多智能体编排的融合催生了一个变革性的概念:复合AI系统。这些架构超越了单个模型或助手,代表了智能代理的生态系统,它们通过协作来大规模交付业务成果。随着企业追求超自动化、持续优化和个性化参与,设计智能体工作流已成为关键的差异化因素。

本文探讨复合AI系统的设计,重点聚焦模块化AI代理、安全编排、实时数据集成和企业治理。旨在为解决方案架构师、工程领导者和数字化转型高管提供一个实用的蓝图,用于在各个领域(包括客户服务、IT运营、营销和现场自动化)构建和扩展智能代理生态系统。

复合AI的兴起

传统的AI应用通常是孤立的,一个机器人专用于服务,另一个专注于分析,还有一个用于营销。然而,真实世界的工作流是相互关联的,需要共享上下文、移交意图并进行自适应协作。复合AI系统通过以下方式解决这一问题:

  • 启用自主但协作的代理(例如,规划器、检索器、执行器)
  • 促进多模态交互(文本、语音、事件)
  • 支持企业级的可解释性、隐私和控制指南

这反映了复杂系统在人类组织中的运作方式:每个单元(代理)都有其角色,但它们共同创造了一个价值链。

企业级智能体工作流的设计原则

设计有效的复合AI系统需要深思熟虑的方法,以确保模块化、可扩展性并与企业目标保持一致。以下是指导智能体工作流开发的关键原则:

1. 模块化代理设计

每个AI代理都应遵循单一职责原则,设计为具有特定、明确界定的职责。这种模块化使维护、测试和可扩展性变得更加容易。例如:

  • 规划器代理:将总体目标分解为可管理的子任务。
  • 检索器代理:从不同来源检索和收集相关数据。
  • 执行器代理:根据规划器的指令执行操作。
  • 评估器代理:评估结果并提供反馈以持续改进。

通过明确定义职责,代理可以独立运作,同时在系统内协同工作。

2. 事件驱动和以意图为中心的架构

从静态的、同步的工作流转向动态的、事件驱动的架构,可增强响应能力和适应性。实施以意图为中心的设计使系统能够有效解释用户或系统意图并据此行动。关键组件包括:

  • 意图路由器:对意图进行分类并将其引导至相应的代理。
  • 事件代理:通过事件消息促进代理之间的通信。
  • 记忆模块:随时间推移保存上下文,使代理能够基于历史数据做出明智决策。

这种架构实现了可扩展性和弹性,这对企业环境至关重要。

3. 企业数据集成与检索增强生成

集成结构化和非结构化数据源可确保AI代理在全面的上下文中运行。利用检索增强生成技术使代理能够访问外部知识库,从而提高其决策能力。策略包括:

  • 数据连接器:创建与企业数据库和API的安全连接。
  • 向量数据库:增强语义搜索和相关信息的检索。
  • 知识图谱:提供数据实体之间关系的结构化表示。

这种集成确保了代理信息灵通、具有上下文意识,并能提供准确的结果。

4. 安全与治理框架

确保智能体系统的安全性和合规性至关重要。实施强大的治理框架有助于维持信任和问责制。关键实践包括:

  • 访问控制:建立并强制执行数据和代理交互的权限。
  • 审计追踪:记录代理活动以实现透明度和合规性。
  • 合规性检查:根据GDPR和HIPAA等监管标准定期评估系统。

结构良好的治理模型可以防范风险,并确保AI的合乎道德的部署。

5. 可观测性与持续监控

实施可观测性实践能够实时监控和诊断代理行为及系统性能。关键组件包括:

  • 日志记录:记录代理行动和决策的全面日志。
  • 指标收集:收集性能指标,如响应时间和错误率。
  • 警报系统:及时向利益相关者通知异常或系统故障。

持续监控允许进行主动维护和持续改进。

6. 人在回路机制

纳入人工监督可确保AI代理在可接受的范围内运行,并适应细微的场景。HITL方法包括:

  • 审批工作流:确保关键决策或行动得到人工验证。
  • 反馈循环:使用户能够就代理性能提供输入,指导其未来行为。
  • 干预协议:允许人员在必要时修改或调整代理行动。

平衡自动化与人工判断可增强系统可靠性并建立用户信任。

7. 可扩展性与性能优化

设计能够有效扩展以处理不断增长工作负载的系统至关重要。实现这一目标的策略包括:

  • 负载均衡:在代理和资源之间均匀分配工作负载。
  • 异步处理:使代理能够独立运行,最大限度地减少瓶颈。
  • 资源管理:有效监控和分配计算资源以维持性能。

针对可扩展性进行优化可确保系统在需求增加时保持响应能力和有效性。

通过遵循这些设计原则,企业可以创建稳健、高效、可靠的智能体工作流,这些工作流既符合组织目标,又能适应不断变化的挑战。

实际应用案例:现场服务代理网格

场景:一家公用事业组织可以利用三个专门的AI代理来增强现场响应操作:

  • 规划器代理:评估收到的用户投诉并制定解决计划。
  • 检索器代理:获取资产位置、历史工单数据和合规性检查清单。
  • 执行器代理:安排技术人员并向移动服务团队发送警报。

影响:提高任务分配效率、缩短解决周期并提高技术人员生产率。

结论

复合AI系统正在通过促进智能、适应性强且可扩展的工作流来改变企业架构。设计模块化、可编排的智能体系统有助于组织:

  • 加速AI驱动的转型
  • 增强运营弹性和灵活性
  • 为客户和员工体验提供更好的结果

未来在于从孤立的AI任务转向复合的代理生态系统,这是一种将创新与强大治理及领域相关性相结合的战略。


【注】本文译自:Architecting Compound AI Systems for Scalable Workflows