Java 程序员第 18 阶段:实战Agent工作流:Java搭建自动化业务智能体

概述

随着大语言模型(LLM)能力的飞速跃升,AI Agent(智能体)已成为企业智能化转型的核心基础设施。相比于简单的问答交互,Agent 能够自主规划任务、调用多样化工具、感知环境变化并持续优化执行策略,真正实现"说一句需求,AI 全程搞定"的业务自动化体验。

在 Java 企业级生态中引入 Agent 架构,不仅是技术选型上的顺势而为,更是工程可靠性的必然选择。Spring Boot 的依赖注入与生命周期管理、成熟的并发编程模型、丰富的生态系统(JDBC、HTTP Client、消息队列),以及稳定可靠的生产级运行时,都为构建企业级 Agent 系统提供了坚实基座。

本文从 Agent 的核心概念出发,深入剖析其工作原理,完整讲解 Java 技术栈下的 Agent 工作流设计与实现,涵盖任务规划、工具调用、状态管理、多步骤编排等关键环节,并通过一个企业级实战案例------智能数据报表分析 Agent------完整呈现从设计到落地的全流程。通过本文,你将掌握构建生产级 Java Agent 系统的核心能力。

一、Agent 核心概念解析

1.1 什么是 Agent

Agent(智能体)是一种能够自主感知环境、做出决策并执行动作的智能系统。如果用一句话概括 Agent 与普通程序的区别,那就是:普通程序执行预定义的固定逻辑,Agent 则根据目标自行决定下一步做什么

一个完整的 Agent 通常由以下四大组件构成:

感知(Perception) ------Agent 通过输入理解当前状态。对于基于 LLM 的软件 Agent 而言,"感知"就是将用户的自然语言指令、当前系统状态、外部数据反馈等信息转化为 Agent 能够理解的内部表示。这一步通常依赖 LLM 的语义理解能力来完成意图识别(Intent Detection)和实体抽取(Entity Extraction)。

规划(Planning) ------这是 Agent 最核心的能力。面对一个复杂任务,Agent 需要将其分解为若干子任务,确定执行顺序,处理子任务之间的依赖关系,并根据执行结果动态调整后续计划。常见的规划策略包括 Chain-of-Thought(CoT,逐步思考)、ReAct(Reason + Act,交替推理与行动)等。

行动(Action) ------Agent 通过调用外部工具来完成任务。工具(Tool)是 Agent 与外部世界交互的桥梁,可以是简单的计算器、搜索引擎,也可以是复杂的 ERP 系统、数据库或第三方 API。一个 Agent 的能力边界很大程度上由其可调用的工具集决定。

反馈(Feedback) ------行动产生结果后,Agent 需要评估结果是否达到目标。如果任务失败或部分完成,Agent 会分析原因并调整计划,形成"规划---行动---评估---再规划"的循环,直到任务完成或明确无法完成。

1.2 Agent 与传统 RPA 的本质区别

很多人在初次接触 Agent 时容易将其与 RPA(Robotic Process Automation,机器人流程自动化)混为一谈。两者虽然都追求业务流程的自动化,但在本质上有着显著差异。

RPA 本质上是一种"像素级录制回放"技术------它忠实地复现人类在图形界面上的每一次点击和输入。RPA 的优点是实施周期短、对现有系统零改造,缺点则是极度脆弱------一旦界面布局变化,RPA 流程就会失效,且无法处理任何未曾"录制"过的异常情况。

Agent 则截然不同。Agent 不关心界面细节,它通过 API 或自然语言接口与系统交互,以业务语义为单位执行操作。Agent 能够理解"查询本月销售额排名前五的客户"这样的高层语义指令,并自主拆解为调用 CRM 系统、SQL 查询、数据排序、格式化输出等一系列操作。Agent 具备推理能力,能够处理未曾预设过的分支情况,具备真正的业务理解力。

从技术架构上看,RPA 是面向过程的、硬编码的;Agent 则是面向目标的、自驱动的。这决定了 Agent 能够处理更复杂、更动态、更长尾的业务场景。

1.3 Agent 的技术架构分层

从工程实现角度,Agent 系统通常分为以下几层:

用户交互层(User Interface Layer) 负责接收用户请求、呈现执行结果。这一层可以是 REST API、Web 界面,甚至是钉钉/飞书机器人。Spring Boot 的 @RestController 是这一层的天然载体。

任务规划层(Planning Layer) 是 Agent 的大脑,基于 LLM 的推理能力完成意图识别、任务分解和执行规划。在 Java 实现中,这一层通常由一个专门的 AgentPlanner 组件封装,它将 LLM 的自然语言推理能力转化为可执行的任务图(Task Graph)。

工具层(Tool Layer) 管理所有可调用的外部工具。每个工具被抽象为一个 Tool 接口的实现,通过 ToolRegistry 统一注册和管理。工具执行由 ToolExecutor 统一调度,支持超时控制、重试策略和结果校验。

执行层(Execution Layer) 负责具体任务步骤的执行,包括并发执行、顺序编排、结果聚合等。对于 Java 而言,CompletableFuture 和 Spring 的 @Async 是这一层的核心支撑。

记忆层(Memory Layer) 存储 Agent 的短期记忆(当前任务上下文)和长期记忆(历史经验知识)。短期记忆通常使用堆内数据结构实现;长期记忆则可借助向量数据库(如 Milvus、Pinecone)或传统知识图谱实现。

二、Java Agent 工作流设计

2.1 整体架构设计

一个生产级的 Java Agent 系统,其架构设计需要兼顾灵活性、可扩展性和工程可靠性。以下是推荐的整体架构:

``_INLINE

用户请求 → REST API (Spring Boot)

Agent Core (核心调度器)

Task Planner (任务规划器,调用 LLM)

Task Graph (有向无环任务图)

Tool Registry (工具注册中心)

Tool Executor (工具执行器,并发/顺序执行)

Result Aggregator (结果聚合器)

Response Serializer (响应序列化)

返回给用户

__`_INLINE

核心设计原则包括:

单一职责原则 ------每个组件只负责一件事。Planner 负责规划,Executor 负责执行,Registry 负责管理。组件之间通过明确定义的接口通信,便于独立测试和替换。

依赖倒置原则 ------核心 Agent 类不直接依赖具体的工具实现,而是通过 _Tool__INLINE 接口与工具交互。这使得新增或替换工具时无需修改核心逻辑,符合开闭原则。

可观测性原则 ------每个任务步骤的执行状态、执行时间、返回结果都应被完整记录。引入 SLF4J + Logback 的日志体系,并考虑集成 Micrometer + Prometheus 实现指标监控。

2.2 核心接口定义

在 Java 中,Agent 的核心接口设计如下:

__`__INLINE_java

// 工具接口,所有外部能力都实现此接口

public interface Tool {

String name(); // 工具唯一标识

String description(); // 工具功能描述,用于 LLM 理解何时调用

ToolSchema inputSchema(); // 输入参数模式(JSON Schema)

ToolResult execute(Map<String, Object> params); // 执行工具

}

// 工具执行结果

public record ToolResult(

boolean success,

String output,

String error,

Map<String, Object> metadata

) {}

// 任务节点

public record Task(

String id,

String description,

Tool tool,

Map<String, Object> params,

TaskStatus status,

Object result

) {}

// Agent 核心接口

public interface BusinessAgent {

AgentResponse execute(AgentRequest request);

void registerTool(Tool tool);

void deregisterTool(String toolName);

List<Tool> getRegisteredTools();

}

__`_INLINE

这套接口设计的核心理念是工具作为一等公民 。每个工具不仅有执行逻辑,还自带元数据描述(名称、描述、输入模式),这些元数据会被传递给 LLM,使其能够准确判断在何种场景下调用哪个工具。

2.3 任务规划器的实现

任务规划器(Task Planner)是 Agent 系统的"智力中枢"。它的职责是将用户的高层语义指令转化为可执行的任务图。

规划过程通常分为两步:

第一步:意图识别 。收到用户请求后,首先判断用户的真实意图是简单问答、单步任务还是复杂多步骤任务。对于简单任务(如"今天天气如何"),无需复杂的任务分解;对于复杂任务(如"帮我分析本季度销售数据,生成对比报告"),则需要进一步拆解。

第二步:任务分解 。使用 ReAct 或 CoT 模式,将复杂任务拆解为有序的子任务序列。每个子任务明确其依赖的前置任务、使用的工具和期望的输出格式。

以下是一个基于 ReAct 模式的 Planner 实现框架:

__`__INLINE_java

@Service

@Slf4j

public class ReActPlanner implements TaskPlanner {

@Autowired

private LlmClient llmClient; // LLM 调用客户端

@Autowired

private ToolRegistry toolRegistry;

@Override

public TaskGraph plan(String userInstruction, List<Tool> availableTools) {

// 构建 Prompt,包含工具描述和用户指令

String prompt = buildReActPrompt(userInstruction, availableTools);

// 调用 LLM 获取推理过程

LlmResponse response = llmClient.complete(prompt);

// 解析 LLM 输出,构建任务图

return parseToTaskGraph(response.getContent());

}

private String buildReActPrompt(String instruction, List<Tool> tools) {

StringBuilder sb = new StringBuilder();

sb.append("你是一个专业的任务规划助手。面对用户指令,你需要按照以下格式逐步推理:\n\n");

sb.append("可用工具:\n");

for (Tool tool : tools) {

sb.append(String.format("- %s: %s (输入: %s)\n",

tool.name(), tool.description(), tool.inputSchema()));

}

sb.append("\n用户指令:").append(instruction).append("\n\n");

sb.append("请按以下格式输出任务规划:\n");

sb.append("思考1: ...\n");

sb.append("动作1: 工具名称\n");

sb.append("观察1: 工具返回结果\n");

sb.append("思考2: ...\n");

sb.append("动作2: 工具名称\n");

sb.append("...");

return sb.toString();

}

}

__`_INLINE

在实际生产中,规划器的 Prompt 工程(Prompt Engineering)是决定 Agent 效果的关键环节。好的 Prompt 需要清晰地描述任务目标、可用的工具能力、输出格式要求,并适当给出示例(Few-shot Learning)来引导 LLM 给出更准确的规划。

2.4 工具注册与执行机制

工具注册中心(Tool Registry)是 Agent 系统的"瑞士军刀"------它管理着所有可用工具的元数据,并提供查找能力供 Planner 使用。

__`__INLINE_java

@Service

@Slf4j

public class ToolRegistryImpl implements ToolRegistry {

private final Map<String, Tool> tools = new ConcurrentHashMap<>();

@Override

public void register(Tool tool) {

if (tools.containsKey(tool.name())) {

throw new IllegalStateException("Tool already registered: " + tool.name());

}

log.info("Registering tool: {}", tool.name());

tools.put(tool.name(), tool);

}

@Override

public void deregister(String name) {

log.info("Deregistering tool: {}", name);

tools.remove(name);

}

@Override

public Optional<Tool> get(String name) {

return Optional.ofNullable(tools.get(name));

}

@Override

public List<Tool> getAll() {

return new ArrayList<>(tools.values());

}

@Override

public ToolResult execute(String toolName, Map<String, Object> params) {

Tool tool = tools.get(toolName);

if (tool == null) {

return ToolResult.failure("Tool not found: " + toolName, Map.of());

}

try {

long start = System.currentTimeMillis();

ToolResult result = tool.execute(params);

long duration = System.currentTimeMillis() - start;

log.info("Tool {} executed in {}ms, success: {}", toolName, duration, result.success());

return result.withMetadata("duration_ms", duration);

} catch (Exception e) {

log.error("Tool {} execution failed", toolName, e);

return ToolResult.failure(e.getMessage(), Map.of());

}

}

}

__`_INLINE

工具执行器(Tool Executor)在 Registry 基础上增加了超时控制、重试策略和并发执行能力:

__`__INLINE_java

@Service

@Slf4j

public class ToolExecutorImpl implements ToolExecutor {

@Autowired

private ToolRegistry registry;

private final ExecutorService executor = Executors.newFixedThreadPool(10);

@Override

public ToolResult execute(String toolName, Map<String, Object> params, ExecuteOptions options) {

int maxRetries = options != null ? options.maxRetries() : 0;

int retryCount = 0;

Exception lastException = null;

while (retryCount <= maxRetries) {

try {

return executeWithTimeout(toolName, params, options);

} catch (Exception e) {

lastException = e;

retryCount++;

if (retryCount <= maxRetries) {

log.warn("Tool {} failed, retrying ({}/{})",

toolName, retryCount, maxRetries);

}

}

}

return ToolResult.failure(lastException.getMessage(), Map.of());

}

@Override

public List<ToolResult> executeParallel(List<ToolCall> calls) {

List<CompletableFuture<ToolResult>> futures = calls.stream()

.map(call -> CompletableFuture.supplyAsync(

() -> registry.execute(call.toolName(), call.params()),

executor))

.toList();

return futures.stream()

.map(CompletableFuture::join)

.toList();

}

}

__`_INLINE

三、多步骤任务编排

3.1 任务图的构建与执行

对于复杂业务场景,单步工具调用往往无法完成任务,需要将多个步骤按照一定逻辑编排执行。这就引出了"任务编排"(Task Orchestration)的概念。

任务编排的核心是任务图(Task Graph) ------一个有向无环图(DAG),节点是具体任务步骤,边代表执行顺序和依赖关系。每个节点包含任务描述、执行工具、输入参数,以及依赖于哪些前置节点的输出。

__`__INLINE_java

public record TaskGraph(

List<TaskNode> nodes,

List<TaskEdge> edges

) {

public List<TaskNode> getRoots() {

// 返回没有前置依赖的起始节点

Set<String> hasParent = edges.stream()

.map(TaskEdge::targetId)

.collect(Collectors.toSet());

return nodes.stream()

.filter(n -> !hasParent.contains(n.id()))

.toList();

}

public List<TaskNode> getDependents(String nodeId) {

// 返回直接依赖某节点的所有后续节点

return edges.stream()

.filter(e -> e.sourceId().equals(nodeId))

.map(e -> nodes.stream()

.filter(n -> n.id().equals(e.targetId()))

.findFirst().orElseThrow())

.toList();

}

}

__`_INLINE

任务图的执行引擎负责按照依赖关系调度任务。对于没有依赖的节点(根节点),可以并行执行;对于有依赖的节点,必须等待所有前置节点完成后才能开始。

3.2 并行执行与顺序依赖

实际业务中,我们经常需要混合使用并行和顺序两种编排模式。例如,数据采集步骤可以从多个数据源并行拉取数据(提速),但数据融合步骤必须等待所有采集任务完成(顺序依赖)。

__`__INLINE_java

@Service

@Slf4j

public class WorkflowOrchestrator {

@Autowired

private ToolExecutor toolExecutor;

/**

* 执行任务图,返回聚合结果

*/

public WorkflowResult execute(TaskGraph graph, Map<String, Object> context) {

Map<String, Object> results = new ConcurrentHashMap<>();

Map<String, TaskStatus> statuses = new ConcurrentHashMap<>();

AtomicInteger completedCount = new AtomicInteger(0);

// 找出所有根节点(无前置依赖的节点)

List<TaskNode> roots = graph.getRoots();

// 并行执行根节点

List<CompletableFuture<Void>> rootFutures = roots.stream()

.map(node -> CompletableFuture.runAsync(() -> {

executeNode(node, context, results, statuses);

completedCount.incrementAndGet();

// 触发后续节点检查

triggerDependents(node.id(), graph, context, results, statuses, completedCount);

}))

.toList();

// 等待所有任务完成

CompletableFuture.allOf(rootFutures.toArray(new CompletableFuture[0])).join();

return new WorkflowResult(results, statuses);

}

private void triggerDependents(String nodeId, TaskGraph graph,

Map<String, Object> context, Map<String, Object> results,

Map<String, TaskStatus> statuses, AtomicInteger completedCount) {

List<TaskNode> dependents = graph.getDependents(nodeId);

for (TaskNode dependent : dependents) {

// 检查该节点的所有前置依赖是否都已完成

boolean allDependenciesMet = graph.getDependencies(dependent.id()).stream()

.allMatch(depId -> statuses.get(depId) == TaskStatus.COMPLETED);

if (allDependenciesMet) {

// 构建当前节点的输入参数(从前置节点结果中提取)

Map<String, Object> params = buildParams(dependent, results, context);

CompletableFuture.runAsync(() -> {

executeNode(dependent, params, results, statuses);

completedCount.incrementAndGet();

triggerDependents(dependent.id(), graph, context, results, statuses, completedCount);

});

}

}

}

private void executeNode(TaskNode node, Map<String, Object> params,

Map<String, Object> results, Map<String, TaskStatus> statuses) {

statuses.put(node.id(), TaskStatus.RUNNING);

try {

log.info("Executing node: {}", node.id());

ToolResult result = toolExecutor.execute(node.toolName(), params, node.options());

results.put(node.id(), result);

statuses.put(node.id(), result.success() ? TaskStatus.COMPLETED : TaskStatus.FAILED);

} catch (Exception e) {

log.error("Node {} execution failed", node.id(), e);

statuses.put(node.id(), TaskStatus.FAILED);

}

}

}

__`_INLINE

3.3 条件分支与循环控制

在真实业务场景中,往往需要根据中间结果动态决定后续分支。例如,数据库连接失败时走告警流程而非继续查询;订单金额超过限额时需要人工审批。这些条件分支逻辑可以通过在任务图中引入"条件节点"来实现。

__`__INLINE_java

public interface TaskCondition {

boolean evaluate(Map<String, Object> context, Map<String, Object> results);

}

// 示例:判断是否有错误

public class ErrorThresholdCondition implements TaskCondition {

private final double threshold;

@Override

public boolean evaluate(Map<String, Object> context, Map<String, Object> results) {

Object errorCount = results.get("error_count");

if (errorCount instanceof Number n) {

return n.doubleValue() > threshold;

}

return false;

}

}

__`_INLINE

循环控制则通过在 Planner 层面实现"重规划"来实现。当某个任务执行失败或结果不满足预期时,将结果反馈给 LLM 进行二次规划,形成 Agent-Executor 的反馈循环。这种机制使得 Agent 能够处理需要多轮交互的复杂业务流程。

四、企业级实战:智能数据报表分析 Agent

4.1 业务场景与需求分析

让我们以一个真实的企业级场景------智能数据报表分析 Agent ------来完整展示 Java Agent 系统的设计与实现全过程。

业务背景:某电商公司数据团队每天需要花费大量时间手动从多个数据源(订单系统、CRM 系统、财务系统)拉取数据,进行汇总计算,生成日报、周报、月报。报表格式相对固定,但数据来源多、计算逻辑复杂、且经常需要根据管理层的新需求临时调整。

我们的目标:构建一个智能数据分析 Agent,用户只需说"帮我生成上个月各区域销售冠军及其同比增长率",Agent 自动完成数据获取、计算、格式化全过程,并返回可直接使用的分析结论。

4.2 系统设计

整体架构分为以下几个模块:

数据采集层(Data Acquisition Layer) :通过 _DatabaseTool 连接数据仓库,通过 HttpTool 调用各业务系统的 RESTful API,通过 FileTool__INLINE 读取本地 CSV/Excel 文件。

数据处理层(Data Processing Layer) :对原始数据进行清洗、转换、聚合计算。使用 _CalculationTool 执行复杂数学运算,使用 GroupingTool__INLINE 执行多维度分组汇总。

分析推理层(Analysis Layer) :基于 LLM 的分析能力,对计算结果进行业务层面的解读,生成文字结论和洞察建议。

报告生成层(Reporting Layer) :将分析结果渲染为指定格式(JSON、HTML、Markdown),支持多种图表类型的嵌入。

4.3 核心工具实现

DatabaseTool 是整个系统的数据中枢。以下是一个生产级的实现:

__`__INLINE_java

@Component

@ToolDef(name = "db_query", description = "执行SQL查询从数据仓库获取数据",

inputSchema = """

{

"type": "object",

"properties": {

"sql": {"type": "string", "description": "待执行的SQL语句"},

"timeout": {"type": "integer", "description": "超时时间(秒)", "default": 30}

},

"required": ["sql"]

}

""")

@Slf4j

public class DatabaseTool implements Tool {

@Autowired

private DataSource dataSource; // 配置好的数据源

@Override

public String name() {

return "db_query";

}

@Override

public String description() {

return "从企业数据仓库执行SQL查询,返回结构化数据结果集";

}

@Override

public ToolSchema inputSchema() {

return ToolSchema.builder()

.addProperty("sql", "string", "待执行的SELECT查询语句")

.addProperty("timeout", "integer", "查询超时时间(秒)", false, 30)

.required("sql")

.build();

}

@Override

public ToolResult execute(Map<String, Object> params) {

String sql = (String) params.get("sql");

int timeout = params.get("timeout") != null

? ((Number) params.get("timeout")).intValue()

: 30;

// SQL 安全校验:仅允许 SELECT 语句

if (!isSelectStatement(sql)) {

return ToolResult.failure("Only SELECT statements are allowed", Map.of());

}

try (Connection conn = dataSource.getConnection();

Statement stmt = conn.createStatement()) {

stmt.setQueryTimeout(timeout);

long start = System.currentTimeMillis();

ResultSet rs = stmt.executeQuery(sql);

List<Map<String, Object>> rows = resultSetToList(rs);

long duration = System.currentTimeMillis() - start;

return ToolResult.success(

JSON.toJSONString(rows),

Map.of("row_count", rows.size(), "duration_ms", duration)

);

} catch (SQLException e) {

log.error("Database query failed", e);

return ToolResult.failure("Query failed: " + e.getMessage(),

Map.of("sql_state", e.getSQLState()));

}

}

private boolean isSelectStatement(String sql) {

String trimmed = sql.trim().toLowerCase();

return trimmed.startsWith("select") && !trimmed.contains("drop")

&& !trimmed.contains("delete") && !trimmed.contains("insert")

&& !trimmed.contains("update") && !trimmed.contains("truncate");

}

}

__`_INLINE

HttpTool 用于调用第三方业务系统的 API:

__`__INLINE_java

@Component

@ToolDef(name = "http_call", description = "发起HTTP请求调用外部REST API",

inputSchema = """

{

"type": "object",

"properties": {

"url": {"type": "string"},

"method": {"type": "string", "enum": ["GET", "POST", "PUT", "DELETE"]},

"headers": {"type": "object"},

"body": {"type": "object"}

},

"required": ["url", "method"]

}

""")

@Slf4j

public class HttpTool implements Tool {

private final RestTemplate restTemplate;

public HttpTool(RestTemplateBuilder builder) {

this.restTemplate = builder

.setConnectTimeout(Duration.ofSeconds(10))

.setReadTimeout(Duration.ofSeconds(30))

.build();

}

@Override

public String name() {

return "http_call";

}

@Override

public ToolResult execute(Map<String, Object> params) {

String url = (String) params.get("url");

String method = (String) params.get("method");

Map<String, Object> headers = params.get("headers") != null

? (Map<String, Object>) params.get("headers") : Map.of();

Object body = params.get("body");

HttpHeaders httpHeaders = new HttpHeaders();

headers.forEach((k, v) -> httpHeaders.set(k, String.valueOf(v)));

httpHeaders.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<Object> entity = new HttpEntity<>(body, httpHeaders);

try {

long start = System.currentTimeMillis();

ResponseEntity<String> response = restTemplate.exchange(

url, HttpMethod.valueOf(method), entity, String.class);

long duration = System.currentTimeMillis() - start;

return ToolResult.success(

response.getBody(),

Map.of("status", response.getStatusCode().value(),

"duration_ms", duration)

);

} catch (HttpClientErrorException e) {

return ToolResult.failure("HTTP " + e.getStatusCode() + ": " + e.getMessage(),

Map.of("status", e.getStatusCode().value()));

} catch (Exception e) {

return ToolResult.failure("HTTP request failed: " + e.getMessage(), Map.of());

}

}

}

__`_INLINE

4.4 Agent 核心服务实现

将所有组件整合起来,构建完整的 _IntelligentReportAgent__INLINE

__`__INLINE_java

@Service

@Slf4j

public class IntelligentReportAgent implements BusinessAgent {

@Autowired

private ToolRegistry toolRegistry;

@Autowired

private TaskPlanner planner;

@Autowired

private WorkflowOrchestrator orchestrator;

@Autowired

private LlmClient llmClient;

@Override

public AgentResponse execute(AgentRequest request) {

log.info("Received request: {}", request.instruction());

long start = System.currentTimeMillis();

try {

// Step 1: 获取所有可用工具

List<Tool> availableTools = toolRegistry.getAll();

// Step 2: 任务规划 - 将自然语言指令转化为任务图

TaskGraph graph = planner.plan(request.instruction(), availableTools);

log.info("Task graph created with {} nodes", graph.nodes().size());

// Step 3: 执行任务图

WorkflowResult result = orchestrator.execute(graph, Map.of(

"user_id", request.userId(),

"org_id", request.orgId(),

"instruction", request.instruction()

));

// Step 4: 结果分析与自然语言生成

String conclusion = generateConclusion(request.instruction(), result);

long duration = System.currentTimeMillis() - start;

log.info("Request completed in {}ms", duration);

return AgentResponse.success(

conclusion,

result.rawResults(),

Map.of("duration_ms", duration, "steps_executed", graph.nodes().size())

);

} catch (Exception e) {

log.error("Agent execution failed", e);

return AgentResponse.failure("处理失败: " + e.getMessage());

}

}

private String generateConclusion(String instruction, WorkflowResult result) {

// 将结构化数据结果交给 LLM 生成自然语言分析结论

String prompt = String.format("""

基于以下原始数据,给出针对用户问题的分析结论。

用户问题:%s

数据结果:

%s

请用专业的数据分析师口吻,给出简洁明了的分析结论,包含关键数字和同比/环比变化。

""", instruction, JSON.toJSONString(result.rawResults()));

return llmClient.complete(prompt).getContent();

}

}

__`_INLINE

4.5 Spring Boot 启动配置

为了让 Agent 系统在 Spring Boot 环境中正确启动,需要进行以下配置:

__`__INLINE_java

@Configuration

public class AgentConfiguration {

@Bean

public ToolRegistry toolRegistry() {

return new ToolRegistryImpl();

}

@Bean

public ToolExecutor toolExecutor(ToolRegistry registry) {

return new ToolExecutorImpl(registry);

}

@Bean

public WorkflowOrchestrator workflowOrchestrator(ToolExecutor toolExecutor) {

return new WorkflowOrchestrator(toolExecutor);

}

@Bean

public RestTemplate restTemplate(RestTemplateBuilder builder) {

return builder.build();

}

// 工具自动注册

@Autowired

private void registerTools(ApplicationContext ctx, ToolRegistry registry) {

Map<String, Tool> tools = ctx.getBeansOfType(Tool.class);

tools.values().forEach(registry::register);

log.info("Registered {} tools", tools.size());

}

}

__`_INLINE

4.6 RESTful API 接口

将 Agent 暴露为 HTTP 接口,供前端或第三方系统调用:

__`__INLINE_java

@RestController

@RequestMapping("/api/v1/agent")

@Slf4j

public class AgentController {

@Autowired

private BusinessAgent reportAgent;

@PostMapping("/report")

public ResponseEntity<AgentResponse> generateReport(

@RequestBody AgentRequest request,

@RequestHeader(value = "X-User-Id", required = false) String userId) {

if (userId != null) {

request = new AgentRequest(request.instruction(), userId, request.orgId());

}

return ResponseEntity.ok(reportAgent.execute(request));

}

@GetMapping("/tools")

public ResponseEntity<List<ToolInfo>> listTools() {

return ResponseEntity.ok(reportAgent.getRegisteredTools().stream()

.map(t -> new ToolInfo(t.name(), t.description(), t.inputSchema()))

.toList());

}

}

__`_INLINE

五、生产级工程实践

5.1 异常处理与容错机制

生产环境中的 Agent 系统面临诸多不确定性:LLM 服务不可用、网络超时、数据库连接池耗尽、工具返回异常数据等。完善的异常处理和容错机制是系统可靠运行的关键。

分层异常处理策略

第一层是工具级容错------每个工具执行时都包装在 try-catch 中,单个工具失败不影响其他工具和整体流程。工具返回 __ToolResult.failure(...)_INLINE 而非抛出异常,使执行引擎能够继续调度后续步骤。

第二层是执行引擎级容错------通过重试机制(_ToolExecutor 的 maxRetries__INLINE 配置)和熔断器模式,防止单个工具的持续失败拖垮整个系统。

第三层是 Agent 级容错------当所有执行路径都失败时,Agent 返回明确的错误信息和部分结果(能返回多少返回多少),而非直接崩溃。

__`__INLINE_java

// 熔断器实现

@Service

@Slf4j

public class CircuitBreakerTool implements Tool {

private final Tool delegate;

private final AtomicInteger failureCount = new AtomicInteger(0);

private final AtomicReference<Long> lastFailureTime = new AtomicReference<>(0L);

private final int failureThreshold;

private final long resetTimeoutMs;

public CircuitBreakerTool(Tool delegate, int failureThreshold, long resetTimeoutMs) {

this.delegate = delegate;

this.failureThreshold = failureThreshold;

this.resetTimeoutMs = resetTimeoutMs;

}

@Override

public ToolResult execute(Map<String, Object> params) {

if (isOpen()) {

return ToolResult.failure("Circuit breaker is open for: " + delegate.name(), Map.of());

}

try {

ToolResult result = delegate.execute(params);

if (!result.success()) {

recordFailure();

} else {

reset();

}

return result;

} catch (Exception e) {

recordFailure();

throw e;

}

}

private boolean isOpen() {

if (failureCount.get() < failureThreshold) return false;

long elapsed = System.currentTimeMillis() - lastFailureTime.get();

if (elapsed > resetTimeoutMs) {

reset();

return false;

}

return true;

}

private void recordFailure() {

failureCount.incrementAndGet();

lastFailureTime.set(System.currentTimeMillis());

}

private void reset() {

failureCount.set(0);

}

}

__`_INLINE

5.2 安全防护

Agent 系统涉及对数据库、文件系统、外部 API 的操作,安全问题尤为重要。

SQL 注入防护 :所有数据库操作必须通过参数化查询,不允许动态拼接用户输入到 SQL 语句中。__DatabaseTool 中的 isSelectStatement()_INLINE 校验仅是第一道防线,真正的安全来自参数化查询。

输入校验 :使用 Spring Validation 对用户输入进行严格校验。使用 JSON Schema(__inputSchema()_INLINE 方法)对工具参数进行模式校验,防止畸形输入导致系统行为异常。

权限控制 :不同用户角色可调用的工具集合不同。引入 _ToolPermissionService__INLINE,在执行工具前检查当前用户权限。

__`__INLINE_java

@Component

@Slf4j

public class SecureToolExecutor implements ToolExecutor {

@Autowired

private ToolExecutor delegate;

@Autowired

private ToolPermissionService permissionService;

@Override

public ToolResult execute(String toolName, Map<String, Object> params, ExecuteOptions options) {

if (!permissionService.hasPermission(getCurrentUser(), toolName)) {

log.warn("User {} attempted to access tool {} without permission",

getCurrentUser(), toolName);

return ToolResult.failure("Permission denied for tool: " + toolName, Map.of());

}

return delegate.execute(toolName, params, options);

}

private String getCurrentUser() {

// 从 Security Context 获取当前用户

Authentication auth = SecurityContextHolder.getContext().getAuthentication();

return auth != null ? auth.getName() : "anonymous";

}

}

__`_INLINE

5.3 性能优化

Agent 系统的性能瓶颈主要在 LLM 调用和数据获取两个环节。

LLM 调用的优化策略 包括:使用支持流式输出的 API 减少等待感;对频繁请求的相同上下文进行缓存(如通过 Redis 缓存 Planner 的中间结果);对实时性要求不高的场景使用异步非阻塞调用。

数据获取的优化 核心是并行拉取 ------当任务图中多个节点之间无依赖时,利用 _CompletableFuture.allOf() 并行执行,大幅缩短总执行时间。在 IntelligentReportAgent__INLINE 的案例中,数据仓库查询、第三方 API 调用、本地文件读取可以并行进行,这往往能将整体耗时从"三步串行"变为"一步并行"。

__`__INLINE_java

// 并行拉取优化示例

private Map<String, Object> fetchAllDataInParallel(List<DataSource> sources) {

List<CompletableFuture<Map.Entry<String, Object>>> futures = sources.stream()

.map(source -> CompletableFuture.supplyAsync(() -> {

Object data = fetchFromSource(source);

return Map.entry(source.getName(), data);

}, dataFetchExecutor))

.toList();

return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))

.thenApply(v -> futures.stream()

.map(CompletableFuture::join)

.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))

.join();

}

__`_INLINE

资源池化 :数据库连接池(HikariCP)、HTTP 连接池(Apache HttpClient 或 OkHttp 的连接池复用)、线程池(_Executors.newFixedThreadPool__INLINE)都是减少资源创建开销的标准手段。

5.4 可观测性与监控

Agent 系统的可观测性建设包括日志、指标和追踪三个方面。

结构化日志 :使用 SLF4J + Logback 的 JSON 格式输出,包含 traceId(请求链路追踪)、taskId(任务 ID)、toolName(工具名称)、duration(执行时长)等字段,便于在 ELK/Graylog 等日志系统中检索和分析。

关键指标 :通过 Micrometer + Prometheus 暴露以下核心指标:

  • _agent.requests.total__INLINE:Agent 请求总数(计数器)

  • _agent.request.duration__INLINE:请求处理耗时(histogram)

  • _agent.tool.executions.total__INLINE:各工具调用次数(带 toolName 标签)

  • _agent.tool.execution.duration__INLINE:各工具执行耗时(带 toolName 标签)

  • _agent.task.completion.rate__INLINE:任务完成率(成功/总数)

__`__INLINE_java

@Timed(value = "agent.tool.execution", extraTags = {"tool", "db_query"})

@Override

public ToolResult execute(String toolName, Map<String, Object> params, ExecuteOptions options) {

return delegate.execute(toolName, params, options);

}

__``

六、总结与展望

本文系统讲解了 Java 技术栈下 Agent 工作流的完整实现方案。从 Agent 的核心概念出发,我们深入剖析了任务规划、工具注册、任务编排等关键环节的设计思路与代码实现,并通过一个企业级实战案例------智能数据报表分析 Agent------展示了从架构设计到生产落地的全流程。

核心要点总结:

Agent 本质是"目标驱动的自主执行系统" ,与传统 RPA 的根本区别在于理解业务语义而非操作界面像素。Java 凭借其企业级生态(Spring Boot、依赖注入、并发框架、成熟的工具链),是构建生产级 Agent 系统的优选语言。

工具接口(Tool Interface)是 Agent 系统的设计核心 。将所有外部能力抽象为统一接口,使系统具备高度的可扩展性和可测试性。工具注册中心(ToolRegistry)负责管理工具的元数据和生命周期,工具执行器(ToolExecutor)提供超时控制、重试策略和并发执行能力。

任务编排是复杂业务场景的关键 。通过构建有向无环任务图(DAG),配合并行执行和顺序依赖的混合编排策略,可以高效处理多数据源、多步骤的企业级业务流程。

生产级工程实践不可忽视 。分层异常处理、熔断器、安全防护(SQL 注入防护、权限控制)、性能优化(并行拉取、资源池化)以及完善的可观测性建设,是 Agent 系统能够稳定可靠运行于生产环境的必要条件。

展望未来,Java Agent 技术将在以下几个方向持续演进:

一是多模态 Agent ------除文本外,Agent 将逐步支持图像、音频、视频等多种模态的输入输出,实现真正的多模态业务自动化。

二是多 Agent 协作 ------多个专业 Agent 通过消息传递和任务协作,形成 Agent 团队(Multi-Agent System),处理更复杂的跨领域问题。

三是长期记忆与持续学习 ------通过向量数据库和知识图谱技术,赋予 Agent 持久化记忆和从历史经验中持续优化的能力,使 Agent 在企业业务中越用越智能。

四是标准化协议 ------随着 Agent 技术的普及,跨框架、跨平台的 Agent 互操作协议(如 Anthropic 的 MCP、OpenAI 的 Plugins)将推动 Agent 生态走向开放和标准化。

Java 作为企业级后端开发的主导语言,在 Agent 时代将继续发挥其核心价值。掌握 Agent 工作流的 Java 实现,不仅是跟上技术趋势的必需,更是构建下一代智能化企业应用的能力基础。

作者:洛水石

相关推荐
Jurio.2 小时前
使用.py脚本下载并加载开源大模型LLMs
python·ai·llama
张哈大2 小时前
解密Function Calling:AI Agent工具调用的标准化核心
人工智能·python·ai
子榆.2 小时前
CANN ATC编译器:模型从Python到达芬奇指令走了多远
开发语言·python·neo4j
zzqssliu2 小时前
SpringBoot框架搭建跨境独立站|Taocarts代购系统订单模块深度开发
java·spring boot·后端
lookaroundd2 小时前
llm-compressor 普通量化调用链分析
python·算法
志栋智能2 小时前
超自动化巡检:保障数字化转型的“底座工程”
运维·自动化
Loo国昌2 小时前
从 Agent 编排到 Skill Runtime:企业 AI 工程化的下一层抽象
大数据·人工智能·后端·python·自然语言处理
dinl_vin2 小时前
FastAPI 系列 ·(四):数据库集成——SQLAlchemy 2.0 异步 ORM 与 Alembic 迁移
java·数据库·fastapi
编码者卢布2 小时前
【Azure Service Bus】Azure Service Bus Java SDK 中 Token 刷新异常的排查思路
java·python·azure