株式会社ホコサキ

LLMに送る直前にトークンを削る、headroomの使い方

天京祐輔
天京祐輔
LLMに送る直前にトークンを削る、headroomの使い方

RAGパイプラインを組んでいると、DBのクエリ結果をそのまま messages に突っ込んでいませんか。
ログ監視エージェントを作ると、数千行のログをまとめてLLMに投げていませんか。
コストが膨らんだり、レート制限に引っかかったり、コンテキストが溢れたり——その原因の多くは「渡しているデータの構造的なノイズ」にあります。

プロンプトを書き直すでも、モデルを変えるでもなく、既存コードに数行足すだけで動く第三の手段があります。
LLMに送る 直前 にトークンを削る前処理アプローチ——それを実装するOSSライブラリが headroom です。
JSON・ログ・RAGチャンクという現場でよく扱うデータ種別ごとに、圧縮がどう効くかをコード例とともに確かめます。

入力トークンが膨らむ、本当の原因

LLMのAPI料金が想定より高くなる理由、レート制限(TPM)に引っかかる理由、コンテキストウィンドウが溢れる理由——この3つは実は同じ根っこから来ています。

「LLMに渡しているデータの大半が、答えを出すのに必要のない構造的なノイズだ」という観察です。

たとえばDBのクエリ結果をJSON形式で渡すとき、フィールド名は何百行にもわたって繰り返されます。
ログファイルを丸ごと渡すとき、正常系のINFOログが膨大なスペースを占めます。
RAGで検索したチャンクを渡すとき、文書構造を示すメタデータや改行・空白が大量に含まれます。
LLMはトークン単位で課金され、コンテキストウィンドウにも上限があります。
でも実際に「意味を運んでいるトークン」と「構造を保持しているだけのトークン」は別物です。

既存の対処法は、それぞれ違う問題を狙い撃ちにしています。
チャンキングは検索精度の問題を解くものであって、渡すデータの密度そのものは変わりません。
要約はトークン数を削れますが、どの情報を捨てるかをモデルが判断するため、精度の低下リスクを伴います。
モデルのダウングレードは入力コストを下げますが、精度とのトレードオフが生まれます。

「送る直前に削る」前処理圧縮はこれらとは独立したレイヤーで機能します。
意味を保ちながら冗長な構造だけを取り除くので、他の対処法と重ねて使えます。
プロンプトエンジニアリングとも競合しません。

headroomが何をしているか——3つのモードと圧縮の仕組み

headroom(headroomlabs-ai/headroom、Apache 2.0)はOSSのコンテキスト圧縮ライブラリです。
ツール出力・ログ・RAGチャンク・ファイル・会話履歴をLLMに届く前にローカルで圧縮します。
圧縮処理はマシン上で完結し、プロンプトの内容が外部サーバーに送信されない点は、業務データを扱う場面では安心できる設計です。

圧縮アルゴリズムはコンテンツの種類に応じて自動で選ばれます。
JSON・DBクエリ結果・構造化データには SmartCrusher が使われ、繰り返し出現するキーや空白・冗長な構造を正規化します。
PythonやJS・Goなどのコードには CodeCompressor(AST解析ベース)が使われ、コメントや余分な空白をASTレベルで取り除きます。
散文・会話テキストにはHuggingFaceの Kompress-base モデルが使われます。
圧縮はCCR(可逆圧縮)の仕組みで元データをローカルにキャッシュするので、必要に応じてオリジナルを参照できます。

デプロイモードは3つあり、状況に応じて選べます。

  • Pythonライブラリ: コード内で compress(messages) を呼ぶだけ。既存コードへの変更が最小限で、細かく制御したい場面に向いている。
  • OpenAI互換プロキシ: エンドポイントURLを差し替えるだけでコード変更ゼロ。チーム全体に一括適用したいときに向いている。
  • MCPサーバー: Claude CodeやCursorなどのエージェントツールに直接組み込む形。コーディングエージェント用途が主。

本記事はPythonライブラリとして既存コードに組み込む方法を主軸に進めます。

既存コードへの組み込み——JSON・ログ・RAGチャンクの実装例

まずインストールします。

pip install headroom-ai[all]

基本的な使い方はシンプルで、LLMを呼び出す直前に compress(messages) を1行挟むだけです。
以下の3パターンは、現場でよくある「重いデータを渡しているケース」に絞りました。

JSONの圧縮 (DBクエリ結果)

DBのクエリ結果をそのままプロンプトに埋め込んでいるケースです。
同じキー名が何百行にもわたって繰り返されるJSONは、SmartCrusherが最も効果を発揮するデータです。

from headroom import compress
import openai

# DBから取得したクエリ結果(数百件のJSONレコード)
query_result = fetch_orders_from_db()

messages = [
    {"role": "system", "content": "注文データを分析して異常を検出してください。"},
    {"role": "user", "content": f"以下の注文データを分析してください:\n{query_result}"}
]

# LLM呼び出し直前にこの1行を足すだけ
compressed = compress(messages)

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=compressed
)

ログの圧縮 (ログ配列)

ログ監視エージェントで数千行のログをまとめて渡しているケースです。
正常系のINFOログが大半を占め、重要なERROR・FATALは数件というパターンで圧縮がよく効きます。

from headroom import compress
import openai

# アプリケーションログを丸ごと渡していたケース
with open("app.log") as f:
    log_content = f.read()

messages = [
    {"role": "system", "content": "エラーの根本原因を特定してください。"},
    {"role": "user", "content": f"以下のログを解析してください:\n{log_content}"}
]

compressed = compress(messages)

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=compressed
)

GitHubのREADMEに載っている実証デモでは、55,957トークンのエージェントプロンプトが24,340トークンに圧縮されても、67番目のFATALエントリがバイト単位でそのまま保持されています。
大量のINFOを削りながら重要なエラー行を残す、という挙動がログ圧縮の肝です。

RAGチャンクの圧縮 (検索結果)

RAGでtop_k件取得した検索結果をそのまま渡しているケースです。
チャンクにはドキュメントのメタデータや構造的な繰り返しが多く含まれることが多く、圧縮が効きやすい傾向があります。

from headroom import compress
import openai

query = "払い戻しポリシーについて教えてください"
retrieved_chunks = vector_store.search(query, top_k=10)

messages = [
    {"role": "system", "content": "社内ドキュメントをもとに質問に答えてください。"},
    {
        "role": "user",
        "content": f"質問: {query}\n\n関連ドキュメント:\n{retrieved_chunks}"
    }
]

compressed = compress(messages)

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=compressed
)

compress() の呼び出し位置はどのケースも同じで、LLMを呼ぶ直前の1行です。
元のコード構造に手を加える必要がほとんどないのが、このアプローチの地味なメリットです。

削減率の実態——公称値と実運用データのギャップを正直に見る

公称値はJSON60〜95%削減・コーディングエージェント20%削減です。
ただ、実運用の数字はこれよりずっとおとなしいです。

50,000+セッション(250+インスタンス)の実運用データとして報告されている数字では、 中央値4.8%・平均11.3% という結果が出ています。
「60〜95%削減」との差が気になるところですが、理由は単純です。
実際のセッションには「短い会話ターン」が大量に混ざるからです。
圧縮が効かない短い入力が統計を引き下げています。

ファイル読み込みやシェル出力が多い ヘビーなツール使用セッション では40〜80%になるという数字も報告されており、効果はデータの種類と使い方によって大きく変わります。

効果が大きいデータ種別:

  • 繰り返し構造を持つJSON(DBクエリ結果・APIレスポンスなど)
  • ログ配列(正常系INFOが大半を占めるもの)
  • RAGの検索結果(メタデータや構造的な繰り返しを含むチャンク)

効果が薄いデータ種別:

  • 短い単一ターンの会話(そもそも削るものがない)
  • 散文プロンプト中心のワークフロー
  • コード生成・ソースコード変換が主体のワークフロー

処理オーバーヘッドについては、プロキシの中央値が52msです。
LLMの推論時間が2〜10秒であることを考えると、実質的に無視できるレベルです。
「圧縮のせいでレスポンスが遅くなる」という心配は、少なくともプロキシモードではほぼ不要です。

導入前に知っておきたいこと——情報欠落・デバッグ・スキップ判断

正直に言うと、圧縮には「攻めすぎるリスク」があります。

MCPツールとheadroomを組み合わせて使う場合、headroomの圧縮が積極的すぎると、MCPツールが組み立てた精密なコードコンテキストが劣化することがあります。
GitHubのディスカッションでも「アーキテクチャレベルのタスクでは圧縮を無効にすることを検討すべき」と指摘されています。
設計書の生成やリファクタリングの判断など、コード全体の文脈を正確に把握する必要があるタスクでは特に注意が必要です。

そういうときは HEADROOM_COMPRESSION 環境変数と headroom stats コマンドが役に立ちます。

# 圧縮を無効にして動作を確認する
export HEADROOM_COMPRESSION=0

# 圧縮統計を確認する
headroom stats

HEADROOM_COMPRESSION=0 を設定するだけで圧縮がバイパスされるので、「圧縮前後で答えが変わっていないか」をデバッグするのに使えます。
headroom stats は圧縮率やオーバーヘッドの統計を出力するので、自分の環境で実際にどれくらい削減できているかを把握するのに便利です。

導入をスキップしてよいケースも整理しておきます。
月額APIコストが20ドル未満のプロジェクト、短い単一ターンのみを扱うユースケース、ローカルプロセスが実行できないサンドボックス環境——この3つは圧縮効果より導入コストのほうが大きくなりがちです。

渡しているデータに「繰り返し構造のJSONやログ配列が多い」と心当たりがあれば、まず headroom stats の出力を眺めてみてください。
そこで実際の削減率が見えてから、本格的に組み込むかどうかを判断すれば十分だと思います。


株式会社ホコサキは山口県宇部市を拠点に、Web制作・業務システム開発・AI活用支援を手がけています。
LLMを使った業務改善や、既存システムへのAI組み込みについてご相談があれば、お問い合わせページ からお気軽にどうぞ。

    LLMに送る直前にトークンを削る、headroomの使い方 | 株式会社ホコサキ