部分配置key修正

This commit is contained in:
wayyoungboy
2026-01-08 23:52:19 +08:00
parent de7aedab96
commit 2bf1123647
3 changed files with 97 additions and 54 deletions
+30 -12
View File
@@ -59,7 +59,9 @@ Memory:
llm_model: qwen-plus llm_model: qwen-plus
# 嵌入模型配置 # 嵌入模型配置
embedding_api_key: 你的嵌入模型API密钥 embedding_api_key: 你的嵌入模型API密钥
embedding_model: text-embedding-v3 embedding_model: text-embedding-v4
# 可选:自定义嵌入服务地址(OpenAI兼容模式)
# embedding_openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
``` ```
### 配置参数详解 ### 配置参数详解
@@ -72,10 +74,21 @@ Memory:
| `embedding_provider` | 嵌入模型提供商 | `qwen` | `qwen`, `zhipu`, `openai`, 等 | | `embedding_provider` | 嵌入模型提供商 | `qwen` | `qwen`, `zhipu`, `openai`, 等 |
| `llm_api_key` | LLM API 密钥 | - | - | | `llm_api_key` | LLM API 密钥 | - | - |
| `llm_model` | LLM 模型名称 | - | 根据提供商选择 | | `llm_model` | LLM 模型名称 | - | 根据提供商选择 |
| `llm_base_url` | LLM API 地址(可选) | - | - | | `llm_base_url` | LLM API 地址(可选) | - | 根据provider自动选择参数名 |
| `embedding_api_key` | 嵌入模型 API 密钥 | - | - | | `embedding_api_key` | 嵌入模型 API 密钥 | - | - |
| `embedding_model` | 嵌入模型名称 | - | 根据提供商选择 | | `embedding_model` | 嵌入模型名称 | - | 根据提供商选择 |
| `embedding_base_url` | 嵌入模型 API 地址(可选) | - | - | | `embedding_base_url` | 嵌入模型 API 地址(可选) | - | 通用配置,根据provider自动选择 |
| `embedding_openai_base_url` | OpenAI兼容的嵌入服务地址(可选) | - | 优先级高于 embedding_base_url |
| `embedding_dashscope_base_url` | 阿里云灵积嵌入服务地址(可选) | - | 优先级高于 embedding_base_url |
#### Base URL 配置优先级
| 配置项 | 优先级 | 适用 Provider |
|--------|--------|---------------|
| `embedding_openai_base_url` | 最高 | openai |
| `embedding_dashscope_base_url` | 最高 | qwen |
| `embedding_base_url` | 中 | 通用,根据 provider 自动选择 |
| 默认值 | 最低 | 使用 provider 的默认服务地址 |
### 记忆模式说明 ### 记忆模式说明
@@ -102,7 +115,9 @@ Memory:
llm_api_key: sk-xxxxxxxxxxxxxxxx llm_api_key: sk-xxxxxxxxxxxxxxxx
llm_model: qwen-plus llm_model: qwen-plus
embedding_api_key: sk-xxxxxxxxxxxxxxxx embedding_api_key: sk-xxxxxxxxxxxxxxxx
embedding_model: text-embedding-v3 embedding_model: text-embedding-v4
# 可选:使用 OpenAI 兼容模式的服务地址
embedding_openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
``` ```
### 使用智谱免费 LLM(完全免费方案) ### 使用智谱免费 LLM(完全免费方案)
@@ -118,14 +133,14 @@ Memory:
powermem: powermem:
type: powermem type: powermem
database_provider: sqlite database_provider: sqlite
llm_provider: zhipu llm_provider: openai # 使用 openai 兼容模式
embedding_provider: zhipu embedding_provider: openai # 使用 openai 兼容模式
llm_api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx llm_api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx
llm_model: glm-4-flash llm_model: glm-4-flash
llm_base_url: https://open.bigmodel.cn/api/paas/v4/ llm_base_url: https://open.bigmodel.cn/api/paas/v4/
embedding_api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx embedding_api_key: xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx
embedding_model: embedding-3 embedding_model: embedding-3
embedding_base_url: https://open.bigmodel.cn/api/paas/v4/ embedding_openai_base_url: https://open.bigmodel.cn/api/paas/v4/
``` ```
### 使用 OpenAI ### 使用 OpenAI
@@ -142,7 +157,7 @@ Memory:
llm_base_url: https://api.openai.com/v1 llm_base_url: https://api.openai.com/v1
embedding_api_key: sk-xxxxxxxxxxxxxxxx embedding_api_key: sk-xxxxxxxxxxxxxxxx
embedding_model: text-embedding-3-small embedding_model: text-embedding-3-small
embedding_base_url: https://api.openai.com/v1 embedding_openai_base_url: https://api.openai.com/v1
``` ```
### 使用 OceanBase(最佳性能方案) ### 使用 OceanBase(最佳性能方案)
@@ -164,7 +179,8 @@ Memory:
llm_api_key: sk-xxxxxxxxxxxxxxxx llm_api_key: sk-xxxxxxxxxxxxxxxx
llm_model: qwen-plus llm_model: qwen-plus
embedding_api_key: sk-xxxxxxxxxxxxxxxx embedding_api_key: sk-xxxxxxxxxxxxxxxx
embedding_model: text-embedding-v3 embedding_model: text-embedding-v4
embedding_openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
# OceanBase 数据库连接配置 # OceanBase 数据库连接配置
vector_store: vector_store:
provider: oceanbase provider: oceanbase
@@ -198,10 +214,11 @@ Memory:
model: qwen-plus model: qwen-plus
# 嵌入模型配置 # 嵌入模型配置
embedder: embedder:
provider: qwen provider: openai # 使用 OpenAI 兼容模式
config: config:
api_key: sk-xxxxxxxxxxxxxxxx api_key: sk-xxxxxxxxxxxxxxxx
model: text-embedding-v3 model: text-embedding-v4
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
``` ```
## 设备记忆隔离 ## 设备记忆隔离
@@ -231,7 +248,8 @@ Memory:
llm_api_key: sk-xxxxxxxxxxxxxxxx llm_api_key: sk-xxxxxxxxxxxxxxxx
llm_model: qwen-plus llm_model: qwen-plus
embedding_api_key: sk-xxxxxxxxxxxxxxxx embedding_api_key: sk-xxxxxxxxxxxxxxxx
embedding_model: text-embedding-v3 embedding_model: text-embedding-v4
embedding_openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
# OceanBase 数据库连接配置 # OceanBase 数据库连接配置
vector_store: vector_store:
provider: oceanbase provider: oceanbase
+7 -1
View File
@@ -301,9 +301,15 @@ Memory:
# 智谱AI(免费): https://bigmodel.cn/usercenter/proj-mgmt/apikeys # 智谱AI(免费): https://bigmodel.cn/usercenter/proj-mgmt/apikeys
llm_api_key: 你的LLM API密钥 llm_api_key: 你的LLM API密钥
llm_model: qwen-plus llm_model: qwen-plus
# llm_base_url: https://dashscope.aliyuncs.com/api/v1 # 可选,自定义LLM服务地址
# 嵌入模型配置 (使用通义千问为例) # 嵌入模型配置 (使用通义千问为例)
embedding_api_key: 你的嵌入模型API密钥 embedding_api_key: 你的嵌入模型API密钥
embedding_model: text-embedding-v3 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 # 阿里云灵积嵌入服务地址
nomem: nomem:
# 不想使用记忆功能,可以使用nomem # 不想使用记忆功能,可以使用nomem
type: nomem type: nomem
@@ -21,13 +21,13 @@ TAG = __name__
class MemoryProvider(MemoryProviderBase): class MemoryProvider(MemoryProviderBase):
""" """
PowerMem memory provider implementation. PowerMem memory provider implementation.
PowerMem is an open-source agent memory component that provides PowerMem is an open-source agent memory component that provides
efficient memory management for AI agents. efficient memory management for AI agents.
Supports multiple storage backends (sqlite, oceanbase, postgres), Supports multiple storage backends (sqlite, oceanbase, postgres),
LLM providers (qwen, openai, etc.) and embedding providers. LLM providers (qwen, openai, etc.) and embedding providers.
Config options: Config options:
- enable_user_profile: bool - Enable UserMemory for user profiling (requires OceanBase) - enable_user_profile: bool - Enable UserMemory for user profiling (requires OceanBase)
- database_provider: str - Storage backend (sqlite, oceanbase, postgres) - database_provider: str - Storage backend (sqlite, oceanbase, postgres)
@@ -40,16 +40,16 @@ class MemoryProvider(MemoryProviderBase):
self.use_powermem = False self.use_powermem = False
self.memory_client = None self.memory_client = None
self.enable_user_profile = False self.enable_user_profile = False
try: try:
# Check if user profile mode is enabled # Check if user profile mode is enabled
self.enable_user_profile = config.get("enable_user_profile", False) self.enable_user_profile = config.get("enable_user_profile", False)
# Get configuration parameters # Get configuration parameters
database_provider = config.get("database_provider", "sqlite") database_provider = config.get("database_provider", "sqlite")
llm_provider = config.get("llm_provider", "qwen") llm_provider = config.get("llm_provider", "qwen")
embedding_provider = config.get("embedding_provider", "qwen") embedding_provider = config.get("embedding_provider", "qwen")
# UserMemory requires OceanBase # UserMemory requires OceanBase
if self.enable_user_profile and database_provider not in ["oceanbase"]: if self.enable_user_profile and database_provider not in ["oceanbase"]:
logger.bind(tag=TAG).warning( logger.bind(tag=TAG).warning(
@@ -57,13 +57,13 @@ class MemoryProvider(MemoryProviderBase):
"Falling back to AsyncMemory mode." "Falling back to AsyncMemory mode."
) )
self.enable_user_profile = False self.enable_user_profile = False
# Build powermem configuration dict # Build powermem configuration dict
# PowerMem supports two config styles: # PowerMem supports two config styles:
# 1. powermem style: database, llm, embedding # 1. powermem style: database, llm, embedding
# 2. mem0 style: vector_store, llm, embedder # 2. mem0 style: vector_store, llm, embedder
powermem_config = {} powermem_config = {}
# Configure vector store / database # Configure vector store / database
if "vector_store" in config: if "vector_store" in config:
powermem_config["vector_store"] = config["vector_store"] powermem_config["vector_store"] = config["vector_store"]
@@ -74,7 +74,7 @@ class MemoryProvider(MemoryProviderBase):
"provider": database_provider, "provider": database_provider,
"config": {} "config": {}
} }
# Configure LLM # Configure LLM
if "llm" in config: if "llm" in config:
powermem_config["llm"] = config["llm"] powermem_config["llm"] = config["llm"]
@@ -84,13 +84,23 @@ class MemoryProvider(MemoryProviderBase):
llm_config["api_key"] = config["llm_api_key"] llm_config["api_key"] = config["llm_api_key"]
if "llm_model" in config: if "llm_model" in config:
llm_config["model"] = config["llm_model"] llm_config["model"] = config["llm_model"]
# Handle base_url based on provider type
# - qwen provider uses dashscope_base_url
# - openai provider uses openai_base_url
if "llm_base_url" in config: if "llm_base_url" in config:
llm_config["base_url"] = config["llm_base_url"] if llm_provider == "qwen":
llm_config["dashscope_base_url"] = config["llm_base_url"]
else:
llm_config["openai_base_url"] = config["llm_base_url"]
if "openai_base_url" in config:
llm_config["openai_base_url"] = config["openai_base_url"]
if "dashscope_base_url" in config:
llm_config["dashscope_base_url"] = config["dashscope_base_url"]
powermem_config["llm"] = { powermem_config["llm"] = {
"provider": llm_provider, "provider": llm_provider,
"config": llm_config "config": llm_config
} }
# Configure embedder # Configure embedder
if "embedder" in config: if "embedder" in config:
powermem_config["embedder"] = config["embedder"] powermem_config["embedder"] = config["embedder"]
@@ -100,13 +110,25 @@ class MemoryProvider(MemoryProviderBase):
embedder_config["api_key"] = config["embedding_api_key"] embedder_config["api_key"] = config["embedding_api_key"]
if "embedding_model" in config: if "embedding_model" in config:
embedder_config["model"] = config["embedding_model"] embedder_config["model"] = config["embedding_model"]
# Handle base_url based on provider type
# - qwen provider uses dashscope_base_url
# - openai provider uses openai_base_url
# Priority: embedding_xxx_base_url > embedding_base_url > xxx_base_url
if "embedding_base_url" in config: if "embedding_base_url" in config:
embedder_config["base_url"] = config["embedding_base_url"] if embedding_provider == "qwen":
embedder_config["dashscope_base_url"] = config["embedding_base_url"]
else:
embedder_config["openai_base_url"] = config["embedding_base_url"]
# Embedding-specific base_url (higher priority)
if "embedding_openai_base_url" in config:
embedder_config["openai_base_url"] = config["embedding_openai_base_url"]
if "embedding_dashscope_base_url" in config:
embedder_config["dashscope_base_url"] = config["embedding_dashscope_base_url"]
powermem_config["embedder"] = { powermem_config["embedder"] = {
"provider": embedding_provider, "provider": embedding_provider,
"config": embedder_config "config": embedder_config
} }
# Initialize memory client based on mode # Initialize memory client based on mode
if self.enable_user_profile: if self.enable_user_profile:
from powermem import UserMemory from powermem import UserMemory
@@ -116,14 +138,14 @@ class MemoryProvider(MemoryProviderBase):
from powermem import AsyncMemory from powermem import AsyncMemory
self.memory_client = AsyncMemory(config=powermem_config) self.memory_client = AsyncMemory(config=powermem_config)
memory_mode = "AsyncMemory (普通记忆模式)" memory_mode = "AsyncMemory (普通记忆模式)"
self.use_powermem = True self.use_powermem = True
logger.bind(tag=TAG).info( logger.bind(tag=TAG).info(
f"PowerMem initialized successfully: mode={memory_mode}, " f"PowerMem initialized successfully: mode={memory_mode}, "
f"database={database_provider}, llm={llm_provider}, embedding={embedding_provider}" f"database={database_provider}, llm={llm_provider}, embedding={embedding_provider}"
) )
except ImportError as e: except ImportError as e:
logger.bind(tag=TAG).error( logger.bind(tag=TAG).error(
f"PowerMem not installed. Please install with: pip install powermem. Error: {e}" f"PowerMem not installed. Please install with: pip install powermem. Error: {e}"
@@ -137,17 +159,17 @@ class MemoryProvider(MemoryProviderBase):
async def save_memory(self, msgs): async def save_memory(self, msgs):
""" """
Save conversation messages to PowerMem. Save conversation messages to PowerMem.
Args: Args:
msgs: List of message objects with 'role' and 'content' attributes msgs: List of message objects with 'role' and 'content' attributes
Returns: Returns:
Result from PowerMem API or None if failed Result from PowerMem API or None if failed
""" """
if not self.use_powermem or self.memory_client is None: if not self.use_powermem or self.memory_client is None:
logger.bind(tag=TAG).warning("PowerMem is not available, skipping save_memory") logger.bind(tag=TAG).warning("PowerMem is not available, skipping save_memory")
return None return None
if len(msgs) < 2: if len(msgs) < 2:
logger.bind(tag=TAG).debug("Not enough messages to save (need at least 2)") logger.bind(tag=TAG).debug("Not enough messages to save (need at least 2)")
return None return None
@@ -159,16 +181,16 @@ class MemoryProvider(MemoryProviderBase):
for message in msgs for message in msgs
if message.role != "system" if message.role != "system"
] ]
# Add memory using PowerMem SDK # Add memory using PowerMem SDK
result = await self.memory_client.add( result = await self.memory_client.add(
messages=messages, messages=messages,
user_id=self.role_id user_id=self.role_id
) )
logger.bind(tag=TAG).debug(f"Save memory result: {result}") logger.bind(tag=TAG).debug(f"Save memory result: {result}")
return result return result
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"Error saving memory: {str(e)}") logger.bind(tag=TAG).error(f"Error saving memory: {str(e)}")
logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}")
@@ -177,24 +199,24 @@ class MemoryProvider(MemoryProviderBase):
async def query_memory(self, query: str) -> str: async def query_memory(self, query: str) -> str:
""" """
Query memories from PowerMem based on similarity search. Query memories from PowerMem based on similarity search.
Args: Args:
query: The search query string query: The search query string
Returns: Returns:
Formatted string of relevant memories or empty string if none found Formatted string of relevant memories or empty string if none found
""" """
if not self.use_powermem or self.memory_client is None: if not self.use_powermem or self.memory_client is None:
logger.bind(tag=TAG).warning("PowerMem is not available, skipping query_memory") logger.bind(tag=TAG).warning("PowerMem is not available, skipping query_memory")
return "" return ""
try: try:
if not getattr(self, "role_id", None): if not getattr(self, "role_id", None):
logger.bind(tag=TAG).debug("No role_id set, returning empty memory") logger.bind(tag=TAG).debug("No role_id set, returning empty memory")
return "" return ""
result_parts = [] result_parts = []
# If user profile mode is enabled, include user profile in results # If user profile mode is enabled, include user profile in results
if self.enable_user_profile: if self.enable_user_profile:
profile = await self.get_user_profile() profile = await self.get_user_profile()
@@ -207,7 +229,7 @@ class MemoryProvider(MemoryProviderBase):
user_id=self.role_id, user_id=self.role_id,
limit=30 limit=30
) )
if results and "results" in results: if results and "results" in results:
# Format each memory entry with its update time # Format each memory entry with its update time
memories = [] memories = []
@@ -218,7 +240,7 @@ class MemoryProvider(MemoryProviderBase):
timestamp = str(entry["updated_at"]) timestamp = str(entry["updated_at"])
elif "created_at" in entry and entry["created_at"]: elif "created_at" in entry and entry["created_at"]:
timestamp = str(entry["created_at"]) timestamp = str(entry["created_at"])
if timestamp: if timestamp:
try: try:
# Parse and reformat the timestamp (remove milliseconds if present) # Parse and reformat the timestamp (remove milliseconds if present)
@@ -231,7 +253,7 @@ class MemoryProvider(MemoryProviderBase):
formatted_time = timestamp formatted_time = timestamp
else: else:
formatted_time = "" formatted_time = ""
memory = entry.get("memory", "") or entry.get("content", "") memory = entry.get("memory", "") or entry.get("content", "")
if memory: if memory:
if formatted_time: if formatted_time:
@@ -251,7 +273,7 @@ class MemoryProvider(MemoryProviderBase):
final_result = "\n\n".join(result_parts) final_result = "\n\n".join(result_parts)
logger.bind(tag=TAG).debug(f"Query results: {final_result}") logger.bind(tag=TAG).debug(f"Query results: {final_result}")
return final_result return final_result
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"Error querying memory: {str(e)}") logger.bind(tag=TAG).error(f"Error querying memory: {str(e)}")
logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}")
@@ -260,40 +282,37 @@ class MemoryProvider(MemoryProviderBase):
async def get_user_profile(self) -> str: async def get_user_profile(self) -> str:
""" """
Get user profile from PowerMem (only available in UserMemory mode). Get user profile from PowerMem (only available in UserMemory mode).
Returns: Returns:
Formatted user profile string or empty string if not available Formatted user profile string or empty string if not available
""" """
if not self.use_powermem or self.memory_client is None: if not self.use_powermem or self.memory_client is None:
return "" return ""
if not self.enable_user_profile: if not self.enable_user_profile:
logger.bind(tag=TAG).debug("User profile mode is not enabled") logger.bind(tag=TAG).debug("User profile mode is not enabled")
return "" return ""
try: try:
if not getattr(self, "role_id", None): if not getattr(self, "role_id", None):
return "" return ""
# Get user profile using UserMemory SDK # Get user profile using UserMemory SDK
profile = await self.memory_client.get_profile(user_id=self.role_id) profile = await self.memory_client.get_profile(user_id=self.role_id)
if not profile: if not profile:
return "" return ""
# Format profile as readable string # Format profile as readable string
profile_parts = [] profile_parts = []
for key, value in profile.items(): for key, value in profile.items():
if value: if value:
profile_parts.append(f"- {key}: {value}") profile_parts.append(f"- {key}: {value}")
return "\n".join(profile_parts) return "\n".join(profile_parts)
except Exception as e: except Exception as e:
logger.bind(tag=TAG).error(f"Error getting user profile: {str(e)}") logger.bind(tag=TAG).error(f"Error getting user profile: {str(e)}")
logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}") logger.bind(tag=TAG).debug(f"Detailed error: {traceback.format_exc()}")
return "" return ""
# Register the memory provider instance
powermem = MemoryProvider({})