mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-23 07:33:53 +08:00
feat(powermem): 升级配置结构并支持多数据库用户画像
- 重构配置结构为模块化设计(llm/embedder/vector_store) - 用户画像功能支持oceanbase/seekdb/sqlite三种存储后端 - 更新文档说明和示例配置
This commit is contained in:
@@ -284,32 +284,46 @@ Memory:
|
||||
# 官网: https://www.powermem.ai/
|
||||
# 使用示例: https://github.com/oceanbase/powermem/tree/main/examples
|
||||
type: powermem
|
||||
# 是否启用用户画像功能(需要OceanBase作为存储后端)
|
||||
# - false: 使用普通记忆模式(AsyncMemory),支持所有数据库
|
||||
# - true: 使用用户画像模式(UserMemory),自动提取用户信息,仅支持OceanBase
|
||||
enable_user_profile: false
|
||||
# 数据库提供者: oceanbase(推荐,最佳性能), seekdb(推荐,AI应用存储一体), postgres, sqlite(轻量备选)
|
||||
# 资源充足时建议使用 oceanbase 或 seekdb
|
||||
# 注意:用户画像功能(enable_user_profile: true)仅支持oceanbase
|
||||
database_provider: sqlite
|
||||
# LLM提供者: qwen(默认), zhipu(免费glm-4-flash), openai, 等
|
||||
llm_provider: qwen
|
||||
# 嵌入模型提供者: qwen(默认), zhipu, openai, 等
|
||||
embedding_provider: qwen
|
||||
# LLM配置 (使用通义千问为例)
|
||||
# 是否启用用户画像功能
|
||||
# - false: 使用普通记忆模式(AsyncMemory)
|
||||
# - true: 使用用户画像模式(UserMemory),自动提取用户信息
|
||||
# 用户画像功能支持: oceanbase、seekdb、sqlite (powermem 0.3.0+)
|
||||
enable_user_profile: true
|
||||
|
||||
# ========== 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
|
||||
# llm_base_url: https://dashscope.aliyuncs.com/api/v1 # 可选,自定义LLM服务地址
|
||||
# 嵌入模型配置 (使用通义千问为例)
|
||||
embedding_api_key: 你的嵌入模型API密钥
|
||||
embedding_model: text-embedding-v4
|
||||
embedding_openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1 # OpenAI兼容的嵌入服务地址
|
||||
# embedding_base_url: https://dashscope.aliyuncs.com/api/v1 # 可选,自定义嵌入模型服务地址
|
||||
# 或使用独立的base_url配置(优先级更高):
|
||||
# embedding_openai_base_url: https://api.openai.com/v1 # OpenAI兼容的嵌入服务地址
|
||||
# embedding_dashscope_base_url: https://dashscope.aliyuncs.com/api/v1 # 阿里云灵积嵌入服务地址
|
||||
llm:
|
||||
provider: openai # 可选: qwen, openai, zhipu 等
|
||||
config:
|
||||
api_key: 你的LLM API密钥
|
||||
model: qwen-plus
|
||||
# openai_base_url: https://api.openai.com/v1 # 可选,自定义服务地址
|
||||
|
||||
# ========== Embedding 配置 ==========
|
||||
embedder:
|
||||
provider: openai # 可选: qwen, openai 等
|
||||
config:
|
||||
api_key: 你的嵌入模型API密钥
|
||||
model: text-embedding-v4
|
||||
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
|
||||
# ========== Database 配置 ==========
|
||||
# oceanbase(推荐,最佳性能), seekdb(推荐,AI应用存储一体), postgres, sqlite(轻量备选)
|
||||
# 用户画像功能支持: oceanbase、seekdb、sqlite
|
||||
vector_store:
|
||||
provider: sqlite
|
||||
config: {} # SQLite 无需额外配置
|
||||
# OceanBase 配置示例:
|
||||
# provider: oceanbase
|
||||
# config:
|
||||
# host: 127.0.0.1
|
||||
# port: 2881
|
||||
# user: root@test
|
||||
# password: your_password
|
||||
# db_name: powermem
|
||||
# collection_name: memories # 默认值
|
||||
# embedding_model_dims: 1536 # 嵌入向量维度,必需参数
|
||||
nomem:
|
||||
# 不想使用记忆功能,可以使用nomem
|
||||
type: nomem
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
@Author: wayyoungboy
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import traceback
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
@@ -40,6 +41,7 @@ class MemoryProvider(MemoryProviderBase):
|
||||
self.use_powermem = False
|
||||
self.memory_client = None
|
||||
self.enable_user_profile = False
|
||||
self.last_profile_content = "" # Cache for user profile from UserMemory
|
||||
|
||||
try:
|
||||
# Check if user profile mode is enabled
|
||||
@@ -50,14 +52,6 @@ class MemoryProvider(MemoryProviderBase):
|
||||
llm_provider = config.get("llm_provider", "qwen")
|
||||
embedding_provider = config.get("embedding_provider", "qwen")
|
||||
|
||||
# UserMemory requires OceanBase
|
||||
if self.enable_user_profile and database_provider not in ["oceanbase"]:
|
||||
logger.bind(tag=TAG).warning(
|
||||
f"UserMemory requires OceanBase as storage backend, but got {database_provider}. "
|
||||
"Falling back to AsyncMemory mode."
|
||||
)
|
||||
self.enable_user_profile = False
|
||||
|
||||
# Build powermem configuration dict
|
||||
# PowerMem supports two config styles:
|
||||
# 1. powermem style: database, llm, embedding
|
||||
@@ -183,12 +177,22 @@ class MemoryProvider(MemoryProviderBase):
|
||||
]
|
||||
|
||||
# Add memory using PowerMem SDK
|
||||
result = await self.memory_client.add(
|
||||
result = self.memory_client.add(
|
||||
messages=messages,
|
||||
user_id=self.role_id
|
||||
)
|
||||
# Handle both sync and async returns
|
||||
if asyncio.iscoroutine(result):
|
||||
result = await result
|
||||
|
||||
logger.bind(tag=TAG).debug(f"Save memory result: {result}")
|
||||
|
||||
# Cache user profile if UserMemory mode and profile was extracted
|
||||
if self.enable_user_profile and result:
|
||||
if result.get('profile_extracted'):
|
||||
self.last_profile_content = result.get('profile_content', '')
|
||||
logger.bind(tag=TAG).debug(f"User profile extracted: {self.last_profile_content}")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
@@ -282,6 +286,9 @@ class MemoryProvider(MemoryProviderBase):
|
||||
async def get_user_profile(self) -> str:
|
||||
"""
|
||||
Get user profile from PowerMem (only available in UserMemory mode).
|
||||
|
||||
In PowerMem 0.3.0+, user profile is automatically extracted during add()
|
||||
and cached in last_profile_content.
|
||||
|
||||
Returns:
|
||||
Formatted user profile string or empty string if not available
|
||||
@@ -293,26 +300,9 @@ class MemoryProvider(MemoryProviderBase):
|
||||
logger.bind(tag=TAG).debug("User profile mode is not enabled")
|
||||
return ""
|
||||
|
||||
try:
|
||||
if not getattr(self, "role_id", None):
|
||||
return ""
|
||||
# Return cached profile content from last add() operation
|
||||
if self.last_profile_content:
|
||||
return self.last_profile_content
|
||||
|
||||
# Get user profile using UserMemory SDK
|
||||
profile = await self.memory_client.get_profile(user_id=self.role_id)
|
||||
|
||||
if not profile:
|
||||
return ""
|
||||
|
||||
# Format profile as readable string
|
||||
profile_parts = []
|
||||
for key, value in profile.items():
|
||||
if value:
|
||||
profile_parts.append(f"- {key}: {value}")
|
||||
|
||||
return "\n".join(profile_parts)
|
||||
|
||||
except Exception as e:
|
||||
logger.bind(tag=TAG).error(f"Error getting user profile: {str(e)}")
|
||||
logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}")
|
||||
return ""
|
||||
return ""
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ loguru==0.7.3
|
||||
requests==2.32.5
|
||||
cozepy==0.20.0
|
||||
mem0ai==1.0.0
|
||||
powermem>=0.2.1
|
||||
powermem>=0.3.0
|
||||
bs4==0.0.2
|
||||
modelscope==1.32.0
|
||||
sherpa_onnx==1.12.17
|
||||
|
||||
Reference in New Issue
Block a user