agent-Specialization/.agents/memory/conversation_model_persistence.md
JOJO f661dc6456 refactor(server,modules,utils,frontend): split oversized modules into packages and fix regressions
- Split server/chat.py, server/status.py, server/tasks.py into sub-packages
- Split utils/context_manager.py, utils/conversation_manager.py into mixin packages
- Split modules/file_manager.py, persistent_terminal.py, terminal_ops.py, mcp_client_manager.py into packages
- Split core/main_terminal_parts/context.py into base + mixins
- Split static/src/app/methods/{ui,message,upload,taskPolling,conversation} into sub-modules
- Fix flattened method exports, dynamic import paths, missing cross-module imports
- Add missing permission/network/execution mode constants and _PERMISSION_MODE_LABEL
- Update AGENTS.md, CLAUDE.md and project memories to reflect new structure
2026-06-20 15:54:48 +08:00

3.2 KiB
Raw Blame History

name description
conversation_model_persistence 当加载/恢复对话时发现模型未恢复,或新建对话后旧对话模型被覆盖时,应该索引本记忆

对话模型持久化约定

核心规则

  • 每个对话的 model_key 保存在对话文件的 metadata.model_key 中。
  • 切换模型时(/api/model)会立即调用 utils/conversation_manager/ConversationManager.save_conversation(..., model_key=terminal.model_key) 写入对话文件。
  • 加载对话时必须恢复该对话保存的模型,不能沿用终端当前的默认模型或用户个性化默认模型。
  • 创建新对话时,必须先保存旧对话、再应用默认模型到新对话,否则旧对话的 model_key 会被默认模型覆盖。

关键实现点

1. 手动加载对话

路径:/api/conversations/<conversation_id>/loadcore/web_terminal.py::WebTerminal.load_conversation()

  • 该方法会从对话元数据中恢复 thinking_modepermission_modeexecution_modenetwork_permission 等状态。
  • 必须同时恢复 model_key
    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_modepermission_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/<id>/load 后,会根据返回的 result.model_key 更新当前模型:
    if (typeof result.model_key === 'string' && result.model_key) {
      this.modelSet(result.model_key);
    }
    
  • 后端修复后,返回的 model_key 即为对话保存的模型,前端会自动应用。

注意事项

  • set_model() 可能因图片/视频不兼容或模型被禁用而抛异常,加载场景下应捕获并记录警告,不要让加载失败。
  • 如果对话元数据中没有 model_key(旧数据或新建对话),保持当前模型即可。