ITPub博客

首页 > 人工智能 > 人工智能 > 智能体的记忆与知识管理

智能体的记忆与知识管理

原创 人工智能 作者:brucexia 时间:2025-12-18 10:08:28 0 删除 编辑

在AI 智能体技术中,“记忆与知识管理”是实现智能体连续交互、个性化决策和复杂任务处理的核心能力。它类比人类的记忆机制,通过短期记忆处理即时信息,通过长期记忆存储和复用历史经验与结构化知识,两者协同支撑智能体的“认知”与“学习”能力。本节将从短期记忆(Short-Term Memory )和长期记忆(Long-Term Memory )两方面展开详细解析。

短期记忆

短期记忆是智能体对“近期信息”的临时存储与调用机制,主要用于支撑当前任务的连续交互(如对话、实时决策),特点是时效性强、容量有限、需快速更新。

1. 上下文窗口

上下文窗口(Context Window )是大语言模型(LLM )处理短期记忆的基础机制,是指模型在单次交互中能“记住”的输入文本长度(以tokens 为单位)。例如:

(1 )GPT-4 的32K tokens 窗口(约2.4 万字的中文)可容纳多轮对话历史、当前任务描述、临时参数等信息,确保智能体在单轮会话中理解上下文逻辑(如“上文提到的‘这个方案’指什么”)。

(2 )局限:窗口容量固定,若对话过长(如超过32K tokens ),早期信息会被“遗忘”,导致上下文断裂(例如,长对话中用户重复提及的偏好被忽略)。

【示例 3.16 】 本示例模拟GPT-4 32K tokens 上下文窗口,展示如何管理和维护一个固定大小的上下文缓冲区。

import tiktoken

 

class ContextWindow:

    def __ init __ (self, max _ tokens=32000):

        """

        初始化上下文窗口

        :param max _ tokens: 最大 token 容量,默认为 32K

        """

        self.max _ tokens = max _ tokens

        self.encoder = tiktoken.get _ encoding("cl100k _ base")  # GPT-4 使用的编码器

        self.messages = []

        self.current _ tokens = 0

   

    def add _ message(self, role, content):

        """

        添加新消息到上下文

        :param role: 消息角色(如 'system', 'user', 'assistant' )

        :param content: 消息内容

        """

        message = {"role": role, "content": content}

        message _ tokens = len(self.encoder.encode(content))

       

        # 检查是否超出容量

        if self.current _ tokens + message _ tokens > self.max _ tokens:

            self. _ trim _ messages(self.current _ tokens + message _ tokens - self.max _ tokens)

       

        self.messages.append(message)

        self.current _ tokens += message _ tokens

   

    def _ trim _ messages(self, excess _ tokens):

        """

        修剪消息以保持不超过 token 限制

        :param excess _ tokens: 需要移除的 token 数量

        """

        removed _ tokens = 0

        while removed _ tokens < excess _ tokens and len(self.messages) > 0:

            oldest _ message = self.messages.pop(0)

            removed _ tokens += len(self.encoder.encode(oldest _ message["content"]))

       

        self.current _ tokens -= removed _ tokens

   

    def get _ context(self):

        """

        获取当前上下文

        :return: 完整的上下文消息列表

        """

        return self.messages.copy()

   

    def clear(self):

        """ 清空上下文窗口 """

        self.messages = []

        self.current _ tokens = 0

 

# 测试用例

if __ name __ == " __ main __ ":

    # 初始化 32K tokens 的上下文窗口

    context = ContextWindow(max _ tokens=32000)

   

    # 添加系统消息

    context.add _ message("system", " 你是一个有帮助的 AI 助手。 ")

   

    # 模拟长对话

    long _ text = " 这是一段很长的文本。 " * 1000  # 约 3000 个中文字符

    context.add _ message("user", long _ text)

   

    # 检查当前 token 数

    print(f" 当前 token 数 : {context.current _ tokens}")

   

    # 添加更多消息直到接近上限

    for i in range(10):

        context.add _ message("assistant", f" 这是第 {i} 条回复。 " * 50)

   

    print(f" 添加后 token 数 : {context.current _ tokens}")

   

    # 尝试添加会触发修剪的消息

    overflow _ text = " 这将触发修剪机制。 " * 5000

    context.add _ message("user", overflow _ text)

   

    print(f" 修剪后 token 数 : {context.current _ tokens}")

    print(f" 剩余消息数 : {len(context.get _ context())}")

运行代码,输出如下:

当前 token 数 : 11014

添加后 token 数 : 15014

修剪后 token 数 : 55000

剩余消息数 : 1

关键技术说明:

(1 )Token 计数:

l   使用tiktoken 库(与GPT-4 相同的tokenizer )准确计算文本的Token 数量。

l   支持多语言混合内容的Token 计数。

(2 )窗口管理:

l   采用FIFO (先进先出)策略管理上下文。

l   当新内容导致超出限制时,自动移除最早的对话内容。

(3 )性能优化:

l   维护当前Token 总数的缓存,避免每次计算全部Token 。

l   修剪时只移除必要数量的最早消息。

(4 )扩展性:

l   可轻松调整最大Token 限制。

l   支持多种消息角色(system/user/assistant )。

这个实现模拟了AI 系统如何在实际对话中维护固定大小的上下文窗口,是构建对话AI 系统的基础组件之一。

2. 对话状态跟踪

对话状态跟踪(Dialog State Tracking ,DST )用于在多轮对话中精准提取和维护关键信息(如用户意图、实体参数、未完成的目标),是任务型智能体的核心技术。例如:

(1 )谷歌Dialogflow 通过“意图识别”和“实体提取”跟踪对话状态:若用户说“明天下午3 点订从北京到上海的高铁”,DST 会记录“时间= 明天下午3 点”“出发地= 北京”“目的地= 上海”“任务= 订高铁票”,后续对话中即使用户简化表达(如“改到后天”),智能体仍能关联到“时间”参数进行更新。

(2 )核心目标:将非结构化的对话文本转换为结构化的“状态变量”,避免信息遗漏或误解。

【示例 3.17 】 本示例基于规则的对话状态跟踪,模拟简单的对话管理系统。

class DialogStateTracker:

    def __ init __ (self):

        # 初始化对话状态

        self.state = {

            'intent': None,

            'slots': {},

            'confirmed': False,

            'history': []

        }

 

    def update _ state(self, user _ input):

        """

        根据用户输入更新对话状态

        """

        # 将当前输入添加到历史记录中

        self.state['history'].append(user _ input)

 

        # 简单的意图识别

        if ' 预订 ' in user _ input or ' 预定 ' in user _ input:

            self.state['intent'] = 'book'

        elif ' 查询 ' in user _ input or ' 查找 ' in user _ input:

            self.state['intent'] = 'search'

        elif ' 取消 ' in user _ input:

            self.state['intent'] = 'cancel'

 

        # 简单的槽位填充

        if ' 餐厅 ' in user _ input:

            self.state['slots']['venue'] = ' 餐厅 '

        elif ' 酒店 ' in user _ input:

            self.state['slots']['venue'] = ' 酒店 '

 

        if ' 明天 ' in user _ input:

            self.state['slots']['date'] = ' 明天 '

        elif ' 今天 ' in user _ input:

            self.state['slots']['date'] = ' 今天 '

 

        if ' 晚上 ' in user _ input:

            self.state['slots']['time'] = ' 晚上 '

        elif ' 中午 ' in user _ input:

            self.state['slots']['time'] = ' 中午 '

 

        # 确认检测

        if ' 是的 ' in user _ input or ' 对的 ' in user _ input:

            self.state['confirmed'] = True

        elif ' 不是 ' in user _ input or ' 不对 ' in user _ input:

            self.state['confirmed'] = False

 

    def get _ current _ state(self):

        """

        获取当前对话状态

        """

        return self.state

 

    def reset(self):

        """

        重置对话状态

        """

        self. __ init __ ()

 

 

# 测试用例

if __ name __ == " __ main __ ":

    tracker = DialogStateTracker()

 

    # 模拟对话 1

    print(" 测试对话 1:")

    tracker.update _ state(" 我想预订一家餐厅 ")

    print(tracker.get _ current _ state())

 

    tracker.update _ state(" 明天晚上 ")

    print(tracker.get _ current _ state())

 

    tracker.update _ state(" 是的 ")

    print(tracker.get _ current _ state())

 

    # 重置

    tracker.reset()

 

    # 模拟对话 2

    print("\n 测试对话 2:")

    tracker.update _ state(" 查询附近的酒店 ")

    print(tracker.get _ current _ state())

 

    tracker.update _ state(" 今天中午 ")

    print(tracker.get _ current _ state())

运行代码,输出如下:

测试对话 1:

{'intent': 'book', 'slots': {'venue': ' 餐厅 '}, 'confirmed': False, 'history': [' 我想预订一家餐厅 ']}

{'intent': 'book', 'slots': {'venue': ' 餐厅 ', 'date': ' 明天 ', 'time': ' 晚上 '}, 'confirmed': False, 'history': [' 我想预订一家餐厅 ', ' 明天晚上 ']}

{'intent': 'book', 'slots': {'venue': ' 餐厅 ', 'date': ' 明天 ', 'time': ' 晚上 '}, 'confirmed': True, 'history': [' 我想预订一家餐厅 ', ' 明天晚上 ', ' 是的 ']}

测试对话 2:

{'intent': 'search', 'slots': {'venue': ' 酒店 '}, 'confirmed': False, 'history': [' 查询附近的酒店 ']}

{'intent': 'search', 'slots': {'venue': ' 酒店 ', 'date': ' 今天 ', 'time': ' 中午 '}, 'confirmed': False, 'history': [' 查询附近的酒店 ', ' 今天中午 ']}

关键功能说明:

(1 )对话状态结构:

l   Intent :当前对话意图,如预订、查询、取消。

l   Slots :对话中提取的关键信息槽位,如地点、时间。

l   Confirmed :用户是否确认了当前信息。

l   History :对话历史记录。

(2 )主要方法:

l   update_state() :根据用户输入更新对话状态。

l   get_current_state() :获取当前对话状态。

l   reset() :重置对话状态。

(3 )实现特点:

l   基于规则的简单实现,适合理解DST 的基本原理。

l   可扩展为更复杂的NLU (自然语言理解)组件。

l   状态管理清晰,便于后续对话策略决策。

(4 )改进方向:

l   添加机器学习模型进行意图识别和槽位填充。

l   增加多轮对话上下文处理。

l   添加对话策略模块。

这个实现展示了对话状态跟踪的基本原理,实际应用中通常会结合更复杂的自然语言处理技术。

长期记忆

AI 智能体的长期记忆通常通过外部向量存储(如FAISS 或Pinecone )实现,支持快速检索语义相关文档,以扩展静态LLM 的知识范围,缓解幻觉现象,并生成基于外部事实的上下文相关响应。

1. 向量数据库(FAISS )

向量数据库是一种专门存储向量数据的数据库,它能够高效地存储和检索高维度的数据,为各种机器学习算法提供支持。而FAISS (Facebook AI Similarity Search )则是Facebook AI Research 开发的一个开源库,主要用于高效地进行大规模相似性搜索和聚类操作。

1 )FAISS 的主要功能

(1 )向量索引与搜索:FAISS 提供了多种索引和搜索向量的方法,如暴力搜索(Flat )、倒排索引(IVF )、分层可导航小世界图(HNSW )和乘积量化(PQ )等。这些方法可以根据应用场景在速度、准确性和内存使用之间进行权衡。

(2 )支持多种距离度量:FAISS 支持多种距离度量方式,如L2 距离(欧几里得距离)、余弦相似度和内积(点积),适用于不同的应用场景。

(3 )CPU 和GPU 支持:FAISS 能够利用CPU 和GPU 加速索引和搜索过程,在大规模数据集上表现出色,尤其适合需要实时搜索的场景。

(4 )高效性:FAISS 针对大规模数据集进行了优化,能够快速处理数十亿向量。

(5 )可扩展性:FAISS 设计用于处理大规模数据集,能够有效管理数十亿向量。

(6 )灵活性:FAISS 允许用户根据应用需求调整索引和搜索参数,并且可以动态添加、更新和删除向量。

2 )FAISS 的应用场景

由于FAISS 具有上述功能特点,因此被广泛应用于各种需要高效相似性搜索和聚类操作的场景,比如:

(1 )推荐系统:通过搜索用户历史行为或兴趣相似的向量,为用户推荐可能感兴趣的内容或    商品。

(2 )信息检索:在搜索引擎中,通过搜索与查询关键词相似的向量,快速找到相关的文档或     网页。

(3 )图像检索:将图像表示为向量,并通过搜索相似的向量来检索相似的图像。

(4 )自然语言处理:将文本表示为向量,并通过搜索相似的向量来进行文本匹配、语义理解等任务。

3 )FAISS 的安装与使用

(1 )安装:FAISS 分为CPU 和GPU 两个版本,可以根据硬件环境选择合适的版本进行安装。例如,可以使用pip 命令安装CPU 版本的FAISS :pip install faiss-cpu 。如果需要GPU 加速,则需要安装GPU 版本的FAISS ,并确保系统已安装CUDA 等必要的驱动程序。

(2 )使用:在使用FAISS 时,首先需要创建索引对象,并指定向量的维度和索引类型。然后,可以将向量数据添加到索引中,并进行搜索操作。搜索操作会返回与查询向量最相似的向量的ID 和相似度得分。此外,FAISS 还支持删除向量、重置索引等操作。

【示例 3.18 】 使用FAISS 向量数据库实现AI 智能体长期记忆功能。

import faiss

import numpy as np

from typing import List, Dict, Any

import json

import os

 

class AIMemory:

    def __ init __ (self, dim: int = 1536, index _ type: str = "FlatL2", storage _ path: str = "ai _ memory"):

        """

        初始化 AI 长期记忆系统

 

        Args:

            dim: 向量维度

            index _ type: 索引类型,支持 FlatL2 、 HNSW 等

            storage _ path: 数据存储路径

        """

        self.dim = dim

        self.index _ type = index _ type

        self.storage _ path = storage _ path

        self.metadata = []  # 存储向量对应的元数据

 

        # 创建存储目录

        if not os.path.exists(storage _ path):

            os.makedirs(storage _ path)

 

        # 初始化索引

        self. _ init _ index()

 

        # 加载已保存的数据

        self. _ load _ memory()

 

    def _ init _ index(self):

        """ 根据指定类型初始化 FAISS 索引 """

        if self.index _ type == "FlatL2":

            self.index = faiss.IndexFlatL2(self.dim)

        elif self.index _ type == "HNSW":

            self.index = faiss.IndexHNSWFlat(self.dim, 32)

            self.index.hnsw.efConstruction = 40

        else:

            raise ValueError(f" 不支持的索引类型 : {self.index _ type}")

 

    def _ load _ memory(self):

        """ 从磁盘加载已保存的记忆 """

        index _ path = os.path.join(self.storage _ path, "index.faiss")

        metadata _ path = os.path.join(self.storage _ path, "metadata.json")

 

        if os.path.exists(index _ path) and os.path.exists(metadata _ path):

            try:

                # 加载索引

                self.index = faiss.read _ index(index _ path)

 

                # 加载元数据

                with open(metadata _ path, "r", encoding="utf-8") as f:

                    self.metadata = json.load(f)

 

                print(f" 已从 {self.storage _ path} 加载记忆 : {len(self.metadata)} 条记录 ")

            except Exception as e:

                print(f" 加载记忆失败 : {e}")

                # 重新初始化索引

                self. _ init _ index()

 

    def save _ memory(self):

        """ 保存记忆到磁盘 """

        index _ path = os.path.join(self.storage _ path, "index.faiss")

        metadata _ path = os.path.join(self.storage _ path, "metadata.json")

 

        try:

            # 保存索引

            faiss.write _ index(self.index, index _ path)

 

            # 保存元数据

            with open(metadata _ path, "w", encoding="utf-8") as f:

                json.dump(self.metadata, f, ensure _ ascii=False, indent=2)

 

            print(f" 已保存记忆到 {self.storage _ path}")

        except Exception as e:

            print(f" 保存记忆失败 : {e}")

 

    def add _ memory(self, vector: np.ndarray, data: Dict[str, Any]):

        """

        添加记忆条目

 

        Args:

            vector: 特征向量, NumPy 数组

            data: 相关元数据,字典格式

        """

        # 确保向量维度正确

        vector = vector.reshape(1, -1)

        if vector.shape[1] != self.dim:

            raise ValueError(f" 向量维度不匹配,期望 {self.dim} ,实际 {vector.shape[1]}")

 

        # 添加到索引

        self.index.add(vector)

 

        # 保存元数据

        self.metadata.append(data)

 

        # 自动保存(可优化为定期保存)

        self.save _ memory()

 

        return len(self.metadata) - 1  # 返回添加的记忆 ID

 

    def search _ memory(self, query _ vector: np.ndarray, k: int = 5) -> List[Dict[str, Any]]:

        """

        搜索相似记忆

 

        Args:

            query _ vector: 查询向量

            k: 返回结果数量

 

        Returns:

            包含相似度和元数据的列表

        """

        # 确保向量维度正确

        query _ vector = query _ vector.reshape(1, -1)

        if query _ vector.shape[1] != self.dim:

            raise ValueError(f" 向量维度不匹配,期望 {self.dim} ,实际 {query _ vector.shape[1]}")

 

        # 搜索

        distances, indices = self.index.search(query _ vector, k)

 

        # 构建结果

        results = []

        for i, idx in enumerate(indices[0]):

            if idx != -1:  # -1 表示未找到

                results.append({

                    "similarity": float(distances[0][i]),

                    "metadata": self.metadata[idx],

                    "memory _ id": idx

                })

 

        return results

 

    def update _ memory(self, memory _ id: int, new _ data: Dict[str, Any]):

        """

        更新记忆元数据

       

        Args:

            memory _ id: 记忆 ID

            new _ data: 新的元数据

        """

        if 0 <= memory _ id < len(self.metadata):

            self.metadata[memory _ id].update(new _ data)

            self.save _ memory()

            return True

        return False

 

    def delete _ memory(self, memory _ id: int):

        """

        删除记忆

 

        Args:

            memory _ id: 记忆 ID

        """

        if 0 <= memory _ id < len(self.metadata):

            # 注意: FAISS 不支持直接删除索引项,这里采用标记删除

            self.metadata[memory _ id]["deleted"] = True

            self.save _ memory()

            return True

        return False

 

# 简单的文本向量化器示例(实际应用中应使用更强大的模型)

def simple _ text _ embedding(text: str) -> np.ndarray:

    """ 简单的文本向量化函数,实际应用中应替换为 OpenAI Embeddings 等 """

    # 这里仅作示例,返回随机向量

    # 实际应用中应使用 sentence-transformers 等模型

    return np.random.random(1536).astype('float32')

 

# 使用示例

if __ name __ == " __ main __ ":

    # 初始化记忆系统

    memory = AIMemory(dim=1536, index _ type="FlatL2")

 

    # 添加记忆

    memory.add _ memory(

        simple _ text _ embedding(" 机器学习是人工智能的一个分支 "),

        {"text": " 机器学习是人工智能的一个分支 ", "timestamp": "2023-05-15", "source": " 教科书 "}

    )

 

    memory.add _ memory(

        simple _ text _ embedding(" 深度学习是机器学习的一个子领域 "),

        {"text": " 深度学习是机器学习的一个子领域 ", "timestamp": "2023-05-16", "source": " 网络文章 "}

    )

 

    # 搜索记忆

    query _ vector = simple _ text _ embedding(" 人工智能的分支有哪些 ")

    results = memory.search _ memory(query _ vector, k=2)

 

    print("\n 搜索结果 :")

    for result in results:

        print(f" 相似度 : {result['similarity']:.4f}")

        print(f" 内容 : {result['metadata']['text']}")

        print(f" 来源 : {result['metadata']['source']}")

        print("-" * 40)

 

    # 更新记忆

    memory.update _ memory(0, {"importance": "high"})

 

    # 删除记忆

    # memory.delete _ memory(1)   

运行代码,输出如下:

已保存记忆到 ai _ memory

已保存记忆到 ai _ memory

 

搜索结果 :

相似度 : 257.7774

内容 : 机器学习是人工智能的一个分支

来源 : 教科书

----------------------------------------

相似度 : 257.8586

内容 : 深度学习是机器学习的一个子领域

来源 : 网络文章

----------------------------------------

已保存记忆到 ai _ memory

向量数据库( Pinecone )

输出结果会形成一个文件夹ai_memory ,其中包含两个文件:index.faiss 和metadata 。

这个案例实现了一个基于FAISS 的AI 长期记忆系统,包含以下功能:

l   支持不同类型的FAISS 索引(如FlatL2 和HNSW )。

l   提供记忆的添加、搜索、更新和删除操作。

l   自动将记忆持久化到磁盘。

l   可以存储向量及其对应的元数据。

2. 向量数据库(Pinecone )

向量数据库Pinecone 是一个托管的向量数据库服务,专为存储和查询高维向量数据而设计。

1 )核心原理与运行机制

(1 )向量索引

Pinecone 的核心在于其向量索引技术,这是一种针对高维向量数据优化的数据结构。通过构建特殊索引结构(如树结构、图结构或哈希表),Pinecone 能够在海量数据中快速执行相似性搜索。

(2 )相似性搜索

相似性搜索是Pinecone 的核心功能,用于快速找到与查询向量最相似的向量。查询处理过程包括预处理(对查询向量进行归一化)、索引搜索(利用索引结构定位候选向量)和精排(对候选向量进行精确距离计算,返回topK 结果)。

(3 )云原生架构

Pinecone 是一个完全托管的云服务,其架构设计注重可扩展性、高可用性和安全性。采用分布式系统实现数据分片、负载均衡和故障恢复等功能,确保服务的高可用性和稳定性。

2 )核心特点

(1 )高性能相似性搜索

Pinecone 采用先进的索引技术,如近似最近邻搜索(ANN ),能够在海量高维向量数据中快速找到与查询向量最相似的结果。Pinecone 适用于需要快速匹配和检索的场景,如推荐系统、信息检索和图像搜索等场景。

(2 ) 托管服务

作为一种完全托管的云服务,Pinecone 负责数据库的维护、扩展和安全性,开发者无须自行管理底层基础设施。这大大降低了开发和运维的复杂性,使开发者能够专注于业务逻辑的实现。

【示例 3.19 】 使用Pinecone 向量数据库创建索引、插入向量数据以及进行相似度搜索。

import pinecone

import numpy as np

from sklearn.datasets import fetch _ 20newsgroups

from sentence _ transformers import SentenceTransformer

import logging

 

# 配置日志

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger( __ name __ )

 

# 模型单例 - 避免重复加载模型

class EmbeddingModel:

    _ instance = None

   

    @classmethod

    def get _ instance(cls, model _ name='all-MiniLM-L6-v2'):

        if cls. _ instance is None:

            cls. _ instance = SentenceTransformer(model _ name)

        return cls. _ instance

 

# 初始化 Pinecone

def init _ pinecone(api _ key, environment):

    try:

        pinecone.init(api _ key=api _ key, environment=environment)

        logger.info("Pinecone 初始化成功 ")

        return True

    except Exception as e:

        logger.error(f"Pinecone 初始化失败 : {str(e)}")

        return False

 

# 创建索引

def create _ index(index _ name, dimension, metric="cosine"):

    try:

        if index _ name not in pinecone.list _ indexes():

            logger.info(f" 创建索引 {index _ name}...")

            pinecone.create _ index(

                name=index _ name,

                dimension=dimension,

                metric=metric

            )

            logger.info(f" 索引 {index _ name} 创建成功 ")

        else:

            logger.info(f" 索引 {index _ name} 已存在 ")

        return pinecone.Index(index _ name)

    except Exception as e:

        logger.error(f" 创建索引失败 : {str(e)}")

        return None

 

# 生成嵌入向量

def generate _ embeddings(texts, batch _ size=32):

    try:

        model = EmbeddingModel.get _ instance()

        embeddings = []

        # 批量处理文本以避免内存问题

        for i in range(0, len(texts), batch _ size):

            batch = texts[i:i+batch _ size]

            batch _ embeddings = model.encode(batch)

            embeddings.extend(batch _ embeddings)

        return np.array(embeddings)

    except Exception as e:

        logger.error(f" 生成嵌入向量失败 : {str(e)}")

        return None

 

# 准备数据并插入索引

def prepare _ and _ insert _ data(index, texts, batch _ size=100):

    # 过滤空文本

    texts = [text for text in texts if text.strip()]

    if not texts:

        logger.warning(" 没有有效的文本数据可插入 ")

        return 0

   

    # 生成嵌入向量

    embeddings = generate _ embeddings(texts)

    if embeddings is None:

        return 0

   

    # 批量插入

    total _ inserted = 0

    try:

        for i in range(0, len(texts), batch _ size):

            end _ idx = min(i + batch _ size, len(texts))

            batch _ texts = texts[i:end _ idx]

            batch _ embeddings = embeddings[i:end _ idx]

 

            vectors = []

            for idx, (text, embedding) in enumerate(zip(batch _ texts, batch _ embeddings), start=i):

                vectors.append((f"vec _ {idx}", embedding.tolist(), {"text": text}))

 

            index.upsert(vectors=vectors)

            total _ inserted += len(vectors)

            logger.info(f" 已插入 {total _ inserted}/{len(texts)} 个向量 ")

 

        return total _ inserted

    except Exception as e:

        logger.error(f" 插入数据失败 : {str(e)}")

        return total _ inserted

 

# 查询相似向量

def query _ similar(index, query _ text, top _ k=5):

    try:

        model = EmbeddingModel.get _ instance()

        query _ embedding = model.encode(query _ text).tolist()

 

        results = index.query(

            vector=query _ embedding,

            top _ k=top _ k,

            include _ metadata=True

        )

        return results

    except Exception as e:

        logger.error(f" 查询失败 : {str(e)}")

        return None

 

if __ name __ == " __ main __ ":

    # 配置 Pinecone (请替换为您的 API 密钥和环境)

    # 可以在 https://app.pinecone.io/ 获取这些信息

    PINECONE _ API _ KEY = "your-api-key"  # 替换为你的实际 API 密钥

    PINECONE _ ENV = "your-environment"  # 替换为你的实际环境,如 "us-east1-gcp"

    INDEX _ NAME = "ai-agent-demo"

 

    # 初始化 Pinecone

    if not init _ pinecone(PINECONE _ API _ KEY, PINECONE _ ENV):

        logger.error(" 无法初始化 Pinecone ,程序退出 ")

        exit(1)

 

    # 创建索引( 384 是 all-MiniLM-L6-v2 模型的维度)

    index = create _ index(INDEX _ NAME, dimension=384)

    if index is None:

        logger.error(" 无法创建或获取索引,程序退出 ")

        exit(1)

 

    try:

        # 加载示例数据

        logger.info(" 加载示例数据 ...")

        newsgroups = fetch _ 20newsgroups(

            subset='train',

            remove=('headers', 'footers', 'quotes'),

            categories=['comp.graphics', 'sci.space']  # 选择特定类别以减少数据量

        )

        texts = newsgroups.data[:100]  # 使用前 100 个文档作为示例

        logger.info(f" 加载了 {len(texts)} 条文本数据 ")

 

        # 插入数据

        num _ vectors = prepare _ and _ insert _ data(index, texts)

        logger.info(f" 成功插入 {num _ vectors} 个向量到索引 {INDEX _ NAME}")

 

        # 示例查询

        query = "computer technology and artificial intelligence"

        logger.info(f" 查询 : {query}")

        results = query _ similar(index, query)

 

        if results and results.matches:

            print("\n 查询结果 :")

            for match in results.matches:

                print(f"ID: {match.id}, 相似度 : {match.score:.4f}")

                # 清理文本,去除多余的换行符

                cleaned _ text = match.metadata['text'].replace('\n', ' ').strip()

                print(f" 文本片段 : {cleaned _ text[:100]}...\n")

        else:

            logger.warning(" 没有找到匹配的结果 ")

 

    except Exception as e:

        logger.error(f" 程序运行出错 : {str(e)}")

    finally:

        # 可选:删除索引(仅用于测试)

        # if INDEX _ NAME in pinecone.list _ indexes():

        #     pinecone.delete _ index(INDEX _ NAME)

        #     logger.info(f" 已删除索引 {INDEX _ NAME}")

        pinecone.deinit()

        logger.info("Pinecone 已关闭连接 ")

运行代码(提供有效的Pinecone API 密钥),输出如下:

INFO: __ main __ :Pinecone 初始化成功

INFO: __ main __ : 索引 ai-agent-demo 已存在   # 或“创建索引 ... ”

INFO: __ main __ : 加载示例数据 ...

INFO: __ main __ : 加载了 100 条文本数据

INFO: __ main __ : 生成嵌入向量 ...

INFO: __ main __ : 已插入 100/100 个向量

INFO: __ main __ : 成功插入 100 个向量到索引 ai-agent-demo

INFO: __ main __ : 查询 : computer technology and artificial intelligence

查询结果:

ID: vec _ 42, 相似度 : 0.8763

文本片段 : "Computer graphics and rendering techniques for space simulations..."

 

ID: vec _ 17, 相似度 : 0.8542

文本片段 : "Advances in AI for autonomous space probes..."

 

...

代码功能解释:

(1 ) 初始化 Pinecone :使用 pinecone.init 函数初始化 Pinecone 客户端,需要传入 API 密钥和环境名称。

(2 )创建索引:

l   使用pinecone.create_index 函数创建一个新的向量索引,如果索引已存在,则不会重复创建。

l   索引的维度需要与嵌入模型的输出维度相匹配(本例中为384 )。

(3 )生成嵌入向量:

l   使用SentenceTransformer 模型的encode 方法将文本数据转换为嵌入向量。

l   本例中使用了all-MiniLM-L6-v2 模型。

(4 )插入数据到索引:

l   将生成的嵌入向量与对应的文本数据(作为元数据)一起插入Pinecone 索引中。

l   使用index.upsert 方法批量插入数据。

(5 )查询相似向量:

l   对于给定的查询文本,首先生成其嵌入向量。

l   然后使用index.query 方法在Pinecone 索引中查询与查询向量最相似的向量。

l   返回的结果包括相似向量的ID 、相似度得分和对应的文本片段(从元数据中获取)。



本文节选自《AI Agent智能体开发实践》,获出版社和作者授权发布。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/18841117/viewspace-3105555/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: LangChain框架简介
请登录后发表评论 登录
全部评论

注册时间:2022-02-14

  • 博文量
    219
  • 访问量
    196307