めもちょー

メモ帳代わりに使っています。

生成AIとastモジュールによるPythonコードの効率的な構造化

前回の記事で下記のPythonコードから、astとtokenizeモジュールを用いてJSONに構造化する方法を記しました。

from dataclasses import dataclass


@dataclass
class LogA:
    """Aの情報を表すログ"""

    __logname__ = "log_a"

    a1: int  # Aの1つ目の情報
    a2: str  # Aの2つ目の情報


@dataclass
class LogB:
    """Bの情報を表すログ"""

    __logname__ = "log_b"

    b1: int  # Bの1つ目の情報
    b2: str
    b3: float  # Bの3つ目の情報


@dataclass
class LogC:
    """Cの情報を表すログ"""

    __logname__ = "log_c"

    c1: int
    c2: str  # Cの2つ目の情報
    c3: float  # Cの3つ目の情報
    c4: bool  # Cの4つ目の情報

このPythonコードを次のJSONに変換する問題を考えます。

{
    "name": "log_a",
    "desc": "Aの情報を表すログ",
    "columns": [
        {"name": "a1", "desc": "Aの1つ目の情報"},
        {"name": "a2", "desc": "Aの2つ目の情報"},
    ]
}
{
    "name": "log_b",
    "desc": "Bの情報を表すログ",
    "columns": [
        {"name": "b1", "desc": "Bの1つ目の情報"},
        {"name": "b2", "desc": ""},
        {"name": "b3", "desc": "Bの3つ目の情報"},
    ]
}
{
    "name": "log_c",
    "desc": "Cの情報を表すログ",
    "columns": [
        {"name": "c1", "desc": ""},
        {"name": "c2", "desc": "Cの2つ目の情報"},
        {"name": "c3", "desc": "Cの3つ目の情報"},
        {"name": "c4", "desc": "Cの4つ目の情報"},
    ]
}

本記事では、これを生成AIで構造化する方法を記します。
最初に示したPythonコードの例のようにクラスのみがまとまっていれば構造化しやすそうですが、実際のPythonコードにはimport文や関数など構造化対象の部分以外の余計な記述がたくさんあります。
変換対象ではない余計な入力が多いと、生成AIを惑わせることになり、精度の悪い生成結果をもたらしてしまいます。

from dataclasses import dataclass
import xxx
import yyy
import zzz


@dataclass
class LogA:
    """Aの情報を表すログ"""

    __logname__ = "log_a"

    a1: int  # Aの1つ目の情報
    a2: str  # Aの2つ目の情報


def hoge():
    print(1)

def fuga():
    print(2)

@dataclass
class LogB:
    """Bの情報を表すログ"""

    __logname__ = "log_b"

    b1: int  # Bの1つ目の情報
    b2: str
    b3: float  # Bの3つ目の情報


def poyo():
    print(3)


@dataclass
class LogC:
    """Cの情報を表すログ"""

    __logname__ = "log_c"

    c1: int
    c2: str  # Cの2つ目の情報
    c3: float  # Cの3つ目の情報
    c4: bool  # Cの4つ目の情報

上記のようなコードからクラスの部分を切り取るのに再びastモジュールが役に立ちます。
astモジュールを用いることで、クラスごとのノードを取得でき、同時にノードの開始行数、終了行数を取得することができます。
これにより、上記のコードからクラスの先頭行、終了行を取得し、対象行数のスライスを取り、その部分を結合し下記を得ます。

@dataclass
class LogA:
    """Aの情報を表すログ"""

    __logname__ = "log_a"

    a1: int  # Aの1つ目の情報
    a2: str  # Aの2つ目の情報
@dataclass
class LogB:
    """Bの情報を表すログ"""

    __logname__ = "log_b"

    b1: int  # Bの1つ目の情報
    b2: str
    b3: float  # Bの3つ目の情報
@dataclass
class LogC:
    """Cの情報を表すログ"""

    __logname__ = "log_c"

    c1: int
    c2: str  # Cの2つ目の情報
    c3: float  # Cの3つ目の情報
    c4: bool  # Cの4つ目の情報

これを順に生成AIに入力していきます。
トークン作成には下記のような文字列を使います。

token="""
Pythonコードが入力として与えられます。
Pythonコードに書かれているclassの情報を解析して「ログ名」「ログの説明」、各フィールドに対して「フィールド名」「フィールドの説明」の情報を表すJSONを作成してください。

[コード例]が与えられた場合、[出力例]のようなJSONを返して下さい。
[コード例]
```python
@dataclass
class LogX:
    \"\"\"Xの情報を表すログ\"\"\"

    __logname__ = "log_x"

    x1: int  # Xの1つ目の情報
    x2: str  # Xの2つ目の情報
    x3: str  # Xの3つ目の情報
```

[出力例]
```
{
    "name": "log_x",
    "desc": "Xの情報を表すログ",
    "columns": [
        {"name": "x1", "desc": "Xの1つ目の情報"},
        {"name": "x2", "desc": "Xの2つ目の情報"},
        {"name": "x3", "desc": "Xの3つ目の情報"}
    ]
}
```

下記がJSON化してほしいPythonコードになります。
```python
@dataclass
class LogA:
    \"\"\"Aの情報を表すログ\"\"\"

    __logname__ = "log_a"

    a1: int  # Aの1つ目の情報
    a2: str  # Aの2つ目の情報
```

jsonの部分のみを返してください。
出力結果に```や```jsonは必要ありません。
columnsの最後の要素に,は必要ありません。
"""