From dadf05ab4ee99f616a62aef1388588be2f8fac10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A0=E7=A3=8A?= Date: Thu, 8 Jan 2026 12:02:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20PowerMem=20?= =?UTF-8?q?=E6=99=BA=E8=83=BD=E8=AE=B0=E5=BF=86=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 PowerMem 配置选项和集成文档 - 更新 README 和多语言文档 - 添加 powermem 依赖包 --- README.md | 3 +- README_de.md | 3 +- README_en.md | 4 +- README_vi.md | 3 +- docs/FAQ.md | 1 + docs/powermem-integration.md | 253 ++++++++++++++++++ main/xiaozhi-server/config.yaml | 24 ++ .../providers/memory/powermem/powermem.py | 227 ++++++++++++++++ main/xiaozhi-server/requirements.txt | 1 + 9 files changed, 515 insertions(+), 4 deletions(-) create mode 100644 docs/powermem-integration.md create mode 100644 main/xiaozhi-server/core/providers/memory/powermem/powermem.py diff --git a/README.md b/README.md index eb718c24..17e2b886 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ | 智能对话 | 支持多种LLM(大语言模型),实现智能对话 | | 视觉感知 | 支持多种VLLM(视觉大模型),实现多模态交互 | | 意图识别 | 支持外挂的大模型意图识别、大模型自主函数调用,提供插件化意图处理机制 | -| 记忆系统 | 支持本地短期记忆、mem0ai接口记忆,具备记忆总结功能 | +| 记忆系统 | 支持本地短期记忆、mem0ai接口记忆、PowerMem智能记忆,具备记忆总结功能 | | 知识库 | 支持RAGFlow知识库,让大模型判断需要调度知识库后再回答 | | 工具调用 | 支持客户端IOT协议、客户MCP协议、服务端MCP协议、MCP接入点协议、自定义工具函数 | | 指令下发 | 依托MQTT协议,支持从智控台将MCP指令下发到ESP32设备 | @@ -330,6 +330,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ | 类型 | 平台名称 | 使用方式 | 收费模式 | 备注 | |:------:|:---------------:|:----:|:---------:|:--:| | Memory | mem0ai | 接口调用 | 1000次/月额度 | | +| Memory | [powermem](./docs/powermem-integration.md) | 本地总结 | 取决于LLM和DB | OceanBase开源,支持智能检索 | | Memory | mem_local_short | 本地总结 | 免费 | | | Memory | nomem | 无记忆模式 | 免费 | | diff --git a/README_de.md b/README_de.md index 0e4c74d5..403fc921 100644 --- a/README_de.md +++ b/README_de.md @@ -240,7 +240,7 @@ Dieses Projekt bietet die folgenden Testwerkzeuge, um Ihnen bei der Überprüfun | Intelligenter Dialog | Unterstützt mehrere LLM (große Sprachmodelle), implementiert intelligenten Dialog | | Visuelle Wahrnehmung | Unterstützt mehrere VLLM (Vision Large Models), implementiert multimodale Interaktion | | Absichtserkennung | Unterstützt LLM-Absichtserkennung, Function Call-Funktionsaufruf, bietet plugin-basierten Absichtsverarbeitungsmechanismus | -| Gedächtnissystem | Unterstützt lokales Kurzzeitgedächtnis, mem0ai-Schnittstellengedächtnis, mit Gedächtniszusammenfassungsfunktion | +| Gedächtnissystem | Unterstützt lokales Kurzzeitgedächtnis, mem0ai-Schnittstellengedächtnis, PowerMem intelligentes Gedächtnis, mit Gedächtniszusammenfassungsfunktion | | Wissensdatenbank | Unterstützt RAGFlow-Wissensdatenbank, ermöglicht großem Modell die Bewertung, ob Wissensdatenbank benötigt wird, bevor geantwortet wird | | Werkzeugaufruf | Unterstützt Client-IOT-Protokoll, Client-MCP-Protokoll, Server-MCP-Protokoll, MCP-Endpunktprotokoll, benutzerdefinierte Werkzeugfunktionen | | Befehlsübermittlung | Basierend auf MQTT-Protokoll, unterstützt die Übermittlung von MCP-Befehlen von der intelligenten Steuerkonsole an ESP32-Geräte | @@ -328,6 +328,7 @@ Tatsächlich kann jedes VLLM, das OpenAI-Schnittstellenaufrufe unterstützt, int | Typ | Plattformname | Verwendungsmethode | Preismodell | Hinweise | |:------:|:---------------:|:----:|:---------:|:--:| | Memory | mem0ai | Schnittstellenaufrufe | 1000 Mal/Monat Kontingent | | +| Memory | [powermem](./docs/powermem-integration.md) | Lokale Zusammenfassung | Abhängig von LLM und DB | OceanBase Open Source, unterstützt intelligente Abfrage | | Memory | mem_local_short | Lokale Zusammenfassung | Kostenlos | | | Memory | nomem | Kein Gedächtnismodus | Kostenlos | | diff --git a/README_en.md b/README_en.md index 196cf16f..e071e7fa 100644 --- a/README_en.md +++ b/README_en.md @@ -238,7 +238,7 @@ This project provides the following testing tools to help you verify the system | Intelligent Dialogue | Supports multiple LLM(large language models), implements intelligent dialogue | | Visual Perception | Supports multiple VLLM(vision large models), implements multimodal interaction | | Intent Recognition | Supports LLM intent recognition, Function Call function calling, provides plugin-based intent processing mechanism | -| Memory System | Supports local short-term memory, mem0ai interface memory, with memory summarization functionality | +| Memory System | Supports local short-term memory, mem0ai interface memory, PowerMem intelligent memory, with memory summarization functionality | | Knowledge Base | Supports RAGFlow knowledge base, enabling LLM to judge whether to schedule the knowledge base after receiving the user's question, and then answer the question | | Command Delivery | Supports MCP command delivery to ESP32 devices via MQTT protocol from Smart Console | | Tool Calling | Supports client IOT protocol, client MCP protocol, server MCP protocol, MCP endpoint protocol, custom tool functions | @@ -330,7 +330,9 @@ In fact, any VLLM that supports OpenAI interface calls can be integrated and use | Type | Platform Name | Usage Method | Pricing Model | Notes | |:------:|:---------------:|:----:|:---------:|:--:| | Memory | mem0ai | Interface calls | 1000 times/month quota | | +| Memory | [powermem](./docs/powermem-integration.md) | Local summarization | Depends on LLM and DB | OceanBase open source, supports intelligent retrieval | | Memory | mem_local_short | Local summarization | Free | | +| Memory | nomem | No memory mode | Free | | --- diff --git a/README_vi.md b/README_vi.md index e6d5f489..4a2fbb6b 100644 --- a/README_vi.md +++ b/README_vi.md @@ -241,7 +241,7 @@ Dự án này cung cấp các công cụ kiểm tra sau để giúp bạn xác m | Đối thoại thông minh | Hỗ trợ nhiều LLM(Mô hình ngôn ngữ lớn), thực hiện đối thoại thông minh | | Cảm nhận thị giác | Hỗ trợ nhiều VLLM(Mô hình lớn thị giác), thực hiện tương tác đa phương thức | | Nhận dạng ý định | Hỗ trợ nhận dạng ý định mô hình lớn gắn ngoài, gọi hàm tự chủ mô hình lớn, cung cấp cơ chế xử lý ý định dạng plugin | -| Hệ thống bộ nhớ | Hỗ trợ bộ nhớ ngắn hạn cục bộ, bộ nhớ giao diện mem0ai, có chức năng tóm tắt bộ nhớ | +| Hệ thống bộ nhớ | Hỗ trợ bộ nhớ ngắn hạn cục bộ, bộ nhớ giao diện mem0ai, bộ nhớ thông minh PowerMem, có chức năng tóm tắt bộ nhớ | | Kho tri thức | Hỗ trợ kho tri thức RAGFlow, cho phép mô hình lớn đánh giá cần gọi kho tri thức trước khi trả lời | | Gọi công cụ | Hỗ trợ giao thức IOT phía client, giao thức MCP phía client, giao thức MCP phía server, giao thức điểm truy cập MCP, hàm công cụ tùy chỉnh | | Gửi lệnh | Dựa vào giao thức MQTT, hỗ trợ gửi lệnh MCP từ bảng điều khiển thông minh xuống thiết bị ESP32 | @@ -329,6 +329,7 @@ Trên thực tế, bất kỳ VLLM nào hỗ trợ gọi giao diện openai đ | Loại | Tên nền tảng | Phương pháp sử dụng | Mô hình thu phí | Ghi chú | |:------:|:---------------:|:----:|:---------:|:--:| | Memory | mem0ai | Gọi giao diện | Hạn mức 1000 lần/tháng | | +| Memory | [powermem](./docs/powermem-integration.md) | Tóm tắt cục bộ | Phụ thuộc vào LLM và DB | OceanBase mã nguồn mở, hỗ trợ tìm kiếm thông minh | | Memory | mem_local_short | Tóm tắt cục bộ | Miễn phí | | | Memory | nomem | Chế độ không có bộ nhớ | Miễn phí | | diff --git a/docs/FAQ.md b/docs/FAQ.md index 69494c3d..b6bec471 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -82,6 +82,7 @@ VAD: 8、[新闻插件源配置指南](./newsnow_plugin_config.md)
9、[知识库ragflow集成指南](./ragflow-integration.md)
10、[如何部署上下文源](./context-provider-integration.md)
+11、[如何集成PowerMem智能记忆](./powermem-integration.md)
### 11、语音克隆、本地语音部署相关教程 1、[如何在智控台克隆音色](./huoshan-streamTTS-voice-cloning.md)
diff --git a/docs/powermem-integration.md b/docs/powermem-integration.md new file mode 100644 index 00000000..76547b60 --- /dev/null +++ b/docs/powermem-integration.md @@ -0,0 +1,253 @@ +# PowerMem 记忆组件集成指南 + +## 简介 + +[PowerMem](https://www.powermem.ai/) 是由 OceanBase 开源的 Agent 记忆组件,通过本地 LLM 进行记忆总结和智能检索,为 AI 代理提供高效的记忆管理功能。 + +费用说明:PowerMem 本身开源免费,实际费用取决于您选择的 LLM 和数据库: +- 使用 SQLite + 免费 LLM(如智谱 glm-4-flash)= **完全免费** +- 使用云端 LLM 或云端数据库 = 按对应服务收费 + +> 💡 **最佳性能提示**:PowerMem 配合 OceanBase 使用可实现最大性能释放,SQLite 仅建议在资源不足的情况下使用。 + +- **GitHub**: https://github.com/oceanbase/powermem +- **官网**: https://www.powermem.ai/ +- **使用示例**: https://github.com/oceanbase/powermem/tree/main/examples + +## 功能特性 + +- **本地总结**:通过 LLM 在本地进行记忆总结和提取 +- **多种存储后端**:支持 OceanBase(推荐,最佳性能)、SeekDB(推荐,AI应用存储一体)、PostgreSQL、SQLite(轻量备选) +- **多种 LLM 支持**:通义千问、智谱(glm-4-flash 免费)、OpenAI 等 +- **智能检索**:基于向量搜索的语义检索能力 +- **私有部署**:完全支持本地私有化部署 +- **异步操作**:高效的异步记忆管理 + +## 安装 + +PowerMem 已添加到项目依赖中,如果需要手动安装: + +```bash +pip install powermem +``` + +## 配置说明 + +### 基础配置 + +在 `config.yaml` 中配置 PowerMem: + +```yaml +selected_module: + Memory: powermem + +Memory: + powermem: + type: powermem + # 数据库提供者: oceanbase(推荐,最佳性能), seekdb, postgres, sqlite(轻量备选) + database_provider: sqlite # 资源充足时建议使用 oceanbase 或 seekdb + # LLM提供者: qwen(默认), openai, 等 + llm_provider: qwen + # 嵌入模型提供者: qwen(默认), openai, 等 + embedding_provider: qwen + # LLM配置 + llm_api_key: 你的LLM API密钥 + llm_model: qwen-plus + # 嵌入模型配置 + embedding_api_key: 你的嵌入模型API密钥 + embedding_model: text-embedding-v3 +``` + +### 配置参数详解 + +| 参数 | 说明 | 默认值 | 可选值 | +|------|------|--------|--------| +| `database_provider` | 存储后端类型 | `sqlite` | `oceanbase`(推荐), `seekdb`, `postgres`, `sqlite`(轻量) | +| `llm_provider` | LLM 提供商 | `qwen` | `qwen`, `zhipu`(免费), `openai`, 等 | +| `embedding_provider` | 嵌入模型提供商 | `qwen` | `qwen`, `zhipu`, `openai`, 等 | +| `llm_api_key` | LLM API 密钥 | - | - | +| `llm_model` | LLM 模型名称 | - | 根据提供商选择 | +| `llm_base_url` | LLM API 地址(可选) | - | - | +| `embedding_api_key` | 嵌入模型 API 密钥 | - | - | +| `embedding_model` | 嵌入模型名称 | - | 根据提供商选择 | +| `embedding_base_url` | 嵌入模型 API 地址(可选) | - | - | + +### 使用通义千问(推荐) + +1. 访问 [阿里云百炼平台](https://bailian.console.aliyun.com/) 注册账号 +2. 在 [API Key 管理](https://bailian.console.aliyun.com/?apiKey=1#/api-key) 页面获取 API 密钥 +3. 配置如下: + +```yaml +Memory: + powermem: + type: powermem + database_provider: sqlite + llm_provider: qwen + embedding_provider: qwen + llm_api_key: sk-xxxxxxxxxxxxxxxx + llm_model: qwen-plus + embedding_api_key: sk-xxxxxxxxxxxxxxxx + embedding_model: text-embedding-v3 +``` + +### 使用智谱免费 LLM(完全免费方案) + +智谱提供免费的 glm-4-flash 模型,配合 SQLite 可实现完全免费使用: + +1. 访问 [智谱AI开放平台](https://bigmodel.cn/) 注册账号 +2. 在 [API Keys](https://bigmodel.cn/usercenter/proj-mgmt/apikeys) 页面获取 API 密钥 +3. 配置如下: + +```yaml +Memory: + powermem: + type: powermem + database_provider: sqlite + llm_provider: zhipu + embedding_provider: zhipu + llm_api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx + llm_model: glm-4-flash + llm_base_url: https://open.bigmodel.cn/api/paas/v4/ + embedding_api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx + embedding_model: embedding-3 + embedding_base_url: https://open.bigmodel.cn/api/paas/v4/ +``` + +### 使用 OpenAI + +```yaml +Memory: + powermem: + type: powermem + database_provider: sqlite + llm_provider: openai + embedding_provider: openai + llm_api_key: sk-xxxxxxxxxxxxxxxx + llm_model: gpt-4o-mini + llm_base_url: https://api.openai.com/v1 + embedding_api_key: sk-xxxxxxxxxxxxxxxx + embedding_model: text-embedding-3-small + embedding_base_url: https://api.openai.com/v1 +``` + +### 使用 OceanBase(最佳性能方案) + +OceanBase 是 PowerMem 的最佳搭档,可实现最大性能释放: + +1. 部署 OceanBase 数据库(支持开源本地部署或使用云服务) + - 开源部署:https://github.com/oceanbase/oceanbase + - 云服务:https://www.oceanbase.com/ +2. 配置如下: + +```yaml +Memory: + powermem: + type: powermem + database_provider: oceanbase + llm_provider: qwen + embedding_provider: qwen + llm_api_key: sk-xxxxxxxxxxxxxxxx + llm_model: qwen-plus + embedding_api_key: sk-xxxxxxxxxxxxxxxx + embedding_model: text-embedding-v3 + # OceanBase 数据库连接配置 + vector_store: + provider: oceanbase + config: + host: 127.0.0.1 + port: 2881 + user: root@test + password: your_password + database: powermem +``` + + +### 高级配置 + +如果需要更精细的控制,可以使用完整的配置结构: + +```yaml +Memory: + powermem: + type: powermem + # 向量存储配置 + vector_store: + provider: sqlite + config: + path: ./data/powermem.db + # LLM 配置 + llm: + provider: qwen + config: + api_key: sk-xxxxxxxxxxxxxxxx + model: qwen-plus + # 嵌入模型配置 + embedder: + provider: qwen + config: + api_key: sk-xxxxxxxxxxxxxxxx + model: text-embedding-v3 +``` + +## 设备记忆隔离 + +PowerMem 会自动使用设备 ID(`device_id`)作为 `user_id` 进行记忆隔离。这意味着: + +- 每个设备拥有独立的记忆空间 +- 不同设备之间的记忆完全隔离 +- 同一设备的多次对话可以共享记忆上下文 + +## 与其他记忆组件的对比 + +| 特性 | PowerMem | mem0ai | mem_local_short | +|------|----------|--------|-----------------| +| 工作方式 | 本地总结 | 云端接口 | 本地总结 | +| 存储位置 | 本地/云端DB | 云端 | 本地YAML | +| 费用 | 取决于LLM和DB | 1000次/月免费 | 完全免费 | +| 智能检索 | ✅ 向量搜索 | ✅ 向量搜索 | ❌ 全量返回 | +| 私有部署 | ✅ 支持 | ❌ 仅云端 | ✅ 支持 | +| 数据库支持 | OceanBase(推荐)/SeekDB/PostgreSQL/SQLite | - | YAML 文件 | + +## 常见问题 + +### 1. API 密钥错误 + +如果出现 `API key is required` 错误,请检查: +- `llm_api_key` 和 `embedding_api_key` 是否正确填写 +- API 密钥是否有效 + +### 2. 模型不存在 + +如果出现模型不存在的错误,请确认: +- `llm_model` 和 `embedding_model` 名称是否正确 +- 对应的模型服务是否已开通 + +### 3. 连接超时 + +如果出现连接超时,可以尝试: +- 检查网络连接 +- 如果使用代理,配置 `llm_base_url` 和 `embedding_base_url` + +## 测试验证 + +可以在虚拟环境中测试 PowerMem 是否正常工作: + +```bash +# 激活虚拟环境 +source .venv/bin/activate + +# 测试 PowerMem 导入 +python -c "from powermem import AsyncMemory; print('PowerMem 导入成功')" +``` + +## 更多资源 + +- [PowerMem 官方文档](https://www.powermem.ai/) +- [PowerMem GitHub 仓库](https://github.com/oceanbase/powermem) +- [PowerMem 使用示例](https://github.com/oceanbase/powermem/tree/main/examples) +- [OceanBase 官网](https://www.oceanbase.com/) +- [OceanBase GitHub](https://github.com/oceanbase/oceanbase) +- [SeekDB GitHub](https://github.com/oceanbase/seekdb)(AI原生搜索数据库) +- [阿里云百炼平台](https://bailian.console.aliyun.com/) + diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index a3c54c17..cb735e63 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -275,6 +275,30 @@ Memory: # https://app.mem0.ai/dashboard/api-keys # 每月有1000次免费调用 api_key: 你的mem0ai api key + powermem: + # PowerMem是OceanBase开源的agent记忆组件,通过本地LLM进行记忆总结 + # 费用说明:PowerMem本身免费,实际费用取决于所选LLM和数据库 + # - 使用sqlite + 免费LLM(如glm-4-flash) = 完全免费 + # - 使用云端LLM或云端数据库 = 按对应服务收费 + # GitHub: https://github.com/oceanbase/powermem + # 官网: https://www.powermem.ai/ + # 使用示例: https://github.com/oceanbase/powermem/tree/main/examples + type: powermem + # 数据库提供者: oceanbase(推荐,最佳性能), seekdb(推荐,AI应用存储一体), postgres, sqlite(轻量备选) + # 资源充足时建议使用 oceanbase 或 seekdb + database_provider: sqlite + # LLM提供者: qwen(默认), zhipu(免费glm-4-flash), openai, 等 + llm_provider: qwen + # 嵌入模型提供者: qwen(默认), zhipu, openai, 等 + embedding_provider: qwen + # LLM配置 (使用通义千问为例) + # 通义千问: https://bailian.console.aliyun.com/?apiKey=1#/api-key + # 智谱AI(免费): https://bigmodel.cn/usercenter/proj-mgmt/apikeys + llm_api_key: 你的LLM API密钥 + llm_model: qwen-plus + # 嵌入模型配置 (使用通义千问为例) + embedding_api_key: 你的嵌入模型API密钥 + embedding_model: text-embedding-v3 nomem: # 不想使用记忆功能,可以使用nomem type: nomem diff --git a/main/xiaozhi-server/core/providers/memory/powermem/powermem.py b/main/xiaozhi-server/core/providers/memory/powermem/powermem.py new file mode 100644 index 00000000..ec9fea7a --- /dev/null +++ b/main/xiaozhi-server/core/providers/memory/powermem/powermem.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +""" +@time: 2026/01/08 +@file: powermem.py +@desc: PowerMem memory provider for xiaozhi-esp32-server + PowerMem is an open-source agent memory component from OceanBase + GitHub: https://github.com/oceanbase/powermem + Website: https://www.powermem.ai/ +""" + +import traceback +from typing import Optional, Dict, Any + +from ..base import MemoryProviderBase, logger +from powermem import AsyncMemory + +TAG = __name__ + + +class MemoryProvider(MemoryProviderBase): + """ + PowerMem memory provider implementation. + + PowerMem is an open-source agent memory component that provides + efficient memory management for AI agents. + + Supports multiple storage backends (sqlite, oceanbase, postgres), + LLM providers (qwen, openai, etc.) and embedding providers. + """ + + def __init__(self, config: Dict[str, Any], summary_memory: Optional[str] = None): + super().__init__(config) + self.use_powermem = False + self.memory_client = None + + try: + # Get configuration parameters + database_provider = config.get("database_provider", "sqlite") + llm_provider = config.get("llm_provider", "qwen") + embedding_provider = config.get("embedding_provider", "qwen") + + # Build powermem configuration dict + # PowerMem supports two config styles: + # 1. powermem style: database, llm, embedding + # 2. mem0 style: vector_store, llm, embedder + powermem_config = {} + + # Configure vector store / database + if "vector_store" in config: + powermem_config["vector_store"] = config["vector_store"] + elif "database" in config: + powermem_config["database"] = config["database"] + else: + powermem_config["vector_store"] = { + "provider": database_provider, + "config": {} + } + + # Configure LLM + if "llm" in config: + powermem_config["llm"] = config["llm"] + else: + llm_config = {} + if "llm_api_key" in config: + llm_config["api_key"] = config["llm_api_key"] + if "llm_model" in config: + llm_config["model"] = config["llm_model"] + if "llm_base_url" in config: + llm_config["base_url"] = config["llm_base_url"] + powermem_config["llm"] = { + "provider": llm_provider, + "config": llm_config + } + + # Configure embedder + if "embedder" in config: + powermem_config["embedder"] = config["embedder"] + else: + embedder_config = {} + if "embedding_api_key" in config: + embedder_config["api_key"] = config["embedding_api_key"] + if "embedding_model" in config: + embedder_config["model"] = config["embedding_model"] + if "embedding_base_url" in config: + embedder_config["base_url"] = config["embedding_base_url"] + powermem_config["embedder"] = { + "provider": embedding_provider, + "config": embedder_config + } + + # Initialize AsyncMemory client + self.memory_client = AsyncMemory(config=powermem_config) + self.use_powermem = True + + logger.bind(tag=TAG).info( + f"PowerMem initialized successfully with database={database_provider}, " + f"llm={llm_provider}, embedding={embedding_provider}" + ) + + except ImportError as e: + logger.bind(tag=TAG).error( + f"PowerMem not installed. Please install with: pip install powermem. Error: {e}" + ) + self.use_powermem = False + except Exception as e: + logger.bind(tag=TAG).error(f"Failed to initialize PowerMem: {str(e)}") + logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") + self.use_powermem = False + + async def save_memory(self, msgs): + """ + Save conversation messages to PowerMem. + + Args: + msgs: List of message objects with 'role' and 'content' attributes + + Returns: + Result from PowerMem API or None if failed + """ + if not self.use_powermem or self.memory_client is None: + logger.bind(tag=TAG).warning("PowerMem is not available, skipping save_memory") + return None + + if len(msgs) < 2: + logger.bind(tag=TAG).debug("Not enough messages to save (need at least 2)") + return None + + try: + # Format the content as a message list for PowerMem + messages = [ + {"role": message.role, "content": message.content} + for message in msgs + if message.role != "system" + ] + + # Add memory using PowerMem SDK + result = await self.memory_client.add( + messages=messages, + user_id=self.role_id + ) + + logger.bind(tag=TAG).debug(f"Save memory result: {result}") + return result + + except Exception as e: + logger.bind(tag=TAG).error(f"Error saving memory: {str(e)}") + logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") + return None + + async def query_memory(self, query: str) -> str: + """ + Query memories from PowerMem based on similarity search. + + Args: + query: The search query string + + Returns: + Formatted string of relevant memories or empty string if none found + """ + if not self.use_powermem or self.memory_client is None: + logger.bind(tag=TAG).warning("PowerMem is not available, skipping query_memory") + return "" + + try: + if not getattr(self, "role_id", None): + logger.bind(tag=TAG).debug("No role_id set, returning empty memory") + return "" + + # Search memories using PowerMem SDK + results = await self.memory_client.search( + query=query, + user_id=self.role_id, + limit=30 + ) + + if not results or "results" not in results: + logger.bind(tag=TAG).debug("No memory results found") + return "" + + # Format each memory entry with its update time + memories = [] + for entry in results.get("results", []): + # Get timestamp from updated_at or created_at + timestamp = "" + if "updated_at" in entry and entry["updated_at"]: + timestamp = str(entry["updated_at"]) + elif "created_at" in entry and entry["created_at"]: + timestamp = str(entry["created_at"]) + + if timestamp: + try: + # Parse and reformat the timestamp (remove milliseconds if present) + if "." in timestamp: + dt = timestamp.split(".")[0] + else: + dt = timestamp + formatted_time = dt.replace("T", " ") + except Exception: + formatted_time = timestamp + else: + formatted_time = "" + + memory = entry.get("memory", "") or entry.get("content", "") + if memory: + if formatted_time: + # Store tuple of (timestamp, formatted_string) for sorting + memories.append((timestamp, f"[{formatted_time}] {memory}")) + else: + memories.append(("", memory)) + + # Sort by timestamp in descending order (newest first) + memories.sort(key=lambda x: x[0], reverse=True) + + # Extract only the formatted strings + memories_str = "\n".join(f"- {memory[1]}" for memory in memories) + logger.bind(tag=TAG).debug(f"Query results: {memories_str}") + return memories_str + + except Exception as e: + logger.bind(tag=TAG).error(f"Error querying memory: {str(e)}") + logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") + return "" + + +# Register the memory provider instance +powermem = MemoryProvider({}) diff --git a/main/xiaozhi-server/requirements.txt b/main/xiaozhi-server/requirements.txt index cc4cd8fe..20167f39 100644 --- a/main/xiaozhi-server/requirements.txt +++ b/main/xiaozhi-server/requirements.txt @@ -23,6 +23,7 @@ loguru==0.7.3 requests==2.32.5 cozepy==0.20.0 mem0ai==1.0.0 +powermem>=0.2.1 bs4==0.0.2 modelscope==1.32.0 sherpa_onnx==1.12.17