はじめに
会話データは AI Agent 運用において最も貴重な資産の一つです。会話記録を分析することで、ユーザーの本当のニーズを理解し、Agent の改善点を発見し、プロンプト効果を最適化し、コスト消費を計算できます。OpenClaw はすべてのセッションデータを JSONL 形式で保存し、柔軟なエクスポートツールと豊富な分析ディメンションを提供しています。
本記事では、JSONL セッションファイルの構造、エクスポート方法、実際のデータ分析シナリオを詳しく紹介します。
JSONL セッションファイル構造
ファイルの場所
OpenClaw のセッションデータはデフォルトで以下のパスに保存されます。
~/.openclaw/agents/<agentId>/sessions/
├── session-abc123.jsonl
├── session-def456.jsonl
└── session-ghi789.jsonl
メッセージ形式
各行は独立した JSON オブジェクトで、1件のメッセージを表します。
{"id":"msg_001","parentId":null,"role":"user","content":"こんにちは","timestamp":1710400000,"metadata":{"userId":"user_123","platform":"telegram","channelType":"dm"}}
{"id":"msg_002","parentId":"msg_001","role":"assistant","content":"こんにちは!何かお手伝いできますか?","timestamp":1710400002,"metadata":{"model":"claude-sonnet-4-20250514","inputTokens":45,"outputTokens":12}}
{"id":"msg_003","parentId":"msg_002","role":"user","content":"Pythonのコードを書いて","timestamp":1710400010,"metadata":{"userId":"user_123"}}
{"id":"msg_004","parentId":"msg_003","role":"assistant","content":"はい、こちらがサンプルコードです...","timestamp":1710400015,"metadata":{"model":"claude-sonnet-4-20250514","inputTokens":120,"outputTokens":280,"toolsUsed":["run_code"]}}
メッセージフィールドの説明
| フィールド | 型 | 説明 |
|---|---|---|
id |
string | メッセージ一意ID |
parentId |
string/null | 親メッセージID、ツリー構造を形成 |
role |
string | ロール:user / assistant / system / tool |
content |
string | メッセージ内容 |
timestamp |
number | Unix タイムスタンプ(秒) |
type |
string | 特殊タイプ:compaction / edit / branch |
metadata |
object | メタデータ |
メタデータフィールド
{
"metadata": {
"userId": "user_123",
"platform": "telegram",
"channelType": "dm",
"channelId": "chat_456",
"model": "claude-sonnet-4-20250514",
"inputTokens": 150,
"outputTokens": 320,
"totalTokens": 470,
"latencyMs": 2340,
"toolsUsed": ["search", "run_code"],
"costUsd": 0.0023
}
}
会話記録のエクスポート
コマンドラインエクスポート
# 特定Agentの全セッションをエクスポート
openclaw export --agent my-agent --output ./export/
# 指定期間でエクスポート
openclaw export --agent my-agent \
--from "2026-03-01" --to "2026-03-14" \
--output ./export/march.jsonl
# CSV形式でエクスポート
openclaw export --agent my-agent \
--format csv --output ./export/conversations.csv
# JSON形式でエクスポート(完全なツリー構造付き)
openclaw export --agent my-agent \
--format json --output ./export/conversations.json
# 特定ユーザーの会話のみエクスポート
openclaw export --agent my-agent \
--user-id "user_123" --output ./export/user123.jsonl
# 特定プラットフォームの会話のみエクスポート
openclaw export --agent my-agent \
--platform telegram --output ./export/telegram.jsonl
API エクスポート
# API経由でエクスポート
curl -X GET "http://localhost:3000/api/v1/export/conversations" \
-H "Authorization: Bearer sk-openclaw-xxx" \
-G -d "agentId=my-agent" \
-d "from=2026-03-01" \
-d "to=2026-03-14" \
-d "format=jsonl" \
-o conversations.jsonl
Dashboard エクスポート
OpenClaw Web Dashboard はビジュアルなエクスポートインターフェースを提供しています。
- Dashboard → セッション管理に移動
- フィルタ条件を設定(期間、Agent、プラットフォームなど) 3.「エクスポート」ボタンをクリック
- 形式を選択(JSONL / CSV / JSON)
- ファイルをダウンロード
データ分析の実践
Python 分析スクリプト
基本データの読み込み
import json
from datetime import datetime
from collections import Counter, defaultdict
def load_sessions(filepath):
messages = []
with open(filepath, "r", encoding="utf-8") as f:
for line in f:
if line.strip():
messages.append(json.loads(line))
return messages
messages = load_sessions("./export/conversations.jsonl")
print(f"総メッセージ数: {len(messages)}")
分析1:Token 消費統計
def analyze_token_usage(messages):
total_input = 0
total_output = 0
daily_usage = defaultdict(lambda: {"input": 0, "output": 0})
for msg in messages:
if msg["role"] == "assistant" and "metadata" in msg:
meta = msg["metadata"]
input_t = meta.get("inputTokens", 0)
output_t = meta.get("outputTokens", 0)
total_input += input_t
total_output += output_t
date = datetime.fromtimestamp(msg["timestamp"]).strftime("%Y-%m-%d")
daily_usage[date]["input"] += input_t
daily_usage[date]["output"] += output_t
print(f"総入力Token: {total_input:,}")
print(f"総出力Token: {total_output:,}")
print(f"日平均入力Token: {total_input // max(len(daily_usage), 1):,}")
return daily_usage
usage = analyze_token_usage(messages)
分析2:ユーザーアクティビティ
def analyze_user_activity(messages):
user_messages = Counter()
user_sessions = defaultdict(set)
for msg in messages:
if msg["role"] == "user" and "metadata" in msg:
uid = msg["metadata"].get("userId", "unknown")
user_messages[uid] += 1
print("Top 10 アクティブユーザー:")
for uid, count in user_messages.most_common(10):
print(f" {uid}: {count} 件のメッセージ")
return user_messages
activity = analyze_user_activity(messages)
分析3:よくある質問のカテゴリ分類
def categorize_topics(messages):
"""シンプルなキーワード分類"""
categories = {
"技術的な質問": ["コード", "バグ", "エラー", "デプロイ", "設定"],
"製品に関する問い合わせ": ["価格", "機能", "比較", "トライアル"],
"使い方のヘルプ": ["どうやって", "方法", "チュートリアル", "手順"],
"フィードバック・提案": ["提案", "希望", "改善", "使いにくい"]
}
results = Counter()
for msg in messages:
if msg["role"] == "user":
content = msg["content"]
for category, keywords in categories.items():
if any(kw in content for kw in keywords):
results[category] += 1
break
return results
topics = categorize_topics(messages)
for topic, count in topics.most_common():
print(f" {topic}: {count}")
分析4:レスポンスレイテンシ分布
def analyze_latency(messages):
latencies = []
for msg in messages:
if msg["role"] == "assistant" and "metadata" in msg:
latency = msg["metadata"].get("latencyMs")
if latency:
latencies.append(latency)
if latencies:
latencies.sort()
print(f"平均レイテンシ: {sum(latencies)/len(latencies):.0f}ms")
print(f"P50レイテンシ: {latencies[len(latencies)//2]:.0f}ms")
print(f"P95レイテンシ: {latencies[int(len(latencies)*0.95)]:.0f}ms")
print(f"P99レイテンシ: {latencies[int(len(latencies)*0.99)]:.0f}ms")
analyze_latency(messages)
分析5:ツール使用統計
def analyze_tool_usage(messages):
tool_counts = Counter()
for msg in messages:
if msg["role"] == "assistant" and "metadata" in msg:
tools = msg["metadata"].get("toolsUsed", [])
for tool in tools:
tool_counts[tool] += 1
print("ツール使用頻度:")
for tool, count in tool_counts.most_common():
print(f" {tool}: {count} 回")
analyze_tool_usage(messages)
データの可視化
matplotlib でグラフを描画
import matplotlib.pyplot as plt
def plot_daily_usage(daily_usage):
dates = sorted(daily_usage.keys())
input_tokens = [daily_usage[d]["input"] for d in dates]
output_tokens = [daily_usage[d]["output"] for d in dates]
fig, ax = plt.subplots(figsize=(12, 6))
ax.bar(dates, input_tokens, label="入力Token", alpha=0.7)
ax.bar(dates, output_tokens, bottom=input_tokens,
label="出力Token", alpha=0.7)
ax.set_xlabel("日付")
ax.set_ylabel("Token数")
ax.set_title("日次Token消費トレンド")
ax.legend()
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig("daily_token_usage.png", dpi=150)
plot_daily_usage(usage)
プライバシーとコンプライアンス
データ匿名化エクスポート
# エクスポート時に自動匿名化
openclaw export --agent my-agent \
--anonymize \
--redact-patterns "phone,email,id_card" \
--output ./export/anonymized.jsonl
データ保持ポリシー
{
storage: {
retention: {
// セッションデータの保持日数
sessionTTL: 90, // 90日後に自動クリーンアップ
// エクスポートデータの保持日数
exportTTL: 30,
// 削除前に自動アーカイブするかどうか
archiveBeforeDelete: true,
archiveDir: "./archive/"
}
}
}
まとめ
OpenClaw の JSONL セッション保存形式はシンプルかつ強力です。1行1メッセージ、JSON 形式で解析が容易、ツリー構造で会話の分岐を完全に記録します。コマンドライン、API、Dashboard からデータをエクスポートした後、Python などのツールで Token 消費、ユーザーアクティビティ、トピック分布からレスポンスレイテンシまで、AI Agent の運用状況を多角的に把握する深い分析が可能です。これらのデータインサイトにより、Agent のパフォーマンスとコスト効率を継続的に最適化できます。