feat(flow): 集成商道智能体平台功能

- 新增 LLM 智能体查询接口,支持文本输入调用 AI 服务
- 实现智能体任务异步执行和取消机制
- 集成 TTS 语音播放功能,支持播放和停止控制
- 添加智能体 API 密钥管理和参数配置
- 实现线程池管理和任务生命周期控制
- 增加异常处理和错误返回机制
This commit is contained in:
lixiaolong 2026-07-21 10:01:20 +08:00
parent 6aaed4dacc
commit 9f462e41fc

View File

@ -1,8 +1,12 @@
package com.cmvr.web.controller.test; package com.cmvr.web.controller.test;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.common.annotation.Anonymous; import com.cmvr.common.annotation.Anonymous;
import com.cmvr.common.core.controller.BaseController; import com.cmvr.common.core.controller.BaseController;
import com.cmvr.common.core.domain.AjaxResult; import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.llm.service.LLMAiAgentPlatformService;
import com.cmvr.test.flow.context.TaskContextManager; import com.cmvr.test.flow.context.TaskContextManager;
import com.cmvr.test.flow.control.FlowControlService; import com.cmvr.test.flow.control.FlowControlService;
import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService; import com.cmvr.test.flow.runtime.engine.FlowTaskRuntimeService;
@ -26,9 +30,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PreDestroy;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@Api(tags = "测试--流程服务") @Api(tags = "测试--流程服务")
@RestController @RestController
@ -36,6 +46,21 @@ import java.util.List;
@RequiredArgsConstructor @RequiredArgsConstructor
public class TeFlowController extends BaseController { public class TeFlowController extends BaseController {
/**
* TTS 播放接口当前按需求固定写死在 Controller
*/
private static final String TTS_PLAY_URL = "http://localhost:8080/tts/play";
/**
* TTS 停止接口停止智能体输出时同步调用
*/
private static final String TTS_STOP_URL = "http://localhost:8080/tts/stop";
/**
* 商道智能体 AppKey当前按需求固定写死不放入配置文件
*/
private static final String AI_AGENT_API_KEY = "d9etum54shheenol3apg";
private final ITeDetectionItemService teDetectionItemService; private final ITeDetectionItemService teDetectionItemService;
private final ITeNodeInstService nodeInstService; private final ITeNodeInstService nodeInstService;
private final FlowTaskRuntimeService flowTaskRuntimeService; private final FlowTaskRuntimeService flowTaskRuntimeService;
@ -43,14 +68,29 @@ public class TeFlowController extends BaseController {
private final FlowControlService flowControlService; private final FlowControlService flowControlService;
private final FlowActionExecutorService flowActionExecutorService; private final FlowActionExecutorService flowActionExecutorService;
private final TiTouchOperateService tiTouchOperateService; private final TiTouchOperateService tiTouchOperateService;
private final LLMAiAgentPlatformService llmAiAgentPlatformService;
private final List<String> taskInstIdList = new ArrayList<>(); private final List<String> taskInstIdList = new ArrayList<>();
/**
* 智能体调用是阻塞式 query这里用单线程保存当前任务句柄便于 stop 接口中断当前请求
*/
private final ExecutorService aiAgentExecutor = Executors.newSingleThreadExecutor(runnable -> {
Thread thread = new Thread(runnable, "flow-ai-agent-query");
thread.setDaemon(true);
return thread;
});
/**
* 当前正在执行的智能体任务当前接口按同一时间只保留一个前端智能体调用处理
*/
private volatile Future<JSONObject> currentAiAgentFuture;
@ApiOperation("流程发布") @ApiOperation("流程发布")
@PostMapping("/publish") @PostMapping("/publish")
public AjaxResult publish(@RequestBody TeDetectionItem detectionItem) { public AjaxResult publish(@RequestBody TeDetectionItem detectionItem) {
return toAjax(teDetectionItemService.publish(detectionItem.getId())); return toAjax(teDetectionItemService.publish(detectionItem.getId()));
} }
@ApiOperation("演示任务执行入口") @ApiOperation("演示任务执行入口")
@PostMapping("/demo") @PostMapping("/demo")
@Anonymous @Anonymous
@ -59,7 +99,7 @@ public class TeFlowController extends BaseController {
try { try {
taskInstIdList.forEach(flowControlService::stop); taskInstIdList.forEach(flowControlService::stop);
} catch (Exception e) { } catch (Exception e) {
// 忽略 // 演示入口清理旧任务失败时继续启动新任务避免影响手动调试
} }
taskInstIdList.clear(); taskInstIdList.clear();
} }
@ -121,10 +161,110 @@ public class TeFlowController extends BaseController {
return AjaxResult.ok(flowActionExecutorService.actionExecute(flowActionRequestVO)); return AjaxResult.ok(flowActionExecutorService.actionExecute(flowActionRequestVO));
} }
/**
* 调用商道智能体平台
*
* <p>前端只需要传入 text后端固定 actionapiKey TTS 参数
* 调用会放入可取消任务中前端可以通过 /flow/llm/stop 停止当前调用并停止 TTS</p>
*/
@GetMapping("/llm/query")
@ApiOperation("调用商道智能体")
public AjaxResult queryAiAgent(String text) {
if (text == null || text.trim().isEmpty()) {
return AjaxResult.error("text不能为空");
}
stopCurrentAiAgentFuture(true);
JSONObject params = new JSONObject();
params.put("url", TTS_PLAY_URL);
params.put("voice", "x4_yezi");
params.put("speed", 45);
params.put("volume", 100);
Future<JSONObject> future = aiAgentExecutor.submit(() -> llmAiAgentPlatformService.query(
null,
text.trim(),
AI_AGENT_API_KEY,
true,
params
));
currentAiAgentFuture = future;
try {
return AjaxResult.ok(future.get());
} catch (CancellationException e) {
return AjaxResult.error("智能体调用已停止");
} catch (InterruptedException e) {
future.cancel(true);
Thread.currentThread().interrupt();
return AjaxResult.error("智能体调用被中断");
} catch (ExecutionException e) {
Throwable cause = e.getCause() == null ? e : e.getCause();
return AjaxResult.error("智能体调用失败:" + cause.getMessage());
} finally {
if (currentAiAgentFuture == future) {
currentAiAgentFuture = null;
}
}
}
/**
* 停止当前智能体调用并通知 TTS 服务停止播放
*
* <p>停止分两步先取消当前 query 任务触发底层 SSE 等待线程中断
* 再调用本地 TTS stop 接口停止已经进入播放队列的音频</p>
*/
@PostMapping("/llm/stop")
@ApiOperation("停止商道智能体和TTS")
public AjaxResult stopAiAgent() {
stopCurrentAiAgentFuture(false);
// String ttsStopResult = stopTts();
return AjaxResult.ok("ok");
}
@GetMapping("/testrun") @GetMapping("/testrun")
@ApiOperation("测试接口") @ApiOperation("测试接口")
public AjaxResult testrun(@RequestParam("imageUrl") String imageUrl, public AjaxResult testrun(@RequestParam("imageUrl") String imageUrl,
@RequestParam("iconUrl") String iconUrl) { @RequestParam("iconUrl") String iconUrl) {
return AjaxResult.ok(tiTouchOperateService.test(imageUrl,iconUrl)); return AjaxResult.ok(tiTouchOperateService.test(imageUrl, iconUrl));
}
/**
* 取消当前智能体任务
*
* @param stopTts 是否同时停止 TTS 播放
*/
private void stopCurrentAiAgentFuture(boolean stopTts) {
Future<JSONObject> future = currentAiAgentFuture;
if (future != null && !future.isDone()) {
future.cancel(true);
}
currentAiAgentFuture = null;
if (stopTts) {
stopTts();
}
}
/**
* 调用本地 TTS 停止接口
*/
private String stopTts() {
try (HttpResponse response = HttpRequest.post(TTS_STOP_URL)
.timeout(3000)
.execute()) {
return response.body();
} catch (Exception e) {
return "调用TTS停止接口失败" + e.getMessage();
}
}
/**
* Spring 容器关闭时释放智能体执行线程
*/
@PreDestroy
public void destroy() {
stopCurrentAiAgentFuture(true);
aiAgentExecutor.shutdownNow();
} }
} }