49ba487e56
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7.7 KiB
7.7 KiB
author, created_at, purpose
| author | created_at | purpose |
|---|---|---|
| 华钟民 | 2026-05-23 | 在 AI 配置管理页面为场景绑定表和接口工作流表各增加行内测试按钮,工作流测试独立走 endpoint 直调链路 |
AI 配置行内测试功能设计
1. 问题背景
当前 web-admin 后台「AI 配置管理」页面中:
- 场景绑定 Tab:只有顶部工具栏的全局「流式测试」按钮,需要手动选择场景
- 接口工作流 Tab:没有任何测试功能
需要在两个表的每行操作列增加「测试」按钮,让用户直接对某一行进行测试。
2. 设计方案
2.1 场景绑定表 — 行内测试
- 每行操作列增加「测试」按钮
- 点击后打开现有测试对话框(
testDialog),自动填入该行的sceneCode - 后续流程不变,走
sceneCode → resolveTarget → adapter链路
2.2 接口工作流表 — 行内测试
工作流测试独立于场景,直接基于 endpoint 调用。
后端新增
AiRuntimeRequest DTO 增强(AiRuntimeRequest.java):
- 新增
endpointId字段 fromPayload()中新增提取逻辑:request.setEndpointId(payload.getString("endpointId"))
AiRuntimeService 接口:
// 非流式 endpoint 测试
AiRuntimeTestResponse testEndpoint(String endpointId, Map<String, Object> inputs);
// 流式 endpoint 测试(SSE 回调)
void invokeEndpointStream(String endpointId, Map<String, Object> inputs, Consumer<AiStreamEvent> consumer);
AiRuntimeServiceImpl 实现:
- 根据
endpointId查 endpoint(getEnabledById,null 时抛AI_ENDPOINT_DISABLED) - 查 provider(
getEnabledById,null 时抛AI_PROVIDER_DISABLED) - 构造
AiRuntimeRequest,设置endpointId和inputs,sceneCode留空 - 通过
enrichInputs()注入 userId(场景注入因 sceneCode 为空自动跳过) - 根据
provider.getProviderType()查找对应的AiProviderAdapter,调用adapter.stream() - 记录 callLog,
sceneCode字段留空,endpointCode填当前 endpoint 编码 - 复用
AiStreamEvent事件体系和AiRuntimeTestResponse响应结构
AiRoutingController 新增接口:
@PostMapping("/endpoint/test")
public Result<AiRuntimeTestResponse> endpointTest(@RequestBody JSONObject payload) {
String endpointId = payload.getString("endpointId");
JSONObject inputs = payload.getJSONObject("inputs");
Map<String, Object> inputMap = inputs == null ? Map.of() : inputs;
return Result.success(runtimeService.testEndpoint(endpointId, inputMap));
}
@PostMapping("/endpoint/stream")
public SseEmitter endpointStream(@RequestBody JSONObject payload) {
String endpointId = payload.getString("endpointId");
JSONObject inputs = payload.getJSONObject("inputs");
Map<String, Object> inputMap = inputs == null ? Map.of() : inputs;
SseEmitter emitter = new SseEmitter(0L);
CompletableFuture.runAsync(() -> {
runtimeService.invokeEndpointStream(endpointId, inputMap, event -> sendEvent(emitter, event));
emitter.complete();
}).exceptionally(error -> {
sendEvent(emitter, AiStreamEvent.error("AI_ENDPOINT_TEST_INTERRUPTED", error.getMessage()));
emitter.completeWithError(error);
return null;
});
return emitter;
}
前端新增
类型定义(web-admin/src/types/aiconfig.ts):
export interface AiEndpointRuntimeRequest {
endpointId: string
inputs: Record<string, any>
}
API 层(web-admin/src/api/aiconfig.ts):
// 非流式 endpoint 测试
export function testEndpointRuntime(data: AiEndpointRuntimeRequest) {
return request({ url: '/ai/endpoint/test', method: 'post', data, timeout: 60000 })
}
// 流式 endpoint 测试(复用 streamAiRuntime 的 SSE 解析逻辑,只是 endpoint 不同)
export async function streamEndpointRuntime(
data: AiEndpointRuntimeRequest,
onEvent: (event: AiRuntimeStreamEvent, output: string) => void
) {
// 内部实现与 streamAiRuntime 相同,只是 URL 为 /ai/endpoint/stream
}
UI 层(AiRoutingList.vue):
场景绑定表改造:
- 操作列宽度从
150调整为220 - 每行操作列增加「测试」按钮:
<el-button link type="success" @click="openSceneRuntimeTest(row)">测试</el-button>
- 新增函数
openSceneRuntimeTest(row):打开现有testDialog,自动填入row.sceneCode - 现有顶部工具栏的「流式测试」按钮保留不变
接口工作流表改造:
- 操作列宽度从
150调整为220 - 每行操作列增加「测试」按钮:
<el-button link type="success" :disabled="row.isEnabled !== 1" @click="openEndpointTest(row)">测试</el-button>
- 点击打开新对话框
endpointTestDialog,标题「接口工作流测试」 - 对话框内容:
- 接口名称(只读文本,显示
row.endpointName+row.endpointCode) - 入参 JSON 框,自动填入该 endpoint 的
defaultInputs(如果有且为合法 JSON),否则填默认模板{} - 非流式/流式测试结果展示区(复用现有
<el-alert>+<pre>样式) - 底部:「关闭」「非流式测试」「流式测试」按钮
- 接口名称(只读文本,显示
- 使用独立的响应式状态变量:
endpointTesting、endpointTestResult、endpointNonStreamResult、endpointInputsJson - 两个对话框可共存,状态互不干扰
2.3 数据流
场景绑定行内测试:
用户点击「测试」→ openSceneRuntimeTest(row) → testDialog 打开,sceneCode= row.sceneCode → 输入 params →
流式: streamAiRuntime({ sceneCode, inputs }) → /ai/runtime/stream → AiRuntimeService.invokeStream()
非流式: testAiRuntime({ sceneCode, inputs }) → /ai/runtime/test → AiRuntimeService.test()
接口工作流行内测试:
用户点击「测试」→ openEndpointTest(row) → endpointTestDialog 打开 → defaultInputs 已填入 →
流式: streamEndpointRuntime({ endpointId, inputs }) → /ai/endpoint/stream → AiRuntimeService.invokeEndpointStream()
非流式: testEndpointRuntime({ endpointId, inputs }) → /ai/endpoint/test → AiRuntimeService.testEndpoint()
endpoint 测试的 enrichInputs 行为:
AiRuntimeRequest中sceneCode为空 →enrichSceneInputs()跳过userId/userName/userType/requestId仍通过withCurrentUser+enrichInputs注入socialInsightContext等场景级注入不执行(因为sceneCode为空)
2.4 响应字段说明
AiRuntimeTestResponse.sceneCode 在 endpoint 测试中为空,前端在 endpointTestDialog 中显示 endpointName + endpointCode 替代。
3. 文件清单
| 操作 | 文件 |
|---|---|
| 修改 | backend-single/src/main/java/com/emotion/dto/request/ai/AiRuntimeRequest.java |
| 修改 | backend-single/src/main/java/com/emotion/service/AiRuntimeService.java |
| 修改 | backend-single/src/main/java/com/emotion/service/impl/AiRuntimeServiceImpl.java |
| 修改 | backend-single/src/main/java/com/emotion/controller/AiRoutingController.java |
| 修改 | web-admin/src/types/aiconfig.ts |
| 修改 | web-admin/src/api/aiconfig.ts |
| 修改 | web-admin/src/views/aiconfig/AiRoutingList.vue |
4. 风险
- endpoint 测试不经过场景绑定,因此不会有场景相关的输入注入(如 socialInsightContext)。这是预期行为——endpoint 测试关注的是接口本身是否通
- endpoint 的
defaultInputs可能不完整或格式不合法,对话框打开时会尝试 JSON.parse,失败时回退到{} - 接口工作流表的「测试」按钮在
isEnabled !== 1时禁用,避免无效调用 - 新增两个后端接口,需确保权限校验和现有
/ai/runtime/*接口一致(走同一个 Admin 鉴权) - 操作列宽度从 150 调整到 220,确保三按钮(编辑/删除/测试)不重叠