基于内存高效算法的 LLM Token 优化:一个有效降低 API 成本的技术方案

news2025/6/3 20:14:02

在使用 OpenAI、Claude、Gemini 等大语言模型 API 构建对话系统时,开发者普遍面临成本不断上升的挑战。无论是基于检索增强生成(RAG)的应用还是独立的对话系统,这些系统都需要维护对话历史以确保上下文的连贯性,类似于 ChatGPT 对历史对话的记忆机制。

这种历史记忆机制虽然提升了对话质量,但同时导致了 Token 消耗的指数级增长。本文提出一种内存高效算法,通过智能化的内存管理策略,将 Token 使用量减少高达 40%,从而显著降低 LLM 推理的运营成本。

该方法的核心原理基于一个关键洞察:LLM 并非需要对每次用户输入都生成回复,而应当区分用户的信息陈述和实际查询请求,仅在后者情况下生成响应。

下图展示了内存高效算法与传统方法的性能对比:

随着对话轮次的增加,两种方法的 Token 消耗差异呈现持续扩大的趋势。内存高效算法的 Token 增长保持相对稳定,图中红线的峰值对应 LLM 生成响应的时刻,其他数据点则表示知识库更新操作。

随着对话深度的增加,这种差异将愈发显著,详细的比较分析将在后续章节中展示。

环境准备与依赖库配置

实现本方案需要导入以下核心库,这些库在大语言模型应用开发中较为常见:

 # 导入所需的标准库和第三方库,用于内存、LLM 和数据处理
importos      # 操作系统交互(此处未直接使用)
importjson    # 用于 JSON 解析/生成(LLM 输出)
importtime    # 用于 API 调用之间的延迟
importuuid    # 用于唯一的内存项 ID
fromdatetimeimportdatetime  # 用于时间戳

importnumpyasnp            # 用于嵌入向量
importpandasaspd           # 用于 DataFrame/表格分析
fromopenaiimportOpenAI     # 用于 OpenAI 兼容的 LLM/嵌入 API
 fromsklearn.metrics.pairwiseimportcosine_similarity  # 用于嵌入相似度

问题分析:Token 消耗的根本原因

本文采用 NebiusAI API 作为演示平台,该 API 基于 OpenAI 接口标准,提供开源 LLM 的访问能力。开发者可根据需要选择其他 LLM 服务提供商,但需确保响应中包含 Token 使用统计信息以便进行分析。

首先,我们初始化 LLM 客户端并配置基础的系统消息:

 # 使用 Nebius API 初始化 OpenAI 客户端
client=OpenAI(  
    base_url="YOUR_BASE_URL_API", # 替换为你的实际基础 URL
    api_key="YOU_LLM_API_KEY"  # 安全地替换为你的实际密钥
)  

# 聊天历史缓冲区(消息列表)
chat_history= [  
    {"role": "system", "content": "You are a helpful assistant."}  
 ]

为了深入理解 Token 消耗模式,我们实现一个集成聊天历史功能的函数。该函数在每次查询后输出 LLM 响应和详细的 Token 使用统计信息:

 defchat_with_history(user_input, max_length=100):  
    # 将用户输入添加到历史记录
    chat_history.append({"role": "user", "content": user_input})  
      
    # 向模型发送请求
    response=client.chat.completions.create(  
        model="meta-llama/Llama-3.2-1B-Instruct",  
        temperature=0.7,  
        messages=chat_history  
    )  

    # 获取助手消息
    assistant_message=response.choices[0].message.content  
    truncated_response= (assistant_message[:max_length] +'...') iflen(assistant_message) >max_lengthelseassistant_message  

    # 将助手响应添加到聊天记录
    chat_history.append({"role": "assistant", "content": assistant_message})  

    # 打印所需信息
    print(truncated_response)  
    print(f"Prompt tokens: {response.usage.prompt_tokens}")  
     print(f"Completion tokens: {response.usage.completion_tokens}")

使用 LLaMA 3.2 1B 模型进行测试,观察典型的用户交互模式:

 # 向我们的 LLM 连续问两个问题
 chat_with_history("Hello, who won the last FIFA World Cup?")  
 chat_with_history("And who was the top scorer?")  

输出结果显示了 Token 消耗的累积效应:

 #### 输出 ####
=== AI 响应(截断)===
The last FIFA World Cup was held in 2022, and the winning ...

=== Token 用量 ===
提示 Token:50
完成 Token:64
===============================

=== AI 响应(截断)===
The top scorer at the 2022 FIFA World Cup was Ciro Immobile ...

=== Token 用量 ===
提示 Token:130
完成 Token:29
 ===============================

关键观察:第二条消息"谁是最佳射手?"依赖于前一条消息的上下文,否则 LLM 无法确定用户询问的具体运动项目和时间范围。第二条消息的提示 Token 总数为 130(包含 50 个先前用户 Token + 64 个先前回复 Token + 16 个当前查询 Token)。

这一现象揭示了传统对话系统的核心问题:对话深度与 Token 消耗呈正相关关系,随着交互轮次的增加,每次调用所需的 Token 数量持续累积。

下图展示了在儿童故事创建场景中的 Token 消耗模式,该场景特别适合展示历史上下文的重要性:

数据表明,提示 Token 随每条消息快速递增,到第 10 条消息时已包含前 9 条消息的完整上下文。这种累积效应是导致成本快速上升的根本原因。

内存高效算法的设计原理

针对上述问题,我们提出内存高效策略作为解决方案。该策略旨在通过智能化的 Token 管理机制减少不必要的计算开销。

算法的工作流程如下图所示:

内存系统的核心逻辑包含以下步骤:

  1. 用户向 AI 系统发送消息
  2. 系统接收并分析消息类型:陈述或查询
  3. 根据消息类型执行相应的处理流程

对于陈述类型的消息,系统执行以下操作:从消息中提取关键事实信息,在内存存储中检索语义相似的现有信息,基于相似度分析决定操作类型(添加新信息、更新现有信息或无操作),将处理结果存储到内存系统中。

对于查询类型的消息,系统执行以下操作:根据查询内容在内存存储中搜索相关信息,结合查询和检索到的信息生成上下文相关的响应,将最终答案返回给用户。

该方法的核心创新在于认识到并非所有用户输入都需要 LLM 生成响应。大多数情况下,用户的输入是需要存储的陈述性信息,只有相关的陈述内容才应在响应生成时被检索和使用。这种方法不仅减少了 LLM 的内存占用,还显著提高了交互效率。

应用场景设计

为了验证算法的实际效果,我们选择营销活动策略场景作为测试用例。该场景贴近实际应用需求,模拟了 RAG 或独立聊天机器人处理营销活动相关查询的真实情况。

测试对话脚本定义如下:

 conversation_script= [  
    {"role": "user", "content": "Hi, let's start planning the 'New Marketing Campaign'. My primary goal is to increase brand awareness by 20%."},  
    {"role": "user", "content": "For this campaign, the target audience is young adults aged 18-25."},  
    {"role": "user", "content": "I want to allocate a budget of $5000 for social media ads for the New Marketing Campaign."},  
    {"role": "user", "content": "What's the main goal for the New Marketing Campaign?"},  
    {"role": "user", "content": "Who are we targeting for this campaign?"},  
    {"role": "user", "content": "Let's also consider influencers. Add a task: 'Research potential influencers for the 18-25 demographic' for the New Marketing Campaign."},  
    {"role": "user", "content": "Actually, let's increase the social media ad budget for the New Marketing Campaign to $7500."},  
    {"role": "user", "content": "What's the current budget for social media ads for the New Marketing Campaign?"},  
    {"role": "user", "content": "What tasks do I have pending for this campaign?"},  
    {"role": "user", "content": "Also, for the New Marketing Campaign, I prefer visual content for this demographic, like short videos and infographics."}  
 ]

这些对话体现了实际应用中常见的交互模式:陈述与查询的交替进行。陈述消息为 LLM 提供上下文信息以改进响应质量,而查询消息则期望获得基于已提供信息的定制化回复。

传统方法的基准测试

在实施内存高效算法之前,我们需要建立传统方法的性能基准。通过脚本化对话模拟 10 轮交互,记录每轮的输入和响应数据:

 # 通过 LLM 运行脚本化对话,存储每一轮的输入和响应
 raw_conversation= []  
 forturninconversation_script:  
     user_input=turn["content"]  # 从脚本中提取用户消息
     response=chat_with_history(user_input)  # 使用聊天历史获取 LLM 响应
     raw_conversation.append(response)  # 存储结果以供后续分析

测试结果显示最后一轮对话(第 10 条消息)的 Token 使用情况:

 print(f"Prompt tokens used in the last turn: {raw_conversation[-1]['prompt_tokens']}")  
 print(f"Completion tokens used in the last turn: {raw_conversation[-1]['completion_tokens']}")  

输出结果表明传统方法的 Token 消耗程度:

 #### 输出 ####
 最后一轮使用的提示 Token:4446
 最后一轮使用的完成 Token:451

仅在 10 轮对话后,最后一条消息的提示 Token 就超过了 4000,这对于相对简短的对话序列而言是相当高的消耗水平。

下图可视化展示了这种不断增长的历史累积效应:

这些数据将作为评估内存高效算法性能改进的基准参考。

核心技术组件实现

嵌入向量生成与响应处理

在实现内存高效方法之前,需要构建若干辅助函数以提高代码的可维护性和复用性。

首先实现嵌入生成功能,该功能用于计算用户查询与存储内存状态之间的语义相似度:

 # 嵌入模型名称
EMBEDDING_MODEL="BAAI/bge-multilingual-gemma2"  

# 生成嵌入的函数
defget_embedding(text_to_embed):  
    """  
    返回给定文本的嵌入向量。

    参数:
    - text_to_embed:要嵌入的输入文本。

    返回:
    - 包含嵌入的 NumPy 数组。
    """  
    response=client.embeddings.create(model=EMBEDDING_MODEL, input=text_to_embed)  
     returnnp.array(response.data[0].embedding)

其次实现 LLM 响应生成功能,负责处理模型响应的生成和 Token 统计:

 defget_llm_chat_completion(messages):  
    """  
    向语言模型发送聊天请求,并返回响应内容和 Token 使用情况。

    参数:
    - messages:聊天格式的消息列表(角色/内容)。

    返回:
    - content:生成的响应文本。
    - prompt_tokens:输入中使用的 Token 数量。
    - completion_tokens:输出中使用的 Token 数量。
    """  
    # 向聊天模型发送请求
    response=client.chat.completions.create(  
        model=LLM_MODEL,  
        messages=messages  
    )  

    # 提取生成的文本
    content=response.choices[0].message.content  

    # 获取 Token 使用信息(如果可用)
    prompt_tokens=response.usage.prompt_tokens  
    completion_tokens=response.usage.completion_tokens  

     returncontent, prompt_tokens, completion_tokens

内存存储架构设计

虽然生产环境中通常采用向量数据库来实现高效的嵌入存储和相似性搜索,但为了保持实现的简洁性和教学目的,我们采用面向对象的设计模式来构建内存管理系统。

内存管理的核心概念包括:内存指用户提供的陈述性信息,这些信息用于增强 LLM 的理解能力而非触发即时响应;这些陈述可能为未来的查询提供重要的上下文信息,LLM 可以从中检索相关内容;为了有效管理这些信息,每个内存条目都需要具备唯一的标识符。

首先定义内存项的类结构:

 classMemoryItem:  
    def__init__(self, text_content, source_turn_indices_list, verbose_embedding=False):  
        """  
        初始化一个 MemoryItem 实例。

        参数:
        - text_content:要存储在内存中的文本。
        - source_turn_indices_list:此项所基于的对话轮次索引列表。
        - verbose_embedding:是否在嵌入过程中打印调试信息。
        """  
        self.id=str(uuid.uuid4())  # 内存项的唯一 ID
        self.text=text_content  # 内存的文本内容
        self.embedding=get_embedding(text_content, verbose=verbose_embedding)  # 文本的嵌入向量
        self.creation_timestamp=datetime.now()  # 此项创建的时间
        self.last_accessed_timestamp=self.creation_timestamp  # 上次访问此项的时间
        self.access_count=0  # 此内存被访问的次数
        self.source_turn_indices=list(source_turn_indices_list)  # 对话轮次的引用

    def__repr__(self):  
        """  
        字符串表示形式,显示内存项的简要摘要。
        """  
        return (f"MemoryItem(id={self.id}, text='{self.text[:60]}...', "  
                f"created={self.creation_timestamp.strftime('%H:%M:%S')}, accessed={self.access_count})")  

    defmark_accessed(self):  
        """  
        在访问内存时更新访问时间和计数。
        """  
        self.last_accessed_timestamp=datetime.now()  
         self.access_count+=1

接下来实现内存存储管理类,提供内存条目的增删查改功能:

 classMemoryStore:  
    def__init__(self, verbose=False):  
        self.memories= {}  # 按 ID 存储内存项的字典

    defadd_memory_item(self, item):  
        self.memories[item.id] =item  

    defget_memory_item_by_id(self, memory_id):  
        item=self.memories.get(memory_id)  
        ifitem:  
            item.mark_accessed()  
        returnitem  

    defupdate_existing_memory_item(self, memory_id, new_text, turn_indices):  
        item=self.memories.get(memory_id)  
        ifnotitem:  
            ifself.verbose:  
                print(f"[MemoryStore] UPDATE FAILED: ID {memory_id} not found.")  
            returnFalse  

        item.text=new_text  
        item.embedding=get_embedding(new_text, verbose=self.verbose)  
        item.creation_timestamp=datetime.now()  
        item.source_turn_indices=list(set(item.source_turn_indices+turn_indices))  
        item.mark_accessed()  

        returnTrue  

    deffind_semantically_similar_memories(self, query_embedding, top_k=3, threshold=0.5):  
        # 过滤掉具有无效嵌入的内存项
        candidates= [  
            (mid, mem.embedding) formid, meminself.memories.items()  
            ifmem.embeddingisnotNoneandmem.embedding.size>0andnp.any(mem.embedding)  
        ]  

        # 将嵌入堆叠成矩阵以进行相似性比较
        ids, embeddings=zip(*candidates)  
        embeddings=np.vstack(embeddings)  
        query_embedding=query_embedding.reshape(1, -1)  

        # 计算余弦相似度并对结果进行排序
        similarities=cosine_similarity(query_embedding, embeddings)[0]  
        sorted_indices=np.argsort(similarities)[::-1]  

        # 返回高于阈值的 top-k 结果
        return [  
            (self.memories[ids[i]], similarities[i])  
            foriinsorted_indices[:top_k]  
            ifsimilarities[i] >=threshold  
         ]

该内存存储类实现了四个核心功能:添加新的内存项(如果不存在)、更新现有内存项的内容、根据 ID 检索特定的内存项、使用嵌入向量查找语义相似的内存条目。开发者还可以根据需要添加删除功能来移除未使用的内存,以进一步优化内存使用效率。

输入分类机制

在处理用户输入之前,系统需要准确判断输入的性质:陈述还是查询。这一分类决定了后续的处理流程。我们利用 LLM 的自然语言理解能力来实现这一功能:

 defclassify_input(user_input):  
    """  
    将用户输入分类为"查询"或"陈述"。

    参数:
    - user_input:用户的文本输入。

    返回:
    - 字符串: "查询"或"陈述"。
    """  
    # 指示 LLM 充当分类器
    system_prompt= (  
        "You are a classifier. "  
        "A 'query' is a question or request for information. "  
        "A 'statement' is a declaration, instruction, or information that is not a question. "  
        "Respond with only one word: either 'query' or 'statement'."  
    )  

    # 向模型发送分类请求
    response=client.chat.completions.create(  
        model="meta-llama/Llama-3.2-1B-Instruct",  
        messages=[  
            {"role": "system", "content": system_prompt},  
            {"role": "user", "content": f"Classify this: {user_input}"}  
        ]  
    )  

    # 返回分类结果(小写)
     returnresponse.choices[0].message.content.strip().lower()

该分类函数在生产环境中会对每个用户查询实时执行。为了演示目的,我们对预定义的对话场景进行批量分类处理:

 # 我们将把这个分类添加到我们的 conversation_script 项中
 forturninconversation_script:  
     classification=classify_input(turn["content"])  
     turn["type"] =classification

验证分类结果的准确性:

 # 打印第一项
 conversation_script[0]  

输出显示分类结果:

 #### 输出 ####
 {  
   'role': 'user',  
   'content': "Hi, let's start planning the 'New Marketing  ... ",  
   'type': 'statement'  
 }

第一个用户查询被正确分类为"陈述",因为它提供信息而非请求回答,其目的是为 LLM 提供上下文信息。

事实提取机制

确定输入类型后,系统需要执行相应的处理逻辑:对于查询类型的输入,LLM 将生成响应;对于陈述类型的输入,系统需要从中提取关键事实并存储到内存系统中。

事实提取需要一个精心设计的提示模板来指导 LLM 识别和抽取关键信息:

 fact_prompt=f"""  
Extract concise, declarative facts from the 'New User Statement' based on the 'Recent Conversation Context'.  
Focus on user's goals, plans, preferences, decisions, and key entities.  
Ignore questions, acknowledgements, fluff, or inference.  
Context:  
---BEGIN---  
{recent_turns_window_textor"(No prior context)"}  
---END---  
Statement: "{current_user_statement_text}"  
Output ONLY a JSON list of strings (facts). Return [] if none.  
 """

该模板专门针对营销活动场景进行了优化,开发者可以根据具体应用领域调整模板内容以获得更好的提取效果。

实现事实提取的核心函数:

 defmem0_extract_salient_facts_from_turn(current_user_statement_text, recent_turns_window_text, current_turn_index_in_script):  
    """  
    从当前用户陈述和最近的对话轮次中提取显著事实。

    参数:
    - current_user_statement_text:当前用户陈述的文本。
    - recent_turns_window_text:用于上下文的最近对话轮次的文本。
    - current_turn_index_in_script:当前轮次在整个脚本中的索引。

    返回:
    - facts:提取的事实列表(从 JSON 解析)。
    """  

    messages= [  
        {"role": "system", "content": "Expert extraction AI. Output ONLY valid JSON list of facts."},  
        {"role": "user", "content": fact_prompt}  
    ]  

    # 调用 LLM 聊天完成函数
    response_text, prompt_tokens, completion_tokens=get_llm_chat_completion(  
        messages  
    )  

    # 从响应文本中安全地提取 JSON 数组
    start=response_text.find('[')  
    end=response_text.rfind(']')  
    json_candidate=response_text[start:end+1]  

    # 解析 JSON 以获取事实列表
    facts=json.loads(json_candidate)  

     returnfacts

该函数将以 JSON 数组格式返回从陈述中提取的事实。提取的事实随后将与现有内存进行比较,以决定是作为新条目添加还是更新现有条目。

内存更新策略

提取的事实需要与现有内存系统建立适当的关联。系统需要评估新事实与已存储信息的关系,并决定采取相应的操作策略。

定义内存操作决策的提示模板:

 prompt=f"""  
You manage a memory store. Decide how to handle this new fact:  
"{candidate_fact_text}"  
{similar_segment}  
Choose ONE:  
- ADD: New info.  
- UPDATE: Improves one existing memory (give memory ID and new text).  
- NOOP: Redundant.  
Respond with JSON:  
{{"operation": "ADD"}} or  
{{"operation": "UPDATE", "target_memory_id": "ID", "updated_memory_text": "Text"}} or  
{{"operation": "NOOP"}}  
Decision:  
 """

该模板指导 LLM 在添加新内存、更新现有内存或忽略冗余信息之间做出决策。

实现内存操作决策的函数:

 S_SIMILAR_MEMORIES_FOR_UPDATE_DECISION=3  # 要考虑的相似内存数量

defmem0_decide_memory_operation_with_llm(candidate_fact_text, similar_existing_memories_list):  
    """  
    使用 LLM 来决定一个候选事实是否应该:
    - 作为新内存添加,
    - 更新现有内存,
    - 或者被忽略(无操作)。

    参数:
    - candidate_fact_text:要评估的新事实。
    - similar_existing_memories_list:(MemoryItem,similarity_score)元组列表。

    返回:
    - 具有以下格式之一的字典:
      {"operation": "ADD"}  
      {"operation": "UPDATE", "target_memory_id": "ID", "updated_memory_text": "Text"}  
      {"operation": "NOOP"}  
    """  

    # 为提示格式化相似内存部分
    similar_segment="No similar memories."  
    ifsimilar_existing_memories_list:  
        formatted= [  
            f"{i+1}. ID: {mem.id}, Sim: {sim_score:.4f}, Text: '{mem.text}'"  
            fori, (mem, sim_score) inenumerate(similar_existing_memories_list)  
        ]  
        similar_segment="Similar Memories:\n"+"\n".join(formatted)  

    # 为 LLM 构建消息
    messages= [  
        {"role": "system", "content": "Decide memory action. Output ONLY valid JSON."},  
        {"role": "user", "content": prompt}  
    ]  

    # 获取 LLM 的响应
    response_text, p_tokens, c_tokens=get_llm_chat_completion(  
        messages  
    )  

    # 跟踪 Token 使用情况
    total_prompt_tokens_mem0_update+=p_tokens  
    total_completion_tokens_mem0_update+=c_tokens  

    # 从响应中提取并解析 JSON
    start=response_text.find('{')  
    end=response_text.rfind('}')  
    json_candidate=response_text[start:end+1]  

     returnjson.loads(json_candidate)

该函数通过分析新事实与现有内存的相似性,返回结构化的 JSON 响应,明确指定需要执行的操作类型和相关参数。

查询响应的检索机制

当用户提交查询时,系统需要从内存中检索相关信息以辅助 LLM 生成准确的响应。这一过程与检索增强生成(RAG)的机制相似。

实现内存检索和格式化的函数:

 # 要检索的顶部相似内存的默认数量
K_MEMORIES_TO_RETRIEVE_FOR_QUERY=3  

defmem0_retrieve_and_format_memories_for_llm_query(user_query_text, memory_store_instance, turn_log_entry, top_k_results=K_MEMORIES_TO_RETRIEVE_FOR_QUERY):  
    """  
    为给定的用户查询从存储中检索并格式化 top-k 相关内存。

    参数:
    - user_query_text:当前用户查询。
    - memory_store_instance:要在其中搜索的 MemoryStore 实例。
    - turn_log_entry:用于存储内存检索信息以进行日志记录的字典。
    - top_k_results:要检索的顶部相似内存的数量。

    返回:
    - 相关内存的格式化字符串或回退消息。
    """  
    turn_log_entry['retrieved_memories_for_query'] = []  

    # 获取用户查询的嵌入
    query_embedding=get_embedding(user_query_text)  

    # 根据查询嵌入查找 top-k 相似内存
    retrieved=memory_store_instance.find_semantically_similar_memories(query_embedding, top_k=top_k_results)  

    # 构建格式化的内存列表
    output="Relevant memories:\n"  
    fori, (mem, score) inenumerate(retrieved):  
        memory_store_instance.get_memory_item_by_id(mem.id)  # 标记为已访问
        output+=f"{i+1}. {mem.text} (Similarity: {score:.3f})\n"  

        # 记录检索详细信息
        turn_log_entry['retrieved_memories_for_query'].append({  
            'id': mem.id,  
            'text': mem.text,  
            'similarity': score  
        })  

     returnoutput.strip()

该函数根据用户查询的语义相似性检索最相关的内存条目,并将其格式化为结构化的上下文信息,供 LLM 在生成响应时参考。

算法实现与执行

整个内存高效算法的核心逻辑通过主循环实现,该循环处理对话场景中的每条消息,并根据消息类型执行相应的操作:

 # 遍历脚本中的每一轮
forturn_index, turn_datainenumerate(script):  
    user_msg=turn_data['content']  # 用户输入文本
    turn_type=turn_data['type']    # 类型:"陈述"、"陈述更新"或"查询"

    # 此轮的日志
    turn_log_entry= {  
        "turn": turn_index+1,  
        "type": turn_type,  
        "user_content": user_msg  
    }  

    assistant_response="(Ack/Internal Processing)"  # 默认占位符

    # 处理陈述和更新
    ifturn_typein ('statement', 'statement_update'):  
        # 收集最近的用户-助手原始对话日志以获取上下文
        recent="\n".join(raw_conversation_log_for_extraction_context[-M_RECENT_RAW_TURNS_FOR_EXTRACTION_CONTEXT:])  

        # 处理用户消息以进行事实提取和内存更新
        mem0_process_user_statement_for_memory(  
            user_msg, recent, memory_store_instance, turn_index, turn_log_entry, verbose=VERBOSE_MEM0_RUN  
        )  

        # 助手的响应取决于陈述类型
        assistant_response="Okay, noted."ifturn_type=='statement'else"Okay, updated."  

        # 记录零 Token,因为此路径不调用 LLM 进行响应
        turn_log_entry.update({  
            'assistant_response_conversational': assistant_response,  
            'prompt_tokens_conversational_turn': 0,  
            'completion_tokens_conversational_turn': 0  
        })  

    # 处理查询(例如,问题)
    elifturn_type=='query':  
        # 根据与查询的语义相似性检索相关内存
        retrieved=mem0_retrieve_and_format_memories_for_llm_query(user_msg, memory_store_instance, turn_log_entry)  

        # 使用内存上下文和查询为 LLM 创建提示
        messages=list(current_short_term_llm_chat_history) + [{  
            "role": "user",  
            "content": f"User Query: '{user_msg}'\n\nRelevant Info from Memory:\n{retrieved}"  
        }]  

        # 获取 LLM 响应
        assistant_response, p_tokens, c_tokens=get_llm_chat_completion(  
            messages, max_tokens=120, verbose=VERBOSE_MEM0_RUN  
        )  

        # 跟踪此查询的 Token 使用情况
        total_prompt_tokens_mem0_conversation+=p_tokens  
        total_completion_tokens_mem0_conversation+=c_tokens  

        # 使用 LLM 输出和 Token 信息更新轮次日志
        turn_log_entry.update({  
            'assistant_response_conversational': assistant_response,  
            'prompt_tokens_conversational_turn': p_tokens,  
            'completion_tokens_conversational_turn': c_tokens  
        })  

    # 更新用于内存提取上下文的原始日志
    raw_conversation_log_for_extraction_context+= [  
        f"T{turn_index+1} U: {user_msg}",  
        f"T{turn_index+1} A: {assistant_response}"  
    ]  

    # 更新短期历史以供将来 LLM 上下文使用
    current_short_term_llm_chat_history+= [  
        {"role": "user", "content": user_msg},  
        {"role": "assistant", "content": assistant_response}  
    ]  

    # 仅保留滑动窗口内最新的聊天历史
    iflen(current_short_term_llm_chat_history) > (1+SHORT_TERM_CHAT_HISTORY_WINDOW*2):  
        current_short_term_llm_chat_history= [current_short_term_llm_chat_history[0]] + \  
            current_short_term_llm_chat_history[-(SHORT_TERM_CHAT_HISTORY_WINDOW*2):]  

    # 处理此轮后记录内存存储大小
     turn_log_entry['mem_store_size_after_turn'] =len(memory_store_instance.memories)

在生产环境中,该循环会实时处理用户输入,对每条消息执行分类、检索或更新操作,并在必要时生成响应。

算法执行的日志输出展示了工作流程:

 #### 我们的 MEMO 算法输出 ####

--- Mem0 第 1/10 轮 (陈述) ---
用户:嗨,让我们开始规划新的营销活动。我的主要目标是增加...
[提取器 LLM] 解析了 2 个事实。
[内存协调器] 提取了 2 个事实。
[内存协调器] 事实 用户想要开始规划...:LLM 决策 -> 添加
[内存协调器] 事实 用户的主要目标是...:LLM 决策 -> 添加
助手 (确认):好的,已记录。

--- Mem0 第 2/10 轮 (陈述) ---
用户:对于这个活动,目标受众是 18-25 岁的年轻人....
[提取器 LLM] 解析了 1 个事实。
[内存协调器] 提取了 1 个事实。
[内存协调器] 事实 N 的目标受众是...:LLM 决策 -> 添加
助手 (确认):好的,已记录。

--- Mem0 第 3/10 轮 (陈述) ---
用户:我想为新营销活动的社交媒体广告分配 5000 美元的预算...
[提取器 LLM] 解析了 1 个事实。
[内存协调器] 提取了 1 个事实。
[内存协调器] 事实 用户想要分配一个 b...:LLM 决策 -> 添加
助手 (确认):好的,已记录。

--- Mem0 第 4/10 轮 (查询) ---
用户:新营销活动的主要目标是什么?...
 助手:新营销活动的主要目标是**将品牌知名度提高...

从前四轮的执行结果可以观察到:前三个输入被正确识别为陈述类型,系统仅进行了事实提取和内存存储操作,未生成 LLM 响应;第四个输入被识别为查询类型,系统检索了相关内存并生成了基于上下文的响应。这证实了算法能够准确区分不同类型的输入并执行相应的处理逻辑。

性能评估与对比分析

为了量化内存高效算法的性能改进,我们对传统方法和新算法的 Token 使用情况进行了详细的比较分析。

首先构建比较数据的 DataFrame 结构:

 # 准备原始方法和 Mem0 方法之间的比较数据
data= {  
    'Metric': [  
        'Prompt Tokens',            # 使用的总提示 Token
        'Completion Tokens',        # 使用的总完成 Token
        'Total Tokens',             # 提示 + 完成的总和
        '',                         # 用于间隔的空行

        # Mem0 方法的 Token 使用明细
        'Mem0: Conv Prompt',        # 对话轮次的提示 Token
        'Mem0: Conv Completion',    # 对话轮次的完成 Token
        'Mem0: Extract Prompt',     # 内存提取的提示 Token
        'Mem0: Extract Completion', # 内存提取的完成 Token
        'Mem0: Update Prompt',      # 内存更新的提示 Token
        'Mem0: Update Completion'   # 内存更新的完成 Token
    ],  
    'Raw Approach': [  
        final_raw_prompt_tokens,       # 原始提示 Token
        final_raw_completion_tokens,   # 原始完成 Token
        final_raw_total_tokens,        # 原始总 Token
        '',                            # 间隔行的空值
        '-', '-', '-', '-', '-', '-'   # 原始方法没有可用的明细
    ],  
    'Mem0 Approach': [  
        final_mem0_overall_prompt_tokens,        # Mem0 中的总提示 Token
        final_mem0_overall_completion_tokens,    # Mem0 中的总完成 Token
        final_mem0_overall_total_tokens,         # Mem0 中的总 Token
        '',                                       # 间隔符
        final_mem0_conv_prompt_tokens,           # 对话的提示 Token
        final_mem0_conv_completion_tokens,       # 对话的完成 Token
        final_mem0_extr_prompt_tokens,           # 提取的提示 Token
        final_mem0_extr_completion_tokens,       # 提取的完成 Token
        final_mem0_upd_prompt_tokens,            # 更新的提示 Token
        final_mem0_upd_completion_tokens         # 更新的完成 Token
    ]  
}  

# 创建一个用于显示和分析的 DataFrame
 comparison_df=pd.DataFrame(data)

在仅包含 10 轮对话的测试中,内存高效算法就实现了高达 40% 的提示 Token 减少。下图展示了两种方法的性能对比:

图中红线代表内存高效算法的 Token 使用模式,仅在用户实际请求响应时(第 4、5、8、9 轮)出现显著的增长,而在其他陈述轮次中,总 Token 数量(提示+完成)保持相对稳定,避免了传统方法中的快速累积现象。

黄色区域表示两种方法之间的差异,随着对话轮次的增加,这一差异呈现持续扩大的趋势。为了进一步验证算法的长期效果,我们进行了 100 轮对话的扩展测试:

在 100 轮对话后,性能差异变得极为显著,内存高效算法实现了超过 60% 的 Token 节省。这种显著的改进直接转化为成本节约,这对于大规模生产环境具有重要的经济价值。

总结

内存高效算法在 Token 使用优化方面展现了显著的性能优势,能够在保持对话质量的同时大幅降低 LLM API 的使用成本。该方法的核心创新在于区分用户输入的类型,并针对不同类型采用差异化的处理策略。

开发者可以基于提供的实现进一步优化算法性能,包括开发更精确的提示模板、增加内存管理功能以提高检索速度和存储效率、集成评估机制以量化响应质量、实现动态内存清理以维护系统性能等方面。

此外,结合响应质量评估技术来比较两种方法的输出质量将为算法的进一步改进提供重要参考。基于这些评估结果,可以持续优化算法的效率和准确性,以适应更广泛的应用场景需求。

未来的研究方向可以包括探索更高效的向量存储和检索机制、开发适应性更强的事实提取算法、以及研究多模态内容的内存管理策略等领域。

https://avoid.overfit.cn/post/5feb4f4f31ed41d1850a01e6d7a78e6a

作者:Fareed Khan

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2395329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python打卡训练营Day42

DAY 42 Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 作业:理解下今天的代码即可 import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvision.transforms as tr…

基于微信小程序的scratch学习系统

博主介绍:java高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,没有什么华丽的语言&#xff0…

【C++ 多态】—— 礼器九鼎,釉下乾坤,多态中的 “风水寻龙诀“

欢迎来到一整颗红豆的博客✨,一个关于探索技术的角落,记录学习的点滴📖,分享实用的技巧🛠️,偶尔还有一些奇思妙想💡 本文由一整颗红豆原创✍️,感谢支持❤️!请尊重原创…

SCSAI平台面向对象建模技术的设计与实现

一、核心设计思想 SCSAI平台的核心目标是通过元建模(Meta-Modeling)技术实现面向对象建模的零编码化。其核心思想为: 自反性设计:定义ObjectClassInfo (OCI)为元类(Meta-Class),所有对象类均为…

pikachu通关教程-CSRF

CSRF(get) 用bp进行抓包 选择action value值的修改 点击test in browser copy然后放在bp代理的浏览器上,会出现一个提交按钮,这时候点击之后信息就被修改了。 CSRF(post) 请求的方式不同,其他都是一样 CSRF Token 存在cookie 首先要先下载一…

智能体觉醒:AI开始自己“动手”了-自主进化开启任务革命时代

1. 智能体:AI从“工具”到“伙伴”的关键跃迁 1.1 什么是智能体? 智能体(Agent)是AI的“进化版”——它不再局限于生成文字或图像,而是能像人类一样“规划任务”“调用工具”甚至“协同合作”。例如,一个…

【C++指南】C++ list容器完全解读(二):list模拟实现,底层架构揭秘

. 💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《C指南》 期待您的关注 文章目录 引言一、链表节点设计:双向链表的基石1.1 节点类的实现 二、list框架与核心成员函…

[神经网络]使用olivettiface数据集进行训练并优化,观察对比loss结果

结合归一化和正则化来优化网络模型结构,观察对比loss结果 搭建的神经网络,使用olivettiface数据集进行训练,结合归一化和正则化来优化网络模型结构,观察对比loss结果 from sklearn.datasets import fetch_olivetti_faces #倒入数…

华院计算出席信创论坛,分享AI教育创新实践并与燧原科技共同推出教育一体机

5月21日,信创论坛于上海漕河泾会议中心举办。本次论坛以“聚力融合,繁荣生态”为主题,话题聚焦工业制造、交通运输、金融、教育、医疗等领域。华院计算技术(上海)股份有限公司(以下简称“华院计算”&#x…

华为OD机试真题——会议接待 /代表团坐车(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》 华为OD机试真题《会议…

LabVIEW Val (Sgnl) 属性

在 LabVIEW 事件驱动架构中,Val (Sgnl) 属性(Value (Signaling))是实现编程触发与用户交互行为一致性的关键技术。与普通 Value 属性不同,Val (Sgnl) 在修改控件值的同时强制生成值改变事件,确保程序逻辑与 UI 交互保持…

STM32G4 电机外设篇(三) TIM1 发波 和 ADC COMP DAC级联

目录 一、STM32G4 电机外设篇(三) TIM1 发波 和 ADC COMP DAC级联1 TIM1 高级定时器发波1.1 stm32cubemx配置 2 TIM1 ADC COMP DAC级联2.1 stm32cubemx配置 附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^) 一、STM32G4 电机外设篇(三&…

DAY 35 超大力王爱学Python

知识点回顾: 三种不同的模型可视化方法:推荐torchinfo打印summary权重分布可视化进度条功能:手动和自动写法,让打印结果更加美观推理的写法:评估模式 作业:调整模型定义时的超参数,对比下效果。…

【数据结构】图的存储(十字链表)

弧节点 tailvex数据域:存储弧尾一端顶点在顺序表中的位置下标;headvex 数据域:存储弧头一端顶点在顺序表中的位置下标;hlink 指针域:指向下一个以当前顶点作为弧头的弧;tlink 指针域:指向下一个…

Redis最佳实践——秒杀系统设计详解

基于Redis的高并发秒杀系统设计(十万级QPS) 一、秒杀系统核心挑战 瞬时流量洪峰:100万 QPS请求冲击库存超卖风险:精准扣减防止超卖系统高可用性:99.99%服务可用性要求数据强一致性:库存/订单/支付状态同步…

STM32软件spi和硬件spi

核心观点 本文主要介绍了SPI通信的两种实现方式:软件SPI和硬件SPI。详细阐述了SPI通信协议的基本概念、硬件电路连接方式、移位示意图、时序基本单元以及四种工作模式。同时,对W25Q64模块进行了详细介绍,包括其硬件电路、框图以及操作注意事…

深度刨析树结构(从入门到入土讲解AVL树及红黑树的奥秘)

目录 树的表示 二叉树的概念及结构(重点学习) 概念 : 特点: 树与非树 特殊的二叉树 二叉树的性质(重点) 二叉树的存储结构 堆的概念及结构 建堆方式: 向下调整算法 向上调整算法 建堆第一步初始化 建…

【Linux】shell的条件判断

目录 一.使用逻辑运算符判定命令执行结果 二.条件判断方法 三.判断表达式 3.1文件判断表达式 3.2字符串测试表达式 3.3整数测试表达式 3.4逻辑操作符 一.使用逻辑运算符判定命令执行结果 && 在命令执行后如果没有任何报错时会执行符号后面的动作|| 在命令执行后…

第九天:java注解

注解 1 什么是注解(Annotation) public class Test01 extends Object{//Override重写的注解Overridepublic String toString() {return "Test01{}";} }2 内置注解 2.1 Override Override重写的注解 Override public String toString() {ret…

十一、【核心功能篇】测试用例管理:设计用例新增编辑界面

【核心功能篇】测试用例管理:设计用例新增&编辑界面 前言准备工作第一步:创建测试用例相关的 API 服务 (src/api/testcase.ts)第二步:创建测试用例编辑页面组件 (src/views/testcase/TestCaseEditView.vue)第三步:配置测试用例…