# Changelog: 发消息支持可选 AI 生成 **日期**: 2026-02-13 **版本**: v1.0 **类型**: 架构优化 (API 简化) **影响范围**: `POST /api/v1/ai-conversations/{conversation_id}/messages` --- ## 📋 概述 将**发消息**和**触发 AI 生成**合并为一步操作,简化前端调用流程。 ### Before (两步操作) ```typescript // 步骤 1: 发送消息 const message = await POST('/conversations/{id}/messages', { content: '生成一个日落场景' }); // 步骤 2: 触发生成 const generation = await POST('/conversations/{id}/generate', { messageId: message.messageId, generationType: 'image', modelId: 'dall-e-3', aiParams: { ... } }); ``` ### After (一步操作) ```typescript // 一步完成 const response = await POST('/conversations/{id}/messages', { content: '生成一个日落场景', generate: { // ✅ 可选:触发 AI 生成 enabled: true, generationType: 'image', modelId: 'dall-e-3', aiParams: { ... } } }); // response.data.message - 消息信息 // response.data.generation - AI 生成信息(如果触发) ``` --- ## 🎯 改动目标 ### 问题背景 原有流程需要两次 API 调用: 1. ❌ **调用复杂**:前端需要管理两次请求 2. ❌ **状态同步**:需要手动关联 messageId 3. ❌ **错误处理**:需要分别处理两个接口的错误 ### 改进方案 将 AI 生成作为**可选参数**嵌入发消息接口: - ✅ **一步完成**:发消息 + 触发生成 - ✅ **向后兼容**:`generate` 参数为可选 - ✅ **统一响应**:返回消息 + 生成信息 --- ## 🛠️ 技术实现 ### 1. Schema 层改动 #### 文件: `server/app/schemas/ai_conversation_message.py` #### 1.1 新增 `GenerateConfig` Schema ```python class GenerateConfig(BaseModel): """AI 生成配置(嵌入消息创建中)""" model_config = ConfigDict(populate_by_name=True) enabled: bool = Field(..., description="是否启用 AI 生成") generation_type: str = Field(..., alias="generationType", description="生成类型(image/video)") model_id: str = Field(..., alias="modelId", description="模型 ID") ai_params: Dict[str, Any] = Field(..., alias="aiParams", description="AI 模型参数") business_params: Optional[Dict[str, Any]] = Field(None, alias="businessParams", description="业务参数(可选)") @field_validator('generation_type') @classmethod def validate_generation_type(cls, v: str) -> str: """验证生成类型""" if v not in ['image', 'video']: raise ValueError(f"不支持的生成类型: {v}") return v ``` **说明**: - `enabled`: 是否启用 AI 生成(布尔值) - `generationType`: 生成类型(`image` 或 `video`) - `modelId`: 模型 ID - `aiParams`: AI 模型参数(与 RFC 144 统一) - `businessParams`: 业务参数(可选,通用字典) #### 1.2 更新 `AIConversationMessageCreate` Schema ```python class AIConversationMessageCreate(BaseModel): """创建消息请求(支持可选的 AI 生成)""" content: str = Field(..., description="消息内容", min_length=1, max_length=10000) generate: Optional[GenerateConfig] = Field(None, description="AI 生成配置(可选,会保存到 meta_data)") # ✅ 新增 model_config = ConfigDict(populate_by_name=True) ``` **变化**: - 新增 `generate` 字段(可选) - 类型为 `GenerateConfig` Pydantic 对象 - ❌ **移除** `meta_data` 字段(`generate` 配置会自动保存到数据库 `meta_data`) #### 1.3 新增 `GenerationInfo` Schema ```python class GenerationInfo(BaseModel): """AI 生成信息(嵌入消息响应中)""" job_id: str = Field(..., alias="jobId", description="任务 ID") task_id: str = Field(..., alias="taskId", description="Celery 任务 ID") status: str = Field(..., description="任务状态") estimated_credits: int = Field(..., alias="estimatedCredits", description="预估积分消耗") reference_images_count: int = Field(0, alias="referenceImagesCount", description="参考图数量") model_config = ConfigDict(populate_by_name=True) ``` #### 1.4 更新 `AIConversationMessageResponse` Schema ```python class AIConversationMessageResponse(BaseModel): """消息响应(可能包含 AI 生成信息)""" message_id: UUID = Field(..., description="消息 ID") conversation_id: UUID = Field(..., description="对话会话 ID") user_id: UUID = Field(..., description="用户 ID") ai_job_id: Optional[UUID] = Field(None, description="关联的 AI 任务 ID") role: int = Field(..., description="消息角色(1=用户 2=AI 3=系统)") content: str = Field(..., description="消息内容") meta_data: Dict[str, Any] = Field(default_factory=dict, description="额外元数据") order_index: int = Field(..., description="消息顺序") created_at: datetime = Field(..., description="创建时间") updated_at: datetime = Field(..., description="更新时间") # 新增:AI 生成信息(如果触发了生成) generation: Optional[GenerationInfo] = Field(None, description="AI 生成信息(如果触发)") # ✅ 新增 model_config = ConfigDict( from_attributes=True, populate_by_name=True, alias_generator=to_camel ) ``` **变化**: - 新增 `generation` 字段(可选) - 类型为 `GenerationInfo` Pydantic 对象 --- ### 2. Service 层改动 #### 文件: `server/app/services/ai_conversation_service.py` #### 2.1 更新 `send_message` 方法签名 ```python async def send_message( self, conversation_id: UUID, user_id: UUID, content: str, generate_config: Optional[Dict[str, Any]] = None # ✅ 新增参数 ) -> Dict[str, Any]: """发送用户消息(支持自动解析提及 + 可选触发 AI 生成) Args: conversation_id: 对话会话 ID user_id: 用户 ID content: 消息内容(可能包含提及标记) generate_config: AI 生成配置(可选,会保存到 meta_data) - enabled: 是否启用 AI 生成 - generation_type: 生成类型(image/video) - model_id: 模型 ID - ai_params: AI 模型参数 - business_params: 业务参数(可选) Returns: Dict 包含消息信息 + AI 生成信息(如果触发) """ ``` #### 2.2 构建 `meta_data` 逻辑(优化版) ```python # 解析提及标记(如果有) mentions, reference_images = await self._parse_mentions( content, user_id, conversation_id ) # 构建 meta_data message_meta_data = {} # 如果提供了 generate_config if generate_config: # 合并提及信息到 business_params business_params = generate_config.get('business_params') or {} if mentions: business_params['mentions'] = mentions business_params['reference_images'] = reference_images logger.info("解析到 %d 个提及,已添加到 business_params", len(mentions)) # 更新 generate_config generate_config['business_params'] = business_params # 保存 generate 配置到 meta_data message_meta_data['generate'] = generate_config logger.info("保存 generate 配置到 meta_data") elif mentions: # 如果没有 generate_config,但有提及,仍然保存提及信息 message_meta_data['mentions'] = mentions message_meta_data['reference_images'] = reference_images logger.info("解析到 %d 个提及", len(mentions)) ``` **关键设计**: - ✅ **提及信息归类到 `business_params`**:当有 `generate` 时,`mentions` 和 `reference_images` 保存在 `business_params` 中 - ✅ **向后兼容**:如果仅有提及无 `generate`,提及信息仍然保存在 `meta_data` 顶层 - ✅ `meta_data` 结构(有 generate): ```json { "generate": { "enabled": true, "generation_type": "image", "model_id": "dall-e-3", "ai_params": {...}, "business_params": { "mentions": [...], // ✅ 提及信息 "reference_images": [...], // ✅ 参考图列表 "customField": "value" // 其他业务参数 } } } ``` - ✅ `meta_data` 结构(仅提及): ```json { "mentions": [...], // ✅ 向后兼容 "reference_images": [...] } ``` #### 2.3 在方法结尾添加生成逻辑 ```python logger.info("用户消息已创建: message_id=%s", message.message_id) # 准备返回结果 result = { 'message': message, 'generation': None # 默认无生成信息 } # 如果提供了 generate_config 且启用了生成 if generate_config and generate_config.get('enabled'): logger.info("触发 AI 生成: message_id=%s", message.message_id) try: # 调用 trigger_ai_generation generation_result = await self.trigger_ai_generation( conversation_id=conversation_id, user_id=user_id, message_id=message.message_id, generation_type=generate_config['generation_type'], model_id=generate_config['model_id'], ai_params=generate_config['ai_params'], business_params=generate_config.get('business_params') ) result['generation'] = generation_result logger.info("AI 生成已触发: job_id=%s", generation_result.get('jobId')) except Exception as gen_error: logger.error( "触发 AI 生成失败: message_id=%s, 错误=%s", message.message_id, str(gen_error), exc_info=True ) # 不抛出异常,允许消息创建成功 return result ``` **关键设计**: - ✅ **容错处理**:生成失败不影响消息创建 - ✅ **条件触发**:只有 `enabled=true` 时才触发 - ✅ **复用逻辑**:调用已有的 `trigger_ai_generation` 方法 - ✅ **generate_config 已保存**:此时 `generate_config` 已在前面保存到 `message.meta_data['generate']` --- ### 3. API Router 改动 #### 文件: `server/app/api/v1/ai_conversations.py` ```python @router.post("/{conversation_id}/messages", response_model=SuccessResponse[AIConversationMessageResponse], summary="发送消息") async def send_message( conversation_id: UUID, request: MessageCreateRequest, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db) ): """发送用户消息(支持可选的 AI 生成) 支持 @ 提及功能: - 自动解析消息中的提及标记 - 验证所有提及的资源 - 构建 meta_data(mentions + reference_images) 支持可选 AI 生成(一步完成): - 提供 `generate` 参数可在发消息后自动触发 AI 生成 - `generate` 配置会保存到消息的 `meta_data['generate']` 中 - 无需单独调用 `/generate` 接口 示例请求 1(仅发消息): ```json { "content": "帮我生成一个日落场景" } ``` 示例请求 2(发消息 + 触发生成): ```json { "content": "生成一个日落场景", "generate": { "enabled": true, "generationType": "image", "modelId": "dall-e-3", "aiParams": { "resolution": "1024", "aspectRatio": "1:1" } } } ``` """ service = AIConversationService(db) # 提取 generate_config generate_config = None if request.generate: generate_config = { 'enabled': request.generate.enabled, 'generation_type': request.generate.generation_type, 'model_id': request.generate.model_id, 'ai_params': request.generate.ai_params, 'business_params': request.generate.business_params } result = await service.send_message( conversation_id=conversation_id, user_id=current_user.user_id, content=request.content, generate_config=generate_config # ✅ 传递 generate_config(会保存到 meta_data) ) # 构建响应 message = result['message'] generation = result.get('generation') # 使用 Pydantic schema 验证并序列化消息 message_response = AIConversationMessageResponse.model_validate(message) # 如果有生成信息,添加到响应中 if generation: from app.schemas.ai_conversation_message import GenerationInfo message_response.generation = GenerationInfo( job_id=generation['jobId'], task_id=generation['taskId'], status=generation['status'], estimated_credits=generation.get('estimatedCredits', 0), reference_images_count=generation.get('referenceImagesCount', 0) ) return SuccessResponse(data=message_response) ``` **关键变化**: 1. 提取 `request.generate` 转换为 `generate_config` 字典 2. 传递给 Service 层(会自动保存到 `meta_data['generate']`) 3. 构建响应时,如果有生成信息,附加到 `message_response.generation` --- ## 📊 对比总结 ### API 调用对比 #### Before (两步操作) ```json // 步骤 1: POST /conversations/{id}/messages { "content": "生成一个日落场景" } // 响应 1 { "data": { "messageId": "xxx", "content": "生成一个日落场景", ... } } // 步骤 2: POST /conversations/{id}/generate { "messageId": "xxx", "generationType": "image", "modelId": "dall-e-3", "aiParams": { ... } } // 响应 2 { "data": { "jobId": "yyy", "taskId": "zzz", "status": "pending" } } ``` #### After (一步操作) ```json // 一步: POST /conversations/{id}/messages { "content": "生成一个日落场景", "generate": { // ✅ 可选 "enabled": true, "generationType": "image", "modelId": "dall-e-3", "aiParams": { "resolution": "1024", "aspectRatio": "1:1" } } } // 响应(合并) { "data": { "messageId": "xxx", "content": "生成一个日落场景", "generation": { // ✅ 生成信息(如果触发) "jobId": "yyy", "taskId": "zzz", "status": "pending", "estimatedCredits": 10 }, ... } } ``` --- ## 🎯 改进效果 ### 1. 简化前端调用 ```typescript // Before (两步) const message = await sendMessage({ content }); const generation = await triggerGeneration({ messageId, ... }); // After (一步) const response = await sendMessage({ content, generate: { enabled: true, ... } }); ``` ### 2. 统一错误处理 ```typescript try { const response = await sendMessage({ content, generate }); // 一次处理消息 + 生成结果 } catch (error) { // 统一错误处理 } ``` ### 3. 自动关联 - ✅ Service 层自动关联 `messageId` - ✅ 无需前端手动管理 ID ### 4. 向后兼容 ```json // 仅发消息(原有功能) { "content": "这是一条消息" } // 发消息 + 生成(新功能) { "content": "生成一个日落场景", "generate": { ... } } ``` --- ## 🧪 验证测试 ### 测试场景 1: 仅发消息(向后兼容) ```bash curl -X POST http://localhost:8000/api/v1/ai-conversations/{id}/messages \ -H "Authorization: Bearer xxx" \ -H "Content-Type: application/json" \ -d '{ "content": "这是一条普通消息" }' ``` **预期结果**: ```json { "data": { "messageId": "xxx", "content": "这是一条普通消息", "generation": null // ✅ 无生成信息 } } ``` ### 测试场景 2: 发消息 + 触发生成 ```bash curl -X POST http://localhost:8000/api/v1/ai-conversations/{id}/messages \ -H "Authorization: Bearer xxx" \ -H "Content-Type: application/json" \ -d '{ "content": "生成一个日落场景", "generate": { "enabled": true, "generationType": "image", "modelId": "dall-e-3", "aiParams": { "resolution": "1024", "aspectRatio": "1:1" } } }' ``` **预期结果**: ```json { "data": { "messageId": "xxx", "content": "生成一个日落场景", "generation": { // ✅ 包含生成信息 "jobId": "yyy", "taskId": "zzz", "status": "pending", "estimatedCredits": 10, "referenceImagesCount": 0 } } } ``` ### 测试场景 3: 生成失败但消息成功 模拟生成失败(如模型不可用): **预期行为**: - ✅ 消息创建成功 - ✅ `generation` 字段为 `null` - ✅ 后台日志记录生成失败 --- ## 📝 后续工作 ### 优先级 1: 前端适配(高) 更新前端调用方式: ```typescript // API 类型定义 interface SendMessageRequest { content: string; metaData?: Record; generate?: { enabled: boolean; generationType: 'image' | 'video'; modelId: string; aiParams: Record; businessParams?: Record; }; } // 使用示例 const sendMessageWithGeneration = async () => { const response = await api.post(`/conversations/${id}/messages`, { content: '生成一个日落场景', generate: { enabled: true, generationType: 'image', modelId: 'dall-e-3', aiParams: { resolution: '1024', aspectRatio: '1:1' } } }); // response.data.message - 消息信息 // response.data.generation - AI 生成信息(可能为 null) }; ``` ### 优先级 2: 废弃旧接口(低) 考虑废弃单独的 `POST /{conversation_id}/generate` 接口: - ⚠️ 标记为 `@deprecated` - ⚠️ 在文档中说明使用新方式 - ⚠️ 保留向后兼容(可选) ### 优先级 3: 监控和日志(中) 增加指标监控: - 📊 发消息并触发生成的比例 - 📊 生成失败但消息成功的次数 - 📊 平均响应时间 --- ## 🔗 相关文档 - [Conversation Params Separation](./2026-02-13-conversation-params-separation.md) - [Parameter Mapper Refactor](./2026-02-13-param-mapper-refactor.md) - [RFC 144: AI Models Capability Configuration](../rfcs/144-ai-models-capability-config.md) --- ## 📌 影响范围 ### 破坏性变更 (Breaking Changes) - ✅ **无破坏性变更**:`generate` 参数为可选,完全向后兼容 ### 建议迁移策略 #### 渐进式迁移(推荐) 1. **Phase 1**:前端继续使用两步操作(无需修改) 2. **Phase 2**:新功能使用一步操作 3. **Phase 3**:逐步迁移旧代码到新方式 #### 直接迁移 前端直接适配新接口,简化代码逻辑。 --- ## ✅ 验证清单 - [x] Schema 定义(`GenerateConfig`, `GenerationInfo`, 更新请求/响应 Schema) - [x] Service 层方法签名更新 - [x] Service 层生成触发逻辑 - [x] API Router 更新 - [x] Python 语法检查通过 - [x] Docker 服务重启成功 - [ ] 前端适配新格式 - [ ] 端到端测试 - [ ] 监控指标添加 --- **最新更新**: 2026-02-13 **版本**: v1.0 **状态**: ✅ 已完成(待前端适配)