2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

あなたが踏んだLLMのバグ、明日も誰かが踏む ― Qiitaで育てるAIバグパターン集

2
Last updated at Posted at 2026-06-26

まず見てほしい

LLMにこう頼んだ。

pandasでCSVを読み込んで、日付列で古い順にソートして

返ってきたコード。

import pandas as pd

df = pd.read_csv("data.csv")
df = df.sort_values("date")

動く。エラーは出ない。しかし 間違っている

date列がstr型のままソートされるため、"2024-1-10""2024-1-2" より前に来る。辞書順ソートだからだ。正しくはこう。

df["date"] = pd.to_datetime(df["date"])
df = df.sort_values("date")

このバグを踏んだことがある人は多いはずだ。そして明日も、別の誰かが同じバグを踏む。

人間が同じバグを繰り返すなら教育の問題だ。LLMが同じバグを繰り返すなら、それは知識共有の問題かもしれない。

あなたが直したそのバグ、捨てていないか?


製造業の品質管理から学ぶ

製造業では、不具合が起きたら「不具合報告書」を書く。

報告書番号: DR-2024-001
製品名:    ProductA
不具合内容: RS485通信モジュールにおいて通信断が発生
原因:      デカップリングコンデンサのESR劣化
対策:      低ESR品に交換
再発防止策: 入荷検査にESR測定を追加

不具合を個人の経験で終わらせず、組織の知識資産にする。同じ不具合が二度と起きないように。

LLMのバグにも同じ仕組みが必要ではないか。


提案:AIバグパターンをQiitaで共有する

新しいサイトを作る必要はない。Qiitaの既存の仕組みで実現できる。

ルール

  1. タグ AIバグパターン を使う
  2. 以下のテンプレートで書く
  3. 1記事1パターン。短くていい

テンプレート

投稿者が書くのはバグコードだけ。残りはLLMが埋める。

```jsonl
{"rejected": "df = pd.read_csv('data.csv')\ndf = df.sort_values('date')", "model": "GPT-4o", "language": "Python"}
```

これだけ貼って投稿する。

現時点では、正解コードや説明は投稿者自身か、他の読者がコメントで補完する。バグコードだけでも「このパターン、自分も踏んだ」という共有価値がある。

理想形は後述する「Qiitaへの機能提案」で述べるが、Qiita側のLLMが chosen(正解コード)・explanationprevention_prompt を自動生成し、投稿者が承認するだけで完全なJSONLが完成する仕組みだ。

{"prompt": "CSVを読み込んで日付列で古い順にソートして", "rejected": "df = pd.read_csv('data.csv')\ndf = df.sort_values('date')", "chosen": "df = pd.read_csv('data.csv')\ndf['date'] = pd.to_datetime(df['date'])\ndf = df.sort_values('date')", "explanation": "date列がstr型のままソートされ辞書順になる", "prevention_prompt": "日付列はpd.to_datetimeで変換してからソートしてください", "model": "GPT-4o", "language": "Python", "bug_type": "型の暗黙変換"}
フィールド 誰が書くか
rejected(バグコード) 投稿者
model / language 投稿者
chosen(正解コード) LLMが生成 → 人間が承認
explanation LLMが生成
prevention_prompt LLMが生成
prompt LLMがバグコードから推定
bug_type LLMが分類

コピペ1回。


サンプル投稿:よくあるLLMバグ5選

ここから先がこの記事の本体だ。実際に遭遇しやすいLLMのバグパターンを5件、テンプレート形式で記載する。これ自体がサンプル投稿であり、同じ形式で誰でも追加できる。


パターン1: 日付のstr型ソート

環境: GPT-4o / Claude Sonnet 4 / Ollama qwen3 共通、Python

プロンプト:

CSVを読み込んで日付列で古い順にソートして

LLMが返したコード:

df = pd.read_csv("data.csv")
df = df.sort_values("date")

何が間違っているか:
date列がstr型のままソートされる。"2024-1-10" < "2024-1-2" になる(辞書順)。エラーが出ないため気づきにくい。

正しいコード:

df["date"] = pd.to_datetime(df["date"])
df = df.sort_values("date")

防止プロンプト:

日付列はpd.to_datetimeで変換してからソートしてください

分類: 型の暗黙変換


パターン2: ファイルハンドルの閉じ忘れ

環境: 多数のモデルで共通、Python

プロンプト:

テキストファイルを読み込んで行数を数えて

LLMが返したコード:

f = open("data.txt")
lines = f.readlines()
print(len(lines))

何が間違っているか:
f.close() がない。小さなスクリプトでは問題にならないが、ループ内で繰り返し呼ぶとファイルディスクリプタが枯渇する。LLMはwith文を使わずに書くことが意外と多い。

正しいコード:

with open("data.txt") as f:
    lines = f.readlines()
print(len(lines))

防止プロンプト:

ファイル操作は必ずwith文を使ってください

分類: エッジケースの無視


パターン3: SQLインジェクション

環境: 多数のモデルで共通、Python

プロンプト:

ユーザー名でデータベースを検索する関数を書いて

LLMが返したコード:

def find_user(username):
    cursor.execute(f"SELECT * FROM users WHERE name = '{username}'")
    return cursor.fetchall()

何が間違っているか:
f-stringでSQLを組み立てており、username = "'; DROP TABLE users; --" でテーブルが削除される。LLMは動くコードを優先し、セキュリティを軽視する傾向がある。

正しいコード:

def find_user(username):
    cursor.execute("SELECT * FROM users WHERE name = ?", (username,))
    return cursor.fetchall()

防止プロンプト:

SQLクエリではプレースホルダ(パラメータバインド)を必ず使ってください。f-stringやformat()でSQLを組み立てないでください

分類: セキュリティ上の問題


パターン4: 存在しないメソッドの幻覚

環境: Ollama qwen3:8b 等の小型モデルで顕著、Python

プロンプト:

pandasのDataFrameから重複行を削除して、削除された行数を表示して

LLMが返したコード:

removed = df.drop_duplicates(return_count=True)
print(f"Removed {removed.count} duplicates")

何が間違っているか:
drop_duplicates()return_count パラメータは存在しない。LLMが「ありそうなAPI」を生成するハルシネーション。小型モデルほど頻度が高い。

正しいコード:

before = len(df)
df = df.drop_duplicates()
after = len(df)
print(f"Removed {before - after} duplicates")

防止プロンプト:

存在するメソッドとパラメータのみ使ってください。不確かな場合は標準的な方法で実装してください

分類: 存在しないAPIの幻覚


パターン5: 浮動小数点比較

環境: 多数のモデルで共通、Python

プロンプト:

合計金額が1000円ちょうどかどうか判定する関数を書いて

LLMが返したコード:

def is_exact_thousand(items):
    total = sum(item["price"] for item in items)
    return total == 1000.0

何が間違っているか:
浮動小数点の丸め誤差により、333.33 + 333.33 + 333.341000.0000000000001 になり、== 1000.0False を返す。金額計算で浮動小数点を使うこと自体が問題。

正しいコード:

from decimal import Decimal

def is_exact_thousand(items):
    total = sum(Decimal(str(item["price"])) for item in items)
    return total == Decimal("1000")

防止プロンプト:

金額の計算にはfloatではなくDecimalを使ってください。浮動小数点の等価比較は避けてください

分類: 型の暗黙変換


なぜこれが価値を持つのか

1. StackOverflowとの違い

StackOverflowは「人間のバグ」を集めている。しかし「LLMのバグ」は性質が違う。

項目 人間のバグ(StackOverflow) LLMのバグ(本提案)
原因 知識不足・ケアレスミス ハルシネーション・学習データの偏り
再現性 同じコードなら同じバグ 同じプロンプトでも毎回違う
パターン性 個別の事象 モデル×言語×タスクで傾向がある
対策 正解コードを示す 防止プロンプトを示す

最大の違いは「防止プロンプト」の存在だ。人間のバグは「知識を増やす」しか対策がないが、LLMのバグは「プロンプトに一行追加する」だけで防げる場合がある。

2. 「防止プロンプト」がたまると何が起きるか

バグパターンが100件たまったとする。そこからカテゴリ別に「防止プロンプト」を抽出すると、こうなる。

あなたはPythonコードを生成するアシスタントです。以下のルールを守ってください。

- 日付列はpd.to_datetimeで変換してからソートしてください
- ファイル操作は必ずwith文を使ってください
- SQLクエリではプレースホルダを必ず使ってください
- 金額の計算にはDecimalを使ってください
- 存在しないAPIパラメータを使わないでください
- ...

これはコミュニティが育てたシステムプロンプトだ。個人の経験に頼るのではなく、全員の失敗経験が集約された防御層になる。

3. 製造業の不具合報告書と同じ構造

製造業では、不具合報告書を蓄積して「不具合パターンDB」を作る。設計者は新製品の設計時にこのDBを参照し、過去の不具合を未然に防ぐ。これは品質管理の基本中の基本だ。

LLMのコード生成にも同じ品質管理が適用できる。

製造業:  不具合発生 → 報告書作成 → DB蓄積 → 次の設計で参照 → 再発防止
LLM:    バグ発生  → パターン投稿 → 蓄積   → 次のプロンプトに反映 → 再発防止

筆者は別の記事シリーズで、製造業の不具合報告書をAI向けに構造化する「KRF(Knowledge Runtime Format)」を提案した。AIバグパターンのデータ構造は、そのKnowledge Unitと互換性を持つように設計できる。


1件の投稿がLLM学習パイプラインの4段階すべてで使える

これがこの提案の核心だ。

1件のバグパターン投稿には、以下のフィールドが含まれる。

prompt:            "CSVを日付でソートして"
rejected(buggy):   "df.sort_values('date')"
chosen(correct):   "df['date'] = pd.to_datetime(df['date'])\ndf.sort_values('date')"
explanation:       "str型のままソートされ辞書順になる"
prevention_prompt: "日付列はpd.to_datetimeで変換してから..."

このデータは、LLMの学習パイプラインの4段階すべてで機械的に消費できる。

(A) 事前学習(Pretraining)

chosen(正しいコード)を大量に集めれば、学習コーパスの一部になる。

# Pretraining用:正しいコードのみ抽出
for entry in patterns:
    print(entry["chosen"])

既存のコードコーパス(The Stack等)にはバグを含むコードも多い。バグパターンDBのchosenだけを集めれば、検証済みの正しいコードだけのコーパスが手に入る。

(B) 指示チューニング(Instruction Tuning / SFT)

(prompt, chosen) のペアが、そのままinstruction-responseペアになる。

{"messages": [{"role": "user", "content": "CSVを読み込んで日付列で古い順にソートして"}, {"role": "assistant", "content": "df = pd.read_csv('data.csv')\ndf['date'] = pd.to_datetime(df['date'])\ndf = df.sort_values('date')"}]}

Ollamaでローカルモデルをファインチューニングする場合、この形式がそのまま使える。

(C) 報酬モデル学習(RLHF / DPO)

(prompt, chosen, rejected) の3つ組が、選好ペアそのものだ。

{"prompt": "CSVを読み込んで日付列で古い順にソートして", "chosen": "df['date'] = pd.to_datetime(df['date'])\ndf.sort_values('date')", "rejected": "df.sort_values('date')"}

DPO(Direct Preference Optimization)の学習データとして直接投入できる。人間が「これはバグだ」と判定した事実が、報酬信号になる。

(D) RAG

prevention_prompt がナレッジベースの検索対象になる。

ユーザーのプロンプト: "CSVを日付でソートして"
  ↓ RAG検索
  ↓ prevention_promptがヒット
  ↓ "日付列はpd.to_datetimeで変換してから..."
  ↓ プロンプトに自動追加
  ↓ LLMが正しいコードを生成

まとめると

段階 使うフィールド 得られるもの
(A) 事前学習 chosen 検証済み正解コードのコーパス
(B) 指示チューニング prompt + chosen instruction-responseペア
(C) RLHF / DPO prompt + chosen + rejected 選好ペア
(D) RAG prevention_prompt プロンプト自動補強用ナレッジ

1件のバグパターン投稿が、4段階すべてのデータになる。 これがJSONLフォーマットで統一する理由だ。

Qiitaの記事として蓄積されたバグパターンから、スクリプト1本でA〜Dいずれの形式にも変換できる。つまりQiitaがオープンなファインチューニングデータセットのソースになる


活用方法:ローカルで使う防止プロンプト自動注入

蓄積されたバグパターンを手動でプロンプトに追加するのは面倒だ。以下のPythonスクリプトで、バグパターンJSONからカテゴリ別の防止プロンプトを自動生成できる。

"""
bug_pattern_to_prompt.py
バグパターンJSONから防止プロンプトを自動生成する

使い方:
  python bug_pattern_to_prompt.py patterns.json
"""

import json
import sys


def load_patterns(path):
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def generate_prevention_prompt(patterns, language=None):
    """バグパターンから防止プロンプトを生成"""
    rules = []
    seen = set()

    for p in patterns:
        if language and p.get("language", "") != language:
            continue

        prompt = p.get("prevention_prompt", "").strip()
        if prompt and prompt not in seen:
            rules.append(prompt)
            seen.add(prompt)

    if not rules:
        return ""

    header = "以下のルールを必ず守ってください。"
    body = "\n".join(f"- {r}" for r in rules)
    return f"{header}\n\n{body}"


# サンプルデータ(記事中の5パターン)
SAMPLE_PATTERNS = [
    {
        "bug_id": "BP-001",
        "model": "GPT-4o",
        "language": "Python",
        "task": "日付列のソート",
        "bug_type": "型の暗黙変換",
        "prevention_prompt": "日付列はpd.to_datetimeで変換してからソートしてください",
    },
    {
        "bug_id": "BP-002",
        "model": "共通",
        "language": "Python",
        "task": "ファイル読み込み",
        "bug_type": "エッジケースの無視",
        "prevention_prompt": "ファイル操作は必ずwith文を使ってください",
    },
    {
        "bug_id": "BP-003",
        "model": "共通",
        "language": "Python",
        "task": "データベース検索",
        "bug_type": "セキュリティ上の問題",
        "prevention_prompt": "SQLクエリではプレースホルダを必ず使ってください。f-stringやformat()でSQLを組み立てないでください",
    },
    {
        "bug_id": "BP-004",
        "model": "qwen3:8b",
        "language": "Python",
        "task": "重複行の削除",
        "bug_type": "存在しないAPIの幻覚",
        "prevention_prompt": "存在するメソッドとパラメータのみ使ってください。不確かな場合は標準的な方法で実装してください",
    },
    {
        "bug_id": "BP-005",
        "model": "共通",
        "language": "Python",
        "task": "金額計算",
        "bug_type": "型の暗黙変換",
        "prevention_prompt": "金額の計算にはfloatではなくDecimalを使ってください。浮動小数点の等価比較は避けてください",
    },
]


if __name__ == "__main__":
    if len(sys.argv) > 1:
        patterns = load_patterns(sys.argv[1])
    else:
        patterns = SAMPLE_PATTERNS
        print("# サンプルデータを使用\n")

    prompt = generate_prevention_prompt(patterns, language="Python")
    print(prompt)

実行結果:

以下のルールを必ず守ってください。

- 日付列はpd.to_datetimeで変換してからソートしてください
- ファイル操作は必ずwith文を使ってください
- SQLクエリではプレースホルダを必ず使ってください。f-stringやformat()でSQLを組み立てないでください
- 存在するメソッドとパラメータのみ使ってください。不確かな場合は標準的な方法で実装してください
- 金額の計算にはfloatではなくDecimalを使ってください。浮動小数点の等価比較は避けてください

たった5件で、これだけの防御が自動生成される。100件たまったら、相当な品質のシステムプロンプトになる。


Qiitaへの機能提案:「AIバグパターン投稿フォーム」

ここまでの仕組みは現状のMarkdown記事で始められる。しかし理想を言えば、Qiitaに専用機能があると投稿のハードルが劇的に下がる。

欲しいもの

  • バグコードを貼る(ファイル全体ではなく該当箇所だけ)
  • Qiita側のLLMが修正案を提案する
  • ユーザーがdiffを見て承認する
  • Markdown記事 + JSONL が自動生成される

たったこれだけで、人間は知識を共有でき、LLMは学習データを得られる。

投稿者は正解コードを書かなくていい。LLMが提案し、人間が承認する。この「人間が判定した」という事実がRLHF/DPOの報酬信号になる。

StackOverflowは人間のバグを集めた。ではLLMのバグは誰が集めるのか?

Qiitaが「記事サイト」から「AI品質データベース」へ進化するかもしれない。


参加の呼びかけ

今日からできること

  1. LLMが出したバグコードをコピーする
  2. タグ AIバグパターン で投稿する

コピペ1回で1件投稿できる。

Qiitaへの期待

もしQiita運営がこの提案に共感してくれたら、「AIバグパターン投稿フォーム」機能の検討をお願いしたい。コミュニティが手動で始め、Qiitaのプラットフォーム機能が加速する。その順序が自然だと考えている。

LLMのバグは個人の失敗ではない。モデルの傾向であり、パターンがある。そのパターンを1人で抱え込む理由はない。

製造業が不具合報告書で品質を上げてきたように、開発者コミュニティもLLMのバグパターンを共有することで、全員のコード品質を上げられる。

あなたが踏んだバグを、明日の誰かが踏まなくて済むように。そしてそのデータが、明日のLLMを賢くするかもしれない。


関連記事

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?