一、本章介绍
接下来,对 domain 领域层中的 service 服务进行 trigger 层封装,对外提供 HTTP 接口能力。这样可以将领域服务的会话能力转换为标准的接口入口,方便前端页面或其他业务系统进行调用。
通过 trigger 层包装后,外部只需要按照接口规范传入对应参数,即可完成智能体列表查询、会话创建、消息发送以及流式消息处理等操作。后续再结合页面功能,就可以通过调用这些 HTTP 接口,串联起完整的智能体会话服务流程。
二、流程设计
如图,会话接口服务所在分层;

本节重点完成
trigger层中 HTTP 接口能力的开发,对外提供智能体列表查询、会话session创建,以及会话发起能力。其中,会话发起同时支持流式和非流式两种响应模式。需要注意的是,在实际业务开发中,接口的暴露方式并不固定,除了 HTTP 接口外,也可能会提供 RPC 接口,或者在接口层之上继续扩展更多业务流程。当业务编排逻辑逐渐复杂时,可以考虑新增
case编排层,也就是独立拆分一个新的 module 模块,用于承接复杂流程的组织与调度。case层的主要作用,是分担trigger层的业务压力,让对外接口层保持轻量。这样trigger层只需要负责参数接收、结果包装和接口适配,而复杂的业务流程则交由case层统一编排处理。
三、功能实现
1. 工程结构
首先,在 ai-agent-scaffold-api 模块中定义统一的接口规范,并将接口所需的 DTO 对象一并放置在 API 层进行管理。DTO(Data Transfer Object)即数据传输对象,如果使用过支付宝、微信支付等第三方 SDK,通常都会看到诸如 XXXRequestDTO、XXXResponseDTO 之类的对象定义,它们本质上都是用于接口之间的数据传输和参数封装。
之所以单独拆分出 API 层,主要有以下几个原因。首先,通过 API 层定义统一标准后,可以基于同一套接口实现不同的业务实现方案,便于后续扩展与切换。其次,接口定义与实现逻辑分离后,能够让 trigger 层职责更加清晰,只关注接口适配和请求处理,而不必承担接口协议定义工作。
另外,在 RPC(如 Dubbo)场景下,API 模块通常需要单独打包为 Jar 包提供给其他服务引用。此时,将接口和 DTO 统一沉淀在 API 层,可以有效避免不同服务之间重复定义接口契约的问题,提高系统间协作效率。
完成 API 层定义后,再由 AgentServiceController 实现对应接口能力,对外提供智能体列表查询、会话创建以及消息处理等功能,实现完整的会话服务访问入口。
2. 功能实现
2.1 接口定义
public interface IAgentService {
Response<List<AiAgentConfigResponseDTO>> queryAiAgentConfigList();
Response<CreateSessionResponseDTO> createSession(CreateSessionRequestDTO requestDTO);
Response<ChatResponseDTO> chat(ChatRequestDTO requestDTO);
ResponseBodyEmitter chatStream(ChatRequestDTO requestDTO);
}- 在
ai-agent-scaffold-api模块下,定义了统一的IAgentService服务接口,作为智能体会话服务的标准访问入口。接口中主要包含三类核心能力,分别为智能体列表查询、会话创建以及消息处理。
2.2 接口开发
@Slf4j
@RestController
@RequestMapping("/api/v1/")
@CrossOrigin(origins = "*")
public class AgentServiceController implements IAgentService {
@Resource
private IChatService chatService;
@RequestMapping(value = "query_ai_agent_config_list", method = RequestMethod.GET)
@Override
public Response<List<AiAgentConfigResponseDTO>> queryAiAgentConfigList() {
try {
log.info("查询智能体配置列表");
List<AiAgentConfigTableVO.Agent> agentConfigs = chatService.queryAiAgentConfigList();
List<AiAgentConfigResponseDTO> responseDTOS = agentConfigs.stream().map(agentConfig -> {
AiAgentConfigResponseDTO responseDTO = new AiAgentConfigResponseDTO();
responseDTO.setAgentId(agentConfig.getAgentId());
responseDTO.setAgentName(agentConfig.getAgentName());
responseDTO.setAgentDesc(agentConfig.getAgentDesc());
return responseDTO;
}).collect(Collectors.toList());
return Response.<List<AiAgentConfigResponseDTO>>builder()
.code(ResponseCode.SUCCESS.getCode())
.info(ResponseCode.SUCCESS.getInfo())
.data(responseDTOS)
.build();
} catch (AppException e) {
log.error("查询智能体配置列表异常", e);
return Response.<List<AiAgentConfigResponseDTO>>builder()
.code(e.getCode())
.info(e.getInfo())
.build();
} catch (Exception e) {
log.error("查询智能体配置列表失败", e);
return Response.<List<AiAgentConfigResponseDTO>>builder()
.code(ResponseCode.UN_ERROR.getCode())
.info(ResponseCode.UN_ERROR.getInfo())
.build();
}
}
@RequestMapping(value = "create_session", method = RequestMethod.GET)
@Override
public Response<CreateSessionResponseDTO> createSession(@RequestBody CreateSessionRequestDTO requestDTO) {
try {
log.info("创建会话 agentId:{} userId:{}", requestDTO.getAgentId(), requestDTO.getUserId());
String sessionId = chatService.createSession(requestDTO.getAgentId(), requestDTO.getUserId());
CreateSessionResponseDTO responseDTO = new CreateSessionResponseDTO();
responseDTO.setSessionId(sessionId);
return Response.<CreateSessionResponseDTO>builder()
.code(ResponseCode.SUCCESS.getCode())
.info(ResponseCode.SUCCESS.getInfo())
.data(responseDTO)
.build();
} catch (AppException e) {
log.error("查询智能体配置列表异常", e);
return Response.<CreateSessionResponseDTO>builder()
.code(e.getCode())
.info(e.getInfo())
.build();
} catch (Exception e) {
log.error("创建会话失败 agentId:{} userId:{}", requestDTO.getAgentId(), requestDTO.getUserId(), e);
return Response.<CreateSessionResponseDTO>builder()
.code(ResponseCode.UN_ERROR.getCode())
.info(ResponseCode.UN_ERROR.getInfo())
.build();
}
}
@RequestMapping(value = "chat", method = RequestMethod.POST)
@Override
public Response<ChatResponseDTO> chat(@RequestBody ChatRequestDTO requestDTO) {
try {
log.info("智能体对话 agentId:{} userId:{}", requestDTO.getAgentId(), requestDTO.getUserId());
String sessionId = requestDTO.getSessionId();
if (sessionId == null || sessionId.isEmpty()) {
sessionId = chatService.createSession(requestDTO.getAgentId(), requestDTO.getUserId());
}
List<String> messages = chatService.handleMessage(requestDTO.getAgentId(), requestDTO.getUserId(), sessionId, requestDTO.getMessage());
ChatResponseDTO responseDTO = new ChatResponseDTO();
responseDTO.setContent(String.join("\n", messages));
return Response.<ChatResponseDTO>builder()
.code(ResponseCode.SUCCESS.getCode())
.info(ResponseCode.SUCCESS.getInfo())
.data(responseDTO)
.build();
} catch (AppException e) {
log.error("智能体对话异常", e);
return Response.<ChatResponseDTO>builder()
.code(e.getCode())
.info(e.getInfo())
.build();
} catch (Exception e) {
log.error("智能体对话败 agentId:{} userId:{}", requestDTO.getAgentId(), requestDTO.getUserId(), e);
return Response.<ChatResponseDTO>builder()
.code(ResponseCode.UN_ERROR.getCode())
.info(ResponseCode.UN_ERROR.getInfo())
.build();
}
}
@RequestMapping(value = "chat_stream", method = RequestMethod.POST)
@Override
public ResponseBodyEmitter chatStream(@RequestBody ChatRequestDTO requestDTO) {
ResponseBodyEmitter emitter = new ResponseBodyEmitter(3 * 60 * 1000L);
try {
log.info("流式对话 agentId:{} userId:{} sessionId:{} message:{}", requestDTO.getAgentId(), requestDTO.getUserId(), requestDTO.getSessionId(), requestDTO.getMessage());
chatService.handleMessageStream(requestDTO.getAgentId(), requestDTO.getUserId(), requestDTO.getSessionId(), requestDTO.getMessage())
.subscribe(
event -> {
try {
emitter.send(event.stringifyContent());
} catch (Exception e) {
log.error("流式对话发送失败", e);
emitter.completeWithError(e);
}
},
emitter::completeWithError,
emitter::complete
);
} catch (Exception e) {
log.error("流式对话失败", e);
emitter.completeWithError(e);
}
return emitter;
}
}四、测试验证
测试时,可以在 IntelliJ IDEA 中同级目录下创建 AgentServiceController.http 文件,之后进行接口测试使用。

- 你可以依次测试这些接口,在入参的地方,传入对应的参数即可。