0Pricing
AI Engineering Academy · 课时

摘要记忆与令牌感知截断

使用 ConversationSummaryMemory 自动总结较早的对话轮次,在保留用户先前提及的关键信息的同时压缩对话内容。

摘要记忆与令牌感知截断 是 CoddyKit 上的免费 AI Engineering Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Engineering Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Engineering Academy 课程共包含 4 节课。

完整历史记录的令牌成本

对话缓冲记忆会保留曾经交换过的每条消息,因此会迅速消耗上下文窗口。一段 100 轮的对话可能仅历史记录就占用 20,000 个令牌,留给实际响应的空间所剩无几。摘要记忆通过将较早的对话轮次替换为压缩摘要来解决这一问题。

摘要记忆的工作方式

当令牌总数超过阈值时,ConversationSummaryMemory 会将最早的对话轮次交给 LLM,并要求它总结要点。完整轮次会被丢弃,并由这份精简摘要取代。后续轮次会继续累积在摘要之上。

  • 较早轮次:替换为摘要
  • 最近轮次:原样保留
  • 最终效果:在信息损失极小的情况下实现有意义的压缩

LangChain ConversationSummaryMemory

LangChain 提供了 ConversationSummaryMemory,会在缓冲区变得过大时自动生成摘要。您需要将一个 LLM 传给记忆对象,以便它按需调用模型生成摘要。

from langchain.memory import ConversationSummaryMemory
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model='gpt-4o-mini')

memory = ConversationSummaryMemory(
    llm=llm,
    return_messages=True
)

# Add messages manually
memory.save_context(
    {'input': 'My name is Alice and I am building a RAG system.'},
    {'output': 'Great, I can help you build a RAG system, Alice.'}
)

print(memory.load_memory_variables({}))

摘要缓冲记忆:兼顾两者优势

ConversationSummaryBufferMemory 是一种混合方案:它为确保准确性而原样保留最近的轮次,只对超过最大令牌数限制的较早轮次进行摘要。这样既能精确回忆近期上下文,也能压缩回忆较早的上下文。

from langchain.memory import ConversationSummaryBufferMemory
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model='gpt-4o-mini')

memory = ConversationSummaryBufferMemory(
    llm=llm,
    max_token_limit=500,  # Summarize when older turns exceed 500 tokens
    return_messages=True
)

# As the conversation grows, old turns get summarized automatically
print('Moving summary:', memory.moving_summary_buffer)

截断前计算令牌数

要做出合理的截断决策,您需要知道消息会消耗多少令牌。tiktoken 库可以计算任意 OpenAI 模型的令牌数。这样,您就能实现严格控制在预算以内的令牌感知截断。

import tiktoken

def count_tokens(messages: list, model: str = 'gpt-4o') -> int:
    encoding = tiktoken.encoding_for_model(model)
    total = 0
    for msg in messages:
        # Each message has overhead tokens for role framing
        total += 4
        total += len(encoding.encode(msg.get('content', '')))
    total += 2  # Reply primer
    return total

messages = [
    {'role': 'user', 'content': 'Explain RAG to me.'},
    {'role': 'assistant', 'content': 'RAG stands for Retrieval-Augmented Generation...'}
]
print('Token count:', count_tokens(messages))

手动进行令牌感知截断

有时您希望在不使用 LangChain 记忆类的情况下完全控制截断。一种简单方法是:保留所有消息,然后逐条删除最早的非系统消息,直到令牌总数符合您的预算。

def truncate_to_budget(messages: list, budget: int, model: str = 'gpt-4o') -> list:
    '''Remove oldest non-system messages until under budget.'''
    import tiktoken
    encoding = tiktoken.encoding_for_model(model)

    def token_count(msgs):
        total = 2
        for m in msgs:
            total += 4 + len(encoding.encode(m.get('content', '')))
        return total

    result = list(messages)
    while token_count(result) > budget and len(result) > 1:
        # Never remove the system prompt at index 0
        if result[0]['role'] == 'system':
            del result[1]
        else:
            del result[0]
    return result

生成摘要提示词

构建自己的摘要记忆时,需要编写提示词,要求 LLM 提炼关键事实。提示词应指示模型记录用户偏好、命名实体和未解决的问题——这些事实最有可能在后续轮次中发挥作用。

from openai import OpenAI

client = OpenAI()

def summarize_history(old_summary: str, new_turns: list) -> str:
    turns_text = '\n'.join(
        f'{m["role"].capitalize()}: {m["content"]}' for m in new_turns
    )
    prompt = f'''Current summary:\n{old_summary}\n\nNew conversation turns:\n{turns_text}\n\nWrite an updated summary that captures key facts, user preferences, and open questions.'''

    resp = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': prompt}]
    )
    return resp.choices[0].message.content

将摘要集成到系统提示词中

获得摘要后,将其注入系统提示词,使 LLM 始终拥有关于对话历史的上下文。将摘要作为简短的上下文块,放在主要角色说明之前。

def build_messages_with_summary(summary: str, recent_turns: list, user_input: str) -> list:
    system_content = (
        'You are a helpful AI assistant.'
        + ('\n\n[Conversation summary]\n' + summary if summary else '')
    )

    messages = [{'role': 'system', 'content': system_content}]
    messages.extend(recent_turns)
    messages.append({'role': 'user', 'content': user_input})
    return messages

自动触发摘要生成

一种常见模式是在对话超过令牌阈值时触发摘要生成,例如运行中的历史记录超过 2,000 个令牌时。此时,对除最近两轮以外的所有内容进行摘要,并用摘要替换这些内容。

class SummaryBufferChat:
    def __init__(self, max_tokens=2000):
        self.summary = ''
        self.recent_turns = []
        self.max_tokens = max_tokens

    def chat(self, user_message: str) -> str:
        from openai import OpenAI
        client = OpenAI()

        messages = build_messages_with_summary(self.summary, self.recent_turns, user_message)
        resp = client.chat.completions.create(model='gpt-4o', messages=messages)
        reply = resp.choices[0].message.content

        self.recent_turns.append({'role': 'user', 'content': user_message})
        self.recent_turns.append({'role': 'assistant', 'content': reply})

        if count_tokens(self.recent_turns) > self.max_tokens:
            to_summarize = self.recent_turns[:-2]
            self.recent_turns = self.recent_turns[-2:]
            self.summary = summarize_history(self.summary, to_summarize)

        return reply

在摘要中保留命名实体

摘要质量在很大程度上取决于摘要提示词。如果用户提到了自己的姓名、位置、偏好或截止时间,提示词必须明确指示模型包含这些事实。通用摘要经常丢弃对个性化最重要的专有名词。

  • 包含:姓名、日期、已做出的技术选择、未解决的问题
  • 排除:填充性对话、确认语、重复的客套话

权衡:摘要记忆与窗口记忆

选择摘要记忆还是窗口记忆取决于使用场景。窗口记忆保留准确措辞,这对精确回忆很重要,但会为无关上下文浪费令牌。摘要记忆会进行激进压缩,但会引入额外的 LLM 调用,并可能丢失边缘细节。大多数生产环境中的聊天机器人会采用混合方案:保留最近的精确轮次,加上较早内容的滚动摘要。

快速检查

测试您对摘要记忆和令牌感知截断概念的理解。

课程回顾

本课中您学到了:摘要记忆通过调用 LLM 压缩较早轮次,ConversationSummaryBufferMemory 将最近的精确轮次与较早的摘要轮次结合起来,而 tiktoken 支持令牌感知截断,使对话保持在预算以内。接下来我们将探索如何将聊天历史持久化到 Redis 和 PostgreSQL,以实现持久且可扩展的存储。

常见问题解答

「摘要记忆与令牌感知截断」课时是免费的吗?

是的 — 「摘要记忆与令牌感知截断」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Engineering Academy 课程的其余内容,请升级到 CoddyKit PRO。 AI Engineering Academy 课程共包含 4 节课。

「摘要记忆与令牌感知截断」这节课中我会学到什么?

使用 ConversationSummaryMemory 自动总结较早的对话轮次,在保留用户先前提及的关键信息的同时压缩对话内容。 你通过在浏览器中直接运行的动手代码来练习 AI Engineering Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Engineering Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Engineering Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「摘要记忆与令牌感知截断」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Engineering Academy 课中编写并运行代码吗?

能。每节 AI Engineering Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 无状态 LLM 为什么需要外部记忆
  2. 缓冲区记忆与窗口记忆
  3. 摘要记忆与令牌感知截断
  4. 在 Redis 与 PostgreSQL 中持久化聊天记录
← 返回 AI Engineering Academy