Files
plates-server/docs/stream-response-solution-options.md
richarjiang ede5730647 feat: 实现饮食记录确认流程
- 新增饮食记录确认流程,将自动记录模式升级为用户确认模式,提升用户交互体验。
- 实现两阶段饮食记录流程,支持AI识别食物并生成确认选项,用户选择后记录到数据库并提供营养分析。
- 扩展DTO层,新增相关数据结构以支持确认流程。
- 更新服务层,新增处理确认逻辑的方法,优化饮食记录的创建流程。
- 增强API文档,详细说明新流程及使用建议,确保开发者理解和使用新功能。
2025-08-18 18:59:36 +08:00

152 lines
3.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 流式响应与结构化数据冲突解决方案
## 问题描述
当前实现中,`#记饮食` 指令在第一阶段需要返回结构化数据(确认选项),但客户端可能设置了 `stream: true`,导致响应类型冲突。
## 解决方案对比
### 方案1强制非流式模式 ⭐ (当前实现)
**优点:**
- 实现简单,改动最小
- 完全向后兼容
- 客户端只需检查 Content-Type
**缺点:**
- 行为不够明确忽略stream参数
- 客户端需要额外处理响应类型检测
**实现:**
```typescript
// 当需要返回确认选项时自动使用JSON响应
if (typeof result === 'object' && 'type' in result) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.send({ conversationId, data: result.data });
return;
}
```
### 方案2分离API端点
**优点:**
- API语义清晰
- 响应类型明确
- 易于测试和维护
**缺点:**
- 需要新增API端点
- 客户端需要适配新API
**建议API设计**
```typescript
// 专门的食物识别API
@Post('analyze-food')
async analyzeFood(body: { imageUrls: string[] }): Promise<FoodRecognitionResponseDto>
// 确认并记录API
@Post('confirm-food-record')
async confirmFoodRecord(body: { selectedOption: any, imageUrl: string }): Promise<DietRecordResponseDto>
// 原有聊天API保持纯文本
@Post('chat')
async chat(): Promise<StreamableFile | { text: string }>
```
### 方案3统一JSON响应格式
**优点:**
- 响应格式统一
- 可以在JSON中指示是否需要流式处理
**缺点:**
- 破坏向后兼容性
- 所有客户端都需要修改
**实现示例:**
```typescript
// 统一响应格式
{
conversationId: string;
responseType: 'text' | 'choices' | 'stream';
data: {
content?: string;
choices?: any[];
streamUrl?: string; // 流式数据的WebSocket URL
}
}
```
### 方案4SSE (Server-Sent Events) 统一
**优点:**
- 可以发送不同类型的事件
- 保持连接状态
- 支持实时交互
**缺点:**
- 实现复杂度高
- 需要客户端支持SSE
**实现示例:**
```typescript
// SSE事件类型
event: text
data: {"chunk": "AI回复的文本片段"}
event: choices
data: {"choices": [...], "content": "请选择食物"}
event: complete
data: {"conversationId": "..."}
```
## 推荐方案
### 短期方案1 (当前实现) ✅
- 快速解决问题
- 最小化影响
- 保持兼容性
### 长期方案2 (分离API端点)
- 更清晰的API设计
- 更好的可维护性
- 更明确的职责分离
## 当前方案的客户端适配
```javascript
async function sendDietRequest(imageUrls, conversationId, stream = true) {
const response = await fetch('/ai-coach/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
conversationId,
messages: [{ role: 'user', content: '#记饮食' }],
imageUrls,
stream
})
});
// 检查响应类型
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
// 结构化数据(确认选项)
const data = await response.json();
return { type: 'choices', data };
} else {
// 流式文本
return { type: 'stream', stream: response.body };
}
}
```
## 总结
当前的方案1实现简单有效能够解决流式响应冲突问题。虽然在语义上不够完美但在实际使用中是可行的。建议
1. **立即采用方案1**,解决当前问题
2. **文档中明确说明**响应类型检测的必要性
3. **后续版本考虑方案2**提供更清晰的API设计