OpenAI API のプロンプトエンジニアリング基礎

概要

LLM を使ったアプリケーションを作るとき、モデルの性能そのものより「どう指示するか」で結果が大きく変わります。同じ GPT モデルでも、プロンプトの設計次第で精度・安定性・コストがまるで違ってきます。

この記事では OpenAI API を題材に、プロンプトエンジニアリングの基礎を入門者向けに整理します。API の呼び出し方から、実務で効くテクニック(役割分担・Few-shot・構造化出力・温度制御)までを、動くコード例つきで解説します。

対象読者は「API キーは取得できたが、思ったような回答が返ってこない」という段階の方です。

環境

  • Python 3.11
  • openai v1 系 Python SDK
  • モデルは gpt-4o-mini 相当(安価で入門に十分)を想定
pip install openai
export OPENAI_API_KEY="(自分のキー)"

APIキーはコードに直書きせず、必ず環境変数やシークレット管理から読み込みます。

なぜプロンプトが重要なのか

LLM は「次に来る確率の高いトークン」を生成しているだけで、こちらの意図を読み取っているわけではありません。曖昧な指示には曖昧な出力で応じます。

こちらが暗黙に期待している「出力形式」「対象読者」「制約」を、モデルは明示されない限り知りません。プロンプトエンジニアリングとは、この暗黙の期待を言語化してモデルに渡す作業です。

入門者が最初につまずく問題

  1. 回答がブレる — 同じ質問でも毎回違う形式・粒度で返ってくる
  2. 指示を無視する — 「JSONで返して」と書いたのに前置きの文章が付く
  3. 余計に長い / 短すぎる — 出力量を制御できずコストが読めない
  4. 前提知識を勝手に補完する — 与えていない情報を創作する(ハルシネーション)

これらの多くは、モデルの能力不足ではなく指示の設計不足が原因です。

基礎の4原則

1. 役割(system メッセージ)で振る舞いを固定する

system ロールでモデルの立ち位置・トーン・制約を宣言します。会話全体に効く「設定」なので、毎回の質問に書くより効率的です。

from openai import OpenAI

client = OpenAI()  # OPENAI_API_KEY を環境変数から読む

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "あなたは丁寧な技術ライターです。専門用語には短い補足を添えます。"},
        {"role": "user", "content": "REST APIを一言で説明してください。"},
    ],
    temperature=0.3,
)

print(response.choices[0].message.content)

2. 具体的に、期待する出力形式まで書く

「要約して」ではなく「3つの箇条書きで、各30文字以内で要約して」のように、粒度・形式・制約を明示します。

# ❌ 曖昧
bad = "この文章を要約して。"

# ✅ 明確
good = """次の文章を要約してください。
制約:
- 箇条書きで3点
- 各30文字以内
- 技術者向けの語彙で
文章: {text}"""

3. Few-shot(例示)で形式を学ばせる

言葉で説明しづらい形式は、入出力の例を1〜2個見せるのが最短です。

messages = [
    {"role": "system", "content": "問い合わせ文をカテゴリに分類します。出力はカテゴリ名のみ。"},
    {"role": "user", "content": "ログインできません"},
    {"role": "assistant", "content": "認証"},
    {"role": "user", "content": "請求書を再発行したい"},
    {"role": "assistant", "content": "請求"},
    {"role": "user", "content": "パスワードを変更したい"},
]

res = client.chat.completions.create(
    model="gpt-4o-mini", messages=messages, temperature=0,
)
print(res.choices[0].message.content)  # → 認証

4. 温度(temperature)で振れ幅を制御する

temperature は出力のランダム性です。分類や抽出など正解が決まっているタスクは低め(0〜0.3)、アイデア出しなど発散したいタスクは高め(0.7〜1.0)にします。

構造化出力(JSON)を確実に受け取る

後続処理でパースするなら、response_format で JSON を強制するのが安全です。

res = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "抽出結果を指定スキーマのJSONで返します。説明文は付けません。"},
        {"role": "user", "content": "田中さんは30歳、東京在住のエンジニアです。"},
    ],
    response_format={"type": "json_object"},
    temperature=0,
)

import json
data = json.loads(res.choices[0].message.content)
print(data)

JSON モードを使うと「前置きの文章が混ざってパースが壊れる」事故を防げます。

動作確認のポイント

  1. 再現性 — 同じ入力を数回投げ、出力がブレていないか
  2. 形式適合 — JSON なら json.loads が例外なく通るか
  3. 境界ケース — 空入力・想定外入力でも壊れないか
  4. トークン量 — response.usage で入出力トークンを確認しコストを見積もる
print(res.usage)  # prompt_tokens / completion_tokens / total_tokens

注意点

  • APIキーは絶対にコードやリポジトリに含めない。環境変数・シークレット管理から読む
  • ハルシネーション対策:「わからない場合は『不明』と答える」と明示する
  • プロンプトインジェクション:ユーザー入力を system 指示と同格に扱わない。データとして囲む
  • モデル更新で挙動が変わる:モデル名を固定し、更新時は回帰テストを回す
  • コストは出力トークンに強く効く:不要に長い出力を求めず max_tokens で上限を設ける

まとめ

プロンプトエンジニアリングの基礎は、突き詰めると「暗黙の期待を明示する」ことに尽きます。

  • system で役割・制約を固定する
  • 出力形式・粒度・件数まで具体的に書く
  • 例示(Few-shot)で形式を学ばせる
  • temperature でタスクに応じて振れ幅を制御する
  • JSON モードと usage 確認で実務品質に仕上げる

まずは手元の1タスクを選び、「曖昧なプロンプト → 制約を足したプロンプト」の前後で出力を比べてみてください。効果を実感するのが上達の近道です。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。