切换主题
TIP
本页内容由本地原稿 02-第一个ReactAgent.md 同步生成。 如果你要长期修改站点内容,优先回原稿修改,再执行 npm run docs:sync。
第 2 章:先把第一个 ReactAgent 跑起来
这章要解决什么问题
光知道 ReactAgent 是个什么东西还不够。
你必须亲手写出一个最小版本,框架才会从“名词”变成“手感”。
这章我们不追求复杂,只做一件事:
写一个最小可运行的 ReactAgent,让它能接问题、返回答案。
1. 先明确我们今天要搭的东西
今天先不接工具、不接 RAG、不做花活。
我们只要一个最小闭环:
你可以把它理解成:
- Controller 负责收请求
ReactAgent负责思考和组织调用ChatModel负责真正去和模型通信
2. 依赖怎么加
如果你已经有 Spring Boot 工程,最核心的是两类依赖:
- Agent Framework
- 具体模型的 starter
下面以 DashScope 为例,版本号建议跟着你本地官方仓库或官网同步,不要手写死老版本。
xml
<properties>
<java.version>17</java.version>
<spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
</dependencies>参考来源:
3. 配置怎么写
最小配置不要上来就抄一堆。
你现在只关心一件事:模型能不能连上。
yaml
spring:
application:
name: saa-study-demo
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
options:
model: qwen3.6-plus这里故意只保留最小字段。
像你们公司项目里的 application-dev.yml(源码路径:D:/idea_space/ai-center-server/ai-center-api-server/src/main/resources/application-dev.yml) 会复杂很多,因为它还要处理:
- 多模型
- embedding
- 向量库
- Nacos 配置导入
- 多环境
但你现在先不要被这些吓到。
4. 项目结构建议长这样
text
src/main/java/com/example/saastudy
├─ StudyApplication.java
├─ config
│ └─ AgentConfig.java
├─ service
│ └─ AgentChatService.java
└─ controller
└─ AgentChatController.java这不是唯一写法,但非常适合学习。
5. 第一个可运行版本
5.1 启动类
java
package com.example.saastudy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args);
}
}5.2 Agent 配置
java
package com.example.saastudy.config;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AgentConfig {
@Bean
public MemorySaver memorySaver() {
return new MemorySaver();
}
@Bean
public ReactAgent studyAgent(ChatModel chatModel, MemorySaver memorySaver) {
return ReactAgent.builder()
.name("study_agent")
.model(chatModel)
.instruction("""
你是一个帮助 Java 开发者学习 Spring AI Alibaba 的助教。
回答请使用中文,解释尽量清楚,不要故作高深。
""")
.saver(memorySaver)
.build();
}
}5.3 Service 层
java
package com.example.saastudy.service;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.stereotype.Service;
@Service
public class AgentChatService {
@Resource
private ReactAgent studyAgent;
public String chat(String message) throws Exception {
AssistantMessage answer = studyAgent.call(message);
return answer.getText();
}
public String chatWithThread(String message, String threadId) throws Exception {
RunnableConfig config = RunnableConfig.builder()
.threadId(threadId)
.build();
AssistantMessage answer = studyAgent.call(message, config);
return answer.getText();
}
}5.4 Controller 层
java
package com.example.saastudy.controller;
import com.example.saastudy.service.AgentChatService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/study/agent")
public class AgentChatController {
@Resource
private AgentChatService agentChatService;
@PostMapping("/chat")
public String chat(@RequestBody ChatRequest request) throws Exception {
if (request.threadId() == null || request.threadId().isBlank()) {
return agentChatService.chat(request.message());
}
return agentChatService.chatWithThread(request.message(), request.threadId());
}
public record ChatRequest(String message, String threadId) {
}
}6. 这段代码到底做了什么
6.1 ChatModel
它是底层模型能力。
starter 帮你把它注入到 Spring 容器里,你在 studyAgent(...) 方法里直接拿来用。
所以 .model(chatModel) 的含义不是“指定一个普通参数”,而是:
这个 Agent 以后要靠哪个模型来推理。
6.2 name("study_agent")
它不是一个花哨名字。
在更复杂的运行时里,Agent 的名字会参与状态追踪、日志定位、工作流编排。
对初学阶段来说,你先把它当成“这个 Agent 的身份标识”就够了。
6.3 instruction(...)
这部分非常重要。
它相当于在告诉 Agent:
- 你是谁
- 你说话的风格是什么
- 你做事时要遵循什么规则
很多人把 Agent 写不好,不是 API 不会用,而是这里写得太空、太假、太像套话。
6.4 saver(memorySaver)
先别把它看复杂。
你现在只要记住:
没有 saver,Agent 很难可靠地把运行状态接起来。
这一章我们只是先把它放上去。
下一章的下一章会专门讲记忆。
7. 跑起来以后,你该怎么验证
请求示例:
http
POST /study/agent/chat
Content-Type: application/json
{
"message": "请用通俗的话解释一下 ReactAgent 是什么",
"threadId": "demo-session-001"
}如果你想看它是否真的“记住”上下文,可以连续发两次:
json
{
"message": "我叫小李,请记住",
"threadId": "demo-session-001"
}然后再发:
json
{
"message": "我刚刚叫什么?",
"threadId": "demo-session-001"
}这里先不深讲,只先让你感受到:
同一个 threadId,对话才有机会接上。
8. 这个最小例子为什么值得你亲手敲一遍
因为它把 Spring AI Alibaba 的第一层心智模型都凑齐了:
ChatModel:模型入口ReactAgent:Agent 主体instruction:行为约束RunnableConfig:运行上下文MemorySaver:状态保存能力
你后面学工具、记忆、RAG,其实都是在这个骨架上继续加东西。
9. 对照你本地源码,它们分别映射到哪
9.1 模型装配
看 AiModelConfiguration.java(源码路径:D:/idea_space/ai-center-server/ai-center-api-server/src/main/java/com/hy/bigdata/ai/config/model/AiModelConfiguration.java)
你会看到公司项目不是只接一个模型,而是注册了多个命名 Bean。
这个动作和你在教程里写的 ChatModel chatModel 是同一件事,只不过工程化程度更高。
9.2 聊天编排
看 AiChatBizImpl.java(源码路径:D:/idea_space/ai-center-server/ai-center-api-server/src/main/java/com/hy/bigdata/ai/biz/impl/AiChatBizImpl.java)
你会发现业务代码不是直接在 Controller 里 new Agent,而是:
- 先拿到 Agent 上下文
- 再选择
ChatClient或ReactAgent - 再挂工具、RAG、拦截器
这说明:
你在这里学的最小例子不是玩具,而是工程主链路的缩小版。
9.3 练手项目参考
你也可以对照 MinimalAgentService.java(源码路径:D:/idea_space/og_ai_test/chat-demo/src/main/java/com/hy/bigdata/chatdemo/concept/MinimalAgentService.java),它就是一个非常接近这章思路的最小 Agent 服务。
10. 初学者最容易踩的坑
10.1 以为 ReactAgent 会自己凭空拥有能力
不会。
没有 model,它不会思考;没有 tools,它不会干活;没有 threadId 和 saver,它也不会稳定记忆。
10.2 一上来就把项目搞得很复杂
不要一开始就加:
- 多模型切换
- 多工具
- RAG
- 流式输出
- Graph
先把一个最小 Agent 写顺。
10.3 把 instruction 写成空话
像“你是一个智能助手,请帮助用户解决问题”这种句子,通常信息量太低。
你要尽量写清角色、风格、边界。
11. 本章小结
这一章你最该带走的,不是某一行代码,而是一个感觉:
原来 ReactAgent 不是一个抽象概念,它就是一个 Spring Bean,只不过这个 Bean 背后连着模型、状态和能力。
下一章,我们不继续堆概念,而是给这个 Agent 装上第一只“手”。