0Pricing
AI Engineering Academy · レッスン

Summary MemoryとTokenを考慮した切り詰め

ConversationSummaryMemoryを使って古いターンを自動的に要約し、ユーザーが以前に述べた重要な事実を保持しながら会話を簡潔に保ちます。

「Summary MemoryとTokenを考慮した切り詰め」はCoddyKit上の無料AI Engineering Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Engineering Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Engineering Academyコースには全4レッスンが含まれています。

全履歴のトークンコスト

Conversation Buffer Memoryは、これまでにやり取りしたすべてのメッセージを保持するため、コンテキストウィンドウをすぐに消費します。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はハイブリッドなアプローチです。正確性のために直近のターンをそのまま保持し、max_token_limitを超えた古いターンだけを要約します。これにより、最近のコンテキストは正確に呼び出せ、古いコンテキストは圧縮された形で呼び出せます。

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のメモリクラスを使わず、切り詰めを完全に制御したい場合があります。簡単な方法は、すべてのメッセージを保持し、合計トークン数が予算内に収まるまで、システム以外の最も古いメッセージを1件ずつ削除することです。

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トークンを超えた時点で、直近2ターンを除くすべてを要約し、それらを要約に置き換えます。

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に永続化する方法を見ていきます。

よくある質問

「Summary MemoryとTokenを考慮した切り詰め」レッスンは無料ですか?

はい。「Summary MemoryとTokenを考慮した切り詰め」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Engineering Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Engineering Academyコースには全4レッスンが含まれています。

「Summary MemoryとTokenを考慮した切り詰め」で何を学びますか?

ConversationSummaryMemoryを使って古いターンを自動的に要約し、ユーザーが以前に述べた重要な事実を保持しながら会話を簡潔に保ちます。 ブラウザで直接実行するハンズオンコードでAI Engineering Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Engineering Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Engineering Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「Summary MemoryとTokenを考慮した切り詰め」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Engineering Academyレッスンでコードを書いて実行できますか?

はい。すべてのAI Engineering Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. StatelessなLLMに外部メモリが必要な理由
  2. Buffer MemoryとWindow Memory
  3. Summary MemoryとTokenを考慮した切り詰め
  4. RedisとPostgreSQLへのチャット履歴の永続化
← AI Engineering Academyに戻る