如果你希望 AI 不仅能聊天,还能真正做事——查数据库、调 API、做决策、从结果中学习——那你来对地方了。

本文将用 Solon AI 4.0 的 ReActAgent 带你构建生产级的 AI Agent。读完你将学会如何构建一个能够推理复杂问题、使用外部工具、并根据实际反馈调整行为的智能体。

ReActAgent 有何不同?

传统 LLM 擅长生成文本,但一旦需要与现实世界交互——查数据库、拉取实时数据、做计算——就无能为力了。

ReActAgent(Reason + Act)打破了这堵墙。它实现了一个认知循环:

思考 → 行动 → 观察 →(重复或结束)

Agent 思考下一步该做什么,行动(调用工具),观察结果,然后决定是继续还是交出最终答案。

这不是理论。Solon AI 的 ReActAgent 已经在自动客服、智能数据分析、多步工作流自动化等生产场景中得到验证。

1. 添加依赖

首先,在项目中添加 solon-ai-agent 模块:

<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-ai-agent</artifactId>
</dependency>

如果使用了 Solon 的父 POM,版本会自动管理。否则请指定最新的 Solon 版本。

2. 构建 ChatModel(Agent 的大脑)

每个 Agent 都需要一个"大脑"——ChatModel 负责推理。用 Builder API 快速构建:

import org.noear.solon.ai.chat.ChatModel;

ChatModel chatModel = ChatModel.of("https://api.moark.com/v1/chat/completions")
        .apiKey("your-api-key-here")
        .model("Qwen3-32B")
        .build();

也可以通过 YAML 配置后注入:

solon.ai.chat:
  demo:
    apiUrl: "http://127.0.0.1:11434/api/chat"
    provider: "ollama"
    model: "llama3.2"
@Inject("${solon.ai.chat.demo}")
ChatModel chatModel;

3. Hello World:你的第一个 ReActAgent

先从一个简单的例子开始——创建一个获取时间的工具和一个基础 Agent:

import org.noear.solon.ai.agent.react.ReActAgent;
import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.ai.annotation.Param;
import org.noear.solon.ai.chat.tool.AbsToolProvider;

import java.time.LocalDateTime;

// 1. 定义工具
public class TimeTool extends AbsToolProvider {
    @ToolMapping(description = "获取当前的日期和时间")
    public String getCurrentTime() {
        return LocalDateTime.now().toString();
    }
}

// 2. 构建并运行 Agent
public class HelloAgent {
    public static void main(String[] args) throws Throwable {
        ChatModel chatModel = ChatModel.of("https://api.moark.com/v1/chat/completions")
                .apiKey("***")
                .model("Qwen3-32B")
                .build();

        ReActAgent agent = ReActAgent.of(chatModel)
                .role("你是一个可以查询时间的助手。")
                .defaultToolAdd(new TimeTool())
                .build();

        String response = agent.prompt("现在几点了?")
                .call()
                .getContent();

        System.out.println(response);
    }
}

运行时,Agent 会:

  1. 思考:“用户想知道时间,我有一个 getCurrentTime 工具”
  2. 行动:调用 getCurrentTime()
  3. 观察:拿到时间戳
  4. 回答:“当前时间是 2026-07-04T14:30:22……”

4. 实战:客服支持 Agent

我们来构建一个更实用的场景——支持查询订单和库存的客服 Agent。

第 1 步:定义工具

import org.noear.solon.ai.chat.tool.AbsToolProvider;
import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.ai.annotation.Param;

public class OrderTool extends AbsToolProvider {

    @ToolMapping(description = "根据订单 ID 查询订单状态")
    public String getOrderStatus(@Param(description = "订单 ID") String orderId) {
        // 模拟数据库查询
        if ("ORD-1001".equals(orderId)) {
            return "订单 ORD-1001:已发货,预计 7 月 7 日送达";
        } else if ("ORD-1002".equals(orderId)) {
            return "订单 ORD-1002:待处理,付款未确认";
        }
        return "未找到订单:" + orderId;
    }

    @ToolMapping(description = "根据商品 ID 查询库存")
    public String checkInventory(@Param(description = "商品 SKU") String sku) {
        if ("SKU-A100".equals(sku)) {
            return "库存充足:42 件";
        } else if ("SKU-B200".equals(sku)) {
            return "库存紧张:仅剩 3 件";
        }
        return "未找到商品:" + sku;
    }
}

第 2 步:构建 Agent

ReActAgent supportAgent = ReActAgent.of(chatModel)
        .name("customer_support")
        .role("客服支持 Agent——处理订单查询和库存检查。")
        .defaultToolAdd(new OrderTool())
        .maxTurns(8)                    // 最大推理步数
        .autoRethink(true)              // 卡住时自动重新思考
        .retryConfig(3, 1000L)          // 重试 3 次,间隔 1 秒
        .modelOptions(options -> {
            options.temperature(0.1);   // 低温度,确定性决策
        })
        .build();

String result = supportAgent.prompt("客户 ORD-1002 想知道订单什么时候到,能帮忙查一下吗?")
        .call()
        .getContent();

System.out.println(result);

Agent 会:

  1. 意识到需要查 ORD-1002
  2. 调用 getOrderStatus("ORD-1002")
  3. 读到结果:“待处理,付款未确认”
  4. 向客户解释:付款尚未确认,所以还未发货

5. 添加拦截器:可观测性

生产环境中,你需要看到 Agent 的思考过程。ReActInterceptor 提供了完整的生命周期钩子:

import org.noear.solon.ai.agent.react.ReActInterceptor;
import org.noear.solon.ai.agent.react.ReActTrace;
import org.noear.solon.ai.agent.react.task.ToolExchanger;

ReActAgent observableAgent = ReActAgent.of(chatModel)
        .name("observable_agent")
        .role("我帮你处理各种任务。")
        .defaultToolAdd(new OrderTool())
        .defaultInterceptorAdd(new ReActInterceptor() {

            @Override
            public void onAgentStart(ReActTrace trace) {
                System.out.println("🤖 Agent 启动。提示词:" + trace.getOriginalPrompt().getUserContent());
            }

            @Override
            public void onThought(ReActTrace trace, String thoughtContent,
                                    AssistantMessage assistantMessage) {
                System.out.println("💭 思考:" + thoughtContent);
            }

            @Override
            public void onAction(ReActTrace trace, ToolExchanger toolExchanger) {
                System.out.println("🛠️  工具:" + toolExchanger.getToolName()
                        + ",参数:" + toolExchanger.getArgs());
            }

            @Override
            public void onObservation(ReActTrace trace, ToolExchanger toolExchanger,
                                        ChatMessage observation, Throwable error,
                                        long durationMs) {
                if (error != null) {
                    System.err.println("❌ 工具调用失败:" + error.getMessage());
                } else {
                    System.out.println("✅ 工具结果返回,耗时 " + durationMs + "ms");
                }
            }

            @Override
            public void onAgentEnd(ReActTrace trace) {
                System.out.println("✅ Agent 任务完成。");
            }
        })
        .build();

这样你就有了 Agent 每次决策的完整审计轨迹。

6. 流式响应

对于长时间运行的任务,使用 stream() 获取实时输出:

agent.prompt("分析我们的 Top 10 商品,给我一份销售摘要。")
     .stream()
     .doOnNext(resp -> {
         System.out.print(resp.getMessage().getContent());
     })
     .doOnComplete(() -> {
         System.out.println("\n✅ 分析完成!");
     })
     .subscribe();

7. 进阶:单次调用选项

通过 .options() 可以为单次调用调整行为:

agent.prompt("分析这个复杂数据集,生成 JSON 报告。")
     .session(mySession)                // 复用已有会话
     .options(o -> o
         .maxTurns(15)                  // 复杂任务给更多步数
         .planningMode(true)            // 启用规划阶段
         .temperature(0.3)              // 平衡创造性与精确性
         .outputSchema("{\"type\":\"object\",\"properties\":{...}}")  // 结构化输出
         .toolAdd(new ReportingTool())  // 临时添加一个工具
     )
     .call();

选项一览

分类 方法 说明 默认值
控制 maxTurns(int) 最大推理步数 8
控制 autoRethink(boolean) 自动重新思考 false
控制 retryConfig(int, long) 重试次数与间隔 3, 1000ms
模型 temperature(double) 随机性(0-2) 0.5
模型 max_tokens(long) 最大生成 Token
工具 toolAdd(FunctionTool) 临时添加工具
工具 talentAdd(Talent) 添加技能模块
扩展 interceptorAdd(interceptor) 添加拦截器

8. 会话与轨迹(Sessions & Traces)

ReActAgent 的会话支持带记忆的长时间对话:

import org.noear.solon.ai.agent.session.InMemoryAgentSession;
import org.noear.solon.ai.agent.AgentSession;

// 创建或复用会话
AgentSession session = InMemoryAgentSession.of("user-session-123");

// 第一轮
String r1 = agent.prompt("帮我找找 50 元以下的商品")
        .session(session)
        .call()
        .getContent();

// 第二轮(Agent 记得上下文)
String r2 = agent.prompt("最便宜的那个运费多少?")
        .session(session)
        .call()
        .getContent();

// 查看轨迹
ReActTrace trace = agent.getTrace(session);
System.out.println("总步数:" + trace.getStepCount());

// 或获取格式化摘要
System.out.println(trace.getFormattedHistory());

Trace 对象提供:

  • 完整的思考/行动/观察历史(getFormattedHistory()
  • 步数和指标(getStepCount()getMetrics()
  • 工具调用参数和结果
  • 原始提示词和会话(getOriginalPrompt()getSession()

9. 轻量模型的 STRUCTURED_TEXT 模式

不是所有模型都原生支持工具调用。ReActAgent 支持 Text ReAct 模式——它通过正则从模型输出的文本中提取 Action: {json} 标签来执行工具。这使得它兼容更小、更轻量的模型,适合边缘部署和对成本敏感的场景。

执行风格(ReActStyle)在构建时通过 ReActAgentConfig 配置,可选择 ReActStyle.NATIVE_TOOL(原生工具调用,默认)和轻量的 ReActStyle.STRUCTURED_TEXT(结构化文本)。使用 STRUCTURED_TEXT 模式时,Agent 从模型输出中解析 Action: {json} 标签并执行对应的工具。

完整示例:电商支持 Agent

可直接复制运行的完整代码:

import org.noear.solon.ai.agent.react.ReActAgent;
import org.noear.solon.ai.agent.react.ReActInterceptor;
import org.noear.solon.ai.agent.react.ReActTrace;
import org.noear.solon.ai.agent.react.task.ToolExchanger;
import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.ai.annotation.Param;
import org.noear.solon.ai.chat.ChatModel;
import org.noear.solon.ai.chat.tool.AbsToolProvider;
import org.noear.solon.ai.chat.message.ChatMessage;

public class ECommerceSupportApp {
    public static void main(String[] args) throws Throwable {
        // 1. 构建模型
        ChatModel model = ChatModel.of("https://api.moark.com/v1/chat/completions")
                .apiKey("${API_KEY}")
                .model("Qwen3-32B")
                .build();

        // 2. 构建 Agent
        ReActAgent agent = ReActAgent.of(model)
                .name("ecommerce_support")
                .role("电商客服支持 Agent")
                .defaultToolAdd(new OrderTool())
                .defaultToolAdd(new InventoryTool())
                .defaultInterceptorAdd(new LoggingInterceptor())
                .defaultInterceptorAdd(new ContextCompressionInterceptor(...)) //【可选】如果轮数过多,需要压缩上下文
                .maxTurns(10)
                .autoRethink(true)
                .build();

        // 3. 运行
        String answer = agent.prompt(
                "客户想下单 SKU-A100,但发现 ORD-1001 还没收到。查一下两个情况,给客户解释。"
        ).call().getContent();

        System.out.println(answer);
    }
}

// 工具
class OrderTool extends AbsToolProvider {
    @ToolMapping(description = "根据订单 ID 查询订单状态")
    public String getOrderStatus(@Param(description = "订单 ID") String orderId) {
        return "ORD-1001:已发货";
    }
}

class InventoryTool extends AbsToolProvider {
    @ToolMapping(description = "根据 SKU 查询商品库存")
    public String checkStock(@Param(description = "商品 SKU") String sku) {
        return "SKU-A100:42 件库存充足";
    }
}

// 拦截器
class LoggingInterceptor implements ReActInterceptor {
    @Override
    public void onThought(ReActTrace trace, String thought,
                          AssistantMessage msg) {
        System.out.println("💭 " + thought);
    }
    @Override
    public void onAction(ReActTrace trace, ToolExchanger tool) {
        System.out.println("🛠️  " + tool.getToolName());
    }
}

要点总结

  1. ReActAgent 遵循 思考→行动→观察 循环——推理、行动、学习
  2. 工具通过 @ToolMapping@Param 注解定义——就是普通的 POJO
  3. 拦截器让你对 Agent 的决策过程有完整的可观测性
  4. 单次调用选项让你在不重建 Agent 的前提下灵活调整行为
  5. Text ReAct 模式兼容不支持原生工具调用的轻量模型
  6. 会话和轨迹支持带完整审计日志的持久化对话
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐