1. 为什么需要一个「私人记忆知识库」

大模型虽然强大,但有两个天然短板:知识是静态的(训练数据有截止日期)、上下文是有限的(几十 K 的 token 塞不下你全部历史资料)。你让它回忆「我上周三提过的那个客户投诉」,它只能编一个听起来合理但完全错误的故事。

解决思路就是检索增强生成(RAG):把外部知识用向量数据库存起来,提问时先检索相关记忆,再喂给大模型回答。而 Milvus 正是当下开源向量数据库中最成熟的选择之一——支持十亿级向量、多种索引类型、分区/分片、动态 Schema,以及完整的 Python SDK。

本文会带你从零搭建一个私人记忆知识库:它能把你的聊天记录、文档、笔记变成向量记忆,存进 Milvus 并建立索引,然后通过与 ChatGPT / Ollama 等大模型配合,实现「记得住、查得到、答得准」的私人 AI 记忆助手。全文包含完整可运行代码,环境为 Python 3.10+。

2. 整体架构与技术选型

在动手写代码前,先看清全局。整个系统分为四个层次:

数据源:聊天记录 / 文档 / 笔记

文本切分器 Chunker

向量化 Embedding 模型

Milvus 向量数据库

用户提问

问题向量化

Top-K 相似记忆召回

拼接 Prompt

大模型 LLM 生成答案

新对话回流存储为长期记忆

技术选型如下:

组件选型说明
向量数据库Milvus 2.4+开源、可扩展、支持多种索引
Embedding 模型OpenAI text-embedding-3-small 或本地 sentence-transformers两者都会演示
大语言模型OpenAI GPT-4o-mini / Ollama 本地模型通过统一接口调用
语言Python 3.10+pymilvus SDK
部署Docker / Docker ComposeMilvus Standalone

3. 环境准备:用 Docker 启动 Milvus

Milvus 官方推荐通过 Docker Compose 启动单机版(Milvus Standalone),它依赖 etcd 和 MinIO 分别负责元数据与对象存储。

3.1 编写 docker-compose.yml

创建项目目录 memory-kb,在其中新建 docker-compose.yml

version: "3.8"

services:
  etcd:
    container_name: milvus-etcd
    image: quay.io/coreos/etcd:v3.5.5
    environment:
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1000
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_SNAPSHOT_COUNT=50000
    volumes:
      - ./volumes/etcd:/etcd
    command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
    healthcheck:
      test: ["CMD", "etcdctl", "endpoint", "health"]
      interval: 30s
      timeout: 20s
      retries: 3

  minio:
    container_name: milvus-minio
    image: minio/minio:RELEASE.2023-03-20T20-16-18Z
    environment:
      MINIO_ACCESS_KEY: minioadmin
      MINIO_SECRET_KEY: minioadmin
    ports:
      - "9001:9001"
      - "9000:9000"
    volumes:
      - ./volumes/minio:/minio_data
    command: minio server /minio_data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

  standalone:
    container_name: milvus-standalone
    image: milvusdb/milvus:v2.4.4
    command: ["milvus", "run", "standalone"]
    security_opt:
      - seccomp:unconfined
    environment:
      ETCD_ENDPOINTS: etcd:2379
      MINIO_ADDRESS: minio:9000
    volumes:
      - ./volumes/milvus:/var/lib/milvus
    ports:
      - "19530:19530"
      - "9091:9091"
    depends_on:
      - etcd
      - minio
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
      interval: 30s
      start_period: 90s
      timeout: 20s
      retries: 3

3.2 启动并验证

cd memory-kb
docker compose up -d

等待约 1 分钟后验证服务状态:

docker ps | grep milvus
# 应看到 milvus-standalone / milvus-etcd / milvus-minio 三个容器均在运行

3.3 安装 Python 依赖

pip install pymilvus==2.4.4 openai==1.35.0 sentence-transformers==3.0.1 numpy

若使用本地大模型,还需要 ollama

pip install ollama

4. 连接 Milvus 并创建第一个集合

Milvus 2.x 使用 Collection(集合) 类比关系型数据库中的表。先写一个工具模块 milvus_client.py,封装连接逻辑:

# milvus_client.py
from pymilvus import (
    connections,
    Collection,
    CollectionSchema,
    FieldSchema,
    DataType,
    utility,
)

# 与 Milvus 建立连接
connections.connect(
    alias="default",
    host="localhost",
    port="19530",
)

def create_memory_collection(collection_name: str = "personal_memory"):
    """创建一个面向私人记忆的向量集合,带动态字段。"""
    # 检查集合是否已存在,避免重复创建报错
    if utility.has_collection(collection_name):
        print(f"[INFO] 集合 {collection_name} 已存在,直接返回。")
        return Collection(collection_name)

    # 定义字段:id、文本内容、向量、时间戳、来源、标签
    fields = [
        FieldSchema(
            name="id",
            dtype=DataType.INT64,
            is_primary=True,
            auto_id=True,
            description="主键 ID,自动生成",
        ),
        FieldSchema(
            name="content",
            dtype=DataType.VARCHAR,
            max_length=8192,
            description="原始文本记忆内容",
        ),
        FieldSchema(
            name="embedding",
            dtype=DataType.FLOAT_VECTOR,
            dim=1536,  # OpenAI text-embedding-3-small 的维度
            description="文本向量",
        ),
        FieldSchema(
            name="timestamp",
            dtype=DataType.INT64,
            description="记忆写入时间戳",
        ),
        FieldSchema(
            name="source",
            dtype=DataType.VARCHAR,
            max_length=256,
            description="数据来源,如 chat、note、doc",
        ),
        FieldSchema(
            name="tag",
            dtype=DataType.VARCHAR,
            max_length=128,
            description="自定义标签",
        ),
    ]

    schema = CollectionSchema(
        fields=fields,
        description="私人记忆知识库集合",
        enable_dynamic_field=True,  # 允许插入未预先定义的结构化字段
    )

    collection = Collection(name=collection_name, schema=schema)
    print(f"[INFO] 集合 {collection_name} 创建成功。")
    return collection

补充说明:如果使用本地 sentence-transformers 模型,embedding 维数通常为 384(all-MiniLM-L6-v2)或 768 / 1024,需要把 dim 改为对应值。下文会统一封装。

5. 向量化:把文字变成向量

Milvus 本身只存向量和标量,不负责把文字变成向量——这是 Embedding 模型的活儿。为了兼容 OpenAI 与本地模型,我们抽象一个统一的 Embedder 类:

# embedder.py
from typing import List
import numpy as np


class OpenAIBgeEmbedder:
    """基于 OpenAI 兼容接口的 Embedding 客户端。

    既支持官方 OpenAI,也兼容任何 OpenAI 协议的服务
    (如 Ollama + bge-m3、vLLM 等)。
    """

    def __init__(
        self,
        model: str = "text-embedding-3-small",
        base_url: str | None = None,
        api_key: str = "sk-your-key-here",
    ):
        from openai import OpenAI

        self.dim = 1536  # text-embedding-3-small 默认维度
        self.client = OpenAI(base_url=base_url, api_key=api_key)
        self.model = model

    def encode(self, texts: List[str]) -> np.ndarray:
        """批量把文本编码为向量矩阵。"""
        texts = [t.strip()[:8000] for t in texts]  # 防御超长输入
        resp = self.client.embeddings.create(model=self.model, input=texts)
        return np.array([item.embedding for item in resp.data], dtype=np.float32)

    def encode_query(self, text: str) -> np.ndarray:
        """编码单条查询文本。"""
        return self.encode([text])[0]


class SentenceTransformerEmbedder:
    """基于 sentence-transformers 的本地 Embedding。"""

    def __init__(self, model_name: str = "BAAI/bge-small-zh-v1.5"):
        from sentence_transformers import SentenceTransformer

        self.model = SentenceTransformer(model_name)
        self.dim = self.model.get_sentence_embedding_dimension()

    def encode(self, texts: List[str], normalize: bool = True) -> np.ndarray:
        emb = self.model.encode(
            texts,
            normalize_embeddings=normalize,
            show_progress_bar=False,
        )
        return np.array(emb, dtype=np.float32)

    def encode_query(self, text: str) -> np.ndarray:
        return self.encode([text])[0]

为什么查询向量要做归一化? 因为 Milvus 的 IP(内积)索引要求归一化向量才能等价于余弦相似度;如果直接用 L2 索引则可不归一化。我们后面统一用 COSINE 度量,sentence-transformers 默认已归一化,OpenAI 向量也建议在使用前归一化(虽然 text-embedding-3-small 的向量模长接近 1,但严谨起见统一处理)。

6. 写入记忆:聊天记录入库

现在把「私人记忆」写入 Milvus。假设你有一段与 AI 助手的历史对话,需要切成小段记忆。先实现文本切分:

# chunker.py
from typing import List


def split_text(
    text: str,
    chunk_size: int = 300,
    overlap: int = 50,
    separator: str = "\n",
) -> List[str]:
    """简单按字数切分文本,带重叠避免语义被截断。

    chunk_size: 每段最大字符数
    overlap: 相邻两段重叠的字符数
    """
    text = text.strip()
    if len(text) <= chunk_size:
        return [text]

    chunks: List[str] = []
    start = 0
    while start < len(text):
        end = min(start + chunk_size, len(text))
        chunk = text[start:end]
        chunks.append(chunk)
        start = end - overlap if end < len(text) else len(text)
    return chunks


def split_conversation(messages: List[dict]) -> List[dict]:
    """把对话消息按角色分组,生成带上下文的记忆片段。

    每条记忆会带上「说话人」前缀,方便后续检索。
    返回: 每个元素含 content / source / tag 字段。
    """
    records = []
    for msg in messages:
        role = msg.get("role", "user")
        content = msg.get("content", "")
        prefix = "用户说:" if role == "user" else "助手说:"
        full = prefix + content
        for chunk in split_text(full, chunk_size=300, overlap=40):
            records.append(
                {
                    "content": chunk,
                    "source": "chat",
                    "tag": role,  # user / assistant
                }
            )
    return records

接着把记忆批量写入 Milvus:

# ingest_memory.py
import time
from pymilvus import Collection
from milvus_client import create_memory_collection
from embedder import OpenAIBgeEmbedder
from chunker import split_conversation


def ingest_memories(
    collection: Collection,
    records: list[dict],
    embedder,
    batch_size: int = 16,
):
    """批量写入记忆:先向量化,再插库。"""
    total = len(records)
    for i in range(0, total, batch_size):
        batch = records[i : i + batch_size]
        contents = [r["content"] for r in batch]
        vectors = embedder.encode(contents)

        # 各批次时间戳统一,便于回溯
        ts = int(time.time())
        entities = [
            [r["content"] for r in batch],  # content 字段
            vectors.tolist(),                # embedding 字段
            [ts] * len(batch),               # timestamp 字段
            [r.get("source", "note") for r in batch],  # source 字段
            [r.get("tag", "") for r in batch],          # tag 字段
        ]
        collection.insert(entities)
        print(f"[INFO] 已写入 {min(i + batch_size, total)}/{total} 条记忆")


if __name__ == "__main__":
    # 示例:模拟一段对话记录
    demo_messages = [
        {"role": "user", "content": "我最近在做一个关于 RAG 的项目,技术栈是 Python 和 Milvus。"},
        {"role": "assistant", "content": "很好,Milvus 非常适合做向量检索,建议使用 COSINE 度量并配合 HNSW 索引。"},
        {"role": "user", "content": "对了,我还想加一个记忆管理模块,能让 AI 记住长期偏好。"},
        {"role": "assistant", "content": "可以设计短期滑动窗口 + 长期向量记忆的混合策略,把重要对话摘要后写回 Milvus。"},
        {"role": "user", "content": "我比较喜欢简洁的代码风格,变量命名用 snake_case。"},
        {"role": "assistant", "content": "明白,后续示例代码都会遵循这个偏好。"},
    ]

    records = split_conversation(demo_messages)
    print(f"[INFO] 共生成 {len(records)} 条记忆片段")

    collection = create_memory_collection("personal_memory")
    embedder = OpenAIBgeEmbedder()
    ingest_memories(collection, records, embedder)
    print("[INFO] 记忆写入完成。")

7. 建立索引:让检索快起来

数据写入后必须建索引才能做高效相似度检索。这里使用 HNSW 索引 + COSINE 度量,这是私人知识库场景的通用高性价比组合:

# build_index.py
from pymilvus import Collection, IndexType, utility


def build_index(collection: Collection, index_type: str = "HNSW"):
    index_params = {
        "metric_type": "COSINE",  # 余弦相似度
        "index_type": index_type,
        "params": {"M": 16, "efConstruction": 200},  # HNSW 参数
    }
    collection.create_index(field_name="embedding", index_params=index_params)
    print(f"[INFO] 为 embedding 字段创建了 {index_type} 索引(COSINE)")


def wait_index_ready(collection: Collection):
    """等待索引构建完成。"""
    utility.wait_for_index_build(collection.name, timeout=60)
    print("[INFO] 索引构建已完成并可查询。")


if __name__ == "__main__":
    from milvus_client import create_memory_collection
    collection = create_memory_collection("personal_memory")
    build_index(collection)
    wait_index_ready(collection)
    print("[INFO] 索引准备就绪,可以开始检索。")

对于百万级以内的记忆库,HNSW 的 M=16efConstruction=200 足够;数据量上亿后再考虑 IVF_PQ / DiskANN 等更省内存的索引。

8. 检索记忆:语义搜索实战

建好索引后,就能对私人记忆做语义检索。模拟用户抛出一个问题,系统从 Milvus 里找回最相关的历史记忆:

# search_memory.py
from pymilvus import Collection
from milvus_client import create_memory_collection
from embedder import OpenAIBgeEmbedder


def search_memories(
    collection: Collection,
    query: str,
    embedder,
    top_k: int = 5,
    filter_expr: str | None = None,
):
    """语义检索记忆。filter_expr 可限定来源或标签,如 'source == "chat"'。"""
    # 1. 先加载集合到内存(HNSW 执行搜索前必须 load)
    collection.load()

    # 2. 把查询文本向量化
    query_vec = embedder.encode_query(query).tolist()

    # 3. 执行检索,返回 top_k 条相似记忆 + 距离分数
    search_params = {
        "metric_type": "COSINE",
        "params": {"ef": 128},  # 查询阶段 HNSW 的搜索宽度
    }
    results = collection.search(
        data=[query_vec],
        anns_field="embedding",
        param=search_params,
        limit=top_k,
        expr=filter_expr,
        output_fields=["content", "timestamp", "source", "tag"],
    )

    hits = []
    for hit in results[0]:
        hits.append(
            {
                "id": hit.id,
                "score": hit.distance,  # COSINE 下介于 -1~1,越大越相似
                "content": hit.entity.get("content"),
                "source": hit.entity.get("source"),
                "tag": hit.entity.get("tag"),
                "timestamp": hit.entity.get("timestamp"),
            }
        )
    return hits


if __name__ == "__main__":
    collection = create_memory_collection("personal_memory")
    embedder = OpenAIBgeEmbedder()

    query = "我之前提到过什么项目?用的什么技术栈?"
    hits = search_memories(collection, query, embedder, top_k=3)

    print(f"\n问题:{query}\n")
    for i, h in enumerate(hits, 1):
        print(f"--- 命中 {i}(相似度 {h['score']:.4f})---")
        print(f"[{h['tag']}] {h['content']}")
        print(f"来源:{h['source']} | 时间戳:{h['timestamp']}\n")

输出类似:

问题:我之前提到过什么项目?用的什么技术栈?

--- 命中 1(相似度 0.8231)---
[user] 用户说:我最近在做一个关于 RAG 的项目,技术栈是 Python 和 Milvus。
来源:chat | 时间戳:1758000000

--- 命中 2(相似度 0.7812)---
[assistant] 助手说:很好,Milvus 非常适合做向量检索,建议使用 COSINE 度量并配合 HNSW 索引。
来源:chat | 时间戳:1758000000

可以观察到,语义检索不需要关键词完全匹配——「什么技术栈」能自动关联到「Python 和 Milvus」这条记忆。

9. 接入大模型:从检索到回答

有了记忆检索能力,下一步就是让大模型基于这些记忆生成回答。这是 RAG 的「生成」环节:

# rag_assistant.py
from typing import List
from openai import OpenAI
from pymilvus import Collection
from milvus_client import create_memory_collection
from embedder import OpenAIBgeEmbedder
from search_memory import search_memories


SYSTEM_PROMPT = """你是一个拥有长期记忆的私人 AI 助手。
回答用户问题前,你会先阅读【相关记忆】。
规则:
1. 如果记忆中有相关信息,优先依据记忆回答,并保持记忆中的表述风格。
2. 不要编造记忆里没有的内容;如果记忆不足,请如实说明。
3. 涉及用户偏好时,严格遵守记忆中的偏好。
4. 作答语言与用户问题保持一致。
"""


def build_prompt(question: str, memories: List[dict]) -> str:
    """把检索到的记忆拼接进 prompt。"""
    memory_lines = []
    for i, m in enumerate(memories, 1):
        memory_lines.append(
            f"{i}. ({m['tag']},相似度 {m['score']:.3f}{m['content']}"
        )
    memory_block = "\n".join(memory_lines) if memory_lines else "(无相关记忆)"

    return f"""【相关记忆】
{memory_block}

【用户问题】
{question}
"""


class MemoryRagAssistant:
    def __init__(self, collection: Collection, embedder, llm: OpenAI, model: str = "gpt-4o-mini"):
        self.collection = collection
        self.embedder = embedder
        self.llm = llm
        self.model = model

    def answer(self, question: str, top_k: int = 5) -> str:
        # 1. 检索记忆
        memories = search_memories(self.collection, question, self.embedder, top_k=top_k)

        # 2. 拼接 prompt
        user_prompt = build_prompt(question, memories)

        # 3. 调用大模型
        resp = self.llm.chat.completions.create(
            model=self.model,
            messages=[
                {"role": "system", "content": SYSTEM_PROMPT},
                {"role": "user", "content": user_prompt},
            ],
            temperature=0.3,  # 记忆问答场景降低随机性
        )
        return resp.choices[0].message.content

执行一个端到端示例:

# run_rag_demo.py
from openai import OpenAI
from milvus_client import create_memory_collection
from embedder import OpenAIBgeEmbedder
from rag_assistant import MemoryRagAssistant


if __name__ == "__main__":
    collection = create_memory_collection("personal_memory")
    embedder = OpenAIBgeEmbedder()
    llm = OpenAI(api_key="sk-your-key-here")

    assistant = MemoryRagAssistant(collection, embedder, llm)

    questions = [
        "我之前说我的代码风格偏好是什么?",
        "我在做的那个项目的技术栈是什么?",
    ]
    for q in questions:
        answer = assistant.answer(q, top_k=3)
        print(f"\n用户:{q}")
        print(f"助手:{answer}")

输出示例:

用户:我之前说我的代码风格偏好是什么?
助手:你提到更喜欢简洁的代码风格,变量命名使用 snake_case,我会在后续示例中遵循这个偏好。

用户:我在做的那个项目的技术栈是什么?
助手:你之前在做的项目是 RAG 方向,技术栈是 Python 和 Milvus。

到这里,一个基本的「记忆检索 + 大模型回答」链路已经跑通。

10. 记忆管理:让 AI 持续「记住」新内容

真正的私人记忆系统不能只读不写——每次对话结束后,新信息要回流到 Milvus,才能形成闭环。这里实现一个「对话后记忆持久化」模块:

# memory_manager.py
from typing import List
from pymilvus import Collection
import time


def persist_conversation(
    collection: Collection,
    question: str,
    answer: str,
    embedder,
    source: str = "conversation",
):
    """把一轮问答作为新记忆写入 Milvus。"""
    # 只持久化有信息量的对话
    if len(question) < 5 and len(answer) < 5:
        print("[WARN] 对话内容过短,跳过持久化。")
        return

    record = f"用户问:{question}\n助手答:{answer}"
    vec = embedder.encode([record])[0].tolist()
    ts = int(time.time())

    entities = [
        [record],      # content
        [vec],         # embedding
        [ts],          # timestamp
        [source],      # source
        ["qa"],        # tag
    ]
    collection.insert(entities)
    print(f"[INFO] 已持久化本轮对话记忆({len(record)} 字符)")


def remember_user_fact(
    collection: Collection,
    fact: str,
    embedder,
    tag: str = "fact",
):
    """专门记录一条结构化事实/偏好,带高优先 tag。"""
    if not fact.strip():
        return
    vec = embedder.encode([f"[重要事实] {fact}"])[0].tolist()
    ts = int(time.time())
    entities = [
        [f"[重要事实] {fact}"],  # content
        [vec],                    # embedding
        [ts],                     # timestamp
        ["user_profile"],         # source
        [tag],                    # tag
    ]
    collection.insert(entities)
    print(f"[INFO] 已记录用户事实:{fact}")

记忆管理最佳实践:

  • tag 区分记忆类型(fact 偏好、qa 对话、event 事件),检索时可用 filter_expr 只搜某一类。
  • 对高频重复的内容做去重:检索到相似度 > 0.95 的记忆时可选择更新而非新增。
  • 定期对长期记忆做摘要压缩,把琐碎对话合并成事实,减小库体积。

11. 完整实战项目:私人记忆助手 CLI

把前面所有模块串起来,实现一个命令行交互式的私人记忆助手。它支持三种命令:

命令作用
/ask <问题>基于记忆向大模型提问
/remember <内容>记忆一条重要事实
/quit退出并保存会话记忆
# memory_assistant_cli.py
import os
from openai import OpenAI
from milvus_client import create_memory_collection
from embedder import OpenAIBgeEmbedder, SentenceTransformerEmbedder
from search_memory import search_memories
from rag_assistant import MemoryRagAssistant
from memory_manager import persist_conversation, remember_user_fact


def print_banner():
    print("=" * 50)
    print("  私人记忆助手  |  Milvus + LLM")
    print("  /ask <问题>      基于记忆提问")
    print("  /remember <内容> 记忆一条事实")
    print("  /quit            退出")
    print("=" * 50)


def main():
    # 根据环境变量选择 Embedding;默认本地模型,无需 API Key
    use_openai = os.getenv("USE_OPENAI_EMBEDDING", "0") == "1"
    if use_openai:
        embedder = OpenAIBgeEmbedder()
    else:
        embedder = SentenceTransformerEmbedder("BAAI/bge-small-zh-v1.5")

    # 如果使用的向量维度与集合不一致,需先删掉旧集合重建
    # from pymilvus import utility
    # dims_changed = True
    # if dims_changed:
    #     utility.drop_collection("personal_memory")

    collection = create_memory_collection("personal_memory")

    # 这里需要保证集合创建时 dim 与 embedder.dim 一致:
    # 若你切换了 embedding 模型,请 drop 集合后重新创建并修改 dim。
    llm = OpenAI(
        base_url=os.getenv("OPENAI_BASE_URL", None),
        api_key=os.getenv("OPENAI_API_KEY", "sk-local"),
    )
    assistant = MemoryRagAssistant(
        collection, embedder, llm, model=os.getenv("LLM_MODEL", "gpt-4o-mini")
    )

    print_banner()
    while True:
        try:
            user_input = input("\n你:").strip()
        except (KeyboardInterrupt, EOFError):
            break

        if not user_input:
            continue
        if user_input == "/quit":
            print("再见!本次会话中你有记住的重要信息吗?")
            break

        if user_input.startswith("/remember "):
            fact = user_input[len("/remember "):].strip()
            remember_user_fact(collection, fact, embedder)
            continue

        if user_input.startswith("/ask "):
            question = user_input[len("/ask "):].strip()
        else:
            # 普通输入默认视为提问
            question = user_input

        print("\n助手思考中...")
        answer = assistant.answer(question, top_k=5)
        print(f"\n助手:{answer}")

        # 这轮问答是否有值得记住的信息?简单策略:问题长度 > 10 才持久化
        if len(question) > 10:
            persist_conversation(collection, question, answer, embedder)


if __name__ == "__main__":
    main()

运行:

python memory_assistant_cli.py

交互示例:

==================================================
  私人记忆助手  |  Milvus + LLM
  /ask <问题>      基于记忆提问
  /remember <内容> 记忆一条事实
  /quit            退出
==================================================

你:/remember 我是一名化学老师,对量子化学很感兴趣。

你:/ask 我的职业是什么?有什么兴趣?
助手思考中...
助手:你的职业是化学老师,并且对量子化学很感兴趣。

你:下周帮我准备一份关于分子轨道的科普提纲
助手思考中...

助手:好的!基于你的化学老师背景和量子化学兴趣,我会侧重用高中可理解的
类比来展开分子轨道理论……

12. 注意事项与进阶方向

12.1 性能与存储

  • 私人记忆库数据量通常不大(几万条以内),HNSW 索引完全够用,查询延迟在毫秒级。
  • 若记忆超过百万条,可考虑对 sourcetag 字段做标量索引并配合分区(Partition)提升过滤性能。
  • 向量维度越高越消耗内存:text-embedding-3-small(1536 维)比 bge-small-zh(512 维)更吃资源,但语义表达能力更强,根据硬件取舍。

12.2 隐私

私人记忆库意味着数据高度敏感。若不上云,推荐全本地方案:

  • 向量库:Milvus 本就私有化部署。
  • Embedding:bge-m3 / bge-small-zh 本地推理。
  • 大模型:Ollama 跑 qwen2.5 / llama3.1 等本地模型。

这样整条链路的数据都不出本机。

12.3 记忆策略进阶

  • 短期记忆:用滑动窗口保存最近 N 轮对话,直接进 prompt,不落库。
  • 长期记忆:通过「提取事实 → 去重 → 写回 Milvus」策略,把短期记忆中的有效信息固化为长期向量。
  • 反思式记忆:定时对新增记忆做聚类总结,生成更高层级的抽象记忆(如「用户整体偏好简洁代码」)。

13. 总结

本文从零搭建了一个完整的私人记忆知识库:

  1. Docker 部署 Milvus 单机版;
  2. 实现统一 Embedding 抽象,兼容 OpenAI 与本地模型;
  3. 完成记忆的分块、写入、索引构建与语义检索
  4. 接入大模型,打通 RAG 问答
  5. 增加记忆回流机制,让私人知识库持续增长;
  6. 最终封装成一个可直接运行的 CLI 私人记忆助手

这套代码稍加扩展就能变成浏览器插件、微信机器人或者本地笔记工具的记忆后端。向量数据库 + 大模型的组合正把「AI 懂你」从口号变成工程问题——而 Milvus 是这一工程问题中最可靠的一块拼图。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐