DeepSeek RAG&MCP + Agent智能体项目 —— 动态决策策略的接口对接

一、前言

至此,对于动态决策的基本逻辑我们都写完了,接下来就是完善和写对接前端的接口了。

这里我们要加上流式响应,所以稍微还要修改一下前面的代码。

二、自动装配

首先,我们前面的装配是要自己写代码实现的,这个很麻烦,因为我们知道,既然都要去使用Agent了,那我肯定要装配一个客户端啊,所以这个应该是一个标准的前置动作,即在使用Agent前必须要做的,因此我们将这个写到配置类中,启动服务时自动装配,当然,也不能完全写死,我们允许在配置文件中修改对应的配置(包括但不限于是否自动装配装配客户端的ids)。

java 复制代码
@Data
@ConfigurationProperties(prefix = "spring.ai.agent.auto-config")
public class AiAgentAutoConfigProperties {

    /**
     * 是否启用AI Agent自动装配
     */
    private boolean enabled = false;

    /**
     * 需要自动装配的客户端ID列表
     */
    private List<String> clientIds;

}
java 复制代码
@Slf4j
@Configuration
@EnableConfigurationProperties(AiAgentAutoConfigProperties.class)
@ConditionalOnProperty(prefix = "spring.ai.agent.auto-config", name = "enabled", havingValue = "true")
public class AiAgentAutoConfiguration implements ApplicationListener<ApplicationReadyEvent> {
    @Resource
    private AiAgentAutoConfigProperties aiAgentAutoConfigProperties;

    @Resource
    private DefaultArmoryStrategyFactory defaultArmoryStrategyFactory;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent 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);

            // 执行自动装配
            StrategyHandler<ArmoryCommandEntity, DefaultArmoryStrategyFactory.DynamicContext, String> 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);
        }
    }
}
bash 复制代码
  ai:
    openai:
      base-url: https://apis.itedus.cn
      api-key: sk-xxxxxxxxxxx
    agent:
      auto-config:
        enabled: true
        client-ids: 3101,3102,3103,3104

三、接口对接

1.服务层

请求体:

java 复制代码
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AutoAgentRequestDTO {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * Ai智能提ID
     */
    private String aiAgentId;
    /**
     * 用户消息
     */
    private String message;
    /**
     * 会话ID
     */
    private String sessionId;
    /**
     * 最大执行步数
     */
    private Integer maxStep;

}

服务接口:在这里去选择策略

java 复制代码
public interface IAiAgentService {

    ResponseBodyEmitter autoAgent(AutoAgentRequestDTO requestDTO, HttpServletResponse response);

}

Controller:

java 复制代码
@Slf4j
@RestController
@RequestMapping("/api/v1/agent")
@CrossOrigin(origins = "*", allowedHeaders = "*", methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})
public class AiAgentController implements IAiAgentService {

    @Resource(name = "autoAgentExecuteStrategy")
    private IExecuteStrategy autoAgentExecuteStrategy;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @RequestMapping(value = "auto_agent", method = RequestMethod.POST)
    public ResponseBodyEmitter autoAgent(@RequestBody AutoAgentRequestDTO request, HttpServletResponse response) {
        log.info("AutoAgent流式执行请求开始,请求信息:{}", JSON.toJSONString(request));

        try {
            // 设置SSE响应头
            response.setContentType("text/event-stream");
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Connection", "keep-alive");

            // 1. 创建流式输出对象
            ResponseBodyEmitter emitter = new ResponseBodyEmitter(Long.MAX_VALUE);

            // 2. 构建执行命令实体
            ExecuteCommandEntity executeCommandEntity = ExecuteCommandEntity.builder()
                    .aiAgentId(request.getAiAgentId())
                    .message(request.getMessage())
                    .sessionId(request.getSessionId())
                    .maxStep(request.getMaxStep())
                    .build();

            // 3. 异步执行AutoAgent
            threadPoolExecutor.execute(() -> {
                try {
                    autoAgentExecuteStrategy.execute(executeCommandEntity, emitter);
                } catch (Exception e) {
                    log.error("AutoAgent执行异常:{}", e.getMessage(), e);
                    try {
                        emitter.send("执行异常:" + e.getMessage());
                    } catch (Exception ex) {
                        log.error("发送异常信息失败:{}", ex.getMessage(), ex);
                    }
                } finally {
                    try {
                        emitter.complete();
                    } catch (Exception e) {
                        log.error("完成流式输出失败:{}", e.getMessage(), e);
                    }
                }
            });

            return emitter;

        } catch (Exception e) {
            log.error("AutoAgent请求处理异常:{}", e.getMessage(), e);
            ResponseBodyEmitter errorEmitter = new ResponseBodyEmitter();
            try {
                errorEmitter.send("请求处理异常:" + e.getMessage());
                errorEmitter.complete();
            } catch (Exception ex) {
                log.error("发送错误信息失败:{}", ex.getMessage(), ex);
            }
            return errorEmitter;
        }
    }

}

这里单独把这一步拿出来说一下,ResponseBodyEmitter 相当于是一个后端往前端持续写数据的通道。是SpringMVC里面提供的一个响应对象,可以允许响应长连接,进而实现流式响应的能力,说白了就是保持连接,后端传一段消息过来就在前端展示一段,类似TCP但是单向。

java 复制代码
// 1. 创建流式输出对象
ResponseBodyEmitter emitter = new ResponseBodyEmitter(Long.MAX_VALUE);

在finally模块中关闭连接:

java 复制代码
 emitter.complete();

同时要配置SSE响应头:是为了让前端和中间网络层知道:这个接口要保持连接,并实时接收服务端一段一段推送的数据。

就相当于:

以前的流程:请求 -> 处理 -> 响应

现在的流程:请求 -> 处理一部分 -> 响应一部分 -> 处理一部分 -> 响应一部分 ... -> 结束连接

2.节点层

首先要引入流式响应,因此要在AbstractExecuteSupport中新增一个方法,用于获取在自动配置阶段装填的流式响应发射器对象(因为我们希望每一个分析的步骤都流式响应回前端)。

java 复制代码
/**
     * 通用的SSE结果发送方法
     * @param dynamicContext 动态上下文
     * @param result 要发送的结果实体
     */
    protected void sendSseResult(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);
        }
    }

接下来就是在每个节点的每个步骤都添加上发射器,所有都要添加,这里只展示一部分:

java 复制代码
 if (line.contains("任务状态分析:")) {
                sendAnalysisSubResult(dynamicContext, currentSection, sectionContext.toString(), sessionId);
                currentSection = "analysis_status";
                log.info("\n🎯 任务状态分析:");
                continue;
            } else if (line.contains("执行历史评估:")) {
                sendAnalysisSubResult(dynamicContext, currentSection, sectionContext.toString(), sessionId);
                currentSection = "analysis_history";
                log.info("\n📈 执行历史评估:");
                continue;
            }
......

四、测试

相关推荐
zfoo-framework1 小时前
跨服架构设计模式(同构进程+选主转发)
java
易知微EasyV数据可视化1 小时前
从卫星影像到法线贴图:为任意区域一键生成真实地形材质
经验分享·ai·数字孪生·材质·数据可视化·贴图
小猿备忘录1 小时前
Spring Security OAuth2 双Token机制精讲:原理、配置与常见坑点全解析
java·前端·spring·中间件
Irissgwe1 小时前
十、LangGraph能力详解(2)LangGraph入门教程,构建AI工作流
ai·langchain·graph·langgraph
yichudu2 小时前
autoResearch 官方项目复现笔记
agent
郑洁文2 小时前
学生信息管理系统
java·毕业设计·学生信息管理系统
淘源码A2 小时前
专科医院云HIS系统源码:技术栈包括SpringBoot、Angular、MySQL等
spring boot·后端·源码·云his·医院信息系统·医院his系统
剑挑星河月2 小时前
31.下一个排列
java·算法·leetcode