LangChainのデバッグを極める：コールバックとロギングの実践的ガイド

## 🎯 この記事の目的

この記事では、LangChainを使用する際の効果的なデバッグ方法について、実践的なコード例を交えて解説します。特に`BaseCallbackHandler`を活用したデバッグ手法に焦点を当てます。

<blockquote class="twitter-tweet" data-media-max-width="800"><p lang="ja" dir="ltr">langchain のRunnableに特化してキャッチアップしていきます⑤<br>langchainでデバッグするときにめっちゃ困ってたけど、CallbackHandler使えばなんとかなりそう！！！ <a href="https://t.co/y456RYcLny">https://t.co/y456RYcLny</a> <a href="https://t.co/yJnSVC8FuJ">pic.twitter.com/yJnSVC8FuJ</a></p>&mdash; Maki@Sunwood AI Labs. (@hAru_mAki_ch) <a href="https://twitter.com/hAru_mAki_ch/status/1856739814701498833?ref_src=twsrc%5Etfw">November 13, 2024</a></blockquote>


## なぜデバッグが重要なのか？

LangChainを使用したAIアプリケーション開発において、以下の課題に直面することがよくあります：

- LLMの応答が予期せぬ形になる
- プロンプトが意図した通りに機能しない
- チェーンの実行フローが把握しづらい
- パフォーマンスのボトルネックが特定できない

これらの課題に対処するために、効果的なデバッグ手法が必要不可欠です。

## コールバックハンドラーとは

LangChainの`BaseCallbackHandler`は、チェーンの実行過程で発生する様々なイベントをフックして情報を取得できる強力な機能です。

主な特徴：
- LLMの開始・終了時の情報取得
- チェーンの実行状態の監視
- エラー発生時の詳細なログ取得
- カスタマイズ可能なデバッグ出力

## 実装方法

### 1. DebugCallbackHandlerの基本構造

```python
class DebugCallbackHandler(BaseCallbackHandler):
    def _format_message(self, message) -> Dict[str, Any]:
        return {
            "content": message.content,
            "type": message.__class__.__name__
        }

    def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs) -> None:
        formatted_prompts = {
            "prompts": prompts,
            "serialized": {
                "name": serialized.get("name", "unknown"),
                "type": serialized.get("type", "unknown")
            }
        }
        logger.debug(f"[Debug] LLM開始:\n{format_dict(formatted_prompts)}")
```

### 2. 主要なコールバックメソッド

```python
# LLM関連のコールバック
def on_llm_start(self, serialized, prompts, **kwargs)
def on_llm_end(self, response, **kwargs)
def on_llm_error(self, error, **kwargs)

# チェーン関連のコールバック
def on_chain_start(self, serialized, inputs, **kwargs)
def on_chain_end(self, outputs, **kwargs)
def on_chain_error(self, error, **kwargs)
```

### 3. デバッグ情報の整形

```python
def format_dict(d: Dict[str, Any], indent: int = 2) -> str:
    return json.dumps(d, ensure_ascii=False, indent=indent)
```

## デバッグの実践

### 1. コールバックハンドラーの設定

```python
model = ChatOpenAI(
    temperature=0.7,
    callbacks=[DebugCallbackHandler()]
)
```

### 2. パフォーマンス計測の実装

```python
@measure_execution_time
def execute_parallel_chain(chain, input_data):
    logger.debug(f"[Debug] 入力データ:\n{format_dict(input_data)}")
    result = chain.invoke(input_data)
    logger.debug(f"[Debug] 実行結果:\n{format_dict(result)}")
    return result
```

### 3. ロギングの設定

```python
logger = setup_logger()
logger.debug("[Debug] 環境変数を読み込み完了")
```

## デバッグ出力の例

実行時には以下のような詳細な情報が出力されます：

```
[Debug] LLM開始:
{
  "prompts": ["象について1文で説明してください。"],
  "serialized": {
    "name": "ChatOpenAI",
    "type": "llm"
  }
}

[Debug] LLM終了:
{
  "generations": [...],
  "token_usage": {
    "completion_tokens": 42,
    "prompt_tokens": 15,
    "total_tokens": 57
  },
  "model_name": "gpt-3.5-turbo"
}
```

## まとめ

LangChainのデバッグには、以下のポイントが重要です：

### 1. **コールバックハンドラーの活用**
   - 実行フローの可視化
   - エラーの早期発見
   - パフォーマンスの監視

### 2. **構造化されたログ出力**
   - JSON形式での整形
   - ログレベルの適切な設定
   - タイムスタンプの付加

### 3. **エラーハンドリング**
   - 例外の適切なキャッチ
   - エラー情報の詳細な記録
   - デバッグ情報の活用

これらの手法を組み合わせることで、LangChainアプリケーションの開発効率を大きく向上させることができます。


## リポジトリ

https://github.com/Sunwood-ai-labs/langchain-sandbox

## 参考リンク
- [LangChain公式ドキュメント](https://python.langchain.com/docs/get_started/introduction)
- [Callbacks in LangChain](https://python.langchain.com/docs/modules/callbacks/)

 <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>