MetaGPTのJSON解析エラー修正とGemini 1.5 Flash 8bの設定方法

## はじめに

MetaGPTは強力なAIエージェントフレームワークですが、使用中にJSONの解析エラーが発生することがあります。また、デフォルトではGeminiモデルが「gemini-pro」にハードコードされているため、無料で長いコンテキストが使える「gemini-1.5-flash-8b」を利用できません。この記事では、これらの問題を解決する方法を紹介します。

## 問題点1: JSONの解析エラー

MetaGPTを使用中に以下のようなエラーが発生することがあります：

```
Expecting value: line 5 column 26 (char 109)
RetryError[<Future at 0x184b35f6ad0 state=finished raised JSONDecodeError>]
```

これは、Google Gemini APIから返されたJSONが不正な形式であることが原因です。特に、JSONにコメントが含まれている場合に発生します。

## 問題点2: Geminiモデルのハードコーディング

MetaGPTのコードを確認すると、`provider/google_gemini_api.py`ファイルの中で、Geminiモデルが以下のようにハードコードされていることがわかります：

```python
def __init__(self, config: LLMConfig):
    self.use_system_prompt = False  # google gemini has no system prompt when use api
    self.__init_gemini(config)
    self.config = config
    self.model = "gemini-pro"  # so far only one model
    self.pricing_plan = self.config.pricing_plan or self.model
    self.llm = GeminiGenerativeModel(model_name=self.model)
```

これでは、設定ファイルでモデルを変更することができず、無料で長いコンテキストが利用できる「gemini-1.5-flash-8b」を利用できません。

## 解決方法1: JSONの解析エラーを修正する

### 1. `repair_llm_output`を有効にする

まず、MetaGPTの設定ファイル（`config2.py`）で`repair_llm_output`を`True`に設定します：

```python
# Misc Parameters
repair_llm_output: bool = True
```

### 2. Google Gemini用のカスタムポストプロセスプラグインを作成する

次に、Google Gemini APIからの応答を処理するためのカスタムポストプロセスプラグインを作成します：

```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Desc   : Google Gemini postprocess plugin to handle JSON parsing issues

import re
from typing import Union

from metagpt.provider.postprocess.base_postprocess_plugin import BasePostProcessPlugin
from metagpt.utils.repair_llm_raw_output import retry_parse_json_text


class GeminiPostProcessPlugin(BasePostProcessPlugin):
    model = "gemini"  # the plugin of the `model`, use to judge in `llm_postprocess`

    def run_retry_parse_json_text(self, content: str) -> Union[dict, list]:
        """
        Override the base method to handle Gemini-specific JSON parsing issues
        """
        # Remove comments from JSON content
        content = self._remove_comments(content)
        
        # Try to parse the JSON
        try:
            parsed_data = retry_parse_json_text(output=content)
            return parsed_data
        except Exception as e:
            # If parsing fails, try to fix common Gemini JSON issues
            content = self._fix_gemini_json_issues(content)
            parsed_data = retry_parse_json_text(output=content)
            return parsed_data
    
    def _remove_comments(self, content: str) -> str:
        """
        Remove comments from JSON content
        """
        # Remove single line comments (# or //)
        content = re.sub(r'(?<!["\'\\])#.*?(?=\n|$)', '', content)
        content = re.sub(r'(?<!["\'\\])//.*?(?=\n|$)', '', content)
        
        return content
    
    def _fix_gemini_json_issues(self, content: str) -> str:
        """
        Fix common Gemini JSON issues
        """
        # Fix missing commas
        content = re.sub(r'"\s*\n\s*"', '",\n"', content)
        content = re.sub(r'}\s*\n\s*{', '},\n{', content)
        content = re.sub(r']\s*\n\s*\[', '],\n[', content)
        content = re.sub(r'"\s*\n\s*{', '",\n{', content)
        content = re.sub(r'"\s*\n\s*\[', '",\n[', content)
        
        # Fix trailing commas
        content = re.sub(r',\s*}', '}', content)
        content = re.sub(r',\s*]', ']', content)
        
        return content
```

### 3. プラグインを登録する

`provider/postprocess/__init__.py`ファイルを編集して、プラグインを登録します：

```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Desc   : init for postprocess plugins

from metagpt.provider.postprocess.base_postprocess_plugin import BasePostProcessPlugin
from metagpt.provider.postprocess.gemini_postprocess_plugin import GeminiPostProcessPlugin

__all__ = ["BasePostProcessPlugin", "GeminiPostProcessPlugin"]
```

### 4. Google Gemini APIでプラグインを使用する

`provider/google_gemini_api.py`ファイルを編集して、プラグインを使用するように設定します：

```python
from metagpt.provider.postprocess.gemini_postprocess_plugin import GeminiPostProcessPlugin

# ...

def __init__(self, config: LLMConfig):
    self.use_system_prompt = False  # google gemini has no system prompt when use api
    self.__init_gemini(config)
    self.config = config
    self.model = config.model or "gemini-pro"  # デフォルトはgemini-proだが、設定ファイルで上書き可能
    self.pricing_plan = self.config.pricing_plan or self.model
    self.llm = GeminiGenerativeModel(model_name=self.model)
    self.postprocess_plugin = GeminiPostProcessPlugin()
```

### 5. プロンプトの改善

JSONの形式を正確に指定するプロンプトを追加します：

```python
def format_msg(self, messages: Union[str, Message, list[dict], list[Message], list[str]]) -> list[dict]:
    """convert messages to list[dict]."""
    from metagpt.schema import Message

    if isinstance(messages, str):
        # Add JSON formatting instruction for string messages
        if "JSON" in messages or "json" in messages:
            messages = f"{messages}\n\nIMPORTANT: Return valid JSON without comments. Do not include # comments in the JSON response."
        return [self._user_msg(messages)]
```

## 解決方法2: Gemini 1.5 Flash 8bを設定ファイルから指定できるようにする

以下のように、ハードコードされた「gemini-pro」を設定ファイルから読み込むように修正します：

```python
def __init__(self, config: LLMConfig):
    self.use_system_prompt = False  # google gemini has no system prompt when use api
    self.__init_gemini(config)
    self.config = config
    self.model = config.model or "gemini-pro"  # デフォルトはgemini-proだが、設定ファイルで上書き可能
    self.pricing_plan = self.config.pricing_plan or self.model
    self.llm = GeminiGenerativeModel(model_name=self.model)
```

この修正により、設定ファイル（`~/.metagpt/config2.yaml`）で「gemini-1.5-flash-8b」を指定できるようになります。

## 実際の修正手順

1. MetaGPTのインストールディレクトリを確認します
   ```bash
   pip show metagpt | grep Location
   ```

2. `config2.py`ファイルを編集して`repair_llm_output`を`True`に設定します
   ```bash
   # Windowsの場合
   notepad <インストールパス>/metagpt/config2.py
   
   # Linuxの場合
   nano <インストールパス>/metagpt/config2.py
   ```

3. `google_gemini_api.py`ファイルを編集します
   ```bash
   # Windowsの場合
   notepad <インストールパス>/metagpt/provider/google_gemini_api.py
   
   # Linuxの場合
   nano <インストールパス>/metagpt/provider/google_gemini_api.py
   ```

4. 53行目付近の`self.model = "gemini-pro"`を`self.model = config.model or "gemini-pro"`に変更します

5. カスタムポストプロセスプラグインを作成します
   ```bash
   # Windowsの場合
   notepad <インストールパス>/metagpt/provider/postprocess/gemini_postprocess_plugin.py
   
   # Linuxの場合
   nano <インストールパス>/metagpt/provider/postprocess/gemini_postprocess_plugin.py
   ```

6. ファイルを保存して閉じます

## Gemini 1.5 Flash 8bの特徴

「gemini-1.5-flash-8b」モデルには、以下の特徴があります：

- **無料で利用可能**：Google AI Studioで無料枠として提供
- **長いコンテキスト**：最大128Kトークン（約10万単語）を処理可能
- **高速処理**：軽量設計のため、レスポンスが速い
- **高い頻度**：1分あたり最大60リクエスト可能

この無料モデルは、通常有料のような長いコンテキスト処理が可能で、MetaGPTのようなエージェントフレームワークで特に有用です。

## 設定ファイルの例

以下は、gemini-1.5-flash-8bを使用するための設定ファイルの例です：

```yaml
llm:
  api_key: "YOUR_GEMINI_API_KEY"
  api_type: "gemini"
  model: "gemini-1.5-flash-8b"  # 無料で長いコンテキストが使えるモデルを指定
  temperature: 0.3
  max_token: 4096
  timeout: 600
# グローバルプロキシ設定（必要な場合）
# proxy: "http://127.0.0.1:7890"
# ワークスペース設定
workspace:
  root: ""  # 空の場合、デフォルトのワークスペースが使用されます
# 言語設定
language: "Japanese"  # 出力言語を日本語に設定
# JSON修復機能を有効化
repair_llm_output: true
```

## 利点

この修正により、以下のメリットがあります：

1. **JSONエラーの解消**：LLMからの応答に含まれるJSONの解析エラーを自動的に修復
2. **無料で長いコンテキスト**：有料プランを契約せずに、長いテキストやコードを処理できる
3. **設定ファイルだけで変更可能**：コードを修正する必要がない
4. **コスト削減**：通常有料で提供される長いコンテキスト処理を無料で実現
5. **将来のモデル対応**：Googleが新しいモデルをリリースした場合も、設定ファイルの変更だけで対応可能

## まとめ

MetaGPTのJSON解析エラーを修正し、Gemini 1.5 Flash 8bモデルを設定ファイルから指定できるようにする方法を紹介しました。これらの修正により、MetaGPTの安定性が向上し、無料で長いコンテキスト処理が可能になります。

この小さな改善がMetaGPTコミュニティにとって役立つことを願っています。オープンソースプロジェクトへの貢献は、多くのユーザーにとって大きな価値をもたらすことがあります。