生成AI入門 2026.04.19

Claude API プロンプト設計完全ガイド【2026年版】5つのテクニックとコード実装

タグ:Claude / プロンプト設計 / 生成AI / 使い方 / 入門

テクニック1:XMLタグで構造化する(Claude固有の強み)

# xml_structured_prompt.py
# ClaudeはXMLタグによる構造化を得意とする

import anthropic
import os

client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

# ❌ 非構造化(情報が混在して解釈が曖昧になりやすい)
bad_prompt = """
あなたはシニアエンジニアです。以下のコードのバグを修正してください。
コードはPythonで書かれています。エラーはZeroDivisionErrorです。
def calc(x, y): return x / y
修正後のコードとその説明をお願いします。
"""

# ✅ XML構造化(セクションが明確でClaudeが正確に解釈)
good_prompt = """
<role>シニアPythonエンジニア</role>

<task>以下のコードのバグを修正して、修正後のコードと説明を返してください</task>

<context>
  <language>Python 3.11</language>
  <error>ZeroDivisionError: division by zero</error>
</context>

<code>
def calc(x, y):
    return x / y
</code>

<output_format>
  1. 修正後のコード(コードブロック)
  2. バグの原因(1-2文)
  3. 修正方法の説明(1-2文)
</output_format>
"""

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=512,
    messages=[{"role": "user", "content": good_prompt}]
)
print(response.content[0].text)

テクニック2:Chain of Thought(段階的推論)

# chain_of_thought.py
# 複雑な問題はステップバイステップで考えさせる

# ❌ 直接答えを求める(複雑な問題では精度が落ちる)
direct = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=64,
    messages=[{"role": "user", "content": "1001人のスタジアムに3/7の確率で左利きの人がいる。左利きの人数は?"}]
)

# ✅ Chain of Thought(推論を明示させる)
cot = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=256,
    messages=[{"role": "user", "content": """
1001人のスタジアムに3/7の確率で左利きの人がいる。左利きの人数は?

ステップバイステップで計算して、最後に答えを出してください。
<thinking>
1. 計算式: ...
2. 計算: ...
3. 答え: ...
</thinking>
"""}]
)

print("直接:", direct.content[0].text)
print("CoT:", cot.content[0].text)

テクニック3:システムプロンプトのキャッシュ(コスト90%削減)

# cached_system_prompt.py
# 長いシステムプロンプトを毎回送るコストをキャッシュで削減

SYSTEM_PROMPT = """
あなたは株式会社〇〇のカスタマーサポートAIです。

# 会社情報
- 設立: 2020年
- 本社: 東京都渋谷区
- 主要製品: クラウド型在庫管理システム

# 回答ポリシー
- 常に丁寧な日本語で回答する
- 技術的な質問は詳しく、料金に関する質問は料金ページに誘導する
- 回答できない場合は「担当者にお繋ぎします」と伝える

# よくある質問と回答
Q: 無料トライアルはありますか?
A: はい、30日間の無料トライアルを提供しています。

Q: 導入期間はどのくらいですか?
A: 通常2週間〜1ヶ月程度です。
...
"""  # 実際は数千字になることが多い

def answer_question(question: str) -> str:
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",  # カスタマーサポートはHaikuが低コスト
        max_tokens=256,
        system=[{
            "type": "text",
            "text": SYSTEM_PROMPT,
            "cache_control": {"type": "ephemeral"}  # ← これでキャッシュ有効
        }],
        messages=[{"role": "user", "content": question}]
    )
    
    usage = response.usage
    cache_hit = getattr(usage, "cache_read_input_tokens", 0)
    if cache_hit > 0:
        print(f"キャッシュヒット: {cache_hit}トークン節約")
    
    return response.content[0].text

# 2回目以降はシステムプロンプトのトークンが90%割引になる
answer_question("料金はいくらですか?")
answer_question("導入サポートはありますか?")

テクニック4:Few-Shotで出力フォーマットを固定する

# few_shot_format.py
# 例を見せることで、出力形式を厳密にコントロールする

FEW_SHOT_PROMPT = """
製品レビューを以下のJSON形式で分析してください。

<examples>
入力: "配送が遅かったけど、商品は良かった。また買います"
出力: {"sentiment": "positive", "rating": 4, "aspects": {"delivery": "negative", "product": "positive"}, "intent": "repurchase"}

入力: "全然使えない。返品します"
出力: {"sentiment": "negative", "rating": 1, "aspects": {"usability": "negative"}, "intent": "return"}
</examples>

分析するレビュー:
{review}

JSONのみを出力してください。説明は不要です。
"""

import json

def analyze_review(review: str) -> dict:
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=128,
        messages=[{"role": "user", "content": FEW_SHOT_PROMPT.format(review=review)}]
    )
    return json.loads(response.content[0].text)

result = analyze_review("思ったより小さかったけど品質は◎。リピートします!")
print(result)
# {"sentiment": "positive", "rating": 4, "aspects": {"size": "negative", "quality": "positive"}, "intent": "repurchase"}

テクニック5:プレフィリングで出力を誘導する

# prefilling.py
# assistantの最初の数文字を指定して、出力形式を強制する

def extract_json_only(text: str) -> dict:
    """JSONのみを確実に出力させる(コードブロックなし)"""
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=256,
        messages=[
            {"role": "user", "content": f"以下のテキストを解析してJSONで出力してください: {text}"},
            {"role": "assistant", "content": "{"},  # ← プレフィリング: JSONで始まることを強制
        ]
    )
    # "{" + Claudeの続き = 完全なJSON
    return json.loads("{" + response.content[0].text)

# もっと強力な使い方: 特定のフィールドを先に入れる
def force_field(text: str) -> dict:
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=128,
        messages=[
            {"role": "user", "content": f"商品名を抽出してJSONで返してください: {text}"},
            {"role": "assistant", "content": '{"product_name": "'},  # フィールド名を指定
        ]
    )
    # 不完全なJSONを補完
    return {"product_name": response.content[0].text.split('"')[0]}

あわせて読みたい

参考ソース