Skip to content
12 changes: 12 additions & 0 deletions main/manager-api/src/main/resources/db/changelog/202506181501.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
INSERT INTO `ai_model_config` (`id`, `model_type`, `model_code`, `model_name`, `is_default`,
`is_enabled`, `config_json`, `doc_link`, `remark`, `sort`, `creator`,
`create_date`, `updater`, `update_date`)
VALUES ('Memory_mem0_milvus', 'memory', 'mem0_milvus', 'Mem0AI本地记忆', 0, 1,
'{\"llm\": {\"config\": {\"model\": \"qwen-plus\", \"top_p\": 1, \"api_key\": \"<your-apikey>\", \"max_tokens\": 2000, \"temperature\": 0.2, \"openai_base_url\": \"https://dashscope.aliyuncs.com/compatible-mode/v1\"}, \"provider\": \"openai\"}, \"type\": \"mem0_milvus\", \"embedder\": {\"config\": {\"model\": \"text-embedding-v4\", \"api_key\": \"<your-apikey>\", \"openai_base_url\": \"https://dashscope.aliyuncs.com/compatible-mode/v1/\"}, \"provider\": \"openai\"}, \"vector_store\": {\"config\": {\"url\": \"http://127.0.0.1:19530\", \"collection_name\": \"mem0_collection\", \"embedding_model_dims\": 1024}, \"provider\": \"milvus\"}}',
'https://app.mem0.ai/dashboard/get-started', 'Mem0AI记忆配置说明:\n1. 配置llm/embedder/vector_store\n', 3, NULL,
NULL, 1, '2025-06-18 17:02:49');
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`,
`creator`, `create_date`, `updater`, `update_date`)
VALUES ('SYSTEM_Memory_mem0_milvus', 'Memory', 'mem0_milvus', 'mem0ai本地记忆',
'[{\"key\": \"llm\", \"type\": \"dict\", \"label\": \"llm\", \"default\": \"\", \"editing\": false, \"selected\": false}, {\"key\": \"embedder\", \"type\": \"dict\", \"label\": \"embedder\", \"default\": \"\", \"editing\": false, \"selected\": false}, {\"key\": \"vector_store\", \"type\": \"dict\", \"label\": \"vector_store\", \"default\": \"\", \"editing\": false, \"selected\": false}]',
0, 1, '2025-06-18 14:00:57', 1, '2025-06-18 14:00:57');
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,13 @@ databaseChangeLog:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202506191643.sql
- changeSet:
id: 202506181501
author: chan
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202506181501.sql
- changeSet:
id: 202506251620
author: Tink
Expand All @@ -239,4 +246,4 @@ databaseChangeLog:
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202506261637.sql
path: classpath:db/changelog/202506261637.sql
28 changes: 27 additions & 1 deletion main/xiaozhi-server/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,33 @@ Memory:
# 如果这里不填,则会默认使用selected_module.LLM的模型作为意图识别的思考模型
# 如果你的不想使用selected_module.LLM记忆存储,这里最好使用独立的LLM作为意图识别,例如使用免费的ChatGLMLLM
llm: ChatGLMLLM

mem0_milvus:
# 本地 mem0+milvus 记忆功能
type: mem0_milvus
llm:
provider: openai
config:
model: qwen-plus
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
# https://bailian.console.aliyun.com/?tab=model#/api-key
api_key: 阿里云百炼平台api_key
temperature: 0.2
max_tokens: 2000
top_p: 1.0
embedder:
provider: openai
config:
model: text-embedding-v4
api_key: 阿里云百炼平台api_key
openai_base_url: https://dashscope.aliyuncs.com/compatible-mode/v1/
vector_store:
#可以使用容器本地部署 milvus 向量数据库
provider: milvus
config:
# 使用你的 milvus 向量数据库地址
url: http://127.0.0.1:19530
collection_name: mem0_collection
embedding_model_dims: 1024
ASR:
FunASR:
type: fun_local
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from ..base import MemoryProviderBase, logger
from mem0 import Memory

TAG = __name__


class MemoryProvider(MemoryProviderBase):
def __init__(self, config, summary_memory=None):
super().__init__(config)

try:
self.client = Memory.from_config(self.config)
self.use_mem0local = True
logger.bind(tag=TAG).info("成功使用Mem0_milvus服务")
except Exception as e:
logger.bind(tag=TAG).error(f"Mem0配置错误: {str(e)}")
self.use_mem0local = False

async def save_memory(self, msgs):
if not self.use_mem0local:
return None
if len(msgs) < 2:
return None

try:
# Format the content as a message list for mem0
messages = [
{"role": message.role, "content": message.content}
for message in msgs if message.role != "system"
]
result = self.client.add(messages, user_id=self.role_id)
logger.bind(tag=TAG).info(f"Save memory result: {result}")
except Exception as e:
print(e)
logger.bind(tag=TAG).error(f"保存记忆失败: {str(e)}")
return None

async def query_memory(self, query: str) -> str:
if not self.use_mem0local:
return ""
try:
results = self.client.search(
query,
user_id=self.role_id
)
logger.bind(tag=TAG).info(f"get memory result: {results}")
if not results or 'results' not in results:
return ""

# Format each memory entry with its update time up to minutes
memories = []
for entry in results['results']:
timestamp = entry.get('updated_at', '') or entry.get('created_at', '')
if timestamp:
try:
# Parse and reformat the timestamp
dt = timestamp.split('.')[0] # Remove milliseconds
formatted_time = dt.replace('T', ' ')
except:
formatted_time = timestamp
memory = entry.get('memory', '')
if timestamp and memory:
# Store tuple of (timestamp, formatted_string) for sorting
memories.append((timestamp, f"[{formatted_time}] {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:
if "collection not found" in e:
return ""
else:
logger.bind(tag=TAG).error(f"查询记忆失败: {str(e)}")
return ""
3 changes: 2 additions & 1 deletion main/xiaozhi-server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ markitdown==0.1.1
mcp-proxy==0.8.0
PyJWT==2.8.0
psutil==7.0.0
portalocker==2.10.1
portalocker==2.10.1
pymilvus==2.5.11