Spring AI 实战系列 | 第 9 篇:AI Agent 开发

📄 约 9000 字 | ⏱️ 阅读约 22 分钟

系列说明:本文是《Spring AI 实战系列》第 9 篇,系统讲解 AI Agent 的核心概念、ReAct 模式、Spring AI Agent 框架实战(智能客服/代码助手/数据分析)、多 Agent 协作,以及评估与优化策略。
前置知识:已完成第 6-8 篇,掌握 Tool Calling、Advisor 和 MCP 基础。


前言

ChatGPT 能回答问题,但让它"帮我订一张明天去上海的机票,查一下酒店,再安排一下行程",它就无能为力了。

AI Agent 正是为了解决此问题而生。 它不只是"回答问题",而是能自主感知、规划决策、调用工具、执行行动——从"聊天机器人"进化为"智能助理"。

若将前面的 RAG 比作给 AI 装上"眼睛",Tool Calling 给 AI 装上"双手",那 Agent 就是给 AI 装上"大脑"

本文从概念到实战,将 AI Agent 论述透彻。


一、什么是 AI Agent

1.1 核心定义

AI Agent 是一个能自主感知环境 → 推理规划 → 执行行动 → 获取反馈的智能系统。(其核心循环机制详见 2.2 ReAct 执行流程

1.2 与传统 AI 的本质区别

维度 传统 AI(Chatbot) AI Agent
交互方式 一问一答,被动响应 自主执行多步骤任务
能力边界 只能生成文本 能调用工具、操作环境
决策能力 无,完全依赖用户引导 自主规划和决策
记忆能力 仅当前对话上下文 长期记忆 + 学习积累
典型场景 客服问答、闲聊 订票、写代码、数据分析

一句话:Chatbot 是"回答问题",Agent 是"完成任务"。

1.3 Agent 的技术栈

AI Agent = LLM(大模型大脑)
         + Tool Calling(动手能力)
         + RAG(知识检索)
         + Memory(长期记忆)
         + Planning(任务规划)
         + Reflection(自我反思)

前面几篇学到的技术在此汇合:

已学技术 在 Agent 中的角色
ChatClient Agent 的基础交互层
Tool Calling Agent 的"手",执行具体操作
RAG Agent 的"知识库",提供背景信息
Advisor Agent 的"技能插件"(记忆、安全等)
MCP Agent 的"工具市场",标准化工具接入

1.4 什么时候该用 Agent

不是所有场景都需要 Agent。选型原则:

复杂度 场景 推荐方案
简单 “北京天气怎么样?” ChatClient
中等 “帮我查下订单状态” ChatClient + Tool Calling
中高 “分析这份销售数据” ChatClient + RAG + Tool Calling
复杂 “帮我策划一次团建活动:查天气、比价、预订、通知所有人” Agent

判断标准:任务是否需要多步推理 + 多工具协作 + 自主决策?若是,则采用 Agent。


二、ReAct 模式:Agent 的核心思维框架

2.1 什么是 ReAct

ReAct = Reasoning(推理)+ Acting(行动)

这是目前最主流的 Agent 思维模式。核心思想:让 AI 一步一步思考,每步思考后决定是继续推理还是调用工具。

2.2 ReAct 执行流程

ReAct 的核心是一个思考 → 行动 → 观察的循环:

ReAct 核心循环

下面通过一个完整的例子来看 ReAct 是如何逐步执行的:

ReAct 执行示例

2.3 ReAct 的 Prompt 模板

Spring AI 内部使用类似以下的 Prompt 结构驱动 ReAct 循环:

你可以使用以下工具完成用户任务:
{tool_definitions}

请按以下格式思考和行动:

思考:[你需要做什么,为什么]
行动:[工具名称]
输入:[工具参数]
观察:[工具返回结果]

...(重复思考→行动→观察循环,直到获得足够信息)

最终回答:[基于所有信息给出的最终答案]

2.4 ReAct vs 其他模式

模式 特点 适用场景
ReAct 思考-行动交替,逐步推进 大多数通用场景
Plan-and-Execute 先制定完整计划,再逐步执行 复杂多步任务
Act-only 不思考直接行动 简单工具调用

Spring AI 默认使用 ReAct 模式,对大多数场景已够用。


三、Spring AI Agent 开发实战

3.1 最简 Agent

从最简单的开始——一个带工具的 Agent:

@Service
public class SimpleAgent {

    private final ChatClient chatClient;

    public SimpleAgent(ChatClient.Builder builder, WeatherService weatherService) {
        this.chatClient = builder
            .defaultTools(weatherService)
            .build();
    }

    public String execute(String task) {
        return chatClient.prompt()
            .system("你是一位智能助手。请利用可用工具完成用户的任务。")
            .user(task)
            .call()
            .content();
    }
}

就这么简单。Spring AI 的 defaultTools() + ChatModel 本身支持的多轮 Tool Calling,就已经构成了一个基础的 Agent。

3.2 自主规划 Agent —— 任务分解

对于复杂任务,Agent 需要先分解再逐个击破:

@Service
public class PlanningAgent {

    private final ChatClient chatClient;

    public PlanningAgent(ChatClient.Builder builder, List<ToolCallback> tools) {
        this.chatClient = builder.defaultTools(tools).build();
    }

    public String execute(String task) {
        // Phase 1: 任务分解
        List<String> steps = decomposeTask(task);
        log.info("任务分解为 {} 个步骤", steps.size());

        // Phase 2: 逐步执行
        StringBuilder executionLog = new StringBuilder();
        for (int i = 0; i < steps.size(); i++) {
            log.info("执行步骤 {}/{}: {}", i + 1, steps.size(), steps.get(i));
            String stepResult = executeStep(steps.get(i));
            executionLog.append(String.format("**步骤 %d**: %s\n\n", i + 1, stepResult));
        }

        // Phase 3: 汇总结果
        return summarize(task, executionLog.toString());
    }

    /**
     * 让 AI 把复杂任务拆解为可执行的子步骤
     */
    private List<String> decomposeTask(String task) {
        String plan = chatClient.prompt()
            .system(""" 
                你是任务规划专家。将用户任务分解为具体的、可执行的步骤。
                要求:
                - 每个步骤应该是原子操作
                - 步骤之间有逻辑依赖关系
                - 输出格式:每行一个步骤,以数字编号开头
                """)
            .user("请分解以下任务:" + task)
            .call()
            .content();

        return Arrays.stream(plan.split("\n"))
            .map(String::trim)
            .filter(s -> !s.isEmpty() && s.matches("\\d+\\.?.*"))
            .collect(Collectors.toList());
    }

    private String executeStep(String step) {
        return chatClient.prompt()
            .system("你正在执行一个复杂任务的某个步骤。专注完成这个步骤。")
            .user(step)
            .call()
            .content();
    }

    private String summarize(String task, String executionLog) {
        return chatClient.prompt()
            .system("基于以下执行记录,生成简洁的任务总结报告。")
            .user("原始任务:" + task + "\n\n执行记录:\n" + executionLog)
            .call()
            .content();
    }
}

效果示例:

String result = planningAgent.execute(
    "帮我们团队安排一次团建活动:查一下下周六北京的天气," +
    "找几个适合户外活动的场地推荐,估算人均预算"
);

// Agent 会自动分解为:
// 1. 查询下周六北京天气预报
// 2. 根据天气搜索合适的团建场地
// 3. 估算各场地的费用和人均预算
// 4. 汇总推荐方案

3.3 多工具编排 Agent

实际项目的 Agent 通常需要同时使用多种工具:

@Service
public class SmartOfficeAgent {

    private final ChatClient chatClient;

    public SmartOfficeAgent(
            ChatClient.Builder builder,
            WeatherService weatherService,
            CalendarService calendarService,
            OrderService orderService,
            DatabaseToolService dbService) {

        this.chatClient = builder
            .defaultTools(weatherService, calendarService, orderService, dbService)
            .build();
    }

    public String handle(String request) {
        return chatClient.prompt()
            .system("""
                你是一位智能办公助手。你可以:
                - 查询天气(规划出行时很有用)
                - 查询日历(安排会议前先看空闲时间)
                - 查询订单(处理客户问题)
                - 查询数据库(获取业务数据)
                
                请根据用户需求选择合适的工具,必要时组合使用多个工具。
                如果需要的信息不足,主动询问用户。
                """)
            .user(request)
            .call()
            .content();
    }
}

3.4 有状态的 Agent —— 对话记忆

Agent 亦需记住上下文:

@Component
public class StatefulAgent {

    private final ChatClient chatClient;
    private final Map<String, AgentSession> sessions = new ConcurrentHashMap<>();

    public StatefulAgent(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    public String execute(String sessionId, String task) {
        AgentSession session = sessions.computeIfAbsent(sessionId, AgentSession::new);

        // 构建包含历史的消息列表
        List<Message> messages = new ArrayList<>(session.getHistory());
        messages.add(new UserMessage(task));

        // 执行
        String result = chatClient.prompt()
            .messages(messages)
            .call()
            .content();

        // 更新会话状态
        session.addInteraction(task, result);

        return result;
    }

    public void clearSession(String sessionId) {
        sessions.remove(sessionId);
    }

    /** Agent 会话状态 */
    private static class AgentSession {
        private final List<Message> history = new ArrayList<>();
        private final LocalDateTime createdAt = LocalDateTime.now();

        public List<Message> getHistory() { return Collections.unmodifiableList(history); }
        
        public void addInteraction(String userMsg, String assistantMsg) {
            history.add(new UserMessage(userMsg));
            history.add(new AssistantMessage(assistantMsg));
            // 限制历史长度,防止 Token 超限
            if (history.size() > 40) {
                history.subList(0, 10).clear();  // 移除最早的消息
            }
        }
    }
}

3.5 自我反思 Agent

高级 Agent 能自我评估并改进:

@Service
public class ReflectiveAgent {

    private final ChatClient chatClient;
    private static final int MAX_REFLECTIONS = 2;

    public ReflectiveAgent(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    public String executeWithReflection(String task) {
        String currentResult = null;
        String reflection = null;

        for (int round = 0; round <= MAX_REFLECTIONS; round++) {
            if (round == 0) {
                // 第一轮:直接执行
                currentResult = execute(task);
            } else {
                // 后续轮:根据反思改进后重新执行
                String improvedPrompt = task + "\n\n【改进要求】\n" + reflection;
                currentResult = execute(improvedPrompt);
                log.info("第 {} 轮反思后重新执行完成", round);
            }

            // 自我反思
            reflection = reflect(task, currentResult);

            // 如果质量达标,提前结束
            if (isQualityAcceptable(reflection)) {
                log.info("经过 {} 轮迭代,结果已达标", round + 1);
                break;
            }
        }

        return currentResult;
    }

    private String execute(String task) {
        return chatClient.prompt().user(task).call().content();
    }

    private String reflect(String task, String result) {
        return chatClient.prompt()
            .system("""
                你是一个严格的质量评审专家。请评估以下任务完成质量。
                
                评估维度:
                1. 是否完整回答了用户的问题?
                2. 信息是否准确?
                3. 回答是否清晰有条理?
                4. 是否有遗漏或错误?
                
                如果质量不达标,请明确指出需要改进的地方。
                如果质量达标,回复"QUALITY_OK"。
                """)
            .user("任务:" + task + "\n\n回答:" + result)
            .call()
            .content();
    }

    private boolean isQualityAcceptable(String reflection) {
        return reflection.contains("QUALITY_OK");
    }
}

四、三大实战案例

4.1 智能客服 Agent

结合 RAG + Tool Calling + 对话记忆的完整客服方案:

@Service
public class CustomerServiceAgent {

    private final ChatClient chatClient;

    public CustomerServiceAgent(
            ChatClient.Builder builder,
            VectorStore vectorStore,
            OrderToolService orderTool,
            ChatMemory chatMemory) {

        this.chatClient = builder
            .defaultTools(orderTool)
            .build();
        this.vectorStore = vectorStore;
        this.chatMemory = chatMemory;
    }

    public String handle(String sessionId, String userMessage) {
        return chatClient.prompt()
            .system("""
                你是某电商平台的智能客服助手。你的能力范围:
                
                1. 产品咨询 → 使用知识库(RAG)回答产品相关问题
                2. 订单查询 → 调用订单工具查询物流和状态
                3. 退换货处理 → 调用退款工具处理申请
                4. 投诉建议 → 共情倾听,必要时转人工
                
                规则:
                - 基于真实数据回答,不要编造
                - 语气友好专业
                - 涉及金钱操作时二次确认
                - 无法解决的问题建议转人工客服
                """)
            .user(userMessage)
            .advisors(
                new QuestionAnswerAdvisor(vectorStore),           // RAG 知识库
                new MessageChatMemoryAdvisor(chatMemory, sessionId, 10),  // 对话记忆
                new SafeGuardAdvisor(),                            // 安全过滤
                new LoggingAdvisor()                               // 日志审计
            )
            .call()
            .content();
    }
}

配套的工具服务:

@Service
public class OrderToolService {

    @Tool(name = "queryOrderStatus", 
          description = "查询订单状态和物流进度。当用户询问订单、物流、发货时使用")
    public OrderStatus queryOrderStatus(
            @ToolParam(description = "订单号") String orderId) {
        
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new RuntimeException("订单不存在:" + orderId));
        return new OrderStatus(order.getId(), order.getStatus(), 
                               order.getTrackingNumber(), order.getEstimatedDelivery());
    }

    @Tool(name = "applyRefund", 
          description = "申请订单退款。需要订单号和退款原因")
    public RefundResult applyRefund(
            @ToolParam(description = "订单号") String orderId,
            @ToolParam(description = "退款原因:质量问题/不想要了/发错货/其他") String reason) {
        
        Order order = orderRepository.findById(orderId).orElse(null);
        if (order == null || !order.canRefund()) {
            return new RefundResult(false, "该订单当前不支持退款");
        }
        
        refundService.createRefund(orderId, reason);
        return new RefundResult(true, "退款申请已提交,预计 3-5 个工作日到账");
    }

    public record OrderStatus(String orderId, String status, 
                               String trackingNo, String estimatedDelivery) {}
    public record RefundResult(boolean success, String message) {}
}

4.2 代码助手 Agent

@Service
public class CodeAssistantAgent {

    private final ChatClient chatClient;

    public CodeAssistantAgent(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    /** 代码生成 */
    public String generateCode(String requirement) {
        return chatClient.prompt()
            .system("""
                你是一位资深 Java/Spring Boot 开发工程师。
                
                代码规范要求:
                1. 遵循阿里巴巴 Java 开发手册
                2. 类和方法要有清晰的 Javadoc 注释
                3. 合理使用设计模式
                4. 必须处理异常和边界条件
                5. 使用现代 Java 特性(record、var、Stream 等)
                6. 提供完整可运行的代码,不要省略
                """)
            .user("需求:" + requirement)
            .call()
            .content();
    }

    /** 代码审查 */
    public String reviewCode(String code) {
        return chatClient.prompt()
            .system("""
                你是一位严格的代码审查专家(Senior Code Reviewer)。
                
                审查维度(按优先级排序):
                1. 🔴 严重:潜在的 Bug、空指针风险、并发安全问题
                2. 🟡 警告:性能问题、资源泄漏风险
                3. 🔵 建议:代码风格、命名规范、设计模式优化
                
                输出格式:按严重程度分类列出问题,每个问题给出:
                - 位置(行号/方法名)
                - 问题描述
                - 修改建议(给出代码片段)
                """)
            .user(code)
            .call()
            .content();
    }

    /** 单元测试生成 */
    public String generateTests(String sourceCode) {
        return chatClient.prompt()
            .system("""
                你是一位测试工程师。为以下代码生成 JUnit 5 单元测试。
                
                要求:
                1. 使用 Mockito mock 外部依赖
                2. 覆盖正常流程和异常流程
                3. 测试方法命名采用 should_ExpectedBehavior_When_State 格式
                4. 使用 AssertJ 断言库
                5. 关键测试添加注释说明测试意图
                """)
            .user(sourceCode)
            .call()
            .content();
    }
}

4.3 数据分析 Agent

Text-to-SQL + 数据分析的完整链路:

@Service
public class DataAnalysisAgent {

    private final ChatClient chatClient;
    private final JdbcTemplate jdbcTemplate;

    public DataAnalysisAgent(ChatClient.Builder builder, DataSource dataSource) {
        this.chatClient = builder.build();
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public String analyze(String question) {
        // Step 1: Text → SQL
        String sql = generateSql(question);
        log.info("生成的 SQL: {}", sql);

        // 安全检查
        if (!isSqlSafe(sql)) {
            return "抱歉,无法执行该查询。";
        }

        // Step 2: 执行 SQL
        List<Map<String, Object>> data = executeQuery(sql);

        // Step 3: 数据分析
        return analyzeData(question, data);
    }

    private String generateSql(String question) {
        String schema = getDatabaseSchema();

        return chatClient.prompt()
            .system("""
                你是 SQL 专家。根据表结构生成 PostgreSQL 查询语句。
                
                规则:
                - 只使用 SELECT 查询,禁止 DDL/DML 操作
                - LIMIT 100 条防止数据量过大
                - 只输出 SQL 语句,不要解释
                - 表名和字段名必须与 schema 一致
                """)
            .user("数据库表结构:\n" + schema + "\n\n问题:" + question)
            .call()
            .content()
            .replace("```sql", "").replace("```", "").trim();
    }

    private List<Map<String, Object>> executeQuery(String sql) {
        try {
            return jdbcTemplate.queryForList(sql);
        } catch (Exception e) {
            log.error("SQL 执行失败: {}", sql, e);
            throw new RuntimeException("查询执行失败:" + e.getMessage());
        }
    }

    private String analyzeData(String question, List<Map<String, Object>> data) {
        if (data.isEmpty()) {
            return "查询结果为空,没有找到相关数据。";
        }

        String dataStr = formatDataForLLM(data, 50);  // 限制条数

        return chatClient.prompt()
            .system("""
                你是数据分析专家。基于查询结果进行分析。
                
                分析要求:
                1. 用通俗语言解释数据含义
                2. 发现关键趋势和异常点
                3. 给出可行的业务建议
                4. 适当使用数据支撑结论
                """)
            .user("问题:" + question + "\n\n查询结果:\n" + dataStr)
            .call()
            .content();
    }

    /** SQL 安全检查 */
    private boolean isSqlSafe(String sql) {
        String upperSql = sql.toUpperCase();
        List<String> dangerousKeywords = List.of(
            "DROP", "DELETE", "INSERT", "UPDATE", "ALTER", "CREATE",
            "TRUNCATE", "EXEC", "EXECUTE"
        );
        return dangerousKeywords.stream().noneMatch(upperSql::contains);
    }

    private String getDatabaseSchema() {
        // 返回数据库表结构描述
        return """
            -- 订单表
            orders(id BIGINT PK, user_id VARCHAR, total_amount DECIMAL(10,2),
                   status VARCHAR, create_time TIMESTAMP)
            
            -- 用户表  
            users(id VARCHAR PK, name VARCHAR, email VARCHAR, 
                  register_date TIMESTAMP)
                  
            -- 商品表
            products(id BIGINT PK, name VARCHAR, category VARCHAR,
                     price DECIMAL(10,2), stock INT)
            """;
    }
}

五、多 Agent 协作

复杂场景需要多个专业 Agent 分工协作:

5.1 Agent 路由器

@Service
public class MultiAgentOrchestrator {

    private final CustomerServiceAgent customerAgent;   // 客服 Agent
    private final CodeAssistantAgent codeAgent;          // 代码 Agent
    private final DataAnalysisAgent dataAgent;           // 数据 Agent
    private final ChatClient router;                      // 路由器

    public MultiAgentOrchestrator(
            CustomerServiceAgent customerAgent,
            CodeAssistantAgent codeAgent,
            DataAnalysisAgent dataAgent,
            ChatClient.Builder builder) {

        this.customerAgent = customerAgent;
        this.codeAgent = codeAgent;
        this.dataAgent = dataAgent;
        this.router = builder.build();
    }

    public String dispatch(String sessionId, String userMessage) {
        // Step 1: 意图识别,路由到对应 Agent
        String agentType = routeToAgent(userMessage);
        log.info("消息路由到 {} Agent", agentType);

        // Step 2: 委托给对应的 Agent 处理
        return switch (agentType) {
            case "customer_service" -> customerAgent.handle(sessionId, userMessage);
            case "code_assistant" -> codeAgent.generateCode(userMessage);
            case "data_analysis" -> dataAgent.analyze(userMessage);
            default -> handleGeneralQuery(userMessage);
        };
    }

    private String routeToAgent(String message) {
        return router.prompt()
            .system(""" 
                判断用户问题的类型,只返回以下之一:
                - customer_service(客服类:订单、售后、产品咨询)
                - code_assistant(代码类:写代码、review、测试)
                - data_analysis(数据分析类:查询数据、报表、统计)
                - general(其他一般性问题)
                """)
            .user(message)
            .call()
            .content()
            .trim()
            .toLowerCase();
    }

    private String handleGeneralQuery(String message) {
        return router.prompt()
            .system("你是一个通用助手。回答用户的一般性问题。")
            .user(message)
            .call()
            .content();
    }
}

5.2 Agent 协作架构

Agent 协作架构


六、Agent 评估与优化

Agent 上线后,评估和优化是持续的工作。

6.1 评估指标体系

指标 说明 目标值
任务完成率 Agent 是否完成了用户要求的任务 ≥ 85%
工具调用准确率 是否选择了正确的工具 ≥ 90%
平均执行步数 完成任务平均需要多少步 越少越好
首轮成功率 第一次就完成任务的比例 ≥ 70%
响应时间 (P99) 99% 请求的响应时间 < 30s
Token 消耗 平均每次任务消耗的 Token 控制成本

6.2 自动化评估框架

@Service
public class AgentEvaluator {

    private static final Logger log = LoggerFactory.getLogger(AgentEvaluator.class);

    public EvaluationReport evaluate(Agent agent, List<TestCase> testCases) {
        int passed = 0, failed = 0;
        List<FailureDetail> failures = new ArrayList<>();
        long totalLatency = 0;

        for (TestCase tc : testCases) {
            long start = System.currentTimeMillis();
            try {
                String result = agent.execute(tc.task());
                long latency = System.currentTimeMillis() - start;
                totalLatency += latency;

                boolean success = verifyResult(result, tc.expectedKeywords());
                if (success) {
                    passed++;
                } else {
                    failed++;
                    failures.add(new FailureDetail(tc, result, "关键词匹配失败"));
                }
            } catch (Exception e) {
                failed++;
                failures.add(new FailureDetail(tc, null, e.getMessage()));
            }
        }

        return new EvaluationReport(testCases.size(), passed, failed, failures, totalLatency);
    }

    /** 验证结果是否包含预期的关键字 */
    private boolean verifyResult(String result, List<String> expectedKeywords) {
        if (result == null) return false;
        return expectedKeywords.stream().allMatch(result::contains);
    }

    public record EvaluationReport(int total, int passed, int failed,
                                    List<FailureDetail> failures, long totalLatency) {
        public double passRate() { return (double) passed / total; }
        public double avgLatency() { return (double) totalLatency / total; }
    }
    public record TestCase(String task, List<String> expectedKeywords) {}
    public record FailureDetail(TestCase testCase, String actualResult, String reason) {}
}

6.3 测试用例集

public class AgentTestSuite {

    public static List<TestCase> customerServiceCases() {
        return List.of(
            new TestCase("我的订单 12345 发货了吗?", List.of("订单", "发货")),
            new TestCase("我想退货,订单号 67890", List.of("退款", "申请")),
            new TestCase("你们的产品有什么保修政策?", List.of("保修")),
            new TestCase("查一个不存在的订单 99999", List.of("不存在", "找不到"))
        );
    }

    public static List<TestCase> dataAnalysisCases() {
        return List.of(
            new TestCase("上个月销售额最高的产品是什么?", List.of("销售", "最高")),
            new TestCase("最近一周有多少新用户注册?", List.of("新用户", "注册")),
            new TestCase("各个品类的销售占比是多少?", List.of("品类", "占比"))
        );
    }
}

6.4 优化策略

策略一:Prompt 优化

根据失败案例针对性改进 System Prompt:

// 失败案例分析
List<FailureDetail> recentFailures = evaluator.evaluate(agent, testCases).failures();

// 用 AI 自动优化 Prompt
String optimizedPrompt = chatClient.prompt()
    .system("你是 Prompt 工程专家。根据失败案例优化系统提示词。")
    .user("""
        当前 Prompt:
        {currentPrompt}
        
        近期失败案例:
        {failures}
        
        请给出优化后的 Prompt。
        """.replace("{currentPrompt}", currentPrompt)
           .replace("{failures}", formatFailures(recentFailures)))
    .call()
    .content();

策略二:工具描述优化

// ❌ 优化前:太模糊
@Tool(name = "getOrder", description = "获取订单")

// ✅ 优化后:明确触发条件
@Tool(name = "queryOrderStatus", 
      description = "查询订单当前状态和物流信息。" +
                   "当用户询问订单进度、物流跟踪、发货状态时使用此工具。" +
                   "需要用户提供订单号。")

策略三:Few-Shot 示例

在 System Prompt 中加入成功案例作为参考:

String systemPrompt = """
    你是智能客服助手。
    
    【参考示例】
    用户:我的东西发了吗?
    → 调用 queryOrderStatus,参数 orderId 从对话历史获取
    
    用户:不想要了要退款
    → 先调用 queryOrderStatus 确认订单状态,再调用 applyRefund
    
    请参考以上示例的风格和方式来处理用户请求。
    """;

6.5 生产监控

@Component
public class AgentMonitor {

    private final MeterRegistry meterRegistry;

    /** 记录每次 Agent 执行 */
    public void recordExecution(String agentType, boolean success, 
                                 int toolCalls, long latencyMs, int tokens) {
        meterRegistry.counter("agent.execution", 
            "type", agentType, "status", success ? "success" : "failure").increment();
        
        meterRegistry.timer("agent.latency", "type", agentType)
            .record(latencyMs, TimeUnit.MILLISECONDS);
        
        meterRegistry.counter("agent.tokens", "type", agentType).increment(tokens);
        meterRegistry.counter("agent.tool_calls", "type", agentType).increment(toolCalls);
    }

    /** 告警规则建议 */
    // - 失败率 > 15% 持续 5 分钟 → 告警
    // - P99 延迟 > 60s → 告警  
    // - 平均 Token > 10000 → 成本告警
}

七、常见问题

Q1:Agent 循环次数太多怎么办?

  • 设置最大迭代次数(maxIterations
  • 优化工具描述,减少误调
  • 在 Prompt 中提示"尽量少的步骤完成任务"

Q2:Agent 调用了错误的工具?

这是最常见的问题。解决思路:

  1. 优化工具描述:让每个工具的边界更清晰
  2. 减少工具数量:合并相似工具
  3. 增加 Few-Shot 示例:展示正确的调用方式
  4. 使用更强的模型:GPT-4o 比 GPT-4o-mini 工具调用更准

Q3:Agent 结果不稳定怎么办?

  • 同样的输入可能产生不同的输出(LLM 的随机性)
  • 解决方案:设置 temperature=0 提高确定性
  • 对于关键业务逻辑,加人工审核环节

Q4:Agent 成本太高?

  • 用小模型做简单任务(路由、格式化)
  • 用大模型做复杂推理
  • 缓存常见问题的答案
  • 限制最大迭代次数和 Token 数

写在总结

AI Agent 是 Spring AI 所有技术的集大成者。回顾一下技术演进路径:

第 1-2 篇:学会让 AI "说话"(ChatClient + 多模型)
第 3 篇:   学会让 AI "理解"(结构化输出 + 多模态)
第 4-5 篇:学会给 AI "喂知识"(Embedding + RAG)
第 6 篇:   学会让 AI "动手"(Tool Calling)
第 7 篇:   学会给 AI "装插件"(Advisor)
第 8 篇:   学会让 AI "接外部世界"(MCP)
第 9 篇:   ★ 学会让 AI "自主思考"(Agent)★
要点 核心内容
核心理念 从"回答问题"到"完成任务"
思维模式 ReAct:思考 → 行动 → 观察 → 循环
开发方式 ChatClient + defaultTools 即可构建基础 Agent
进阶能力 任务分解、自我反思、多 Agent 协作
关键实践 评估指标、测试集、持续优化

下一篇也是本系列的最后一篇——企业级应用与最佳实践,把前面所有知识点整合到一起,讲怎么在生产环境中落地。


系列目录:


若本文对您有所帮助,欢迎点赞、收藏与关注,系列持续更新中!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐