""" Mem0 记忆层初始化与健康检查。 启动时验证 Ollama 可达和模型可用,失败则抛出 RuntimeError 阻止后端启动。 """ import logging import httpx from mem0 import Memory from app.config import ( get_deepseek_api_key, get_ollama_base_url, get_mem0_embedding_model, ) logger = logging.getLogger(__name__) _memory: Memory | None = None def _check_ollama_health() -> None: """检查 Ollama 服务是否可达,以及 embedding 模型是否已安装。""" base_url = get_ollama_base_url() model_name = get_mem0_embedding_model() try: resp = httpx.get(f"{base_url}/api/tags", timeout=5) resp.raise_for_status() except (httpx.ConnectError, httpx.TimeoutException, httpx.HTTPStatusError) as e: raise RuntimeError( f"无法连接 Ollama 服务({base_url})。" f"请确认 Ollama 已启动:启动方式参见 https://ollama.com\n" f"原始错误:{e}" ) from e models = resp.json().get("models", []) installed = [m.get("name", "").split(":")[0] for m in models] if model_name not in installed: raise RuntimeError( f"Ollama 已运行,但未找到 embedding 模型 '{model_name}'。\n" f"已安装的模型:{installed}\n" f"请运行:ollama pull {model_name}" ) logger.info("Ollama 健康检查通过:%s 模型可用", model_name) def init_memory() -> Memory: """初始化 Mem0 Memory 单例。首次调用时执行健康检查。""" global _memory if _memory is not None: return _memory _check_ollama_health() deepseek_key = get_deepseek_api_key() if not deepseek_key: raise RuntimeError( "DEEPSEEK_API_KEY 未配置。Mem0 需要该 key 进行事实提取。\n" "请在 .env 中设置 DEEPSEEK_API_KEY" ) config = { "llm": { "provider": "deepseek", "config": { "model": "deepseek-chat", "temperature": 0.1, "max_tokens": 1500, "api_key": deepseek_key, }, }, "embedder": { "provider": "ollama", "config": { "model": get_mem0_embedding_model(), "ollama_base_url": get_ollama_base_url(), }, }, "vector_store": { "provider": "qdrant", "config": { "collection_name": "epeekit_memories", "embedding_model_dims": 768, "path": "./data/qdrant", }, }, } _memory = Memory.from_config(config) logger.info("Mem0 记忆层初始化成功") return _memory def get_memory() -> Memory: """获取已初始化的 Memory 实例。未初始化时自动调用 init_memory()。""" if _memory is None: return init_memory() return _memory