5种AI Agent模式 - 详细解释和Java示例

本文有一个短视频:YouTubeTikTok

1. 概述

当你用几行代码将LLM和自动执行能力结合起来时,你就已经有了一个基础的AI Agent —— 但这种agent通常提问即答,不知道如何自我纠正、不知道如何调用工具、不知道如何分工。本文解释5种设计模式,让agent更加“智能”:Reflection、Tool Use、Planning、Multi-Agent和ReAct。每种模式都有简短的、可运行的Java(Spring AI)代码示例,以及何时应该和不应该使用的说明。读完后,你能够为自己的问题选择合适的模式,而不是把所有东西都硬塞到一个巨大的prompt中。

在深入细节之前,下面的示例需要Spring AI的依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.chat.options.model=gpt-4o-mini

注意: 本文代码演示的是模式的结构,不是生产产品 —— 当实际部署时,你需要添加重试、超时和日志记录。

2. Reflection模式:自我评论和修正答案

Reflection是最简单的模式:agent生成一个答案,然后用同一个LLM(或不同的LLM)给答案评分/评论,然后修正。这个循环类似于你写初稿,然后在发送前自己检查。

2.1. 工作流

  1. 生成:agent生成初始答案。
  2. 评论:agent(或另一个agent)扮演“审阅者”角色,指出错误。
  3. 修正:agent根据评论进行修改,可能重复2-3步直到达到质量要求。
@Service
public class ReflectionAgent {

    private final ChatClient chatClient;

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

    public String answerWithReflection(String question, int maxRounds) {
        String draft = chatClient.prompt()
                .user("简明扼要地回答问题:" + question)
                .call()
                .content();

        for (int round = 0; round < maxRounds; round++) {
            String critique = chatClient.prompt()
                    .user("""
                        你是一个严格的审阅者。指出以下答案中的错误和不足。
                        如果答案已经足够好,回复"OK"。
                        问题:%s
                        答案:%s
                        """.formatted(question, draft))
                    .call()
                    .content();

            if (critique.strip().equalsIgnoreCase("OK")) {
                break;
            }

            draft = chatClient.prompt()
                    .user("根据以下反馈修改答案:" + critique
                            + "\n旧答案:" + draft)
                    .call()
                    .content();
        }
        return draft;
    }
}

answerWithReflection生成初始草稿,然后循环最多maxRounds次:调用LLM充当审阅者进行评分,如果审阅者返回“OK”则停止,否则生成修改版本。结果是答案往往比只调用一次LLM更准确。

注意: Reflection消耗的LLM调用次数是平常的2-3倍。仅应用于需要高精度的任务(代码审查、计算、撰写合同),不应用于常规聊天,因为它很慢且成本高。

3. Tool Use模式:让agent调用外部工具

LLM不知道当前时间,无法访问数据库,无法精确计算大数字。Tool Use模式让agent在需要时“调用函数” —— LLM决定调用哪个tool,代码执行该tool然后将结果返回给LLM继续推理。

@Component
public class OrderTools {

    private final OrderRepository orderRepository;

    public OrderTools(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Tool(description = "根据订单号查询订单状态")
    public String getOrderStatus(String orderId) {
        return orderRepository.findById(orderId)
                .map(Order::getStatus)
                .orElse("找不到订单 " + orderId);
    }

    @Tool(description = "计算订单总金额,包含10%税费")
    public double calculateTotal(double subtotal) {
        return subtotal * 1.1;
    }
}
@Service
public class CustomerSupportAgent {

    private final ChatClient chatClient;

    public CustomerSupportAgent(ChatClient.Builder builder, OrderTools orderTools) {
        this.chatClient = builder
                .defaultTools(orderTools)
                .build();
    }

    public String handle(String userMessage) {
        return chatClient.prompt()
                .user(userMessage)
                .call()
                .content();
    }
}

@Tool将方法标记为LLM可以调用的函数,description帮助模型理解何时使用。当注册defaultTools(orderTools)时,Spring AI自动:将工具的schema发送给LLM,接收工具调用请求(JSON格式),执行相应的Java方法,然后将结果返回给LLM完成答案。

例如,用户问“订单ORD-001在哪里?”,agent自己决定调用getOrderStatus("ORD-001"),你不需要手动编写意图解析逻辑。

用户:订单ORD-001在哪里?
Agent:订单ORD-001当前状态是"配送中"。

用JUnit 5 + Mockito编写简单测试来检查tool正常运行,无需调用真实的LLM:

class OrderToolsTest {

    private final OrderRepository repository = mock(OrderRepository.class);
    private final OrderTools tools = new OrderTools(repository);

    @Test
    void shouldReturnStatusWhenOrderExists() {
        when(repository.findById("ORD-001"))
                .thenReturn(Optional.of(new Order("ORD-001", "配送中")));

        String result = tools.getOrderStatus("ORD-001");

        assertThat(result).isEqualTo("配送中");
    }
}

注意: 在tool访问数据库或调用真实API之前,始终验证输入 —— 如果prompt不够清楚,LLM可能会传递错误格式的参数(例如空的orderId)。

4. Planning模式:将大任务分解为小步骤

对于复杂的请求(“编写比较3个供应商的报告,包含价格表和建议”),让LLM一次性完成容易遗漏步骤。Planning模式分为两个角色:Planner制定步骤列表,Executor依次执行每个步骤。

public record PlanStep(int order, String description) {}

@Service
public class PlannerAgent {

    private final ChatClient chatClient;

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

    public List<PlanStep> createPlan(String goal) {
        String json = chatClient.prompt()
                .user("""
                    将下面的目标分解为最多5个执行步骤,
                    返回JSON数组格式[{"order":1,"description":"..."}]:
                    %s
                    """.formatted(goal))
                .call()
                .content();

        return parseSteps(json);
    }
}
@Service
public class ExecutorAgent {

    private final ChatClient chatClient;

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

    public String runPlan(String goal, List<PlanStep> steps) {
        StringBuilder context = new StringBuilder();
        for (PlanStep step : steps) {
            String result = chatClient.prompt()
                    .user("目标:" + goal
                            + "\n已完成的上下文:" + context
                            + "\n执行步骤:" + step.description())
                    .call()
                    .content();
            context.append("步骤 ").append(step.order())
                    .append(":").append(result).append("\n");
        }
        return context.toString();
    }
}

PlannerAgent仅负责以JSON形式生成步骤列表,ExecutorAgent运行每个步骤并累积context,以便下一步知道之前做了什么。分离planner/executor有助于调试:如果结果错误,首先检查计划(是否合理),然后检查每个执行步骤。

"编写比较3个供应商的报告"的计划:
1. 收集每个供应商的价格和功能信息
2. 制作对比表
3. 分析优缺点
4. 提出建议
5. 编写执行摘要部分

注意: Planning模式在任务有许多相互依赖的步骤时最有效。对于单步任务(例如“总结这段文字”),添加planner只会减速并浪费不必要的token。

5. Multi-Agent模式:多个专业agent协作

Multi-Agent模式不是单一的“全能”agent,而是将工作分配给专业化的agent:研究agent、写作agent、审核agent等,然后由Orchestrator协调它们之间的流程。就像一个小团队的模型,每个人擅长一项工作。

@Service
public class ResearchAgent {
    private final ChatClient chatClient;

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

    public String research(String topic) {
        return chatClient.prompt()
                .user("列出关于以下主题的5个最重要的要点:" + topic)
                .call()
                .content();
    }
}

@Service
public class WriterAgent {
    private final ChatClient chatClient;

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

    public String write(String topic, String researchNotes) {
        return chatClient.prompt()
                .user("根据以下数据,编写一篇200字的文章,主题为'" + topic
                        + "':" + researchNotes)
                .call()
                .content();
    }
}
@Service
public class OrchestratorAgent {

    private final ResearchAgent researchAgent;
    private final WriterAgent writerAgent;

    public OrchestratorAgent(ResearchAgent researchAgent, WriterAgent writerAgent) {
        this.researchAgent = researchAgent;
        this.writerAgent = writerAgent;
    }

    public String produceArticle(String topic) {
        String notes = researchAgent.research(topic);
        return writerAgent.write(topic, notes);
    }
}

OrchestratorAgent不是自己调用LLM,而是协调:调用ResearchAgent获取原始数据,将其传给WriterAgent写成完整文章。每个agent有自己的prompt,针对一项特定工作进行优化,因此每个部分的质量通常比把所有内容堆入一个长prompt要好。

用mock编写测试来检查协调流程是否正确,不依赖LLM:

class OrchestratorAgentTest {

    @Test
    void shouldPassResearchNotesToWriter() {
        ResearchAgent research = mock(ResearchAgent.class);
        WriterAgent writer = mock(WriterAgent.class);
        when(research.research("Spring AI")).thenReturn("note-A, note-B");
        when(writer.write("Spring AI", "note-A, note-B")).thenReturn("完整文章");

        OrchestratorAgent orchestrator = new OrchestratorAgent(research, writer);
        String article = orchestrator.produceArticle("Spring AI");

        assertThat(article).isEqualTo("完整文章");
        verify(writer).write("Spring AI", "note-A, note-B");
    }
}

注意: Multi-Agent增加了延迟(多个连续的LLM调用)和系统复杂性。只有当每个agent确实有明显不同的prompt/context时才应该分离 —— 过度分割(每个agent一句话)只会造成不必要的开销。

6. ReAct模式:推理交织的行动

ReAct(推理+行动)是结合Tool Use和推理循环的模式:agent思考(Thought)→行动(Action,例如调用tool)→观察结果(Observation)→重复直到有足够信息回答。与单纯的Tool Use(调用1个tool然后回答)不同,ReAct允许agent按灵活顺序调用多个tool,根据之前的observation自行决定下一步。

@Service
public class ReActAgent {

    private final ChatClient chatClient;
    private final List<Object> tools;

    public ReActAgent(ChatClient.Builder builder, WeatherTool weatherTool, SearchTool searchTool) {
        this.tools = List.of(weatherTool, searchTool);
        this.chatClient = builder.defaultTools(tools.toArray()).build();
    }

    public String solve(String question) {
        return chatClient.prompt()
                .system("""
                    当需要真实数据时,回答前调用适当的tool。
                    逐步推理:Thought -> Action -> Observation -> Thought...
                    只有当掌握足够信息时才给出最终答案。
                    """)
                .user(question)
                .call()
                .content();
    }
}

对于问题“这个周末我去大叻应该带雨衣吗?”,agent可以自行推理:调用weatherTool.forecast("大叻")获取预报,观察结果“70%降雨”,然后回答 —— 整个Thought/Action/Observation序列在框架内部进行,你无需手动编写循环,因为Spring AI自动处理循环调用tool直到模型决定停止。

Thought: 需要了解大叻周末的天气
Action: forecast("大叻")
Observation: 降雨70%,18-22摄氏度
Thought: 应该带雨衣
Answer: 你应该带雨衣,预报显示大叻周末降雨概率70%。

注意: 如果agent不断调用tool但不收敛到答案,ReAct容易陷入“无限循环”。始终设置最大工具调用次数(maxToolCalls或超时)以避免无法控制的API成本。

7. 5种模式的快速比较

模式 解决的问题 成本(LLM调用次数) 何时使用
Reflection 答案质量低 高(2-3倍) 需要高精度、可自检的任务
Tool Use LLM无法访问真实数据/系统 低-中等 需要查询、计算、调用API
Planning 多步任务,容易遗漏 中等 复杂的结构化任务
Multi-Agent 单个agent专业性不足 高 清晰角色分工的pipeline(研究/写作/审核)
ReAct 需要tool但步骤数未知 可变 需要灵活探索信息的任务

在实践中,这些模式并不互斥 —— 客户支持agent可以使用Tool Use查询订单、用Planning处理复杂请求、用Reflection检查敏感答案再发送。

8. 结论

上述5种模式 —— Reflection、Tool Use、Planning、Multi-Agent、ReAct —— 是构建能够自我纠正、与真实系统交互、处理复杂任务并灵活探索信息的AI Agent的基础构建块。你不需要对所有问题都使用全部5种;从Tool Use开始(几乎每个agent都需要),然后在任务更复杂时添加Planning或ReAct,只有当额外成本值得提升质量时才使用Reflection/Multi-Agent。根据问题选择正确的模式比试图将所有东西都硬塞到单个agent中更重要。

ai-agentspring-aijavallmdesign-pattern