使用 Docker 部署 PostgreSQL + pgvector 完整步骤(映射端口 5433),适用于memu项目数据库支持!

测试代码

python 复制代码
"""
MemU 框架完整 CRUD 操作示例

这个脚本演示了如何使用 MemU 框架进行:
- Create(创建):创建新的记忆项
- Read(读取):读取和查询记忆项
- Update(更新):更新现有记忆项
- Delete(删除):删除记忆项
- Retrieve(检索):使用自然语言检索记忆

使用方法:
    python test.py
"""

import asyncio
import os
from typing import Dict, List, Any

from memu.app import MemoryService


class MemUCRUDDemo:
    """MemU CRUD 操作演示类"""

    def __init__(self, api_key: str):
        """初始化 MemoryService"""
        self.service = MemoryService(
            # LLM 配置
            llm_profiles={
                "default": {
                    "base_url": "https://api.siliconflow.cn/v1",
                    "api_key": api_key,
                    "chat_model": "Qwen/Qwen2.5-Coder-32B-Instruct",
                    "client_backend": "sdk"
                },
                "embedding": {
                    "base_url": "https://api.siliconflow.cn/v1",
                    "api_key": api_key,
                    "embed_model": "BAAI/bge-large-zh-v1.5"
                }
            },
            # PostgreSQL 数据库配置(Docker 容器,端口 5433)
            database_config={
                "metadata_store": {
                    "provider": "postgres",
                    "dsn": "postgresql://root:123456@localhost:5433/root"
                }
            },
            # 记忆类别配置
            memorize_config={
                "memory_categories": [
                    {"name": "personal_info", "description": "个人信息和基本资料"},
                    {"name": "skills", "description": "技能和专业能力"},
                    {"name": "preferences", "description": "偏好和习惯"},
                    {"name": "work_experience", "description": "工作经验和项目经历"},
                ]
            },
        )
        self.created_items = []  # 存储创建的记忆项 ID

    async def demo_create(self) -> List[str]:
        """演示创建(Create)操作"""
        print("\n" + "="*60)
        print("1. CREATE(创建)操作演示")
        print("="*60)

        # 创建多个不同类型的记忆项
        memories_to_create = [
            {
                "memory_type": "profile",
                "memory_content": "用户名叫张三,是一位30岁的软件工程师,居住在北京",
                "memory_categories": ["personal_info"],
            },
            {
                "memory_type": "skill",
                "memory_content": "精通 Python、JavaScript 和 Go 语言,有5年全栈开发经验",
                "memory_categories": ["skills"],
            },
            {
                "memory_type": "behavior",
                "memory_content": "喜欢早上编码,习惯使用 VS Code 编辑器,偏好敏捷开发方法",
                "memory_categories": ["preferences"],
            },
            {
                "memory_type": "event",
                "memory_content": "2023年主导开发了一个微服务架构的电商平台,服务百万用户",
                "memory_categories": ["work_experience"],
            },
        ]

        created_ids = []
        for idx, memory_data in enumerate(memories_to_create, 1):
            try:
                result = await self.service.create_memory_item(**memory_data)
                item_id = result.get("memory_item", {}).get("id")
                created_ids.append(item_id)

                print(f"\n[OK] 记忆项 {idx} 创建成功:")
                print(f"  - ID: {item_id}")
                print(f"  - 类型: {memory_data['memory_type']}")
                print(f"  - 内容: {memory_data['memory_content'][:50]}...")
                print(f"  - 分类: {', '.join(memory_data['memory_categories'])}")

            except Exception as e:
                print(f"\n[FAIL] 记忆项 {idx} 创建失败: {e}")

        self.created_items = created_ids
        print(f"\n总计创建了 {len(created_ids)} 个记忆项")
        return created_ids

    async def demo_read(self, item_ids: List[str]):
        """演示读取(Read)操作"""
        print("\n" + "="*60)
        print("2. READ(读取)操作演示")
        print("="*60)

        if not item_ids:
            print("\n没有可读取的记忆项")
            return

        # 读取单个记忆项(通过 list 方法)
        print("\n--- 读取单个记忆项 ---")
        try:
            first_item_id = item_ids[0]
            # MemU 没有 get_memory_item 方法,使用 list_memory_items 获取所有项
            all_items_result = await self.service.list_memory_items()
            all_items = all_items_result.get('items', [])

            # 在结果中查找指定 ID 的项
            item = next((i for i in all_items if i.get('id') == first_item_id), None)

            if item:
                print(f"\n[OK] 成功读取记忆项:")
                print(f"  - ID: {item.get('id')}")
                print(f"  - 类型: {item.get('memory_type')}")
                print(f"  - 摘要: {item.get('summary')}")
                print(f"  - 创建时间: {item.get('created_at')}")
            else:
                print(f"\n[FAIL] 未找到 ID 为 {first_item_id} 的记忆项")

        except Exception as e:
            print(f"\n[FAIL] 读取失败: {e}")

        # 列出所有记忆项
        print("\n--- 列出所有记忆项 ---")
        try:
            result = await self.service.list_memory_items()
            items = result.get('items', [])

            print(f"\n[OK] 找到 {len(items)} 个记忆项:")
            for idx, item in enumerate(items, 1):
                print(f"\n  {idx}. ID: {item.get('id')}")
                print(f"     类型: {item.get('memory_type')}")
                print(f"     摘要: {item.get('summary', '')[:60]}...")

        except Exception as e:
            print(f"\n[FAIL] 列出记忆项失败: {e}")

        # 列出所有类别
        print("\n--- 列出所有记忆类别 ---")
        try:
            result = await self.service.list_memory_categories()
            categories = result.get('categories', [])

            print(f"\n[OK] 找到 {len(categories)} 个类别:")
            for idx, category in enumerate(categories, 1):
                print(f"\n  {idx}. 名称: {category.get('name')}")
                print(f"     描述: {category.get('description', 'N/A')}")
                summary = category.get('summary', '')
                if summary:
                    print(f"     摘要: {summary[:80]}...")

        except Exception as e:
            print(f"\n[FAIL] 列出类别失败: {e}")

    async def demo_update(self, item_ids: List[str]):
        """演示更新(Update)操作"""
        print("\n" + "="*60)
        print("3. UPDATE(更新)操作演示")
        print("="*60)

        if not item_ids:
            print("\n没有可更新的记忆项")
            return

        # 更新第一个记忆项
        try:
            item_id = item_ids[0]

            # 先读取原始内容
            all_items_result = await self.service.list_memory_items()
            all_items = all_items_result.get('items', [])
            original = next((i for i in all_items if i.get('id') == item_id), None)

            if original:
                print(f"\n原始内容:")
                print(f"  - 摘要: {original.get('summary')}")

                # 更新内容(参数名是 memory_id,不是 item_id)
                updated_content = "用户名叫张三,是一位30岁的高级软件工程师,居住在北京,拥有计算机硕士学位"

                result = await self.service.update_memory_item(
                    memory_id=item_id,  # 注意:参数名是 memory_id
                    memory_content=updated_content,
                )

                print(f"\n[OK] 记忆项更新成功:")
                print(f"  - ID: {item_id}")
                print(f"  - 新内容: {updated_content}")

                # 再次读取验证更新
                await asyncio.sleep(1)  # 等待更新完成
                all_items_result = await self.service.list_memory_items()
                all_items = all_items_result.get('items', [])
                updated = next((i for i in all_items if i.get('id') == item_id), None)

                if updated:
                    print(f"\n更新后的摘要:")
                    print(f"  - {updated.get('summary')}")
            else:
                print(f"\n[FAIL] 未找到 ID 为 {item_id} 的记忆项")

        except Exception as e:
            print(f"\n[FAIL] 更新失败: {e}")

    async def demo_retrieve(self):
        """演示检索(Retrieve)操作"""
        print("\n" + "="*60)
        print("4. RETRIEVE(检索)操作演示")
        print("="*60)

        # 定义多个查询
        queries = [
            "用户的基本信息是什么?",
            "用户会哪些编程语言?",
            "用户的工作习惯和偏好",
            "用户有什么项目经验?",
        ]

        for idx, query_text in enumerate(queries, 1):
            print(f"\n--- 查询 {idx}: {query_text} ---")

            try:
                # 使用 RAG 方法检索(方法在初始化时配置)
                result = await self.service.retrieve(
                    queries=[{"role": "user", "content": {"text": query_text}}]
                )

                # 显示检索结果
                items = result.get("items", [])
                categories = result.get("categories", [])

                print(f"\n[OK] 找到 {len(items)} 个相关记忆项:")
                for item_idx, item in enumerate(items[:3], 1):  # 只显示前3个
                    print(f"\n  {item_idx}. 类型: {item.get('memory_type')}")
                    print(f"     摘要: {item.get('summary', '')[:80]}...")
                    if 'score' in item:
                        print(f"     相似度: {item.get('score', 0):.4f}")

                if categories:
                    print(f"\n  相关类别: {', '.join([c.get('name', '') for c in categories[:3]])}")

            except Exception as e:
                print(f"\n[FAIL] 检索失败: {e}")

    async def demo_delete(self, item_ids: List[str]):
        """演示删除(Delete)操作"""
        print("\n" + "="*60)
        print("5. DELETE(删除)操作演示")
        print("="*60)

        if not item_ids:
            print("\n没有可删除的记忆项")
            return

        # 删除最后一个记忆项
        if len(item_ids) > 0:
            item_id = item_ids[-1]

            try:
                # 先读取要删除的项
                all_items_result = await self.service.list_memory_items()
                all_items = all_items_result.get('items', [])
                item = next((i for i in all_items if i.get('id') == item_id), None)

                if item:
                    print(f"\n准备删除记忆项:")
                    print(f"  - ID: {item_id}")
                    print(f"  - 类型: {item.get('memory_type')}")
                    print(f"  - 摘要: {item.get('summary', '')[:60]}...")

                    # 执行删除(参数名是 memory_id,不是 item_id)
                    await self.service.delete_memory_item(memory_id=item_id)
                    print(f"\n[OK] 记忆项删除成功")

                    # 验证删除
                    await asyncio.sleep(1)  # 等待删除完成
                    all_items_result = await self.service.list_memory_items()
                    all_items = all_items_result.get('items', [])
                    deleted_item = next((i for i in all_items if i.get('id') == item_id), None)

                    if deleted_item:
                        print(f"\n[FAIL] 警告: 记忆项仍然存在")
                    else:
                        print(f"[OK] 验证: 记忆项已被删除")
                else:
                    print(f"\n[FAIL] 未找到 ID 为 {item_id} 的记忆项")

            except Exception as e:
                print(f"\n[FAIL] 删除失败: {e}")

    async def demo_memorize(self):
        """演示 memorize() 方法 - 从文件提取记忆"""
        print("\n" + "="*60)
        print("6. MEMORIZE(记忆化)操作演示")
        print("="*60)

        # 创建一个临时对话文件
        import json
        import tempfile

        conversation = {
            "messages": [
                {"role": "user", "content": "我最近在学习 Rust 语言"},
                {"role": "assistant", "content": "很好!Rust 是一门很棒的系统编程语言"},
                {"role": "user", "content": "是的,我特别喜欢它的内存安全特性"},
                {"role": "assistant", "content": "Rust 的所有权系统确实很独特"},
            ]
        }

        # 创建临时文件
        with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8') as f:
            json.dump(conversation, f, ensure_ascii=False)
            temp_file = f.name

        try:
            print(f"\n从对话文件提取记忆...")
            print(f"文件: {temp_file}")

            result = await self.service.memorize(
                resource_url=temp_file,
                modality="conversation"
            )

            items = result.get("items", [])
            categories = result.get("categories", [])

            print(f"\n[OK] 记忆化成功:")
            print(f"  - 提取了 {len(items)} 个记忆项")
            print(f"  - 更新了 {len(categories)} 个类别")

            if items:
                print(f"\n  提取的记忆项:")
                for idx, item in enumerate(items, 1):
                    print(f"\n    {idx}. 类型: {item.get('memory_type')}")
                    print(f"       摘要: {item.get('summary', '')[:60]}...")

        except Exception as e:
            print(f"\n[FAIL] 记忆化失败: {e}")
        finally:
            # 清理临时文件
            try:
                os.unlink(temp_file)
            except:
                pass


async def main():
    """主函数 - 运行所有 CRUD 演示"""
    print("\n" + "="*60)
    print("MemU 框架 CRUD 操作完整演示")
    print("="*60)

    # 创建演示实例
    api_key = "硅基流动秘钥!"
    demo = MemUCRUDDemo(api_key)

    try:
        # 1. 创建记忆项
        created_ids = await demo.demo_create()
        print(created_ids)

        # 等待一下,确保数据已保存
        await asyncio.sleep(1)

        # 2. 读取记忆项
        await demo.demo_read(created_ids)

        # 3. 更新记忆项
        await demo.demo_update(created_ids)

        # 4. 检索记忆
        await demo.demo_retrieve()

        # 5. 记忆化操作
        await demo.demo_memorize()

        # 6. 删除记忆项
        await demo.demo_delete(created_ids)

    except Exception as e:
        print(f"\n发生错误: {e}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    asyncio.run(main())

前置条件

✅ Docker Desktop 已安装并运行

✅ 端口 5433 未被占用


步骤 1:拉取并启动 PostgreSQL 容器

打开命令行,执行:

bash 复制代码
 docker run -d \
    --name memu-postgres \
    -e POSTGRES_USER=root \
    -e POSTGRES_PASSWORD=123456 \
    -e POSTGRES_DB=root \
    -p 5433:5432 \
    pgvector/pgvector:pg16

参数说明:

  • -d: 后台运行
  • --name memu-postgres: 容器名称
  • -e POSTGRES_USER=root: 数据库用户名
  • -e POSTGRES_PASSWORD=123456: 数据库密码
  • -e POSTGRES_DB=root: 默认数据库名
  • -p 5433:5432: 端口映射(宿主机5433 → 容器5432)
  • pgvector/pgvector:pg16: 使用带 pgvector 的 PostgreSQL 16 镜像

步骤 2:验证容器运行状态

bash 复制代码
 docker ps | findstr memu-postgres

预期输出:

f045f26e59ce pgvector/pgvector:pg16 ... Up X minutes 0.0.0.0:5433->5432/tcp memu-postgres


步骤 3:验证 pgvector 扩展

bash 复制代码
docker exec memu-postgres psql -U root -d root -c "CREATE EXTENSION IF NOT EXISTS vector;"

预期输出:

CREATE EXTENSION


步骤 4:安装 Python 依赖

bash 复制代码
  pip install psycopg2-binary asyncpg pgvector

预期输出:

Successfully installed asyncpg-0.31.0 psycopg2-binary-2.9.11 pgvector-0.4.2


步骤 5:配置 MemU 框架

编辑 test.py 文件,在 MemoryService 初始化中添加:

bash 复制代码
  self.service = MemoryService(
    llm_profiles={
        "default": {
            "base_url": "https://api.siliconflow.cn/v1",
            "api_key": api_key,
            "chat_model": "Qwen/Qwen2.5-Coder-32B-Instruct",
            "client_backend": "sdk"
        },
        "embedding": {
            "base_url": "https://api.siliconflow.cn/v1",
            "api_key": api_key,
            "embed_model": "BAAI/bge-large-zh-v1.5"
        }
    },
    # PostgreSQL 数据库配置
    database_config={
        "metadata_store": {
            "provider": "postgres",
            "dsn": "postgresql://root:123456@localhost:5433/root"
        }
    },
    memorize_config={
        "memory_categories": [
            {"name": "personal_info", "description": "个人信息和基本资料"},
            {"name": "skills", "description": "技能和专业能力"},
            {"name": "preferences", "description": "偏好和习惯"},
            {"name": "work_experience", "description": "工作经验和项目经历"},
        ]
    },
)

步骤 6:测试连接

运行测试脚本:

bash 复制代码
python test.py

预期输出:

bash 复制代码
============================================================
MemU 框架 CRUD 操作完整演示
============================================================

============================================================
1. CREATE(创建)操作演示
============================================================

[OK] 记忆项 1 创建成功:
  - ID: b0e80599-1910-46a5-a7cf-bea4f4a35fbc
  ...

---

步骤 7:验证数据持久化

查看数据库表:

bash 复制代码
docker exec memu-postgres psql -U root -d root -c "\dt"

预期输出:

bash 复制代码
   Schema |       Name        | Type  | Owner
  --------+-------------------+-------+-------
   public | alembic_version   | table | root
   public | category_items    | table | root
   public | memory_categories | table | root
   public | memory_items      | table | root
   public | resources         | table | root

查看记忆项数量:

bash 复制代码
  docker exec memu-postgres psql -U root -d root -c "SELECT COUNT(*) FROM memory_items;"

步骤 8:Navicat 连接配置

bash 复制代码
在 Navicat 中创建新的 PostgreSQL 连接:
连接名: MemU-PostgreSQL
主机: localhost
端口: 5433
初始数据库: root
用户名: root
密码: 123456

点击"测试连接" → "连接成功" → "确定"

常用容器管理命令

查看容器状态

bash 复制代码
docker ps | findstr memu-postgres

停止容器

bash 复制代码
docker stop memu-postgres

启动容器

bash 复制代码
 docker start memu-postgres

重启容器

bash 复制代码
 docker restart memu-postgres

查看容器日志

bash 复制代码
docker logs memu-postgres

删除容器(会丢失数据)

bash 复制代码
 docker rm -f memu-postgres

数据库操作

进入 PostgreSQL 命令行

bash 复制代码
docker exec -it memu-postgres psql -U root -d root

查看所有表

bash 复制代码
docker exec memu-postgres psql -U root -d root -c "\dt"

查看记忆项

bash 复制代码
docker exec memu-postgres psql -U root -d root -c "SELECT * FROM memory_items;"

查看记忆类别

bash 复制代码
docker exec memu-postgres psql -U root -d root -c "SELECT * FROM memory_categories;"

清空所有数据(慎用)

bash 复制代码
docker exec memu-postgres psql -U root -d root -c "TRUNCATE memory_items, category_items, memory_categories, resources CASCADE;"

数据备份与恢复

备份数据库

bash 复制代码
 docker exec memu-postgres pg_dump -U root root > memu_backup.sql

恢复数据库

bash 复制代码
docker exec -i memu-postgres psql -U root -d root < memu_backup.sql

优势总结

✅ 一键安装:一条命令完成 PostgreSQL + pgvector 安装
✅ 环境隔离:不影响现有的 PostgreSQL(端口 5432)
✅ 数据持久化:数据存储在 Docker volume 中,容器重启不丢失
✅ 易于管理:使用 Docker 命令轻松管理
✅ 跨平台:Windows/Linux/Mac 都可以使用相同的命令

故障排查

问题 1:端口被占用

检查端口占用
bash 复制代码
  netstat -ano | findstr :5433
使用其他端口(如 5434)
bash 复制代码
docker run -d --name memu-postgres ... -p 5434:5432 ...
记得修改 test.py 中的端口号

问题 2:容器无法启动

查看错误日志
bash 复制代码
docker logs memu-postgres
删除旧容器重新创建
bash 复制代码
docker rm -f memu-postgres
然后重新执行步骤 1

问题 3:连接被拒绝

确认容器正在运行
bash 复制代码
docker ps | findstr memu-postgres
确认端口映射正确
bash 复制代码
docker port memu-postgres

这就是完整的安装和配置流程!你现在已经有一个功能完整的 PostgreSQL + pgvector 数据库用于 MemU 框架了。🎉

相关推荐
@hdd1 小时前
工作节点组件详解:kubelet、kube-proxy 与容器运行时
容器·kubernetes
@hdd1 小时前
Kubernetes 网络模型:Pod 通信、Service 网络与 CNI
网络·云原生·容器·kubernetes
2401_848009722 小时前
Docker学习后续
docker·云原生·eureka
封奚泽优2 小时前
Docker常用命令(Windows 11)
运维·docker·容器
前路不黑暗@3 小时前
Java项目:Java脚手架项目的文件服务(八)
java·开发语言·spring boot·学习·spring cloud·docker·maven
only_Klein6 小时前
kubernetes-ReplicaSet控制器
容器·kubernetes
@@神农7 小时前
PostgreSQL-SQL语句的执行过程(一)
数据库·sql·postgresql
杨浦老苏7 小时前
本地优先的AI个人助手Moltis
人工智能·docker·ai·群晖
only_Klein8 小时前
Kubernetes-DaemonSet控制器
容器·kubernetes
数据知道8 小时前
PostgreSQL:如何通过progres_fdw跨库关联查询?
数据库·postgresql