docs: require streaming AI runtime outputs
This commit is contained in:
@@ -37,12 +37,14 @@
|
|||||||
5. 支持按服务商和按场景测试,测试结果可用于排错和保存配置。
|
5. 支持按服务商和按场景测试,测试结果可用于排错和保存配置。
|
||||||
6. 保留现有 Coze 能力,并支持短片小说生成、剧本生成优先绑定 Dify。
|
6. 保留现有 Coze 能力,并支持短片小说生成、剧本生成优先绑定 Dify。
|
||||||
7. 后续新增 AI 场景时,业务代码只新增稳定 `sceneCode`,不直接依赖服务商。
|
7. 后续新增 AI 场景时,业务代码只新增稳定 `sceneCode`,不直接依赖服务商。
|
||||||
|
8. 所有面向用户的 AI 业务调用必须流式输出,用户端必须逐段显示生成内容;后台不可等完整结果生成后再一次性返回。
|
||||||
|
|
||||||
## 非目标
|
## 非目标
|
||||||
|
|
||||||
- 本期不做复杂流量权重、AB 实验和多配置灰度。
|
- 本期不做复杂流量权重、AB 实验和多配置灰度。
|
||||||
- 本期不删除旧 `t_ai_config`,它作为迁移来源和兼容兜底保留。
|
- 本期不删除旧 `t_ai_config`,它作为迁移来源和兼容兜底保留。
|
||||||
- 本期不要求所有历史 Coze 调用代码一次性完全清理,但新调用入口必须具备统一路由能力。
|
- 本期不要求所有历史 Coze 调用代码一次性完全清理,但新调用入口必须具备统一路由能力。
|
||||||
|
- 本期不把 blocking 响应作为用户端正式输出方式。blocking 只允许用于后台健康检查、兼容旧接口排障或服务商不支持流式时的禁用前诊断,不能绑定为已启用用户场景。
|
||||||
|
|
||||||
## 总体架构
|
## 总体架构
|
||||||
|
|
||||||
@@ -55,23 +57,25 @@
|
|||||||
运行时新增 `AiRuntimeService`,业务服务只传 `sceneCode` 和输入参数:
|
运行时新增 `AiRuntimeService`,业务服务只传 `sceneCode` 和输入参数:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
aiRuntimeService.invoke("chat", params, userId);
|
aiRuntimeService.invokeStream("chat", params, userId, streamConsumer);
|
||||||
aiRuntimeService.invoke("script_generate", params, userId);
|
aiRuntimeService.invokeStream("script_generate", params, userId, streamConsumer);
|
||||||
aiRuntimeService.invoke("short_story_generate", params, userId);
|
aiRuntimeService.invokeStream("short_story_generate", params, userId, streamConsumer);
|
||||||
aiRuntimeService.invoke("diary_summary", params, userId);
|
aiRuntimeService.invokeStream("diary_summary", params, userId, streamConsumer);
|
||||||
```
|
```
|
||||||
|
|
||||||
调用链:
|
调用链:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
业务服务
|
业务服务
|
||||||
-> AiRuntimeService.invoke(sceneCode, inputs, userId)
|
-> AiRuntimeService.invokeStream(sceneCode, inputs, userId, streamConsumer)
|
||||||
-> 查询 ai_scene_binding 当前启用绑定
|
-> 查询 ai_scene_binding 当前启用绑定
|
||||||
-> 加载 ai_endpoint_config
|
-> 加载 ai_endpoint_config
|
||||||
-> 加载 ai_provider
|
-> 加载 ai_provider
|
||||||
-> 根据 provider_type 选择 DifyProviderAdapter / CozeProviderAdapter
|
-> 根据 provider_type 选择 DifyProviderAdapter / CozeProviderAdapter
|
||||||
-> 按请求模板组包并调用外部服务
|
-> 按请求模板组包并以流式模式调用外部服务
|
||||||
-> 按响应解析规则提取结果
|
-> 将服务商事件转换为统一 AiStreamEvent
|
||||||
|
-> 边接收边推送到用户端
|
||||||
|
-> 完成后汇总最终文本和元数据
|
||||||
-> 写 ai_call_log
|
-> 写 ai_call_log
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -83,10 +87,11 @@ aiRuntimeService.invoke("diary_summary", params, userId);
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `AiRuntimeService` | 按 `sceneCode` 查询绑定、选择接口配置、执行 fallback、返回统一结果 | 不关心 Dify/Coze 请求细节 |
|
| `AiRuntimeService` | 按 `sceneCode` 查询绑定、选择接口配置、执行 fallback、返回统一结果 | 不关心 Dify/Coze 请求细节 |
|
||||||
| `AiProviderAdapter` | 服务商适配器接口,定义组包、调用、解析的统一契约 | 不读取业务场景 |
|
| `AiProviderAdapter` | 服务商适配器接口,定义组包、调用、解析的统一契约 | 不读取业务场景 |
|
||||||
| `DifyProviderAdapter` | 处理 Dify `/chat-messages` 的 blocking/streaming 请求和响应解析 | 不处理 Coze 格式 |
|
| `DifyProviderAdapter` | 处理 Dify `/chat-messages` 的 streaming 请求、SSE 解析和事件转换 | 不处理 Coze 格式 |
|
||||||
| `CozeProviderAdapter` | 迁移现有 Coze workflow 请求、SSE 解析和错误处理 | 不处理 Dify 格式 |
|
| `CozeProviderAdapter` | 迁移现有 Coze workflow 请求、SSE 解析和错误处理 | 不处理 Dify 格式 |
|
||||||
| `AiTemplateRenderer` | 渲染请求模板,合并场景输入、endpoint 默认值和运行时变量 | 不发送 HTTP |
|
| `AiTemplateRenderer` | 渲染请求模板,合并场景输入、endpoint 默认值和运行时变量 | 不发送 HTTP |
|
||||||
| `AiCallLogService` | 统一写入调用日志和测试日志,敏感字段脱敏 | 不决定业务结果 |
|
| `AiCallLogService` | 统一写入调用日志和测试日志,敏感字段脱敏 | 不决定业务结果 |
|
||||||
|
| `AiStreamGateway` | 将统一流式事件转发到用户端 SSE 或 WebSocket 连接 | 不直接调用 Dify/Coze |
|
||||||
|
|
||||||
业务服务只依赖 `AiRuntimeService`,不直接依赖 `DifyProviderAdapter`、`CozeProviderAdapter` 或数据库配置表。
|
业务服务只依赖 `AiRuntimeService`,不直接依赖 `DifyProviderAdapter`、`CozeProviderAdapter` 或数据库配置表。
|
||||||
|
|
||||||
@@ -162,6 +167,8 @@ INDEX(environment, is_enabled)
|
|||||||
| `request_template` | JSON,请求模板 |
|
| `request_template` | JSON,请求模板 |
|
||||||
| `response_parser` | JSON,响应解析规则 |
|
| `response_parser` | JSON,响应解析规则 |
|
||||||
| `support_stream` | 是否支持流式 |
|
| `support_stream` | 是否支持流式 |
|
||||||
|
| `stream_protocol` | 服务商流式协议,`sse`、`websocket`、`chunked`、`custom` |
|
||||||
|
| `stream_required` | 是否强制流式。面向用户的 AI 场景必须为 `true` |
|
||||||
| `support_file_upload` | 是否支持文件上传 |
|
| `support_file_upload` | 是否支持文件上传 |
|
||||||
| `timeout_ms` | 接口级超时时间 |
|
| `timeout_ms` | 接口级超时时间 |
|
||||||
| `retry_count` | 接口级重试次数 |
|
| `retry_count` | 接口级重试次数 |
|
||||||
@@ -191,6 +198,14 @@ INDEX(endpoint_type, is_enabled)
|
|||||||
endpoint 配置值 > provider 默认值 > 系统默认值
|
endpoint 配置值 > provider 默认值 > 系统默认值
|
||||||
```
|
```
|
||||||
|
|
||||||
|
用户场景启用约束:
|
||||||
|
|
||||||
|
1. 绑定到 `ai_scene_binding` 且 `is_enabled = 1` 的 endpoint 必须满足 `support_stream = 1`、`stream_required = 1`。
|
||||||
|
2. 后台保存或启用场景绑定时,如果 endpoint 不支持流式,直接拒绝启用并提示 `AI_ENDPOINT_STREAM_REQUIRED`。
|
||||||
|
3. 用户场景 endpoint 的 `response_parser.mode` 必须使用流式解析,例如 `sse`、`websocket` 或等价自定义流式解析器。
|
||||||
|
4. `fallback_endpoint_id` 如果存在,也必须满足同样的流式要求,不能用非流式接口作为用户场景兜底。
|
||||||
|
5. 非流式 endpoint 只能用于后台连接诊断、迁移排障或未来非用户端离线任务,不允许承接对话、剧本生成、短篇小说生成、总结分析等用户可见 AI 场景。
|
||||||
|
|
||||||
### ai_scene_binding
|
### ai_scene_binding
|
||||||
|
|
||||||
业务场景到接口配置的绑定。
|
业务场景到接口配置的绑定。
|
||||||
@@ -245,6 +260,9 @@ INDEX(scene_category, is_enabled)
|
|||||||
| `response_status` | HTTP 状态码 |
|
| `response_status` | HTTP 状态码 |
|
||||||
| `response_body` | 原始响应 |
|
| `response_body` | 原始响应 |
|
||||||
| `parsed_result` | 解析后的结果 |
|
| `parsed_result` | 解析后的结果 |
|
||||||
|
| `stream_chunk_count` | 流式片段数量 |
|
||||||
|
| `first_chunk_duration_ms` | 首个片段返回耗时 |
|
||||||
|
| `completed_at` | 流式完成时间 |
|
||||||
| `duration_ms` | 耗时 |
|
| `duration_ms` | 耗时 |
|
||||||
| `status` | `success`、`failed` |
|
| `status` | `success`、`failed` |
|
||||||
| `error_code` | 错误码 |
|
| `error_code` | 错误码 |
|
||||||
@@ -298,14 +316,14 @@ Dify 请求体模板应支持:
|
|||||||
{
|
{
|
||||||
"inputs": {},
|
"inputs": {},
|
||||||
"query": "{{input}}",
|
"query": "{{input}}",
|
||||||
"response_mode": "blocking",
|
"response_mode": "streaming",
|
||||||
"conversation_id": "{{conversationId}}",
|
"conversation_id": "{{conversationId}}",
|
||||||
"user": "{{userId}}",
|
"user": "{{userId}}",
|
||||||
"workflow_id": "{{workflowId}}"
|
"workflow_id": "{{workflowId}}"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
响应解析规则:
|
后台排障用 blocking 响应解析规则:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -314,7 +332,7 @@ Dify 请求体模板应支持:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
流式响应本期定义为可测试、可解析,但业务是否启用由 endpoint 的 `support_stream` 和请求模板共同决定。Dify SSE 解析规则如下:
|
流式响应是本期用户场景的强制输出方式。Dify endpoint 如果要绑定到用户场景,`support_stream`、`stream_required` 必须同时为 `true`,请求模板必须使用 `response_mode = streaming`。Dify SSE 解析规则如下:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -327,7 +345,9 @@ Dify 请求体模板应支持:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`DifyProviderAdapter` 在 blocking 模式下返回 `$.answer`,在 streaming 模式下拼接所有 `event=message` 的 `answer` 内容;遇到 `event=error` 时标记调用失败并写入 `ai_call_log.error_message`。
|
`DifyProviderAdapter` 的正式运行时必须使用 `response_mode = streaming`。适配器收到 Dify SSE 后,逐个事件转换为统一 `AiStreamEvent` 并立即下发给用户端;同时在后端累积完整文本,用于业务落库和 `ai_call_log.parsed_result`。遇到 `event=error` 时,立即发送错误事件、标记调用失败并写入 `ai_call_log.error_message`。
|
||||||
|
|
||||||
|
blocking 解析能力只保留给后台排障:例如验证某个 Dify 应用是否还能返回 `$.answer`。后台排障结果不能作为用户端输出,也不能让该 endpoint 通过用户场景启用校验。
|
||||||
|
|
||||||
## Coze 适配
|
## Coze 适配
|
||||||
|
|
||||||
@@ -358,7 +378,7 @@ Coze 默认请求模板:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Coze 响应解析先沿用现有 `AiChatServiceImpl` 中的 SSE 解析逻辑,提取 End 节点 `content.output` 或文本回答;迁移完成后该逻辑归属 `CozeProviderAdapter`。
|
Coze 响应解析先沿用现有 `AiChatServiceImpl` 中的 SSE 解析逻辑,但运行时必须边解析边输出 `AiStreamEvent.delta`。如果 Coze 只在 End 节点返回 `content.output`,适配器至少要在收到 End 前持续转发可用文本片段或阶段事件,不能让用户端一直空白等待;迁移完成后该逻辑归属 `CozeProviderAdapter`。
|
||||||
|
|
||||||
## 请求模板与响应解析
|
## 请求模板与响应解析
|
||||||
|
|
||||||
@@ -413,6 +433,8 @@ Coze 响应解析先沿用现有 `AiChatServiceImpl` 中的 SSE 解析逻辑,
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
用户场景只能启用流式解析模式。`json_path` 和 `raw_text` 仅用于后台诊断、旧数据兼容或非用户端离线任务;如果把这两类解析器绑定到用户场景,启用时必须失败并返回 `AI_ENDPOINT_STREAM_REQUIRED`。
|
||||||
|
|
||||||
解析失败时:
|
解析失败时:
|
||||||
|
|
||||||
1. `ai_call_log.status = failed`。
|
1. `ai_call_log.status = failed`。
|
||||||
@@ -420,6 +442,34 @@ Coze 响应解析先沿用现有 `AiChatServiceImpl` 中的 SSE 解析逻辑,
|
|||||||
3. 日志保留原始响应。
|
3. 日志保留原始响应。
|
||||||
4. 如果场景配置了 fallback,则继续调用 fallback。
|
4. 如果场景配置了 fallback,则继续调用 fallback。
|
||||||
|
|
||||||
|
### 统一流式事件
|
||||||
|
|
||||||
|
所有 provider adapter 必须输出统一事件,运行时和用户端只处理该统一协议:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"traceId": "string",
|
||||||
|
"sceneCode": "chat",
|
||||||
|
"type": "start|delta|message|error|done",
|
||||||
|
"content": "本次新增文本片段",
|
||||||
|
"metadata": {
|
||||||
|
"providerType": "dify",
|
||||||
|
"endpointCode": "dify.short_story.chat_messages",
|
||||||
|
"conversationId": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
事件规则:
|
||||||
|
|
||||||
|
1. `start`:服务商请求建立后立即发送,用于用户端进入生成中状态。
|
||||||
|
2. `delta`:每个文本片段到达后立即发送,用户端追加显示,不等待完整响应。
|
||||||
|
3. `message`:可选结构化消息,例如引用、工具调用状态或阶段提示。
|
||||||
|
4. `error`:调用失败时发送,包含统一错误码和可展示提示。
|
||||||
|
5. `done`:服务商正常结束后发送,包含最终统计信息,例如 `durationMs`、`chunkCount`。
|
||||||
|
|
||||||
|
后端可以在内部累积完整文本,但不能因为落库、解析或审核而阻塞 `delta` 下发。业务落库发生在 `done` 之后;如果中途失败,保存已输出片段和失败原因,便于排查。
|
||||||
|
|
||||||
统一错误码:
|
统一错误码:
|
||||||
|
|
||||||
| 错误码 | 场景 |
|
| 错误码 | 场景 |
|
||||||
@@ -428,6 +478,7 @@ Coze 响应解析先沿用现有 `AiChatServiceImpl` 中的 SSE 解析逻辑,
|
|||||||
| `AI_SCENE_DISABLED` | 场景被禁用 |
|
| `AI_SCENE_DISABLED` | 场景被禁用 |
|
||||||
| `AI_ENDPOINT_NOT_FOUND` | endpoint 不存在或已删除 |
|
| `AI_ENDPOINT_NOT_FOUND` | endpoint 不存在或已删除 |
|
||||||
| `AI_ENDPOINT_DISABLED` | endpoint 被禁用 |
|
| `AI_ENDPOINT_DISABLED` | endpoint 被禁用 |
|
||||||
|
| `AI_ENDPOINT_STREAM_REQUIRED` | 用户场景绑定的 endpoint 不支持流式输出 |
|
||||||
| `AI_PROVIDER_NOT_FOUND` | provider 不存在或已删除 |
|
| `AI_PROVIDER_NOT_FOUND` | provider 不存在或已删除 |
|
||||||
| `AI_PROVIDER_DISABLED` | provider 被禁用 |
|
| `AI_PROVIDER_DISABLED` | provider 被禁用 |
|
||||||
| `AI_TEMPLATE_VARIABLE_MISSING` | 请求模板缺少必填变量 |
|
| `AI_TEMPLATE_VARIABLE_MISSING` | 请求模板缺少必填变量 |
|
||||||
@@ -447,6 +498,7 @@ Coze 响应解析先沿用现有 `AiChatServiceImpl` 中的 SSE 解析逻辑,
|
|||||||
/ai/endpoints
|
/ai/endpoints
|
||||||
/ai/scenes
|
/ai/scenes
|
||||||
/ai/runtime/test
|
/ai/runtime/test
|
||||||
|
/ai/runtime/stream
|
||||||
/ai/call-logs
|
/ai/call-logs
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -492,6 +544,17 @@ POST /ai/scenes/test
|
|||||||
- 按 endpoint 测试:验证某个接口配置能否调通。
|
- 按 endpoint 测试:验证某个接口配置能否调通。
|
||||||
- 按 scene 测试:验证某个业务场景当前绑定是否能返回结果。
|
- 按 scene 测试:验证某个业务场景当前绑定是否能返回结果。
|
||||||
|
|
||||||
|
`GET /ai/runtime/stream` 或 `POST /ai/runtime/stream` 用于后台和用户端统一流式调试,返回 SSE 或 WebSocket 消息。测试页面必须显示:
|
||||||
|
|
||||||
|
- 首个片段耗时。
|
||||||
|
- 实时片段内容。
|
||||||
|
- 最终拼接文本。
|
||||||
|
- chunk 数量。
|
||||||
|
- 错误事件。
|
||||||
|
- traceId 和调用日志入口。
|
||||||
|
|
||||||
|
所有返回 AI 生成内容的业务接口都必须改造为流式接口,或通过现有 WebSocket 通道输出统一 `AiStreamEvent`。旧的一次性响应接口在迁移期只能作为内部兼容入口,不能继续作为用户端调用入口;如果某个页面仍调用一次性 AI 接口,该页面不算完成迁移。
|
||||||
|
|
||||||
## 后台页面
|
## 后台页面
|
||||||
|
|
||||||
保留现有菜单 `AI配置管理`,内部升级为四个 Tab 或子路由:
|
保留现有菜单 `AI配置管理`,内部升级为四个 Tab 或子路由:
|
||||||
@@ -586,11 +649,42 @@ POST /ai/scenes/test
|
|||||||
- 解析结果。
|
- 解析结果。
|
||||||
- fallback 信息。
|
- fallback 信息。
|
||||||
|
|
||||||
|
## 用户端流式显示
|
||||||
|
|
||||||
|
所有用户可见 AI 场景都必须走流式链路:
|
||||||
|
|
||||||
|
```text
|
||||||
|
用户端发起 AI 场景请求
|
||||||
|
-> 后端创建 traceId
|
||||||
|
-> AiRuntimeService.invokeStream
|
||||||
|
-> ProviderAdapter 接收 Dify/Coze 流式事件
|
||||||
|
-> AiStreamGateway 转为用户端 SSE 或 WebSocket 消息
|
||||||
|
-> 用户端逐段追加文本
|
||||||
|
-> done 后用户端结束 loading,并展示最终内容
|
||||||
|
```
|
||||||
|
|
||||||
|
用户端要求:
|
||||||
|
|
||||||
|
1. 对话、剧本生成、短篇小说生成、日记总结、情绪总结、情绪分析、人生事件疗愈等所有 AI 输出都逐段显示。
|
||||||
|
2. 用户端收到 `start` 后立即展示生成中状态,收到第一个 `delta` 后显示正文。
|
||||||
|
3. 用户端收到多个 `delta` 时只追加新增内容,不重复覆盖整段文本。
|
||||||
|
4. 用户端收到 `done` 后关闭生成中状态,保留最终文本。
|
||||||
|
5. 用户端收到 `error` 后停止生成中状态,展示可理解错误提示,并保留已输出内容。
|
||||||
|
6. 网络中断时允许用户重新发起同一场景请求;后端通过 `traceId` 和调用日志排查失败原因。
|
||||||
|
|
||||||
|
协议选择:
|
||||||
|
|
||||||
|
- 已有 WebSocket 场景继续使用 WebSocket 下发统一 `AiStreamEvent`。
|
||||||
|
- 普通 HTTP 页面优先使用 SSE。若小程序运行环境不稳定支持 SSE,则通过现有 WebSocket 通道承载同样的事件结构。
|
||||||
|
- 前端不直接连接 Dify 或 Coze,所有流式消息都由后端转发,避免 Token 暴露。
|
||||||
|
|
||||||
|
任何新 AI 功能在上线前必须提供用户端流式展示验证;如果只能一次性显示完整文本,则不能进入验收。
|
||||||
|
|
||||||
## 运行时行为
|
## 运行时行为
|
||||||
|
|
||||||
### 立即生效
|
### 立即生效
|
||||||
|
|
||||||
每次 `AiRuntimeService.invoke` 都从数据库读取当前启用的场景绑定、接口配置和服务商配置。因此以下修改在保存后立即影响下一次调用:
|
每次 `AiRuntimeService.invokeStream` 都从数据库读取当前启用的场景绑定、接口配置和服务商配置。因此以下修改在保存后立即影响下一次调用:
|
||||||
|
|
||||||
- 服务商 Token。
|
- 服务商 Token。
|
||||||
- 服务商 Base URL。
|
- 服务商 Base URL。
|
||||||
@@ -641,6 +735,8 @@ scene_code + environment
|
|||||||
|
|
||||||
调用日志必须记录主配置失败原因和是否发生 fallback。
|
调用日志必须记录主配置失败原因和是否发生 fallback。
|
||||||
|
|
||||||
|
兜底配置仍然必须流式输出。运行时在调用 fallback 前再次校验 `support_stream`、`stream_required` 和流式解析器;如果 fallback 不满足流式要求,直接返回 `AI_ENDPOINT_STREAM_REQUIRED`,不能退回 blocking 输出。
|
||||||
|
|
||||||
### 禁用策略
|
### 禁用策略
|
||||||
|
|
||||||
- 服务商禁用:其下接口不可被调用。
|
- 服务商禁用:其下接口不可被调用。
|
||||||
@@ -686,7 +782,7 @@ Dify 初始化:
|
|||||||
| `coze.emotion_analysis.default` | `emotion_analysis` |
|
| `coze.emotion_analysis.default` | `emotion_analysis` |
|
||||||
| `coze.summary.default` | `emotion_summary` |
|
| `coze.summary.default` | `emotion_summary` |
|
||||||
|
|
||||||
迁移后,业务代码逐步从 `callWorkflowByConfigKey(configKey, ...)` 迁移到 `invoke(sceneCode, ...)`。
|
迁移后,业务代码逐步从 `callWorkflowByConfigKey(configKey, ...)` 迁移到 `invokeStream(sceneCode, ...)`。
|
||||||
|
|
||||||
迁移后的兼容规则:
|
迁移后的兼容规则:
|
||||||
|
|
||||||
@@ -720,9 +816,11 @@ Dify 初始化:
|
|||||||
后端测试:
|
后端测试:
|
||||||
|
|
||||||
- `AiRuntimeServiceTest`:验证场景路由、禁用状态、fallback。
|
- `AiRuntimeServiceTest`:验证场景路由、禁用状态、fallback。
|
||||||
- `DifyProviderAdapterTest`:验证 Dify 请求体生成和 `answer` 解析。
|
- `DifyProviderAdapterTest`:验证 Dify 请求体强制生成 `response_mode = streaming`,并能转换统一流式事件。
|
||||||
- `DifyProviderAdapterStreamTest`:验证 Dify SSE 的 `message` 拼接、`message_end` 结束和 `error` 失败处理。
|
- `DifyProviderAdapterStreamTest`:验证 Dify SSE 的 `message` 拼接、`message_end` 结束和 `error` 失败处理。
|
||||||
- `CozeProviderAdapterTest`:验证 Coze 请求体生成和 SSE 解析。
|
- `CozeProviderAdapterTest`:验证 Coze 请求体生成和 SSE 解析。
|
||||||
|
- `AiRuntimeStreamTest`:验证 `invokeStream` 会按 `start`、`delta`、`done` 顺序输出事件,且不会等待完整响应才返回。
|
||||||
|
- `AiStreamGatewayTest`:验证 SSE/WebSocket 转发格式、错误事件、断连处理和 traceId 透传。
|
||||||
- `AiTemplateRendererTest`:验证占位符替换、对象注入、缺失变量报错和 JSON 校验。
|
- `AiTemplateRendererTest`:验证占位符替换、对象注入、缺失变量报错和 JSON 校验。
|
||||||
- 迁移脚本测试:验证旧 `t_ai_config` 能拆成 provider、endpoint、scene。
|
- 迁移脚本测试:验证旧 `t_ai_config` 能拆成 provider、endpoint、scene。
|
||||||
|
|
||||||
@@ -733,6 +831,7 @@ Dify 初始化:
|
|||||||
- 场景绑定切换后,场景测试走新的接口配置。
|
- 场景绑定切换后,场景测试走新的接口配置。
|
||||||
- 禁用配置后不能被正常调用。
|
- 禁用配置后不能被正常调用。
|
||||||
- 测试请求不在浏览器暴露明文 Token。
|
- 测试请求不在浏览器暴露明文 Token。
|
||||||
|
- 用户端和后台测试页都能逐段显示 AI 输出,不出现等待接口完成后一次性渲染的行为。
|
||||||
|
|
||||||
集成验收:
|
集成验收:
|
||||||
|
|
||||||
@@ -741,6 +840,7 @@ Dify 初始化:
|
|||||||
- 对话绑定 Coze 后保持现有可用。
|
- 对话绑定 Coze 后保持现有可用。
|
||||||
- 将剧本生成从 Dify 切回 Coze 后,不发版,下一次调用立即生效。
|
- 将剧本生成从 Dify 切回 Coze 后,不发版,下一次调用立即生效。
|
||||||
- 主配置失败且配置了兜底时,调用日志记录 fallback。
|
- 主配置失败且配置了兜底时,调用日志记录 fallback。
|
||||||
|
- 用户端对话、剧本生成、短篇小说生成、总结分析等 AI 场景都能看到流式文本逐段输出。
|
||||||
|
|
||||||
## 风险与缓解
|
## 风险与缓解
|
||||||
|
|
||||||
@@ -763,6 +863,9 @@ Dify 初始化:
|
|||||||
5. 业务调用只依赖 `sceneCode`,不再需要知道当前场景使用 Dify 还是 Coze。
|
5. 业务调用只依赖 `sceneCode`,不再需要知道当前场景使用 Dify 还是 Coze。
|
||||||
6. 修改场景绑定后,下一次业务调用立即走新配置。
|
6. 修改场景绑定后,下一次业务调用立即走新配置。
|
||||||
7. 调用日志能区分服务商、接口配置、场景、成功/失败和 fallback。
|
7. 调用日志能区分服务商、接口配置、场景、成功/失败和 fallback。
|
||||||
8. Dify blocking 和 streaming 两种响应都能被后台测试解析。
|
8. Dify、Coze 的用户场景调用都通过统一流式事件输出,后台测试页能实时显示 `start`、`delta`、`done` 或 `error`。
|
||||||
9. 管理后台和调用日志不泄露明文 Token。
|
9. 管理后台和调用日志不泄露明文 Token。
|
||||||
10. 旧 Coze 对话在迁移期间保持可用。
|
10. 旧 Coze 对话在迁移期间保持可用。
|
||||||
|
11. 对话、剧本生成、短篇小说生成、总结分析、疗愈等所有 AI 用户端入口都能逐段显示输出,不能一次性等完整结果后再渲染。
|
||||||
|
12. 非流式 endpoint 不能启用到用户场景,启用时必须返回 `AI_ENDPOINT_STREAM_REQUIRED`。
|
||||||
|
13. 每个已迁移 AI 场景都必须满足“有 start、有 delta、有 done 或 error、有调用日志、有用户端可见输出”,不能出现接口成功但页面无输出的情况。
|
||||||
|
|||||||
Reference in New Issue
Block a user