前回の記事では、APIキー(生成AIのサービスを使うための、パスワードのような長い文字列)をPythonのコードへ直接書いてはいけない理由を整理しました。
次のような書き方です。
from openai import OpenAI
client = OpenAI(
api_key="sk-xxxxxxxxxxxxxxxx"
)
この書き方は動きます。動くのですが、このファイルを1つ誰かに渡した瞬間、APIキーも一緒に渡ってしまいます。
では、キーをどこに置けばよいのか。その答えが環境変数です。
今回のゴールは、次のコードを動かすことです。
from openai import OpenAI client = OpenAI()
APIキーが1文字も出てきません。それでも動きます。この記事は、なぜ動くのかと、そのための準備を扱います。
作業は4段階です。
- OS側に
OPENAI_API_KEYという環境変数を設定する - Pythonから「設定されているか」だけを確認する
OpenAI()を作る- APIを1回呼んで動作を確認する
この流れのどこにも、APIキーをPythonのコードへコピーする工程はありません。
この記事を読み終えると、次のことができるようになります。
- 環境変数とは何かを、人に説明できる
- Windowsで
OPENAI_API_KEYを設定できる(画面からとコマンドから、両方) - macOS / Linuxで
OPENAI_API_KEYを設定できる - APIキー本体を画面に出さずに、設定できているか確認できる
OpenAI()だけで認証が通る理由を説明できる- 「設定したのに
Noneになる」を、自分で切り分けられる
先に、この記事で出てくる用語を整理します
環境変数の話は、用語が分からないだけで急に難しく感じます。この記事で使う言葉を先にまとめておきます。
いま全部覚える必要はありません。本文中でも初出のたびに説明しますので、分からなくなったらここへ戻ってきてください。
| 用語 | この記事での意味 |
|---|---|
| 環境変数 | OSや実行環境の側に保存しておく「名前=値」の設定。プログラムの外に置けるので、コードに書かずに済みます |
| プロセス | いま動いている1つのプログラム。JupyterLabも、Pythonも、ターミナルもそれぞれ別のプロセスです |
| ターミナル | 文字でコマンドを打ってパソコンを操作する画面。Windowsならコマンドプロンプトや PowerShell、macOSならターミナル.app |
| シェル | ターミナルの中で、打ったコマンドを解釈して実行しているプログラム。macOSの標準は zsh(ゼットシェル) |
| ユーザー環境変数 | Windowsで、いまログインしている自分だけに効く環境変数。今回はこちらを使います |
| システム環境変数 | Windowsで、そのPCを使う全員に効く環境変数。管理者権限が要ります |
| setx(セットエックス) | Windowsで環境変数をコマンドから登録するコマンド |
| SDK(エスディーケー) | そのサービスを使いやすくするために配布されている部品集。ここでは pip install openai で入るPythonのライブラリのこと |
| JupyterLab | ブラウザ上でPythonのコードを少しずつ実行できる開発環境。Notebook(.ipynb)を編集します |
| Google Colab | ブラウザだけで使えるNotebook環境。プログラムはGoogleのサーバー上で動きます |
.env(ドットエンブ) |
設定値をまとめて書いておくテキストファイル。それ自体は環境変数ではありません(後述) |
| None(ノン) | Pythonで「値が無い」ことを表す特別な値。環境変数が見つからないとこれが返ります |
環境変数とは何か
「名前=値」をOS側に置いておく仕組み
環境変数は、OSや実行環境の側で保持している「名前と値の組み合わせ」です。
名前:OPENAI_API_KEY 値 :自分のOpenAI APIキー
いったんOSへ登録しておけば、そこから起動したプログラムは、必要なときにこの値を読み取れます。Pythonのファイルの中にキーを書く必要がなくなる、というのがポイントです。
コードへ書く場合と、環境変数へ分ける場合
左のように書くと、app.py というファイル1つの中に、処理コードとAPIキーが同居します。このファイルを同僚に渡す、GitHubへ保存する、Notebookごと共有する──どれをしてもキーが一緒に出ていきます。
右のように分けると、app.py に残るのは処理コードだけです。APIキーは自分のPCのOS側にあり、ファイルには含まれません。「コードだけを渡す」ということが、はじめてできるようになります。
同じ「環境変数」でも、有効範囲が3段階ある
ここが、あとで出てくるトラブルの原因にもなる大事なところです。「環境変数を設定する」と言っても、やり方によってどこまで有効か(どこまで引き継がれるか)が違います。
1. Pythonの中だけ
import os os.environ["OPENAI_API_KEY"] = "sk-xxxxxxxxxxxxxxxx"
Pythonのコードから環境変数を書き込む方法です。効くのは、いま動いているそのPythonプロセスの中だけ。Notebookを閉じれば消えますし、別のPythonを起動しても引き継がれません。
はじめてAPIを呼び出した回で使った getpass() は、この段階に当たります。キーをコードに書かずに済む点は良いのですが、実行のたびに手入力が必要です。決まった時刻に自動で動かす処理やアプリケーションでは使えません。
2. そのターミナルだけ
ターミナルの中で一時的に設定する方法です。シェル(コマンドを解釈しているプログラム)ごとに書き方が違います。
# macOS / Linux export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxx"
# Windows(PowerShell) $env:OPENAI_API_KEY = "sk-xxxxxxxxxxxxxxxx"
効くのはそのターミナルの中だけです。そこから起動したJupyterLabには引き継がれますが、別のターミナルを新しく開くと消えています。その場かぎりの検証には便利ですが、毎日使うには向きません。
3. OSに保存する
Windowsの設定画面や setx、macOSのシェル設定ファイルへ書いておく方法です。一度設定すれば、そのあと起動するプログラムすべてに引き継がれます。PCを再起動しても残ります。
この記事で目指すのは3番目です。1度設定してしまえば、次からは何もしなくてよくなります。
なぜ変数名は OPENAI_API_KEY なのか
環境変数の名前は、自分で好きに決められます。MY_SECRET_KEY でも構いませんし、Pythonからはどんな名前でも読み込めます。
では、なぜ OPENAI_API_KEY なのか。答えは、OpenAIのPython SDKが、この名前を自動で探しに行くからです。公式のSDKには、次のように書かれています。
client = OpenAI(
# This is the default and can be omitted
api_key=os.environ.get("OPENAI_API_KEY"),
)
コメントの「This is the default and can be omitted(これが既定値なので、省略できます)」が答えです。api_key= を書かなかった場合、SDKは黙って os.environ.get("OPENAI_API_KEY") を実行します。つまり、
client = OpenAI()
と書くのは、
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
と書くのと同じ意味です。省略しているだけで、裏で環境変数を読んでいます。だからこの名前でなければならず、だからこの名前にしておけばコードに何も書かなくてよいわけです。
チームで名前をそろえておくと、
client = OpenAI()という同じ1行をそのまま共有できます。人によって変数名が違うと、コードを配るたびに書き換えが必要になります。
Windowsで設定する
Windowsには設定方法が2つあります。はじめてなら、設定画面から登録する方法をおすすめします。打ち間違いに気づきやすく、あとから値を確認・修正できるためです。
方法1:設定画面から登録する
画面の呼び出し方から順に書きます。
- タスクバーの検索欄に 環境変数 と入力します
- 候補に出てくる「システム環境変数の編集」または「アカウントの環境変数を編集」を選びます
- 「システムのプロパティ」が開いた場合は、右下の「環境変数」ボタンを押します
- 画面が上下2段に分かれます。上段が「ユーザー環境変数」です。こちらの「新規」ボタンを押します
- 次の2つを入力します
欄 入力する内容 変数名 OPENAI_API_KEY変数値 自分のOpenAI APIキー( sk-から始まる文字列) - 「OK」を3回押します。新規作成のダイアログ、環境変数の画面、システムのプロパティの順です。最後まで押さないと保存されません
APIキーは、貼り付けたあとに前後に空白や改行が入っていないかを目で確認してください。ここで混ざった余計な1文字が、あとで認証エラーの原因になります。
手順を記事や研修資料にする場合、この画面のスクリーンショットには変数値がそのまま写ります。キャプチャを撮るなら、値を入力する前か、ダミーの値に差し替えてからにしてください。
ユーザー環境変数とシステム環境変数、どちらを選んだ方がいいでしょうか?
先ほどの画面は上下2段に分かれていました。違いは次のとおりです。
| ユーザー環境変数(上段) | システム環境変数(下段) | |
|---|---|---|
| 効く範囲 | いまログインしている自分だけ | そのPCを使う全員 |
| 管理者権限 | 不要 | 必要 |
| APIキーの置き場所として | こちらを使う | 避ける |
APIキーは自分だけの認証情報なので、ユーザー環境変数へ置きます。システム環境変数に置くと、そのPCの他のユーザーからも読めてしまいます。共用PCでは特に注意してください。
方法2:コマンドで登録する(setx)
OpenAIの公式ドキュメントでは、Windowsでの設定方法として次のコマンドが案内されています。PowerShellまたはコマンドプロンプトで実行します。
setx OPENAI_API_KEY "sk-xxxxxxxxxxxxxxxx"
"sk-xxxxxxxxxxxxxxxx" の部分を自分のAPIキーに置き換えます。/m を付けるとシステム環境変数になりますが、APIキーでは付けません。付けなければユーザー環境変数として登録されます。
以下、setxで気をつける3つのことです。
手軽な反面、setx にはつまずきどころがあります。Microsoftの公式ドキュメントに明記されているものを3つ挙げます。
1つ目。いま開いているウィンドウには反映されません。公式の説明は「setx で設定した変数は、将来のコマンドウィンドウでのみ利用でき、現在のコマンドウィンドウでは利用できない」です。実行した直後に同じ画面で確認しても見つかりません。これは失敗ではなく、仕様です。ターミナルを閉じて開き直してください。
2つ目。値は1024文字までです。公式には「setx で変数へ内容を割り当てる際には1024文字の制限があり、超えた分は切り捨てられ、切り捨てられた文字列がそのまま変数に設定される」とあります。APIキーなら収まりますが、長い値を扱うときは黙って壊れることを覚えておいてください。
3つ目。既存の変数に使うと、他の変数への参照が展開されてしまいます。これがいちばん危険です。たとえば PATH の中に %JAVADIR% のような参照が含まれている状態で PATH を setx で書き換えると、参照はその時点の値に置き換わって固定されます。あとから JAVADIR を変えても PATH に反映されなくなります。
まとめると。
OPENAI_API_KEYのような新しい変数を作る分にはsetxで問題ありません。しかしPATHのような既存の重要な変数には使わないでください。応用するときに効いてくる注意点です。
PowerShellとコマンドプロンプトでは書き方が違う
Windowsのターミナルには、コマンドプロンプトと PowerShell の2種類があります。環境変数の書き方が違うので、ここで整理しておきます。
| やりたいこと | コマンドプロンプト | PowerShell |
|---|---|---|
| OSに保存する | setx 名前 "値" |
setx 名前 "値" |
| その画面だけで一時的に設定 | set 名前=値 |
$env:名前 = "値" |
| 値を参照する書き方 | %名前% |
$env:名前 |
setx だけは共通です。一時的な設定と参照の書き方が違うので、ネットで見つけたコマンドが動かないときは、自分がどちらのターミナルを開いているかを疑ってみてください。
macOS / Linuxで設定する
zshを使っている場合
macOSの標準シェルはzshです。OpenAIの公式ドキュメントでも、次の方法が案内されています。ターミナルで実行します。
echo 'export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxx"' >> ~/.zshrc
このコマンドは、~/.zshrc というファイルの末尾に1行を書き足しています。~/.zshrc は、ターミナルを開くたびに自動で読み込まれる設定ファイルです。ここに export の行を書いておけば、以後どのターミナルでも環境変数が設定された状態になります。
>> は「ファイルの末尾に書き足す」という意味です。よく似た >(1本)はファイルの中身を全部消してから書き込むので、間違えると既存の設定が消えます。必ず2本にしてください。
書き足しただけでは、いま開いているターミナルにはまだ反映されていません。次のコマンドで読み込み直します。
source ~/.zshrc
source は「このファイルを今すぐ読み込む」という命令です。これ以降、そのターミナルで環境変数が使えるようになります。
bash など、別のシェルを使っている場合
書き足す先のファイル名が変わります。自分が使っているシェルは次で確認できます。
echo $SHELL
| シェル | 書き足すファイル |
|---|---|
| zsh(macOSの標準) | ~/.zshrc |
| bash | ~/.bashrc または ~/.bash_profile |
どちらを使うかは環境によって異なります。迷ったら、そのファイルがすでに存在するほうへ書き足してください。
Google Colab は別の仕組みを使う
ブラウザだけで使える Google Colab は、プログラムがGoogleのサーバー上で動きます。自分のPCのOSに環境変数を設定しても、Colabからは見えません。ここまでの方法がそのままでは使えない、唯一の環境です。
Colabには代わりにシークレット(Secrets)という専用の仕組みがあります。
- 左側のサイドバーにある鍵のアイコンをクリックします
- 名前を
OPENAI_API_KEYとして、値にAPIキーを登録します - そのノートブックからのアクセスを許可するスイッチを入れます(ノートブックごとに個別の許可が必要です)
読み出しは、専用の書き方になります。
from google.colab import userdata
api_key = userdata.get("OPENAI_API_KEY")
登録したシークレットは自分のGoogleアカウントに紐づくため、ノートブックを共有しても、相手には値が見えません。相手は自分のシークレットを登録して実行することになります。共同作業をするときは、この点を伝えておくと親切です。
Colabでの具体的な手順は環境が変わりやすいため、この記事では仕組みの紹介にとどめます。実際に使うときは、Colab側の最新の画面表示を確認してください。
Pythonから「設定されているか」だけ確認する
APIキー本体は画面に出さない
設定できたか確かめたくなりますが、次のような確認方法は避けてください。
# これは避ける(コマンドプロンプト) echo %OPENAI_API_KEY%
# これも避ける(Python) print(api_key)
どちらもAPIキー本体が画面に表示されます。学習用のPCであっても、その画面はスクリーンショット、画面共有、録画、ターミナルのログといった形で残る可能性があります。一度外に出た認証情報は、取り消すまで有効なままです。
確認したいのは「設定されているかどうか」であって、キーの中身ではありません。それなら、表示せずに確かめられます。
os.getenv()で確認する
Pythonでは os という標準の部品(モジュール)を使って環境変数を読み取ります。
import os
api_key = os.getenv("OPENAI_API_KEY")
OPENAI_API_KEY が設定されていれば api_key に値が入り、設定されていなければ None が入ります。None はPythonで「値が無い」ことを表す特別な値です。
これを踏まえて、中身を出さずに結果だけ表示するのが次のコードです。
import os
api_key = os.getenv("OPENAI_API_KEY")
if api_key:
print("OPENAI_API_KEY: 設定済み")
else:
print("OPENAI_API_KEY: 未設定")
出力はどちらかになります。
OPENAI_API_KEY: 設定済み
OPENAI_API_KEY: 未設定
これで十分です。if api_key: は「api_key に中身があれば」という意味で、None や空文字のときは else のほうへ進みます。
ターミナルから1行で確認したいときは、次のように書けます。設定画面やコマンドで登録したあと、新しいターミナルを開いて実行してみてください。
python -c "import os; print(os.getenv('OPENAI_API_KEY') is not None)"
python -c は「続く文字列をPythonのコードとして実行する」という指定です。is not None は「None ではない」という意味なので、True か False だけが表示されます。キーの中身は出ません。
os.getenv()とos.environ[]の違い
環境変数を読むコードとして、2つの書き方を見かけます。
| 書き方 | 設定されているとき | 設定されていないとき |
|---|---|---|
os.getenv("OPENAI_API_KEY") |
値が返る | None が返る |
os.environ["OPENAI_API_KEY"] |
値が返る | KeyError で止まる |
違うのは、設定されていないときの振る舞いです。os.environ[] は、その場でエラーを出してプログラムを止めます。KeyError(キーエラー)は「その名前が見つからない」という意味のエラーです。
この記事では、設定状況を自分で確認しながら進めたいので os.getenv() を使います。止まってほしいのは、自分で書いた分かりやすいメッセージと一緒に止まるときだけだからです。それを次に書きます。
未設定なら、その場で止める
実務では、None のまま後続の処理へ進むより、最初に止めてしまうほうが原因を追いやすくなります。
import os
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError(
"OPENAI_API_KEY が見つかりません。"
"環境変数を設定してから再実行してください。"
)
print("OPENAI_API_KEY: 設定済み")
raise は「エラーを発生させてプログラムを止める」命令、RuntimeError はエラーの種類の1つです。未設定のまま実行すると、こう表示されます。
RuntimeError: OPENAI_API_KEY が見つかりません。環境変数を設定してから再実行してください。
これがないと、APIへ接続する段階まで進んでから認証エラーになります。認証エラーのメッセージは「キーが未設定なのか、キーが間違っているのか」を区別してくれません。手前で止めておくと、そこで迷わずに済みます。
実際にAPIを呼んでみる
「設定済み」になったら、APIを1回呼んで確かめます。確認から呼び出しまでをまとめると、次のようになります。
import os
from openai import OpenAI
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError(
"OPENAI_API_KEY が見つかりません。環境変数を設定し、"
"JupyterLabやターミナルを起動し直してから再実行してください。"
)
print("OPENAI_API_KEY: 設定済み")
client = OpenAI()
response = client.responses.create(
model="gpt-5.6-luna",
input="環境変数からAPIキーを読み込めました、とだけ回答してください。",
)
print(response.output_text)
出力例です。
OPENAI_API_KEY: 設定済み 環境変数からAPIキーを読み込めました
ここで見てほしいのは、この1行です。
client = OpenAI()
APIキーを引数として渡していません。前の章で見たとおり、SDKが OPENAI_API_KEY を自分で読みに行っています。api_key という変数を上で取得していますが、これは設定できているかを確認するためだけに使っていて、OpenAI() には渡していません。
「設定したのに None」の切り分け
ここが、この記事でいちばんつまずく場所です。設定画面でちゃんと登録したのに、Pythonから見ると None になる。よくあります。
なぜ起動し直す必要があるのか
理由はひとつです。プログラムは、起動した瞬間のOSの環境をコピーして持ち、そのまま動き続けます。あとからOS側を変更しても、すでに動いているプログラムには届きません。
だから、JupyterLabを起動したあとで環境変数を追加すると、そのJupyterLabからは見えないままになります。おかしくなっているわけではなく、そういう仕組みです。
対処も単純で、起動し直すだけです。
- Notebookを保存する
- JupyterLabを終了する
- ターミナルも閉じる
- 新しいターミナルを開く
- そこからJupyterLabを起動する
- コードを実行し直す
Kernel(Notebookのコードを実行しているPython本体)の再起動だけで反映されることもありますが、JupyterLab自体を新しいターミナルから起動し直すほうが確実です。ターミナルもJupyterLabも、それぞれが古い環境を持ったままになりうるためです。
上から順に確認する
それでも None のままなら、次の順で切り分けます。
それぞれ、何を見るのかを書いておきます。
1. 新しいターミナルで見えるか。新しくターミナルを開いて、先ほどの1行を実行します。
python -c "import os; print(os.getenv('OPENAI_API_KEY') is not None)"
False なら、OSへの登録そのものができていません。設定画面からやり直してください。True なら登録はできているので、次へ進みます。
2. JupyterLabを起動し直したか。ターミナルでは True なのにJupyterLabでは None なら、原因はほぼこれです。JupyterLabを終了し、いま True が出たターミナルから起動し直してください。
3. 変数名のスペルは合っているか。OPENAI_API_KEY です。OPENAI_APIKEY(API と KEY の間のアンダースコアが抜けている)は、Pythonから見れば完全に別の変数です。設定画面で見直してください。
4. 値に余計な文字が入っていないか。前後の空白、改行、コピー時に付いてきた引用符。これらが混ざっていると、変数自体は「設定済み」になるのに認証だけが通りません。この場合、os.getenv() は None ではなく値を返すので、症状が変わります。認証エラーが出るときはここを疑い、不安ならAPIキーを作り直して入れ直すのが早いです。
5. 別のPython環境から起動していないか。複数のPython環境や開発ツールを併用していると、「コマンドプロンプトでは見えるのに、特定のツールからは見えない」ということが起きます。環境変数はPythonのパッケージ側の設定ではなく、そのプロセスがどこから起動されたかで決まります。1〜2の手順(新しいターミナルを開き、そこから起動する)で切り分けてください。
実際には、ほとんどが1番と2番で解決します。3番以降は、そこで直らなかったときだけ見れば十分です。
.envファイルとの違い
Python開発では .env というファイルもよく使われます。中身は次のような行の並びです。
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
ここで押さえておきたいのは、.env はOSの環境変数そのものではないということです。ただのテキストファイルなので、置いてあるだけでは何も起きません。python-dotenv というライブラリがこのファイルを読んで、プログラムの中で環境変数として設定し直しています。
from dotenv import load_dotenv load_dotenv() # .env を読んで、環境変数として設定する
OpenAIの公式SDKも、ソースコード管理にキーを含めないための方法として python-dotenv の利用を案内しています。プロジェクトごとに設定を分けたいときには便利な方法です。
ただし、前回の記事で触れたとおり.env ファイルをGitHubへcommitしてしまえば、結局キーは漏れます。ファイル名が変わっただけで、中身は同じだからです。使う場合は .gitignore(履歴に含めないファイルを書いておく指示書)へ .env を必ず加えてください。
この記事では仕組みを分かりやすくするため、まずOSの環境変数から直接読む方法を中心にしました。.env は、それが分かったあとの選択肢だと考えてください。
環境変数にすれば安全、というわけではない
環境変数を使う目的は、次の一点です。
APIキーをソースコードから切り離すこと。
それ以上でも以下でもありません。環境変数へ保存しても、そのPCへ不正にアクセスされたり、余計な権限を持つプログラムから読み出されたりすれば、キーは取り出せます。
OpenAIも、本番環境やチームでの利用にあたっては、安全な保存、アクセス制御、利用状況の監視、キーの定期的な入れ替え、必要に応じた鍵管理サービスの利用を案内しています。環境変数にしたからセキュリティ対策が終わり、ではありません。
とはいえ、NotebookやGitHubへAPIキーを残さないための最初の基本対策としては、これが出発点です。ここができていないと、他の対策はすべて後手に回ります。
まとめ
今回は、APIキーをコードから切り離して、環境変数から読み込む方法を扱いました。流れは次のとおりです。
OSへ OPENAI_API_KEY を設定
↓
ターミナル / JupyterLab を新しく起動
↓
os.getenv("OPENAI_API_KEY") で「設定済み」を確認
↓
client = OpenAI()
↓
APIを呼び出す
要点を4つに整理します。
| 覚えておくこと | |
|---|---|
| なぜ環境変数か | APIキーをソースコードから切り離すため。コードだけを共有できるようになる |
| なぜこの名前か | SDKが api_key 省略時に OPENAI_API_KEY を自動で読むから |
| 有効範囲は3段階 | Pythonの中だけ / そのターミナルだけ / OSに保存。目指すのは3番目 |
None になったら |
新しいターミナルで確認 → JupyterLabを起動し直す。ほぼこれで解決する |
そして、実務で意外につまずくのは「環境変数を設定したあと、起動し直していない」ケースです。「設定したのに None になる」ときは、まずここを確認してみてください。
