Image of Claude Codeの対話ログをDuckDBで分析する

Claude Codeの対話ログをDuckDBで分析する

Text by 宮城 広隆

Published

Claude Codeの対話ログ(jsonl形式)をDuckDBで分析し、AIの行動を理解し、開発プロセスを改善する方法について解説します。
Table of Contents

はじめに

こんにちは、Liam のプロダクトマネージャーをしている宮城です。

みなさんは Claude Code を使っていますか?最近の私はほぼ全ての開発を Claude Code 一本で進めるようになりました。また近頃はClaude Code Actionも発表され、GitHub Actions 上で様々な作業を Claude Code に任せられるようになっています。

しかし Claude Code Action では allowed_tools の適切な設定が課題となります。Claude Code Action は非対話モードで動作し、途中で人間が介入することはできません。例えば pnpm lint:* を allowed_tools で許可しなかった場合、セッション内で lint を実行することができないため、CI が fail する状態でもタスク完了とみなしてしまいます。
また Claude Code は権限不足で自身がやりたい行動ができなかった場合でも、代替案を考えてなんとかゴールに到達しようと試みます。それは素晴らしいものの、迂回作業で会話数も増え時間とコストが増加してしまいます。

権限設定ミスを改善するために Claude Code(Claude Code Action)の対話ログからエラーを分析したいですが、jsonl 形式の膨大なログを人間の目で探すのは大変です。Gemini などコンテキストウィンドウが大きい LLM に分析させる手もありますが、より効果的に分析する方法はないかと考えました。そこで、jsonl 形式のログを duckdb で分析する方法にたどり着きました。

本記事では、Claude Code の対話ログ(jsonl 形式)を DuckDB で分析し、AI の行動を理解し、Claude Code の開発プロセスを改善する方法について解説します。

Claude Code の対話ログ

Claude Code の対話ログは、~/.claude/projects/ 以下に、プロジェクトのディレクトリごとに保存されています。ファイルは jsonl 形式で、各行が 1 つのイベント(ユーザーの発言、AI の思考、ツールの使用など)を表す JSON オブジェクトになっています。

{
  "parentUuid": null,
  "isSidechain": false,
  "userType": "external",
  "cwd": "/Users/mh4gf/ghq/github.com/liam-hq/liam",
  "sessionId": "797df13f-41e5-4ccd-9f00-d6f6b9bee0b3",
  "version": "1.0.38",
  "type": "user",
  "message": {
    "role": "user",
    "content": "<command-message>implement-regression-tests is running…</command-message>\n<command-name>/implement-regression-tests</command-name>\n<command-args>db-structure package</command-args>"
  },
  "uuid": "d02cab21-cc42-407e-80cb-6305ac542803",
  "timestamp": "2025-07-01T10:43:40.323Z"
}
{
  "parentUuid": "2e7836df-0d59-428f-b193-8a07732705c7",
  "isSidechain": false,
  "userType": "external",
  "cwd": "/Users/mh4gf/ghq/github.com/liam-hq/liam",
  "sessionId": "797df13f-41e5-4ccd-9f00-d6f6b9bee0b3",
  "version": "1.0.38",
  "message": {
    "id": "msg_01SS3c1HZmneNCpZf5WazgHq",
    "type": "message",
    "role": "assistant",
    "model": "claude-opus-4-20250514",
    "content": [
      {
        "type": "text",
        "text": "I'll implement the regression tests for the db-structure package by finding and implementing all `it.skip` tests. Let me start by searching for these tests."
      }
    ],
    "stop_reason": null,
    "stop_sequence": null,
    "usage": {
      "input_tokens": 4,
      "cache_creation_input_tokens": 6462,
      "cache_read_input_tokens": 14187,
      "output_tokens": 1,
      "service_tier": "standard"
    }
  },
  "requestId": "req_011CQgAJYLtgENSsjEPSFFGu",
  "type": "assistant",
  "uuid": "02f2a73e-d98f-4931-a22f-98cb7a9d4f56",
  "timestamp": "2025-07-01T10:43:45.885Z"
}

なぜ DuckDB なのか?

jsonl ファイルは、そのままでは人間が読んで分析するには不向きです。そこで、インプロセス OLAP データベースである DuckDB が活躍します。DuckDB は、ローカルのファイル(CSV, Parquet, JSON など)を直接 SQL でクエリできるため、事前のデータロードなしに、インタラクティブな分析を始めることができます。
また、DuckDB は CLI をインストールしておけば Claude Code が duckdb をそのまま扱えるというのも嬉しいポイントです。そのため SQL を人間が書くわけではなく、Claude Code に全て任せることができます。

分析の目的

今回の分析の主な目的は以下の 2 点です。

  1. 期待通りの結果が得られなかった場合の原因究明: Claude Code がなぜ特定のコマンドを実行し、それがなぜ失敗したのかを理解する。
  2. 権限設定の最適化: Claude Code が使用したがるツールを特定し、あらかじめ適切な権限を渡しておくことで、手戻りをなくし、開発をスムーズにする。

DuckDB を使った分析例

それでは、DuckDB を使って Claude Code に Claude Code の対話ログを分析してもらいましょう。以下は Claude Code のセッションログをほぼそのまま掲載しています。一部筆者により整形をしています。


> duckdb cliを使用し、 797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl のClaude Codeの対話ログの中で権限エラーになった tool を分析して
 
17:43
 
 I'll help you analyze the permission errors in the Claude Code conversation log using DuckDB CLI. Let me start by examining the JSONL file to
  understand its structure and then identify permission-related tool errors.
 
⏺ Update Todos
  ⎿  ☐ Examine the structure of the JSONL file to understand the data format (P0)
     ☐ Use DuckDB CLI to analyze permission errors in tool usage (P0)
     ☐ Identify which tools encountered permission errors and their context (P1)
 
⏺ Read(797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl)
  ⎿  Read 20 lines (ctrl+r to expand)
 
⏺ Let me examine the file structure to understand what I'm working with, then use DuckDB to analyze the permission errors:
 
 Update Todos
  ☐ Check the structure of the full JSONL file to understand the data format (P0)
 Set up DuckDB CLI to analyze the JSONL file (P0)
 Query for tool usage patterns and permission errors (P0)
 Analyze which tools had permission errors and their context (P1)
 
 Bash(wc -l 797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl)
       837 797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl
 
 Bash(head -1 797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl | jq keys)
  [
       "leafUuid",
       "summary",
     "type"
     ]
 
 Bash(tail -n +5 797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl | head -1 | jq keys)
  [
       "cwd",
       "isSidechain",
       "message",
       "parentUuid",
       "sessionId",
       "timestamp",
       "type",
       "userType",
       "uuid",
     "version"
     ]
 
 Update Todos
  ☒ Check the structure of the full JSONL file to understand the data format (P0)
 Set up DuckDB CLI to analyze the JSONL file (P0)
 Query for tool usage patterns and permission errors (P0)
 Analyze which tools had permission errors and their context (P1)
 
 Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');
      SELECT DISTINCT type FROM conversation;
      ")
  ┌───────────┐
   type
  varchar
     ├───────────┤
 summary
 assistant
 user
     └───────────┘
 
 Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');
      SELECT
          COUNT(*) as total_messages,
          COUNT(CASE WHEN type = 'assistant' THEN 1 END) as assistant_messages,
          COUNT(CASE WHEN type = 'user' THEN 1 END) as user_messages
      FROM conversation;
      ")
  ┌────────────────┬────────────────────┬───────────────┐
 total_messages assistant_messages user_messages
     int64       int64     int64
     ├────────────────┼────────────────────┼───────────────┤
            837                532           299
     └────────────────┴────────────────────┴───────────────┘
 
 Update Todos
  ☒ Check the structure of the full JSONL file to understand the data format (P0)
 Set up DuckDB CLI to analyze the JSONL file (P0)
 Query for tool usage patterns and permission errors (P0)
 Analyze which tools had permission errors and their context (P1)
 
17:43
 
 Now let me look for permission errors in the tool usage:
 
 Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');
      -- Look for tool usage in assistant messages
      SELECT
          COUNT(*) as messages_with_tools
      FROM conversation
      WHERE type = 'assistant'
        AND json_extract_string(message, '$.content') LIKE '%tool_use%';
      ")
  ┌─────────────────────┐
 messages_with_tools
        int64
     ├─────────────────────┤
                 286
     └─────────────────────┘
 
 Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');…)

    … +264 lines (ctrl+r to expand)
 
 
17:43
 
⏺ I can see there are entries with errors. Let me search more specifically for tool errors and permission issues:
 
⏺ Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');)

 +78 lines (ctrl+r to expand)
 
17:43
 
 Now let me look for specific tool permission errors:
 
 Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');…)
  ⎿  ┌──────────────────────────┬──────────────────────────────────────┬───────────┬──────────────┐
     │        timestamp         │                 uuid                 │ tool_name │ content_type │
     │         varchar          │                 uuid                 │  varchar  │   varchar    │
     … +24 lines (ctrl+r to expand)
 
17:43
 
⏺ Let me look for any errors in the tool results with a specific pattern:
 
⏺ Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');)
 ┌──────────────────────────┬──────────────────────────────────────┬────────────────────────────────┬──────────┬────
    ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    ─────────────────────────────────────────┐
 +117 lines (ctrl+r to expand)
 
17:43
 
 Perfect! I found the permission errors. Now let me get a summary of the different types of errors:
 
 Update Todos
  ☒ Check the structure of the full JSONL file to understand the data format (P0)
 Set up DuckDB CLI to analyze the JSONL file (P0)
 Query for tool usage patterns and permission errors (P0)
 Analyze which tools had permission errors and their context (P1)
 
 Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');…)
  ⎿  ┌───────────────────────────┬───────┐
     │        error_type         │ count │
     │          varchar          │ int64 │
     … +16 lines (ctrl+r to expand)
 
⏺ Bash(duckdb -c "
      CREATE TABLE conversation AS SELECT * FROM read_json_auto('797df13f-41e5-4ccd-9f00-d6f6b9bee0b3.jsonl');)
  ┌──────────────────────────┬───────────────────────────────────────────────────────────────────────────┐
        timestamp                               error_message
         varchar                                  varchar
 +5 lines (ctrl+r to expand)
 
 Update Todos
  ☒ Check the structure of the full JSONL file to understand the data format (P0)
 Set up DuckDB CLI to analyze the JSONL file (P0)
 Query for tool usage patterns and permission errors (P0)
 Analyze which tools had permission errors and their context (P1)
 
17:43
 
 分析結果
 
  Claude Code対話ログの権限エラー分析結果:
 
  主な権限エラーの種類と発生回数
 
  1. Bash Permission Required (3回)
    - Claude requested permissions to use Bash, but you haven't granted it yet.
    - 発生タイミング: 2025-07-01 10:56:15, 11:00:53, 11:04:43
  2. その他のエラー (9回) - 多様な技術的エラー
  3. Test Failure (8回) - テスト実行失敗
  4. No Changes Error (3回) - ファイル編集時の変更なしエラー
  5. Directory Not Found Error (2回) - ディレクトリ存在エラー
  6. String Not Found Error (2回) - 文字列置換エラー
 
  権限エラーの詳細
 
  Bashツール権限エラーが主要な権限問題で、合計3回発生しています。これらは以下の状況で発生:
 
  - Claude CodeがBashコマンドを実行しようとしたが、ユーザーがBashツールの使用許可をまだ与えていない状況
  - 対話中にユーザーが明示的にBashツールへのアクセス許可を与える必要がある状況
 
  対話の特徴
 
  - 総メッセージ数: 837
  - アシスタントメッセージ: 532
  - ユーザーメッセージ: 299
  - ツール使用メッセージ: 286
 
  権限エラーは主にファイル操作やテスト実行のためのBashコマンド実行時に発生しており、Claude Codeの安全性機能が適切に動作していることを示しています。

ここまでで、権限エラーがいくつか存在することがわかりました。Claude Code がセッション中に duckdb のクエリで失敗しても、自律的に修復してゴールまで辿り着いていることもわかります。
権限エラーの詳しい内容を調べてもらいましょう。

> 権限を追加するために、権限エラーで失敗したコマンドを特定してください。現在の設定は次のようになっています: "Read,Write,Edit,MultiEdit,Bash(pnpm fmt:\*),Bash(pnpm install),Bash(pnpm lint:\*),Bash(pnpm test:\*)"
 
~~ 省略 ~~
 
 権限エラーで失敗した 3 つの Bash コマンドを特定しました:
 
権限エラーになったコマンド
 
1. `node -e "console.log(/^\/tables\/([^/]+)\/indexes\/([^/]+)$/.test('/tables/users/indexes/idx_email/unique'))"`
 
   - Node.js JavaScript コードを実行
   - 正規表現のテスト
 
2. `npx vitest run src/diff/constraints/**tests**/buildConstraintNameDiffItem.test.ts`
 
   - npx を使った vitest の実行
   - 特定のテストファイルを直接実行
 
3. `cd /Users/mh4gf/ghq/github.com/liam-hq/liam-worktree/coverage && find . -name "\*.ts" -type f | xargs grep -l "foreignKeyConstraintReferenceOptionSchema" | head -10`
 
   - ディレクトリ移動とファイル検索
   - grep を使った文字列検索
 
推奨される追加権限
 
現在の権限に以下を追加することを推奨します:
 
Bash(node:\*),Bash(npx:\*),Bash(cd:\*),Bash(find:\*),Bash(grep:\*)
 
特に重要なのは:
 
- Bash(npx:\*) - npx コマンドの実行(vitest 等のツール実行に必要)
- Bash(node:\*) - Node.js でのスクリプト実行
- Bash(cd:\*) - ディレクトリ移動(開発フローで頻繁に使用)

このように、効率的にログファイルを分析してくれることがわかります。

終わりに

今回は、Claude Code の対話ログ(jsonl 形式)を Claude Code と DuckDB で分析する方法を紹介しました。今回は権限設定のデバッグのために利用しましたが、タスク終了後に別セッションで振り返りさせドキュメントを更新する、と言ったユースケースでも有効かと思います。

Liamの開発チームでは、AI開発ツールを積極的に活用していくだけではなく、新しいAI機能の開発を行っています。今後もそういった取り組みを紹介していきたいと思います。

Text by宮城 広隆

ROUTE06のテックリード。Web開発を専門とするフルスタックエンジニアで、複数のスタートアップで豊富な経験を積んだ後、ROUTE06に参画し、技術的な取り組みをリードしている。

Last edited on

Categories
Glossary

現在、このブログ記事に関する用語集はありません。

Related Blogs