Agent服务接口和UI对接
一、介绍
本节以实现 AI Auto Agent 的 SSE(Server-Sent Events)流式响应接口 为目标,重点在于设计 SSE 异步响应结果对象,用于承载 Step1~4 的过程数据,并以异步流的方式实时返回。
实现步骤:
- 定义异步结果对象结构,确保可逐步承载并推送任务的过程数据。
- 将 Step1–Step4 的执行过程(任务分析、精准执行、质量监督、执行总结)拆解为可流式推送的片段。
- 构建 SSE 接口,保证数据按步骤顺序、异步推送至前端。
- 与前端 UI 对接,实现实时可视化的任务过程反馈。
**应用场景:**类似 Cursor、trae.ai 等 AI 产品,其前端代码逻辑相对简洁,能轻松对接后端 SSE 接口。对于后端工程师而言,这意味着在不需要编写复杂前端逻辑的情况下,也能快速实现 UI 产品化,加速从接口到产品的落地。
二、最终效果
如图,流式响应(SSE)接口对接UI效果;

三、功能流程
如图,从Agent服务的装配到接口调用和响应的关系图;
程序启动与自动化装配: 在程序启动时,首先进行自动化装配过程。我们会对数据库中预先配置的 Agent 客户端进行初始化,确保所需的配置项已经准备好。接着,将这些初始化的客户端对象写入到 Spring 容器 中,以便在后续执行 Agent 时能够快速调用。这一做法的原因是为了提高系统的模块化与灵活性,使得客户端可以在多个执行环节中复用。
SSE 流式响应与前端接口对接 在客户端 (UI) 发起 POST 请求 后,系统需要通过封装一个 SSE (Server-Sent Events) 流式响应接口,向前端持续推送 Step 1~4 执行过程中的动态数据。每个步骤的数据应包含其对应的 类型(如步骤、节点、过程等),这样可以清晰地反馈当前 Agent 的执行状态,并让前端实时显示 Agent 正在执行的具体操作。
四、工程实现
1. 工程结构

2. 修改说明
数据库更新:在 ai_client 表里新增了一个 ID 3104,负责处理响应式请求。可以直接导入最新的库表数据来更新这个记录。同时,记得为这个客户端配置相关的 prompt 和 关联关系。另外,在 AiClientTypeEnumVO 枚举里新增了一个值:RESPONSE_ASSISTANT,主要是为了在 Step 4 时能够返回完整的执行结果。
自动装配服务:在 app 模块里,增加了 AiAgentAutoConfiguration 自动装配服务。这个服务会根据 YML 配置文件里的 agent 客户端服务列表 自动进行装配,省去手动配置的麻烦。
新接口设计:在 api 模块下,新增了 IAiAgentService.autoAgent 这个接口,它会返回一个 ResponseBodyEmitter 类型的异步流式响应。这意味着可以实时返回执行中的数据,而不是等待全部完成后再反馈。
触发器模块更新:在 trigger 模块中的 http 包,实现了 IAiAgentService.autoAgent 接口,并且通过 autoAgentExecuteStrategy 来处理结果响应。为支持异步流式返回,在 autoAgentExecuteStrategy 中加入了新的异步响应参数。
自动执行参数:创建 AutoAgentExecuteResultEntity 对象来记录每个执行阶段的状态,包含以下字段:
type(数据类型)subType(动作细节)step(当前步骤)content(执行数据内容)completed(是否完成)timestamp(时间戳)
同时,封装几个方法来创建不同阶段的结果对象,比如:
- 创建分析阶段结果
- 创建执行阶段细分结果
- 创建监督阶段的结果
- 创建总结阶段结果
- 创建错误结果
- 创建执行完成标识
通用方法抽取:在 AbstractExecuteSupport 中,提取了 sendSseResult 这个通用方法,它能让在 Step 1~4 期间实时将数据流式推送给前端,确保用户能够看到实时反馈。
3. 自动装配 - Ai Agent Client
ai-agent-station-study-app/application-dev.yml
spring:
ai:
agent:
auto-config:
enabled: true
client-ids: 3101,3102,3103,3104添加自定义配置客户端集合列表。
@Slf4j
@Configuration
@EnableConfigurationProperties(AiAgentAutoConfigProperties.class)
@ConditionalOnProperty(prefix = "spring.ai.agent.auto-config", name = "enabled", havingValue = "true")
public class AiAgentAutoConfiguration implements ApplicationListener<ApplicationEvent> {
@Resource
private AiAgentAutoConfigProperties aiAgentAutoConfigProperties;
@Resource
private DefaultArmoryStrategyFactory defaultArmoryStrategyFactory;
@Override
public void onApplicationEvent(ApplicationEvent event) {
try {
log.info("AI Agent 自动装配开始,配置: {}", aiAgentAutoConfigProperties);
// 检查配置是否有效
if (!aiAgentAutoConfigProperties.isEnabled()) {
log.info("AI Agent 自动装配未启用");
return;
}
List<String> clientIds = aiAgentAutoConfigProperties.getClientIds();
if (CollectionUtils.isEmpty(clientIds)) {
log.warn("AI Agent 自动装配配置的客户端ID列表为空");
return;
}
// 遍历配置的客户端ID,进行自动装配
List<String> commandIdList;
if (clientIds.size() == 1 && clientIds.get(0).contains(Constants.SPLIT)) {
// 处理逗号分隔的字符串
commandIdList = Arrays.stream(clientIds.get(0).split(Constants.SPLIT))
.map(String::trim)
.filter(id -> !id.isEmpty())
.collect(Collectors.toList());
} else {
commandIdList = clientIds;
}
log.info("开始自动装配AI客户端,客户端ID列表: {}", commandIdList);
// 执行自动装配
var armoryStrategyHandler =
defaultArmoryStrategyFactory.armoryStrategyHandler();
String result = armoryStrategyHandler.apply(
ArmoryCommandEntity.builder()
.commandType(AiAgentEnumVO.AI_CLIENT.getCode())
.commandIdList(commandIdList)
.build(),
new DefaultArmoryStrategyFactory.DynamicContext());
log.info("AI Agent 自动装配结果: {}", result);
} catch (Exception e) {
log.error("AI Agent 自动装配失败", e);
}
}
}- 自动装配的过程包括读取配置文件并进行解析,之后调用已开发的装配方法 armoryStrategyHandler.apply 进行装配处理。该方法负责根据解析后的配置,完成相关组件和服务的自动装配,确保系统能够按需加载并初始化必要的模块。
4. 执行策略 - 异步通知
4.1 策略接口 - 新增入参
/**
* 执行策略接口
*/
public interface IExecuteStrategy {
void execute(ExecuteCommandEntity requestParameter, ResponseBodyEmitter emitter) throws Exception;
}ResponseBodyEmitter 是 Spring MVC 提供的一个类,主要用于异步地向 HTTP 响应体写入数据。它支持异步非阻塞的响应输出,允许服务器在处理请求的过程中,分多次将数据发送给客户端,而不需要等待整个请求处理完成。
在策略类接口中,新增了一个流式响应参数,用于在 AutoAgent 策略执行过程中,实时返回执行结果。这使得执行过程中的数据可以逐步返回给客户端,支持更高效的实时交互。
4.2 定义对象 - 执行结果
/**
* AutoAgent 执行结果实体
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AutoAgentExecuteResultEntity {
/**
* 数据类型:analysis(分析阶段), execution(执行阶段), supervision(监督阶段), summary(总结阶段), error(错误信息), complete(完成标识)
* 细分类型:analysis_status(任务状态分析), analysis_history(执行历史评估), analysis_strategy(下一步策略), analysis_progress(完成度评估)
* execution_target(执行目标), execution_process(执行过程), execution_result(执行结果), execution_quality(质量检查)
* supervision_assessment(质量评估), supervision_issues(问题识别), supervision_suggestions(改进建议), supervision_score(质量评分)
*/
private String type;
/**
* 子类型标识,用于前端细粒度展示
*/
private String subType;
/**
* 当前步骤
*/
private Integer step;
/**
* 数据内容
*/
private String content;
/**
* 是否完成
*/
private Boolean completed;
/**
* 时间戳
*/
private Long timestamp;
/**
* 会话ID
*/
private String sessionId;
/**
* 创建分析阶段结果
*/
public static AutoAgentExecuteResultEntity createAnalysisResult(Integer step, String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("analysis")
.step(step)
.content(content)
.completed(false)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建分析阶段细分结果
*/
public static AutoAgentExecuteResultEntity createAnalysisSubResult(Integer step, String subType, String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("analysis")
.subType(subType)
.step(step)
.content(content)
.completed(false)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建执行阶段结果
*/
public static AutoAgentExecuteResultEntity createExecutionResult(Integer step, String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("execution")
.step(step)
.content(content)
.completed(false)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建执行阶段细分结果
*/
public static AutoAgentExecuteResultEntity createExecutionSubResult(Integer step, String subType, String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("execution")
.subType(subType)
.step(step)
.content(content)
.completed(false)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建监督阶段结果
*/
public static AutoAgentExecuteResultEntity createSupervisionResult(Integer step, String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("supervision")
.step(step)
.content(content)
.completed(false)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建监督阶段细分结果
*/
public static AutoAgentExecuteResultEntity createSupervisionSubResult(Integer step, String subType, String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("supervision")
.subType(subType)
.step(step)
.content(content)
.completed(false)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建总结阶段细分的结果
*/
public static AutoAgentExecuteResultEntity createSummarySubResult(String subType, String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("summary")
.subType(subType)
.step(4)
.content(content)
.completed(false)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建总结阶段结果
*/
public static AutoAgentExecuteResultEntity createSummaryResult(String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("summary")
.step(null)
.content(content)
.completed(true)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建错误结果
*/
public static AutoAgentExecuteResultEntity createErrorResult(String content, String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("error")
.step(null)
.content(content)
.completed(true)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
/**
* 创建完成标识
*/
public static AutoAgentExecuteResultEntity createCompleteResult(String sessionId) {
return AutoAgentExecuteResultEntity.builder()
.type("complete")
.step(null)
.content("执行完成")
.completed(true)
.timestamp(System.currentTimeMillis())
.sessionId(sessionId)
.build();
}
}- 创建了一个返回结果的实体类对象,方便前端在接收到接口数据后进行处理。该实体类封装了需要返回的数据结构,确保数据能以一致的格式传递到前端。
- 在该实体类中,createAnalysisResult 是聚合的核心对象。具体实现可以参考课程中的相关代码。
4.3 抽象方法 - 共用操作
public abstract class AbstractExecuteSupport extends AbstractMultiThreadStrategyRouter<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> {
// ... 省略部分代码
/**
* 通用发送SSE消息
*
* @param dynamicContext 动态上下文
* @param result 执行结果
*/
protected void sendSseMessage(DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext, AutoAgentExecuteResultEntity result) {
try {
ResponseBodyEmitter emitter = dynamicContext.getValue("emitter");
if (emitter != null) {
// 发送SSE格式的数据
String sseData = "data: " + JSON.toJSONString(result) + "\n\n";
emitter.send(sseData);
}
} catch (IOException e) {
log.error("发送SSE结果失败:{}", e.getMessage(), e);
}
}
}- 在抽象类中,新增了一个 sendSseResult 方法,用于异步发送结果数据。该方法的返回格式为
data拼接结果,可以设计为包括 code、info 和 data 的对象结构。这样,数据发送的格式更加规范化和统一。 - 有了这个通用方法,其他步骤(Step 1~4)都可以复用该方法,减少重复代码,提高代码的可维护性和复用性。
4.4 发送数据 - 举例 Step1
@Slf4j
@Service
public class Step1AnalyzerNode extends AbstractExecuteSupport {
@Override
protected String doApply(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
log.info("\n🎯 === 执行第 {} 步 ===", dynamicContext.getStep());
// 第一阶段:任务分析
log.info("\n📊 阶段1: 任务状态分析");
String analysisPrompt = String.format("""
**原始用户需求:** %s
**当前执行步骤:** 第 %d 步 (最大 %d 步)
**历史执行记录:**
%s
**当前任务:** %s
**分析要求:**
请深入分析用户的具体需求,制定明确的执行策略:
1. 理解用户真正想要什么(如:具体的学习计划、项目列表、技术方案等)
2. 分析需要哪些具体的执行步骤(如:搜索信息、检索项目、生成内容等)
3. 制定能够产生实际结果的执行策略
4. 确保策略能够直接回答用户的问题
**输出格式要求:**
任务状态分析: [当前任务完成情况的详细分析]
执行历史评估: [对已完成工作的质量和效果评估]
下一步策略: [具体的执行计划,包括需要调用的工具和生成的内容]
完成度评估: [0-100]%%
任务状态: [CONTINUE/COMPLETED]
""",
requestParameter.getMessage(),
dynamicContext.getStep(),
dynamicContext.getMaxStep(),
!dynamicContext.getExecutionHistory().isEmpty() ? dynamicContext.getExecutionHistory().toString() : "[首次执行]",
dynamicContext.getCurrentTask()
);
// 获取对话客户端
AiAgentClientFlowConfigVO aiAgentClientFlowConfigVO = dynamicContext.getAiAgentClientFlowConfigVOMap().get(AiClientTypeEnumVO.TASK_ANALYZER_CLIENT.getCode());
ChatClient chatClient = getChatClientByClientId(aiAgentClientFlowConfigVO.getClientId());
String analysisResult = chatClient
.prompt(analysisPrompt)
.advisors(a -> a
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, requestParameter.getSessionId())
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 1024))
.call().content();
assert analysisResult != null;
parseAnalysisResult(dynamicContext, analysisResult, requestParameter.getSessionId());
// 将分析结果保存到动态上下文中,供下一步使用
dynamicContext.setValue("analysisResult", analysisResult);
// 检查是否已完成
if (analysisResult.contains("任务状态: COMPLETED") ||
analysisResult.contains("完成度评估: 100%")) {
dynamicContext.setCompleted(true);
log.info("✅ 任务分析显示已完成!");
return router(requestParameter, dynamicContext);
}
return router(requestParameter, dynamicContext);
}
@Override
public StrategyHandler<ExecuteCommandEntity, DefaultAutoAgentExecuteStrategyFactory.DynamicContext, String> get(ExecuteCommandEntity requestParameter, DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext) throws Exception {
// 如果任务已完成或达到最大步数,进入总结阶段
if (dynamicContext.isCompleted() || dynamicContext.getStep() > dynamicContext.getMaxStep()) {
return getBean("step4LogExecutionSummaryNode");
}
// 否则继续执行下一步
return getBean("step2PrecisionExecutorNode");
}
private void parseAnalysisResult(DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext, String analysisResult, String sessionId) {
log.info("\n📊 === 第 {} 步分析结果 ===", dynamicContext.getStep());
String[] lines = analysisResult.split("\n");
String currentSection = "";
StringBuilder sectionContent = new StringBuilder();
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) continue;
if (line.contains("任务状态分析:")) {
// 发送上一个section的内容
sendAnalysisSubResult(dynamicContext, currentSection, sectionContent.toString(), sessionId);
currentSection = "analysis_status";
sectionContent = new StringBuilder();
log.info("\n🎯 任务状态分析:");
continue;
} else if (line.contains("执行历史评估:")) {
// 发送上一个section的内容
sendAnalysisSubResult(dynamicContext, currentSection, sectionContent.toString(), sessionId);
currentSection = "analysis_history";
sectionContent = new StringBuilder();
log.info("\n📈 执行历史评估:");
continue;
} else if (line.contains("下一步策略:")) {
// 发送上一个section的内容
sendAnalysisSubResult(dynamicContext, currentSection, sectionContent.toString(), sessionId);
currentSection = "analysis_strategy";
sectionContent = new StringBuilder();
log.info("\n🚀 下一步策略:");
continue;
} else if (line.contains("完成度评估:")) {
// 发送上一个section的内容
sendAnalysisSubResult(dynamicContext, currentSection, sectionContent.toString(), sessionId);
currentSection = "analysis_progress";
sectionContent = new StringBuilder();
String progress = line.substring(line.indexOf(":") + 1).trim();
log.info("\n📊 完成度评估: {}", progress);
sectionContent.append(line).append("\n");
continue;
} else if (line.contains("任务状态:")) {
// 发送上一个section的内容
sendAnalysisSubResult(dynamicContext, currentSection, sectionContent.toString(), sessionId);
currentSection = "analysis_task_status";
sectionContent = new StringBuilder();
String status = line.substring(line.indexOf(":") + 1).trim();
if (status.equals("COMPLETED")) {
log.info("\n✅ 任务状态: 已完成");
} else {
log.info("\n🔄 任务状态: 继续执行");
}
sectionContent.append(line).append("\n");
continue;
}
if (!currentSection.isEmpty()) {
sectionContent.append(line).append("\n");
switch (currentSection) {
case "analysis_status":
log.info(" 📋 {}", line);
break;
case "analysis_history":
log.info(" 📊 {}", line);
break;
case "analysis_strategy":
log.info(" 🎯 {}", line);
break;
default:
log.info(" 📝 {}", line);
break;
}
}
}
// 发送最后一个section的内容
sendAnalysisSubResult(dynamicContext, currentSection, sectionContent.toString(), sessionId);
}
/**
* 发送分析阶段细分结果到流式输出
*/
private void sendAnalysisSubResult(DefaultAutoAgentExecuteStrategyFactory.DynamicContext dynamicContext,
String subType, String content, String sessionId) {
if (!subType.isEmpty() && !content.isEmpty()) {
AutoAgentExecuteResultEntity result = AutoAgentExecuteResultEntity.createAnalysisSubResult(
dynamicContext.getStep(), subType, content, sessionId);
sendSseMessage(dynamicContext, result);
}
}
}- 以 Step1 为例,其他步骤的操作类似。在 parseAnalysisResult 分析结果过程中,数据会一步步地异步发送出去。特别地,sendAnalysisSubResult 方法在发送数据时,使用 AutoAgentExecuteResultEntity.createAnalysisSubResult 来创建不同的结果对象。这一过程是针对每个步骤进行差异化处理的,确保每个步骤的数据能够正确响应。
- 对于 Step 2、3、4,可以参考工程中的相关代码实现,操作逻辑与 Step 1 类似。
5. 话术调整
Step1 ~ 4,在测试的过程又细化了一些话术,这部分不是固定的内容,你也可以根据测试结果进行细微的调整,让结果更加准确。
// 优化前
log.info("\n📊 阶段1: 任务状态分析");
String analysisPrompt = String.format("""
**原始用户需求:** %s
**当前执行步骤:** 第 %d 步 (最大 %d 步)
**历史执行记录:**
%s
**当前任务:** %s
请分析当前任务状态,评估执行进度,并制定下一步策略。
""",
// 优化后
log.info("\n📊 阶段1: 任务状态分析");
String analysisPrompt = String.format("""
**原始用户需求:** %s
**当前执行步骤:** 第 %d 步 (最大 %d 步)
**历史执行记录:**
%s
**当前任务:** %s
**分析要求:**
请深入分析用户的具体需求,制定明确的执行策略:
1. 理解用户真正想要什么(如:具体的学习计划、项目列表、技术方案等)
2. 分析需要哪些具体的执行步骤(如:搜索信息、检索项目、生成内容等)
3. 制定能够产生实际结果的执行策略
4. 确保策略能够直接回答用户的问题
**输出格式要求:**
任务状态分析: [当前任务完成情况的详细分析]
执行历史评估: [对已完成工作的质量和效果评估]
下一步策略: [具体的执行计划,包括需要调用的工具和生成的内容]
完成度评估: [0-100]%%
任务状态: [CONTINUE/COMPLETED]
""",- 现已经明确了分析要求和输出格式,以确保结果更加准确且易于使用。然而,当前的 prompt 优化并非最终版本,未来可能会根据测试和验证结果进行进一步调整。对于 prompt 优化,GitHub 上有一些相关资料可以参考:Prompt Optimizer。
五、接口文档
该接口提供AI智能体自动对话功能,支持流式响应,实时返回AI的思考过程和执行结果。
1. 接口信息
- 接口地址 :
POST /api/v1/agent/auto_agent - 请求方式 : POST
- 响应格式 : Server-Sent Events (SSE) 流式响应
- Content-Type :
application/json - Accept :
text/event-stream
2. 请求参数
请求体(JSON格式):
{
"aiAgentId": "3",
"message": "检索Springboot相关项目,列出一份学习计划",
"sessionId": "session_1ee4e56f",
"maxStep": 5
}参数说明:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| aiAgentId | String | 是 | AI 管理员类型ID,目前支持 "3"(Auto Agent - 自动语音对话体) |
| message | String | 是 | 用户输入的问题内容,最大长度1000字符 |
| sessionId | String | 是 | 会话ID,用于标识唯一一对一对话,格式:session_时间戳_随机字符串 |
| maxStep | Integer | 是 | 最大执行步数,可选值:1, 2, 3, 5, 10, 20, 50 |
3. 响应格式
响应采用Server-Sent Events格式,每条消息以data: 开头,包含JSON格式的数据:
data: {"type":"analysis","subType":"analysis_status","step":1,"content":"开始分析用户需求...","completed":false,"timestamp":1642345678901,"sessionId":"session_1ee4e56f"}
data: {"type":"execution","subType":"execution_process","step":2,"content":"正在执行搜索任务...","completed":false,"timestamp":1642345678902,"sessionId":"session_1ee4e56f"}
data: {"type":"summary","subType":"summary_overview","step":5,"content":"## 学习计划\n\n基于检索结果,为您制定以下学习计划...","completed":true,"timestamp":1642345678905,"sessionId":"session_1ee4e56f"}响应字段说明:
| 字段名 | 类型 | 说明 |
|---|---|---|
| type | String | 消息类型,详见下方类型说明 |
| subType | String | 消息子类型,详见下方子类型说明 |
| step | Integer | 当前执行步骤 |
| content | String | 消息内容,支持Markdown格式 |
| completed | Boolean | 是否完成 |
| timestamp | Long | 时间戳 |
| sessionId | String | 会话ID |
4. 消息类型说明
主要类型(type):
| 类型 | 名称 | 图标 | 说明 |
|---|---|---|---|
| analysis | 分析阶段 | 🧑🔬 | AI分析用户需求和制定策略 |
| execution | 执行阶段 | ⚡️ | AI执行具体任务 |
| supervision | 监管阶段 | 🦸♂️ | AI监督和质量检查 |
| summary | 总结阶段 | 📊 | AI总结结果和输出最终答案 |
| error | 错误信息 | ❌ | 执行过程中的错误信息 |
| complete | 完成 | ✅ | 任务执行完成标识 |
子类型(subType):
| 子类型 | 说明 |
|---|---|
| analysis_status | 任务状态 |
| analysis_history | 历史评估 |
| analysis_strategy | 执行策略 |
| analysis_progress | 完成度 |
| execution_target | 执行目标 |
| execution_process | 执行过程 |
| execution_result | 执行结果 |
| execution_quality | 质量检查 |
| assessment | 质量评估 |
| issues | 问题识别 |
| suggestions | 改进建议 |
| score | 质量评分 |
| pass | 检查结果 |
| completed_work | 已完成工作 |
| incomplete_reasons | 未完成原因 |
| evaluation | 效果评估 |
| summary_overview | 总结概况 |
5. 前端集成示例
JavaScript调用示例:
// 准备请求数据
const requestData = {
aiAgentId: "3",
message: "检索小傅哥的相关项目,列出一份学习计划",
sessionId: "session_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9),
maxStep: 5
};
// 发送POST请求
fetch('http://localhost:8091/api/v1/agent/auto_agent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/event-stream'
},
body: JSON.stringify(requestData)
})
.then(response => {
if (!response.ok) {
throw new Error('网络请求失败: ' + response.status);
}
// 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
function readStream() {
reader.read().then(({ done, value }) => {
if (done) {
console.log('流式响应结束');
return;
}
// 解码数据块
const chunk = decoder.decode(value, { stream: true });
// 处理SSE数据
const lines = chunk.split('\n');
for (let line of lines) {
if (line.startsWith('data: ')) {
const data = line.substring(6).trim();
if (data && data !== '[DONE]') {
try {
const jsonData = JSON.parse(data);
handleSSEMessage(jsonData);
} catch (e) {
console.warn('无法解析JSON数据:', data);
}
}
}
}
// 继续读取流
readStream();
});
}
readStream();
})
.catch(error => {
console.error('请求错误:', error);
});
// 处理SSE消息
function handleSSEMessage(jsonData) {
const { type, subType, step, content, completed, timestamp, sessionId } = jsonData;
// 根据消息类型进行不同处理
if (type === 'summary') {
// 显示最终结果
displayFinalResult(content);
} else {
// 显示思考过程
displayThinkingProcess(type, subType, content, step);
}
}- 根据接口文档和必要的描述,可以利用 AI 工具自动生成一套前端页面。这些页面将基于文档中的 API 结构和功能需求,确保前端与后端的接口能够有效对接并实现预期的功能。
六、前端对接

- 这部分的 UI 页面生成是基于 AI 技术处理的,类似于之前在 阶段 1 和 阶段 2 中所采用的方法。虽然这个过程需要经过几轮迭代才能最终稳定和实用,但也可以直接使用工程中提供的现成模板。
七、功能测试
1. 导入数据

- 最新SQL放到了工程下 docs/dev-ops/mysql/sql 下。
- 另外注意,你要给 ai_client_api 配置你的 base_url、api_key
2. 启动服务
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.9)
25-09-23.12:57:19.568 [main ] INFO Application - Starting Application using Java 17.0.6 with PID 18888 (D:\Personal_projects\ai-agent-station-study\ai-agent-station-study-app\target\classes started by Dell in D:\Personal_projects\ai-agent-station-study)
25-09-23.12:57:19.569 [main ] INFO Application - The following 1 profile is active: "dev"
25-09-23.12:57:20.391 [main ] INFO TomcatWebServer - Tomcat initialized with port 8091 (http)
25-09-23.12:57:20.396 [main ] INFO Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8091"]
25-09-23.12:57:20.398 [main ] INFO StandardService - Starting service [Tomcat]
25-09-23.12:57:20.398 [main ] INFO StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.44]
25-09-23.12:57:20.446 [main ] INFO [/] - Initializing Spring embedded WebApplicationContext
25-09-23.12:57:20.446 [main ] INFO ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 850 ms
25-09-23.12:57:21.043 [main ] INFO PgVectorStore - Using the vector table name: vector_store_openai. Is empty: false
25-09-23.12:57:21.046 [main ] INFO PgVectorStore - Initializing PGVectorStore schema for table: vector_store_openai in schema: public
25-09-23.12:57:21.046 [main ] INFO PgVectorStore - vectorTableValidationsEnabled false
25-09-23.12:57:21.190 [main ] INFO OptionalValidatorFactoryBean - Failed to set up a Bean Validation provider: jakarta.validation.NoProviderFoundException: Unable to create a Configuration, because no Jakarta Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.
25-09-23.12:57:21.398 [main ] INFO Http11NioProtocol - Starting ProtocolHandler ["http-nio-8091"]
25-09-23.12:57:21.405 [main ] INFO TomcatWebServer - Tomcat started on port 8091 (http) with context path '/'
25-09-23.12:57:21.405 [main ] INFO AiAgentAutoConfiguration - AI Agent 自动装配开始,配置: AiAgentAutoConfigProperties(enabled=true, clientIds=[3101, 3102, 3103, 3104])
25-09-23.12:57:21.405 [main ] INFO AiAgentAutoConfiguration - 开始自动装配AI客户端,客户端ID列表: [3101, 3102, 3103, 3104]
25-09-23.12:57:21.406 [pool-2-thread-1 ] INFO AiClientLoadDataStrategy - 查询配置数据(ai_client_api) [3101, 3102, 3103, 3104]
25-09-23.12:57:21.406 [pool-2-thread-2 ] INFO AiClientLoadDataStrategy - 查询配置数据(ai_client_model) [3101, 3102, 3103, 3104]
25-09-23.12:57:21.407 [pool-2-thread-3 ] INFO AiClientLoadDataStrategy - 查询配置数据(ai_client_tool_mcp) [3101, 3102, 3103, 3104]
25-09-23.12:57:21.407 [pool-2-thread-4 ] INFO AiClientLoadDataStrategy - 查询配置数据(ai_client_system_prompt) [3101, 3102, 3103, 3104]
25-09-23.12:57:21.407 [pool-2-thread-5 ] INFO AiClientLoadDataStrategy - 查询配置数据(ai_client_advisor) [3101, 3102, 3103, 3104]
25-09-23.12:57:21.407 [pool-2-thread-6 ] INFO AiClientLoadDataStrategy - 查询配置数据(ai_client) [3101, 3102, 3103, 3104]
25-09-23.12:57:21.419 [pool-2-thread-6 ] INFO HikariDataSource - MainHikariPool - Starting...
25-09-23.12:57:21.523 [pool-2-thread-6 ] INFO HikariPool - MainHikariPool - Added connection com.mysql.cj.jdbc.ConnectionImpl@6c380980
25-09-23.12:57:21.524 [pool-2-thread-6 ] INFO HikariDataSource - MainHikariPool - Start completed.
25-09-23.12:57:21.697 [main ] INFO RootNode - Ai Agent 构建,数据加载节点 {"commandIdList":["3101","3102","3103","3104"],"commandType":"client","loadDataStrategy":"aiClientLoadDataStrategy"}
25-09-23.12:57:21.697 [main ] INFO AiClientApiNode - Ai Agent 构建,API 构建节点 {"commandIdList":["3101","3102","3103","3104"],"commandType":"client","loadDataStrategy":"aiClientLoadDataStrategy"}
25-09-23.12:57:21.705 [main ] INFO AbstractArmorySupport - 注册Bean: ai_client_api_1001 -> org.springframework.ai.openai.api.OpenAiApi@6eb81db
25-09-23.12:57:21.705 [main ] INFO AiClientToolMcpNode - Ai Agent 构建节点,Tool MCP 工具配置{"commandIdList":["3101","3102","3103","3104"],"commandType":"client","loadDataStrategy":"aiClientLoadDataStrategy"}
25-09-23.12:57:22.162 [HttpClient-10-Worker-0] INFO McpAsyncClient - Server response with Protocol: 2024-11-05, Capabilities: ServerCapabilities[completions=null, experimental={}, logging=null, prompts=null, resources=null, tools=ToolCapabilities[listChanged=false]], Info: Implementation[name=bing-search, version=1.9.4] and Instructions null
25-09-23.12:57:22.342 [main ] INFO AiClientToolMcpNode - Tool SSE MCP Initialized InitializeResult[protocolVersion=2024-11-05, capabilities=ServerCapabilities[completions=null, experimental={}, logging=null, prompts=null, resources=null, tools=ToolCapabilities[listChanged=false]], serverInfo=Implementation[name=bing-search, version=1.9.4], instructions=null]
25-09-23.12:57:22.344 [main ] INFO AbstractArmorySupport - 注册Bean: ai_client_tool_mcp_5006 -> io.modelcontextprotocol.client.McpSyncClient@7e390387
25-09-23.12:57:22.344 [main ] INFO AiClientModelNode - Ai Agent 构建节点,Mode 对话模型{"commandIdList":["3101","3102","3103","3104"],"commandType":"client","loadDataStrategy":"aiClientLoadDataStrategy"}
25-09-23.12:57:22.497 [main ] INFO AbstractArmorySupport - 注册Bean: ai_client_model_2001 -> OpenAiChatModel [defaultOptions=OpenAiChatOptions: {"streamUsage":false,"model":"gpt-5-mini"}]
25-09-23.12:57:22.508 [main ] INFO AiClientAdvisorNode - Ai Agent 构建节点,Advisor 顾问角色{"commandIdList":["3101","3102","3103","3104"],"commandType":"client","loadDataStrategy":"aiClientLoadDataStrategy"}
25-09-23.12:57:22.515 [main ] INFO AbstractArmorySupport - 注册Bean: ai_client_advisor_4001 -> org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor@1943eb92
25-09-23.12:57:22.515 [main ] INFO AiClientNode - Ai Agent 构建节点,客户端{"commandIdList":["3101","3102","3103","3104"],"commandType":"client","loadDataStrategy":"aiClientLoadDataStrategy"}
25-09-23.12:57:22.619 [main ] INFO AbstractArmorySupport - 注册Bean: ai_client_3101 -> org.springframework.ai.chat.client.DefaultChatClient@6f4aecdc
25-09-23.12:57:22.758 [main ] INFO AbstractArmorySupport - 注册Bean: ai_client_3102 -> org.springframework.ai.chat.client.DefaultChatClient@1235f171
25-09-23.12:57:22.841 [main ] INFO AbstractArmorySupport - 注册Bean: ai_client_3103 -> org.springframework.ai.chat.client.DefaultChatClient@8f79e0b
25-09-23.12:57:22.841 [main ] INFO AbstractArmorySupport - 注册Bean: ai_client_3104 -> org.springframework.ai.chat.client.DefaultChatClient@3574481d
25-09-23.12:57:22.841 [main ] INFO AiAgentAutoConfiguration - AI Agent 自动装配结果: null3. 访问页面
进入工程后的 html 文件,点击浏览器打开访问页面。

之后你就可以进行提问体验了。