AIに渡すAndroidログを大幅削減する — LLM向け意味圧縮パイプラインの設計

AIに渡すAndroidログを大幅削減する — LLM向け意味圧縮パイプラインの設計

はじめに

Claude CodeやCursorでAndroid開発をしていると、必ず壁にぶつかります。

  • logcatをそのまま貼ると数千行
  • UIAutomator XMLは1画面で数百KB
  • adb dumpsys の出力は何万行にもなる

単純にトークン数を削減するだけなら通常のgzip圧縮で済みます。しかし**LLMに渡すデータには「意味圧縮」**が必要です。目的が根本的に違うからです。

通常の圧縮 LLM向け意味圧縮
目的 バイト数を減らす reasoningノイズを減らす
基準 再現可能性 AIの理解しやすさ
出力 元データと等価 意味的に等価

多段パイプライン構成

LLM向け意味圧縮の基本構成はこうです。

input
  ↓ parser           (構造化)
  ↓ normalizer       (表記揺れ削除)
  ↓ dedupe           (重複除去)
  ↓ semantic reduction (不要情報除去)
  ↓ structuring      (整理)
  ↓ encoder          (TOON/DSL化)
  ↓ LLM

各ステップを順に説明します。


1. Parser — まず構造化する

入力を構造化することが最初の仕事です。

logcatの例:

05-28 10:00:00 E MyApp: NullPointerException

{
  "time": "05-28 10:00:00",
  "level": "E",
  "tag": "MyApp",
  "message": "NullPointerException"
}

構造化することで後段のフィルタリングが容易になります。


2. Normalizer — 表記揺れを削除する

同じ意味の情報がフォーマット違いで重複しているのを統一します。

package名の短縮:

com.example.myapp.feature.login.LoginViewModel

LoginViewModel

例外クラスの正規化:

java.lang.NullPointerException

NPE

3. Dedupe — 重複を除去する

LLMにとって繰り返しはほぼ有害です。

入力:

Loading...
Loading...
Loading...
Loading...

圧縮後:

Loading... x4

さらに重大な例として、同じstacktraceが100回繰り返される場合:

EXCEPTION repeated x100 { type=NPE source=LoginViewModel.kt line=42 }

これだけで大量のトークンを節約できます。


4. Semantic Reduction — AIに不要なものを落とす

ここがLLM圧縮の核心です。

Android特有の除外すべきノイズログ

以下はAIのデバッグに必要ないノイズです。

タグ 内容
BufferQueue グラフィックスシステム内部
OpenGLRenderer レンダリングエンジン
libEGL OpenGL ES
AudioTrack 音声内部処理
TrafficStats ネットワーク統計
chatty ログ省略メッセージ
GC_ ガベージコレクション

フィルタ例:

NOISE_PATTERNS = [
    r"BufferQueue",
    r"OpenGLRenderer",
    r"libEGL",
    r"AudioTrack",
    r"chatty",
    r"GC_",
]

def filter_noise(line: str) -> bool:
    return not any(re.search(p, line) for p in NOISE_PATTERNS)

残すべき重要ログ

Exception / ANR / Activity lifecycle / Network error / Firebase / WorkManager

5. Structuring — LLMが理解しやすい形に整理する

LLMは整理された情報に強いです。

Before:

MainActivity created
Fragment onResume called
API request started

After:

ACTIVITY{ name=MainActivity state=created }
FRAGMENT{ state=onResume }
API{ state=start }

ラベルを明示するだけで、AIの理解率が大きく変わります。


6. Encoder — TOON/DSL化

最後に独自フォーマット(TOON)に変換します。

events[3]{type,target,state}:
  ACT|MainActivity|created
  API|LoginApi|start
  ERR|Auth|401

1行でより多くの情報を表現でき、コンテキスト内に収まる情報量が増えます。


UIAutomatorとUI tree圧縮

Android MCPを使う場合、UIAutomator XMLはさらに冗長です。

元のXML(抜粋):

{
  "x": 120,
  "y": 400,
  "width": 200,
  "height": 48,
  "padding": 0,
  "alpha": 1.0,
  "focusable": false,
  "clickable": true,
  "text": "Login"
}

圧縮後:

BTN[text=Login]

LLMにとってpaddingやalphaはほぼ不要です。clickable=true かつ text があるものだけ残すルールで圧縮すると、同じ情報を1/20以下のトークンで渡せます。


ドメイン特化が一番効く

汎用圧縮より、Android専用の圧縮ロジックを持つことが最も効果的です。

ログカテゴリ自動分類

CATEGORIES = {
    "lifecycle":   ["onCreate", "onResume", "onPause", "onDestroy"],
    "crash":       ["Exception", "ANR", "Fatal"],
    "network":     ["Retrofit", "OkHttp", "HttpException"],
    "database":    ["Room", "SQLite"],
    "compose":     ["Recomposition", "Composition"],
    "firebase":    ["Firebase", "Firestore", "FCM"],
    "workmanager": ["WorkManager", "Worker"],
}

カテゴリ別に分類してAIに渡すと、関連情報がまとまってデバッグ精度が上がります。

重要度スコアによるコンテキスト制御

priority=10  EXCEPTION / ANR
priority=8   Network error
priority=5   Lifecycle event
priority=3   Debug log
priority=1   Verbose

コンテキスト制限があるときは高優先度から埋めていきます。


用途別コマンド化

スラッシュコマンド形式にするとAIとの連携が自然になります。

/logcat     → logcat専用圧縮
/uiauto     → UIAutomator XML専用圧縮
/json       → 大型JSON専用圧縮
/stacktrace → stacktraceのみ抽出

使用例:

/logcat
05-28 10:00:00 D MyApp: Loading
05-28 10:00:01 D MyApp: Loading
05-28 10:00:02 E MyApp: NullPointerException at LoginViewModel.kt:42

LOG{
  repeated[ "Loading" x2 ]
  error{ type=NPE source=LoginViewModel.kt line=42 }
}

MCPサーバー化まで

このパイプラインはMCPサーバーとして実装できます。

@mcp.tool()
def compress_logcat(text: str) -> str:
    return run_pipeline(text, mode="logcat")

@mcp.tool()
def compress_uiauto(xml: str) -> str:
    return run_pipeline(xml, mode="uiauto")

@mcp.tool()
def compress_json(data: str) -> str:
    return run_pipeline(data, mode="json")

Claude DesktopやClaude Codeからツールとして自動的に呼び出せるようになります。圧縮を意識せず使えるのが理想形です。


まとめ

ステップ 効果
Parser 後段フィルタリングを可能にする
Normalizer 同一情報の重複を排除する
Dedupe 繰り返しログを折りたたむ
Semantic Reduction AIに不要なノイズを除く
Structuring AIが理解しやすい形に整える
Encoder (TOON) 情報密度を高める

重要なのは「AIに不要なものを知っていること」です。通常の圧縮はバイトを減らしますが、LLM向け意味圧縮はreasoningノイズを減らします。この違いを意識することで、Android MCPとAIの組み合わせが格段に強くなります。