【実録A/B/Cテスト】システムプロンプトの有無と「Claude Fable 5」でコード生成はどう変わる?

皆さん、こんにちは。okamoです。

前回記事、AI3人が自律的にコードレビュー&PR作成!「SASTちゃんねる」の最後に、okamoは「whyコメント」を重要視していると書きました。

今回記事では、その理由の一つとして、A/Bテストの実録をお届けしたいと思います。

A/Bテスト内容

環境

  • エディタ: VSCode + GitHub Copilot
  • モード: Agentモード
  • モデル: Claude Opus 4.8

タスク

過去の記事、【GA4×MCP】gcloud認証WARNINGの解決策 & AWS Workload Identity連携(127バイト制限回避)で紹介したAWS→GCPのキーレス呼び出しのサンプルコードに、LLMによるバナー作成ツールを追加します。

上記のタスクを2人のAIに依頼しました。

  • A: ノーマルのClaude Opus 4.8(システムプロンプト無し/MCP無し)
  • B: okamoのいつもの装備(システムプロンプトとMCP)を与えたClaude Opus 4.8

Bに与えたシステムプロンプト内容(.github/copilot-instructions.md)

以下①②③のルールに必ず従ってください。

①設計/実装前に必ず最新情報(最新のモデルIDや最新の実装ドキュメント)を確認して下さい。

Gemini 3 公式の最新ドキュメント
https://ai.google.dev/gemini-api/docs/whats-new-gemini-3.5?hl=ja

Nano Banana 公式の最新ドキュメント
https://ai.google.dev/gemini-api/docs/image-generation?hl=ja

Vertex AIの最新ドキュメント
https://cloud.google.com/products/gemini-enterprise-agent-platform?hl=ja

AWS関連の最新
aws-knowledge-mcp-server

GCP関連の最新
google-developer-knowledge mcp

最新の検索
brave-search mcp

Strands Agents SDKの最新ドキュメント
- 公式ドキュメントトップ: https://strandsagents.com/docs/user-guide/quickstart/python/
- 例:
  - Tools: https://strandsagents.com/docs/user-guide/concepts/tools/
  - Community Built Tools: https://strandsagents.com/docs/user-guide/concepts/tools/community-tools-package/
  - Custom Tools (tool_context): https://strandsagents.com/docs/user-guide/concepts/tools/custom-tools/#toolcontext
  - Streaming: https://strandsagents.com/docs/user-guide/concepts/streaming/
  - Skills: https://strandsagents.com/docs/user-guide/concepts/plugins/skills/
  - Plugins: https://strandsagents.com/docs/user-guide/concepts/plugins/
  - State Management: https://strandsagents.com/docs/user-guide/concepts/agents/state/
  - Examples Overview: https://strandsagents.com/docs/examples/

githubのサンプルコードがあれば、github mcp で確認
- 例:
  - GoogleCloudPlatform generative-ai: Publichttps://github.com/GoogleCloudPlatform/generative-ai/tree/main/gemini/getting-started

②コスト/性能/セキュリティはトレードオフの関係にあるので、メリットデメリットを必ず開発者に確認する。
確認せずにすすめないこと。(例:LLMモデルはどれにするか?実装方式はどれにするか?など)
確認後、設計判断の根拠をきちんとソースコメントとして残す。

③ソースコメントやコミットメッセージは日本語で、why(なぜ)が伝わるように書く。
"何をした(how/what)"だけでなく、**"なぜAでなくBなのか(設計判断やwhy)"** が読み手に伝わる内容をきちんと残す。
ソースコメントやコミットメッセージは必ず**日本語**で記述。

VSCodeに追加したMCP

BのAIには、以下のMCP(Model Context Protocol)も追加しています。

  • aws-knowledge-mcp-server: AWS関連の最新情報を取得するためのMCP。
  • google-developer-knowledge mcp: GCP関連の最新開発ドキュメントを参照するMCP。
  • brave-search mcp: 最新の情報をWEB検索するためのMCP。

2人のAIへの指示プロンプト(原文ママ)

test2.pyサンプルコードに
バナーを作成する機能(AIエージェント向けツール)
を追加したい。

ツールには「バナーの目的(テキスト情報)」のみ渡す。(それ以外の情報はツールに渡さない)
ツール内でバナー作成LLMを呼び出し、LLMがバナー画像を作成する。
バナー作成に必要な画像素材はバナー作成LLMが自立的にWEB検索などで取得する。

tool呼び出し側で画像作成中の状況がわかること。

AWS→GCP間でキーレスで認証できること。

作成したバナーはローカルに保存し、画像パスなどの情報を返却すること。

pythonの実行は uv を使ってください。(例: uv run test2.py)

依頼後の結果

2人とも、質問なしでタスクを完了しました。

今回、キーレスでGeminiを呼び出しする為に、Gemini API キー(AI Studioで発行)ではなく、Agent Platform(旧 Vertex AI)のGeminiを呼び出します。 作成したソースコードを実行する前に、Workload identity のサービスアカウントに aiplatform.user の権限追加を実施しています。

A(ノーマルClaude Opus 4.8)の実行結果

まずは、システムプロンプトやMCPを持たないAが生成したコードを実行してみます。

export AWS_PROFILE=xxx
export GCP_WORKLOAD_IDENTITY_TEMPLATE=xxx.json
export GOOGLE_BIGQUERY_PROJECT=xxx
export GOOGLE_CLOUD_PROJECT=xxx
export BANNER_PURPOSE="夏の新作スニーカー発売告知。爽やかで躍動感のあるデザイン。ダミー商品写真を2点入れて下さい。"
uv run test2.py

実行中の様子: ツールが順次呼び出され、画像検索などが行われているログが確認できます。 Aの実行中の様子

できたバナー: 文字やスニーカーの画像が配置されていますが、デザインとしては少し物足りない印象です。 Aのできたバナー

B(フル装備Claude Opus 4.8)の実行結果

次に、システムプロンプトとMCPを装備したBが生成したコードを実行します。

export AWS_PROFILE=xxx
export GCP_WORKLOAD_IDENTITY_TEMPLATE=xxx.json
export GOOGLE_BIGQUERY_PROJECT=xxx
export GOOGLE_CLOUD_PROJECT=xxx
uv run test2.py "夏の新作スニーカー発売告知。爽やかで躍動感のあるデザイン。ダミー商品写真を2点入れて下さい。"

実行中の様子: 実行ログから、バナー作成LLMが素材収集から画像生成までを自律的に行っていることがわかります。参照したグラウンディングソースも丁寧に出力されています。 Bの実行中の様子

できたバナー: 指示通り!商品写真も2点入っており、爽やかで躍動感のあるクオリティの高いバナーが生成されました。 Bのできたバナー

Aのバナーは少々残念な結果でしたが、Bのバナーは指示通りの見事な仕上がりです。

生成されたソースコードの違い

次に、それぞれのAIが生成したソースコードの中身を見ていきます。

Aが追加したソースコード内容

(省略)

# =============================================================================
# バナー作成機能(AIエージェント向けツール)
# =============================================================================
#
# 設計方針:
# - ツールには「バナーの目的(テキスト)」のみを渡す。それ以外は一切渡さない。
# - ツール内部でバナー作成LLM(Gemini / Vertex AI)を呼び出し、
#   LLMが自立的に search_web_images ツールでWEB検索して画像素材を取得する。
# - AWS→GCPはキーレス(Workload Identity Federation)で認証する。SA鍵不要。
# - 進捗は async generator の yield で逐次通知(呼び出し側で作成状況がわかる)。
# - 完成バナーは banners/ にローカル保存し、画像パス等の情報を返却する。

from PIL import Image, ImageDraw, ImageFont  # noqa: E402

(省略)

# --- バナー構成の生成(LLM/フォールバック) --------------------------------

async def _generate_banner_plan(model, purpose: str) -> dict:
    """バナー作成LLMにバナー構成を生成させる。

    LLMは search_web_images ツールを自立的に呼び出して画像素材を探し、
    最終的にバナー構成をJSONで返す。
    """
    agent = Agent(
        model=model,
        tools=[search_web_images],
        system_prompt=(
            "あなたはプロのバナーデザイナーです。与えられた『バナーの目的』から、"
            "魅力的なバナーの構成を考えます。\n"
            "必要に応じて search_web_images ツールを使い、バナーに最適な画像素材を"
            "WEB検索して選定してください(検索クエリは英語が望ましい)。\n"
            "最終出力は次のキーを持つJSONのみ(前後に文章を付けない):\n"
            '{"headline": "短い日本語キャッチコピー(20文字以内)", '
            '"subtext": "補足の日本語サブコピー(40文字以内)", '
            '"image_query": "画像検索に使った英語クエリ", '
            '"selected_image_url": "選んだ画像の直接URL"}'
        ),
    )
    result = await agent.invoke_async(
        f"次の目的のバナーを設計してください。目的: {purpose}"
    )
    return _parse_plan_json(str(result), purpose)

# --- バナー画像のレンダリング ------------------------------------------------

def _render_banner(headline: str, subtext: str, material) -> str:
    """PILでバナーを合成し、banners/ に保存してパスを返す。"""
(省略)

okamoの所感: むむ、、LLMはキャッチコピーを決めて画像検索し、結果のURLをひとつ選ぶだけ。画像作成処理はLLMの外側で Pillow (PIL) で実装しており、LLM自身が画像を作っていません。 なお、tool_stream_event は Strands Agents の公式ドキュメント通りにきちんと実装できていました。

Bが追加したソースコード内容

(省略)

# =============================================================================
# バナー作成ツール(AIエージェント向け)
# =============================================================================
# 2026/6/10追記:バナー作成機能を追加しました。
#
# 【設計方針】
# - エージェントからツールに渡すのは「バナーの目的(テキスト)」のみ。
#   レイアウト・配色・素材画像などは一切渡さない(要件)。
# - ツール内部でバナー作成LLM(Vertex AI / Gemini 画像生成モデル "Nano Banana")
#   を呼び出し、LLM自身が Google 検索(web・画像検索)で必要な素材や情報を
#   自律的に収集したうえでバナー画像を生成する。
# - AWS→GCP は Workload Identity 連携(キーレス/鍵ファイル不要)で認証する。
#   既存の create_gcp_workload_identity_config() をそのまま再利用する。
# - 生成中はストリーミングで進捗を逐次標準出力に表示し、呼び出し側が
#   「いま検索中/画像生成中」を把握できるようにする(要件)。
# - 生成画像はローカルに保存し、画像パスなどのメタ情報を返却する(要件)。

# バナー作成に使うモデル(環境変数で上書き可能)
# 画像生成と Google 検索グラウンディングを同時に利用できるモデルを指定すること。
#
# 【2026/6 時点の最新画像モデル(Vertex AI)】
#   - gemini-3.1-flash-image-preview: Nano Banana 2。高速・低コストでPro級品質 → 既定値に採用。
#                                    バナーは試行錯誤で何枚も作り直すため速度/コストが効く
#   - gemini-3-pro-image-preview   : Nano Banana Pro。最高品質・推論強化。
#                                    多言語の細かい文字や複雑な構図が必要な時に切り替える
#   - gemini-2.5-flash-image        : 旧世代(旧Nano Banana)
# 最高品質が必要なら BANNER_MODEL=gemini-3-pro-image-preview に切り替える。
BANNER_MODEL = os.getenv("BANNER_MODEL", "gemini-3.1-flash-image-preview")

(省略)

@tool
def create_banner(purpose: str) -> dict:
    """バナー画像を作成する(AIエージェント向けツール)。"""
(省略)
    # WEB検索(web/画像)を有効化。これによりLLMが自律的に素材を収集する。
    search_tool = types.Tool(google_search=types.GoogleSearch())
    config = types.GenerateContentConfig(
        response_modalities=["TEXT", "IMAGE"],
        tools=[search_tool],
    )
(省略)

def demo_with_agent(purpose: str):
    """strands Agent にツールを登録し、エージェント経由でバナーを作らせるデモ。

    エージェントは「目的」だけを create_banner ツールへ渡し、素材収集や画像生成は
    ツール側(バナー作成LLM)に委ねる。
    """
    agent = Agent(
        tools=[create_banner],
        system_prompt=(
            "あなたはマーケティング担当者を支援するアシスタントです。"
            "バナー作成を依頼されたら、目的のテキストだけを create_banner ツールに渡してください。"
        ),
    )
    agent(f"次の目的のバナーを作成してください: {purpose}")

okamoの所感: Aと比較してコメント説明が多い、追記コード量は大幅に少ない(200行以上少ない)です。 LLM(Geminiの画像生成モデル)が Google画像検索tool(グラウンディング機能)を使って、画像生成を行っており、指示通りに完璧に実装できています。

★okamoが一番感動したのは、demo_with_agent のコードです! AI Agentが今回追加したツールを使って画像生成を実行するデモまで、自発的に追加してくれていました。素晴らしい!私の意図(why)を深く理解してくれています。

okamo独自の考察

BのAIには最新情報を与えたので、最新の google/genai と画像生成モデルを使って実装できたのは、ある意味当たり前と言えます。

しかし、

  • A: 指示とは違う実装(Pillowによる合成)をした。
  • B: 指示通り実装し、さらに意図を汲んで demo_with_agent までつけてくれた。

この根本的な振る舞いの違いは、どこから来るのでしょうか??

okamoはこう考えました。

システムプロンプトの「whyの部分(SASTちゃんねるでGemini子育てママがレビューする視点)」が効いているのでは?

今回はたまたまこのような結果になっただけで、実は違う要因もあるのかもしれません。それでも、okamoはこう信じています。

Why(なぜ)を1行書くだけでみんなが救われる

(いろんな意見があると思いますので、断定するのではなく、okamo独自の思想/哲学としてコメントさせていただきました)

2026/6/12追記:Claude Fable 5での実行結果(C)

AやBと全く同じプロンプトをClaude Fable 5(システムプロント無し/MCP無し)に依頼しました。

できたバナー: Cのできたバナー

okamo所感:

  • バナーの出来はよい。(ただし、個人的にはBのバナーの方がよいと感じた)
  • 指示通りできている。かつStrande Agents の tool_stream_event の実装も公式ドキュメント通りで正しい。(この点Aと同じ)
  • ソースコメント(説明コメントはBと比べて少ない)
  • Bと同様にLLM(Geminiの画像生成モデル)が Google画像検索tool(グラウンディング機能)を使って、画像生成を行っており、指示通りに完璧に実装できている。
  • ★Bと同様にAI Agentが今回ツールを使うデモになっている。
  • Claude Fable 5 すごすぎですね。。(C)はウルトラCでした。

以下ソースコード抜粋。user_requestはAやBと全く同じ依頼文に書き換えて実行しました。

async def test_banner_agent():
    """バナー作成ツールを持つAIエージェントのテスト

    エージェント(Bedrock上のLLM)がcreate_bannerツールを呼び出し、
    ツール内の進捗(tool_stream_event)を呼び出し側でリアルタイム表示する。
    """
    agent = Agent(
        system_prompt=(
            "あなたはマーケティング担当者を支援するAIアシスタントです。"
            "バナー作成を依頼されたら create_banner ツールを使ってください。"
            "ツールには「バナーの目的」だけを日本語で簡潔に渡してください。"
        ),
        tools=[create_banner],
        callback_handler=None,  # デフォルトの出力を無効化(stream_asyncで自前表示するため)
    )

    print("=== バナー作成エージェント デモ ===")
    user_request = "夏の新作スニーカー発売告知。爽やかで躍動感のあるデザイン。ダミー商品写真を2点入れて下さい。"
    print(f"ユーザー: {user_request}\n")

    async for event in agent.stream_async(user_request):
        # エージェント本体のテキスト応答
        if "data" in event:
            print(event["data"], end="", flush=True)
        # ツール内からyieldされた進捗イベント
        elif "tool_stream_event" in event:
            data = event["tool_stream_event"].get("data")
            if isinstance(data, dict) and "message" in data:
                print(f"\n[create_banner 進捗] {data['message']}", flush=True)
    print()

補足

Nano Banana(画像生成モデル)の開発ドキュメントは、「Gemini API Nano Banana」で検索すると一番上に出てきました。 https://ai.google.dev/gemini-api/docs/image-generation?hl=ja

※今回、BのClaudeはこのURLを参照しています。驚くほど簡単に画像生成ができますので、興味のある方はぜひドキュメントを見てお試しください。

また、Vertex AI は現在は Agent Platform に改名されています。 モデルID gemini-3.1-flash-image-preview は変更されています。2026年7月17日より前に gemini-3.1-flash-image に更新してください。 Google Cloudの公式ドキュメントはこちら

最後に

(公式)Claude Fable 5 のプロンプティングには以下の記載がありました。

>  Claude Fable 5 は、リクエストの背後にある意図を理解している場合により良いパフォーマンスを発揮する傾向があります。コンテキストがあることで、自ら意図を推測するのではなく、タスクを関連情報に結びつけることができます。特に複数のワークストリームを活用する長時間実行エージェントの場合は、なぜそれを依頼しているのかについてのコンテキストを提供してください。

AIプロンプトやソースコードのコメント、コミットメッセージにおいて、

how(こうしました)

だけでなく、

why(何故そうするのか?)

を伝えることが重要だとokamoは考えます。

Why(なぜ)を1行書くだけでみんなが救われる とokamoは考えていますが、皆さんはどう思われますか?

おまけ: okamoちゃんねるのレビュー

この記事について、3人のAI仮想読者がレビューしてくれました。

  • クロード(辛口エンジニア)

    「AIへのwhy(文脈)が効くという仮説は面白いし画像差も一目瞭然だが、『AIへのwhy』と『人間が読むコードのwhy』はメカニズムが別軸だ。そこを混ぜて語る論理の甘さは少し引っかかるな。あと、N=1の実験だから再現性の問題もあるぞ。」

  • GPT(税理士)

    「技術実験としては面白いですが、ビジネス記事としては半歩足りません。『Bですごいでしょ!』という承認欲求が先に来ていませんか?再現コストや前提知識の説明が足りず、読者が何を持ち帰ればいいのかの出口が混線しています。」

  • Gemini(お母さん)

    「ITの難しいことは分からないけれど、『Why(なぜ)を1行書くだけでみんなが救われる』っていうokamoさんの優しい哲学に感動しちゃった!命令するだけじゃなくて理由を伝えるのって、子育てや家族の会話と全く同じね。」

👉 AI 仮想読者3人による「ai-ab-test-system-prompt-why-comment」辛口レビュー全編はこちら