Day 13 / 共 20 天 · 第 3 周 工具/记忆/资源

向量记忆 vector store

给智能体"长期记忆"的核心。原理是 RAG——把文本转向量存进向量库,需要时按语义相似度检索回来。今天读 vector_store/

📍 你在整门课的位置
第1周 核心概念 第2周 Agent执行 D11-12 工具体系/市场 D13 向量记忆 D14 资源 D15 数据模型 第4周 平台/异步
L01

长期记忆 = RAG

🤔 已经有 Feed 记对话历史了,为什么还要一套向量库? 因为 Feed(短期记忆)全塞进上下文,而上下文有限——记不下"一个月前的经历""你上传的 100 页 PDF"。要让智能体"想起"海量的、跨运行的知识,就不能靠全塞,得靠"按需检索出相关的那几段"。这就是向量库要解决的事。
💡 一句话本质 长期记忆 = RAG = "把文本变成向量存起来,需要时按语义相似度捞回来"。关键词是"语义":不是关键词匹配,而是"意思相近就能召回"。VectorStore 抽象只有两个动作——add_texts(存)和 get_matching_text(查),记住这两个动词,整套记忆机制就抓住了。

向量记忆就像生活中的"图书馆索引卡":每本书(每段记忆)入馆时,管理员给它做一张按"内容主题"归档的索引卡(向量);你来查资料不用报书名,只要说"我想找讲职场报销规矩的",管理员就按"意思相近"抽出最相关的几张卡、领你到那几本书面前。今天全天沿用这个"图书馆"世界观。

Day 02 讲的 Feed 是短期记忆(对话历史,会被压缩)。今天讲长期记忆——用向量数据库存,靠语义检索。原理是 RAG(检索增强生成)

RAG 三步(一分钟懂):把文本(智能体的经历、知识)用 embedding 模型转成"向量"(一串数字,代表语义),存进向量库。② :需要时把"当前问题"也转成向量,在向量库找语义最接近的那些文本。③ :把检索到的相关文本喂进 LLM 提示,让它"想起"相关记忆再回答。关键:这是"语义检索"而非"关键词匹配"——问"苹果多少钱"能找到讲"iPhone 定价"的记忆(语义相近),即使没有"苹果"这个词。这就是智能体拥有"长期记忆"的技术。
RAG:存的时候转向量,查的时候比距离 ① 存 add_texts "报销上限3000元" embedding [0.02,-0.1,0.3,…] 向量库(语义空间) 报销上限 出差标准 今天天气(很远) 近=语义相似 ② 查 get_matching_text "报销能报多少?" 查询也转成向量,在库里找距离最近的 top_k 段 → 拿原文喂 LLM
存:文本→向量落库;查:问题→向量→找最近邻。"意思相近"= "向量距离近"。
L02

VectorStore 抽象

VectorStorevector_store/base.py:7)抽象基类,两个核心方法:

class VectorStore(ABC):
    @abstractmethod
    def add_texts(self, texts, metadatas=None, **kwargs):    # 写入记忆
    @abstractmethod
    def get_matching_text(self, query, top_k, metadata):    # 按语义检索
读法:add_texts(存)+ get_matching_text(查)就是记忆的两个核心操作。基类是抽象——Pinecone/Redis/Qdrant 各实现一遍。上层代码只依赖这个接口,不关心底层用哪个向量库(又是面向接口,Day 10 同理)。最小数据单元是 Documentdocument.py:4,只有 text_content + metadata)。
L03

工厂选向量库

VectorFactory.get_vector_storagevector_store/vector_factory.py:15)按类型返回对应实现(Pinecone/Weaviate/Qdrant/Redis)。以 Pinecone 为例(:40):

if index_name not in pinecone.list_indexes():
    sample_embedding = embedding_model.get_embedding("sample")
    pinecone.create_index(index_name, dimension=len(sample_embedding), ...)  # 自动建索引
index = pinecone.Index(index_name)
return Pinecone(index, embedding_model, 'text')
读法:工厂封装了每种库的连接、建索引细节——先用样本向量算出维度、索引不存在就自动建。你在配置里写 PINECONEREDIS,工厂负责剩下的。又见工厂模式(Day 10 的 LLM 工厂同理)——按配置选实现。
L04

写入 add_texts

Pinecone.add_textsvector_store/pinecone.py:71):

for text, id in zip(texts, ids):
    metadata[self.text_field] = text          # 把原文也存进 metadata(检索后能取回)
    vectors.append((id, self.embedding_model.get_embedding(text), metadata))  # 文本→向量
self.add_embeddings_to_vector_db({"vectors": vectors})
读法:给每段文本生成 id、用 embedding 模型转成向量、连同元数据(含原文)打包 upsert 进向量库。注意把原文也存进 metadata——因为向量库存的是向量(数字),检索时得靠 metadata 里的原文取回可读文本。Day 06 的 add_text_to_memory 就调这个把工具结果写入长期记忆。
📝 存进去的一条记录 调用 add_texts(["单次报销上限为3000元"], metadatas=[{"agent_id":7}]) 后,库里存的是:
id="uuid-abc"vector=[0.02,-0.1,…](1536 个数),metadata={"agent_id":7, "text":"单次报销上限为3000元"}
原文被塞进了 metadata 的 text 字段——否则检索命中一个向量后,你只拿到一串数字,没法还原成人能读的句子。
💡 生活类比(图书馆世界观) 把原文存进 metadata,就像图书馆的索引卡上除了编号,还直接抄了那段原文——查到卡片就能当场读到内容,不用再跑回书架找书。向量(编号)负责"被找到",metadata 里的原文负责"被读懂"。
⚠️ 小白常误以为 "向量库里存着我的文字,检索就是搜文字"。其实向量库的主体索引里只有数字(1536 维向量),文字是"搭车"放在 metadata 里的——相似度计算完全在数字之间进行,命中后才从 metadata 把原文取出来给你看。
L05

检索 get_matching_text

Pinecone.get_matching_textpinecone.py:96):

embed_text = self.embedding_model.get_embedding(query)   # 查询也转向量
res = self.index.query(embed_text, filter=filters, top_k=top_k, include_metadata=True)
search_res = self._get_search_text(res, query)   # 拼成 "Chunk0:...\nChunk1:..." 文本
documents = self._build_documents(res)
return {"documents": documents, "search_res": search_res}
读法:把查询转成向量,在库里找 top_k 个最相似的,支持 metadata 过滤。返回值同时给结构化 documents拼好的 search_res 文本——后者可直接塞进 LLM 提示做 RAG。
📝 一次语义检索的输入→输出 输入:get_matching_text(query="公司允许报多少钱", top_k=2, metadata={"agent_id": 7})
输出(注意:库里根本没有"报多少钱"这几个字,但语义命中了):
documents=[Document("单次报销上限为3000元…"), Document("出差住宿标准…")]
search_res="Chunk0: 单次报销上限为3000元…\nChunk1: 出差住宿标准…"
最后把 search_res 这段文本拼进 LLM 提示——智能体就"想起"了相关记忆。
⚡ 错误驱动:如果检索时不带 agent_id 过滤,会出什么事故? 所有智能体的记忆存在同一个索引里。设想"客服智能体"提问"客户 A 的投诉进展",检索时没过滤——命中的却是"销售智能体"记的"客户 A 的报价底线是 8 折"。记忆串门:机密信息跨智能体泄漏、答案张冠李戴。所以每次检索都带 metadata={"agent_id": …},只在"自己的记忆"里找——就像图书馆的"个人借阅记录"只能查自己的。
top_k 和 metadata 过滤 top_k:只取最相似的前 k 条(不是全返回,控量)。metadata 过滤:可以只搜"这个智能体的记忆"(按 agent_id 过滤)——不同智能体的记忆隔离。"语义相似 + 按需过滤 + 取 top-k"是 RAG 检索的标准姿势。Qdrant 实现同理,返回结构一致。
L06

Embedding 转向量

OpenAiEmbedding.get_embeddingvector_store/embedding/openai.py:4)——文本→向量这一步,用 text-embedding-ada-002

def get_embedding(self, text):
    response = openai.Embedding.create(api_key=..., input=[text], engine=self.model)
    return response['data'][0]['embedding']   # 返回一串数字(向量)
👶🧑‍🏫 对话体:一串数字凭什么能代表"意思"? 👶 小白:1536 个小数就能装下一句话的意思?听着像玄学。
🧑‍🏫 老师:可以把每个维度想成一个"语义特征打分"——比如"和金钱相关吗""是动物吗""正式还是口语"……1536 个维度一起打分,就给这句话在"语义地图"上定了个坐标。
👶 小白:那"语义相近 = 向量相近"是怎么来的?
🧑‍🏫 老师:embedding 模型在海量语料上训练时被逼着满足一条规则:"经常在相似语境出现的话,坐标要靠近"。所以"猫"和"小猫"落在隔壁,"猫"和"汽车"隔了半张地图。找相似记忆 = 在地图上找邻居,纯几何计算。
Embedding 是什么? Embedding 模型把一段文本转成一个向量(比如 1536 个浮点数)。神奇之处:语义相近的文本,向量也相近(在向量空间里距离小)。"猫"和"小猫"的向量很近,"猫"和"汽车"的很远。于是"找语义相似的文本"= "找向量距离近的"——这就是语义检索的数学原理。所有向量库操作都依赖一个 embedding 模型(OpenAI/Palm 等)把文本和查询转成向量。
注意 vector_embeddings/ 目录(易混)vector_store/ 管"运行时智能体记忆的读写";vector_embeddings/ 管"预先把知识库文档批量灌进向量库"的格式适配(接收已切块、已算 embedding 的 chunk)。两者服务不同场景。
L07

短期 vs 长期记忆

短期记忆 Feed对话历史(Day 02)
存 DB,会压缩
+ 长期记忆 向量库语义可检索
存向量库,永久
= 完整记忆
两种记忆各管什么? 短期记忆(Feed):这次运行的完整对话历史,全塞进上下文(超了就压缩,Day 08)——像"工作记忆",记得近期每一步。长期记忆(向量库):跨运行的、海量的知识/经历,用语义检索按需召回——像"长期记忆库",需要时"想起"相关的。智能体每步:Feed 给近期上下文 + 向量库检索相关长期记忆,两者一起喂 LLM。Day 06 的 add_text_to_memory 把工具结果写入长期记忆,execute_next_step(Day 06)OpenAI 模型才初始化长期记忆。短期求全、长期求相关——和人的记忆结构类似。
生活类比(图书馆世界观):短期记忆 Feed = 摊在书桌上的便签和草稿(这次任务的全部过程,随手可见,桌面满了就归拢压缩);长期记忆向量库 = 身后的图书馆(历年积累的一切,平时不占桌面,需要时按索引卡借出最相关的几本放上桌)。一句话复述:桌面记近事求全,馆藏存旧事求准,每一步都是"桌面 + 借来的几本"一起摆给 LLM 看。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • RAG 三步是什么?为什么是"语义检索"而非关键词?
  • VectorStore 的两个核心方法?为什么用抽象接口?
  • add_texts 为什么把原文也存进 metadata?
  • Embedding 是什么?为什么"语义相似 = 向量相近"?
  • 短期记忆(Feed)和长期记忆(向量库)各管什么?

✋ 动手

P=superagi/vector_store
sed -n '7,38p' base.py                       # 抽象接口
sed -n '40,52p' vector_factory.py            # 工厂 + 自动建索引
sed -n '71,101p' pinecone.py                 # add_texts + get_matching_text
sed -n '4,31p' embedding/openai.py           # get_embedding
明天预告 · Day 14资源管理 resource manager——智能体产出/使用的文件怎么存(本地/S3 双写)、灌进向量库、生成摘要。智能体的"工作区"。
← Day 12 工具市场 Day 14 · 资源管理 →