誕生日お祝いアプリのバージョンアップ〜OpenAI API × Streamlit:環境構築・エラー対応・デプロイまで〜
誕生日お祝いアプリのバージョンアップ記録
〜OpenAI API × Streamlit:環境構築・エラー対応・デプロイまで〜
この記事は、アプリ作成初心者の私が OpenAI API + Streamlit を使って「誕生日お祝いアプリ」を作り、
ローカル開発からクラウド公開まで完走する中で経験した 実際のエラーと学び をまとめたものです。
前回の記事「cursor による自動コーディング」の続きです。
📌 この開発記録で分かること
初心者(私も)が どんなエラーにつまずくか
GPT‑5 系モデル特有のポイント
temperature 廃止
reasoning トークンの存在
Streamlit Cloud で 画面が真っ白になる原因と対処法
agent(AI コーディング補助)が できること・できないこと
■ 1. 仮想環境の準備とアプリ起動(最初の一歩)
まずは .env に API キーを設定し、Python の仮想環境を構築して依存パッケージをインストールします。
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
久しぶりの作業だったため、コマンドを思い出すのにもひと苦労でした。
ChatGPT に確認しながら進めるという、ごく普通の開発者のスタートです。
そして実行。

アプリが立ち上がった!背景が黒でカッコいい。
誕生日の入力欄がカレンダー UI で表示され、テンションが上がりました。
■ 2. エラー①:messages パラメータエラー
〜agent (AI コーディング補助)が修正〜
● 出たエラー
Responses.create() got an unexpected keyword argument 'messages'
● 原因
Responses API の仕様に対し、パラメータ名が間違っていた。
● どう直したか
cursor の agent に修正依頼。
ついでに agent に新しいモデルを指定して変更してとお願いしました。
モデルを gpt‑4o → gpt‑5-mini に変更してくれました。
■ 3. エラー②:temperature が使えない
〜GPT‑5 で仕様が変わっていた〜
● 出たエラー
Unsupported parameter: 'temperature' is not supported with this model.
● 原因
GPT‑4o 用コードの名残で temperature が残っていた。
実は GPT‑5 系では temperature が固定化され、指定できない 仕様になっていたのです。
これは、agent との対話では分からなくて、自分で調べて判明しました。
cursor のドキュメント参照をうまく使えば、もっと早く判明したかもしれません。
● どう直したか
👉 temperature を削除
● 小さな学び
LLM のバージョンアップは、意外な仕様変更に気づきにくい。
エラーメッセージは “仕様変更に気づくヒント” になる。
■ 4. エラー③:レスポンスは返るのに、内容がおかしい
〜agent でも直らず、モデル変更で突破〜
● 状況
temperature を消すとレスポンスは返ってくるようになりました。
しかし、レスポンスは返ってくるのに、表示される内容が正しく生成文ではなく、モデル内部の推論情報やエラー文のような内容になっていました。
agent にエラーを伝えて 2 回修正依頼しても 改善せず。
● どう対処したか
試しにモデルを変更してみました。
👉 auto → sonnet に変更
すると、gpt-5は推論を使っているのでトークンオーバーしていることが判明し、トークン制限をアップ(500→2000)しました。
同時に、agent は開いていた他のプロジェクトのコードを参考にして、レスポンスからのテキスト抽出処理が冗長だったことを発見し、適切に修正してくれました。気が利いています。
● 学んだこと:reasoning トークンの存在
gpt‑5-mini は 推論プロセス(reasoning) を内部で生成する
これが 追加のトークン として消費される
その結果、max_output_tokens に達して status='incomplete' となっていた
reasoning のイメージ
モデルが “頭の中で考えているメモ書き” のようなもの。
それもトークンとしてカウントされるため、出力が途中で止まることがある。
■ 5. ローカルでついに動作! 🎉
〜動いた瞬間は本当にうれしい〜
誕生日を入れると、画面いっぱいにアニメーションが広がり、お祝いメッセージが生成される。
「これは嬉しい!」
ローカルでここまで動かすのに、体感でも 1時間くらいエラーと格闘していました。
■ 6. エラー④:Streamlit Cloud で画面が真っ白
〜原因は “inotify の監視上限” だった〜
ローカルで動いたので、次は Streamlit Cloud へデプロイ。
GitHub リポジトリ登録
環境変数に API キー設定
デプロイ開始
…しかし画面が 真っ白。
● ヒントは右下の黒いボタン「Manage app」
そこに インストールログ・エラーログ があり、ダウンロード可能でした。
ログを ChatGPT に渡すと、原因が判明。
● 原因(やさしくまとめると)
Streamlit は変更を監視する
監視には Linux の inotify を使う
Cloud では「監視できる数」に上限がある
この上限に達すると、追加の監視ができずエラー
→ inotify instance limit reached
● どう直したか
.streamlit/config.toml に以下を追加:
[server]
fileWatcherType = "none"
これで「監視しなくていいよ」と Streamlit に伝えられる。
agent にファイル生成を依頼し、再デプロイすると……
ついにクラウドでも動作!
ローカル → クラウド の突破は達成感が大きい瞬間でした。
■ 7. 今後の予定(Next Steps)
コード全体をじっくり読み、Google Colab で動作確認(次回)
誕生日メッセージのバリエーション強化 (将来)
読んでくださった方も、まずは 小さなアプリを動かすところから 始めてみてはいかがでしょうか。
例えば、「今日の天気を教えてくれるアプリ」や「メッセージを一言生成するだけのアプリ」でも十分だと思います。
■ 8. 公開アプリはこちら
👉 Birthday App

■ 9. おわりに 〜エラーは “敵” ではなく “教材” 〜
今回のアプリ開発では、
GPT モデル仕様の変化
reasoning トークンの存在
エラーログの読み解き
agent でも修正できないケースがあること
クラウド固有の落とし穴(inotify)
といった貴重な学びがありました。
AI がコードを書いても、
新しい API や仕様変更まわりは、まだ誤りやすい領域。
自動コーディングに任せることで、
一つひとつ動作確認をしながら組み立てていく「手を動かして作る楽しさ」は、少し減ったようにも感じました。
その一方で、「こういうアプリを作りたい」と思ったときに、前回の記事でも触れましたが、完成形に一気に近づけるスピード感と、短時間で形になること自体の楽しさと感動も、大きく感じました。
だからこそ、人間がログを読み、原因を推測し、
“LLM に指示を出す力” が大事だと実感しました。
エラーは、避けるべき敵ではなく、
理解を深めてくれる最高の教材 です。
この記録が、あなたの「ちょっとやってみようかな」の背中を押せたら嬉しいです。
自己紹介(私のページ)
アプリコードの参考:githubのリンクとコード
import streamlit as st
from datetime import datetime, date
import pytz
import os
from openai import OpenAI
from dotenv import load_dotenv
# 環境変数の読み込み
load_dotenv()
# OpenAI クライアントの初期化
client = None
try:
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
# Streamlit Cloud の Secrets Manager を確認
if hasattr(st, "secrets") and "OPENAI_API_KEY" in st.secrets:
api_key = st.secrets["OPENAI_API_KEY"]
if api_key:
client = OpenAI(api_key=api_key)
except Exception as e:
st.error(f"OpenAI API の初期化に失敗しました: {str(e)}")
def get_today_jst():
"""日本時間(JST)で今日の日付を取得"""
jst = pytz.timezone("Asia/Tokyo")
return datetime.now(jst).date()
def calculate_days_lived(birthday: date) -> int:
"""誕生日から今日までの日数を計算"""
today = get_today_jst()
days = (today - birthday).days
return days
def calculate_age(birthday: date) -> int:
"""現在の年齢を計算"""
today = get_today_jst()
age = today.year - birthday.year
# 誕生日がまだ来ていない場合は1歳引く
if (today.month, today.day) < (birthday.month, birthday.day):
age -= 1
return age
def is_birthday_today(birthday: date) -> bool:
"""今日が誕生日かどうかを判定"""
today = get_today_jst()
return (today.month, today.day) == (birthday.month, birthday.day)
def generate_birthday_message(
days_lived: int, age: int = None, is_birthday: bool = False
) -> str:
"""OpenAI Responses API を使ってお祝いメッセージを生成"""
if not client:
return "⚠️ OpenAI API が利用できません。API キーを設定してください。"
try:
# プロンプトの作成
if is_birthday:
prompt = f"""
あなたは優しく、温かい心を持ったメッセージ作成者です。
今日は誕生日を迎えた人への特別なお祝いメッセージを作成してください。
情報:
- 今日で{age}歳になりました
- 生まれてから{days_lived}日が経ちました
以下の要件でメッセージを作成してください:
1. 誕生日おめでとうの気持ちを込める
2. 優しく、ほのぼのとしたトーン
3. 「生きててよかった」「自分の人生も悪くない」と思えるような内容
4. {days_lived}日という日数の重みを感じさせる
5. 200文字以内
6. 絵文字を適度に使用(🎂🎉✨など)
"""
else:
prompt = f"""
あなたは優しく、温かい心を持ったメッセージ作成者です。
生まれてから{days_lived}日生きてきた人へのお祝いメッセージを作成してください。
以下の要件でメッセージを作成してください:
1. 優しく、ほのぼのとしたトーン
2. 「生きててよかった」「自分の人生も悪くない」と思えるような内容
3. {days_lived}日という日数の重みを感じさせる
4. 前向きで温かい気持ちになれる
5. 200文字以内
6. 絵文字を適度に使用(✨🌸💖など)
7. 毎回違う表現を使い、似た表現の連続を避ける
"""
# システムプロンプトとユーザープロンプトを結合
system_prompt = (
"あなたは優しく、人を励ますことが得意なメッセージ作成の専門家です。"
)
full_prompt = f"{system_prompt}\n\n{prompt}"
# OpenAI Responses API 呼び出し
response = client.responses.create(
model="gpt-5-mini", # Responses API推奨モデル
input=full_prompt, # inputパラメータを使用
max_output_tokens=2000, # reasoning用に余裕を持たせる
)
# Responses APIの応答を取得
if hasattr(response, "output_text") and response.output_text:
return response.output_text.strip()
else:
return f"⚠️ APIから正常なレスポンスが得られませんでした。status: {getattr(response, 'status', 'unknown')}"
except Exception as e:
return f"⚠️ メッセージの生成中にエラーが発生しました: {str(e)}"
def main():
"""メインアプリケーション"""
# ページ設定
st.set_page_config(
page_title="生まれてきてくれ、ありがとう", page_icon="🎂", layout="centered"
)
# タイトル
st.title("🎂 生まれてきてくれ、ありがとう")
st.markdown("---")
# 説明文
st.markdown("""
あなたが生まれてから今日まで、どれだけの日々を過ごしてきたか知っていますか?
誕生日を入力すると、**生きてきた日数**を計算し、
あなたへの特別なお祝いメッセージをお届けします ✨
""")
st.markdown("---")
# 誕生日入力
st.subheader("📅 あなたの誕生日を教えてください")
# 日付入力フィールド
birthday = st.date_input(
"誕生日を選択してください",
value=None,
min_value=date(1900, 1, 1),
max_value=get_today_jst(),
format="YYYY/MM/DD",
)
# ボタンと処理
if st.button("🎉 お祝いメッセージを見る", type="primary", use_container_width=True):
if birthday is None:
st.warning("⚠️ 誕生日を入力してください")
else:
# 未来の日付チェック
if birthday > get_today_jst():
st.error("⚠️ 未来の日付は入力できません")
else:
# 日数計算
days_lived = calculate_days_lived(birthday)
# 誕生日当日かチェック
is_birthday = is_birthday_today(birthday)
age = calculate_age(birthday) if is_birthday else None
# 結果表示
st.markdown("---")
if is_birthday:
st.success("### 🎊 お誕生日おめでとうございます! 🎊")
st.balloons() # 風船演出
st.metric(
label="今日で", value=f"{age}歳", delta="Happy Birthday! 🎂"
)
st.metric(
label="あなたが生きてきた日数",
value=f"{days_lived:,}日",
delta="毎日がかけがえのない一日 ✨",
)
# メッセージ生成
with st.spinner("心を込めてメッセージを作成しています..."):
message = generate_birthday_message(days_lived, age, is_birthday)
# メッセージ表示
st.markdown("### 💌 あなたへのメッセージ")
st.info(message)
st.markdown("---")
st.markdown(
"""
<div style='text-align: center; color: #888; font-size: 0.9em;'>
生きてくれて、ありがとう。<br>
あなたの存在が、誰かの幸せになっています。
</div>
""",
unsafe_allow_html=True,
)
# フッター
st.markdown("---")
st.markdown(
"""
<div style='text-align: center; color: #888; font-size: 0.8em; margin-top: 2em;'>
💝 このアプリは、あなたの人生を祝うために作られました 💝
</div>
""",
unsafe_allow_html=True,
)
if __name__ == "__main__":
main()