--- name: conversation_model_persistence description: 当加载/恢复对话时发现模型未恢复,或新建对话后旧对话模型被覆盖时,应该索引本记忆 --- # 对话模型持久化约定 ## 核心规则 - 每个对话的 `model_key` 保存在对话文件的 `metadata.model_key` 中。 - 切换模型时(`/api/model`)会立即调用 `utils/conversation_manager/ConversationManager.save_conversation(..., model_key=terminal.model_key)` 写入对话文件。 - **加载对话时必须恢复该对话保存的模型**,不能沿用终端当前的默认模型或用户个性化默认模型。 - **创建新对话时,必须先保存旧对话、再应用默认模型到新对话**,否则旧对话的 `model_key` 会被默认模型覆盖。 ## 关键实现点 ### 1. 手动加载对话 路径:`/api/conversations//load` → `core/web_terminal.py::WebTerminal.load_conversation()` - 该方法会从对话元数据中恢复 `thinking_mode`、`permission_mode`、`execution_mode`、`network_permission` 等状态。 - **必须同时恢复 `model_key`**: ```python saved_model_key = meta.get("model_key") if saved_model_key: try: self.set_model(saved_model_key) except Exception as exc: logger.warning("加载对话模型 %s 失败: %s", saved_model_key, exc) ``` - 恢复后,`terminal.model_key` 即对话保存的模型,`get_status()` 和 API 返回的 `model_key` 都会正确。 ### 2. 程序重启后自动恢复最近对话 路径:`core/web_terminal.py::WebTerminal._ensure_conversation()` - Web 终端初始化时会自动加载最近的对话。 - 原来的实现只调用 `utils/context_manager/ContextManager.load_conversation_by_id()`,不会恢复 `model_key` 等运行时状态。 - 应改为调用 `self.load_conversation(conv_id)`,以复用完整的状态恢复逻辑(含模型恢复)。 ### 3. 创建新对话时保护旧对话的模型 路径:`core/web_terminal.py::WebTerminal.create_new_conversation()` - **错误顺序**:先 `set_model(默认模型)` → 再 `start_new_conversation()` → `start_new_conversation()` 内部先 `save_current_conversation()` → 旧对话被按默认模型保存,覆盖了之前的模型。 - **正确顺序**: 1. 计算默认 `run_mode`、`permission_mode` 并应用(不改 `model_key`)。 2. 调用 `start_new_conversation()`,此时 `terminal.model_key` 仍是旧对话的模型,旧对话被正确保存。 3. 新对话创建完成后,再 `set_model(默认模型)`,并 `save_conversation` 更新新对话的 `model_key`。 ### 4. 前端 路径:`static/src/app/methods/conversation/load.ts::loadConversation()` - 前端在调用 `/api/conversations//load` 后,会根据返回的 `result.model_key` 更新当前模型: ```typescript if (typeof result.model_key === 'string' && result.model_key) { this.modelSet(result.model_key); } ``` - 后端修复后,返回的 `model_key` 即为对话保存的模型,前端会自动应用。 ## 注意事项 - `set_model()` 可能因图片/视频不兼容或模型被禁用而抛异常,加载场景下应捕获并记录警告,不要让加载失败。 - 如果对话元数据中没有 `model_key`(旧数据或新建对话),保持当前模型即可。