銀河鉄道

Python×OpenAI|感情の言語化ボットを作ってみる

サムネイル
AI BotFeeling-to-Words
言語化してくれる
ボットを作ろう

たとえば
こんな感じ

python main.py
💬 入力してください(終了するには空のままEnter)

仕事が遅いと言われて悲しくなった

📅 Date: 2025-05-16 19:00:21
🌙 Summary (EN): The sadness came not just from being told I was slow, but from the feeling that my unseen effort and careful process were dismissed as worthless.
🌙 Summary (JA): 「遅い」と言われた悲しさの本質は、見えない努力や丁寧なプロセスが無価値とされたように感じたことにある。
🏷 Tags (EN): [‘sadness’, ‘effort-dismissal’, ‘validation-seeking’]
🏷 Tags (JA): [‘悲しみ’, ‘努力の否定’, ‘承認欲求’]

悲しい理由を
分析して出してくれる

何度か、やりとりすると
こんな感じで整理してくれる

# 🪞 Emotional Insight Log
📅 **2025-05-17(土)**

---

### 💬 感情のきっかけ

**「仕事が遅い」と言われて悲しくなった。**

---

### 🔍 気づきのログ一覧

#### 1. 悲しみの正体

> The sadness came not just from being told I was slow, but from the feeling that my unseen effort and careful process were dismissed as worthless.
> 「遅い」と言われた悲しさの本質は、見えない努力や丁寧なプロセスが無価値とされたように感じたことにある。

* **Tags**: `sadness` `effort-dismissal` `validation-seeking`

---

#### 2. なぜ無価値と感じるのか

> Because when my process isn't understood, I start to equate 'not visible' with 'not valuable', as if only speed proves my worth.
> 自分のプロセスが理解されないと、「見えないこと=価値がないこと」と思い込み、速さだけが価値だと錯覚してしまうから。

* **Tags**: `invisibility` `self-worth` `cognitive-distortion`

---

#### 3. 自分が重視している価値

> Deep down, I value thoughtfulness, precision, and integrity over speed—but I live in a world that often rewards the opposite, which creates inner conflict.
> 本当は、速さよりも思慮深さや正確さ、誠実さを大切にしているのに、世の中はその逆を評価することが多く、そのギャップが内なる葛藤を生む。

* **Tags**: `value-conflict` `internal-struggle` `misalignment`

---

#### 4. ギャップをどう乗り越えるか

> The inner gap can be bridged not by choosing one side, but by redefining value in my own terms—and daring to embody it, even when the world doesn't echo it back.
> 内なるギャップは、どちらかを選ぶことでなく、自分なりの価値基準を再定義し、それが世の中に受け入れられなくてもなお、体現する勇気によって埋められる。

* **Tags**: `integration` `self-definition` `courage`

---

ボットの目的

利用目的

  • ボヤッとした感情を言語化してもらう
  • 原因がわからないと無駄に他人のせいにしてしまうから
  • 自分の望みを明確にしたいから

詳細

  • ドキュメント自動生成(ログ自動整理)
  • 振り返り文化・ナレッジマネジメント
  • 毎日の「メモまとめ→振り返り」の自動化

学べる技術

  • 文脈要約(プロンプト設計)
  • sqlite or json保存
  • CLIやWebUIで出力(Flask / Streamlit)

基本構成

基本は
3つのモジュールで動かす

remind_bot/
├── main.py               # Entry point
├── gpt_helper.py         # GPT API interaction (prompt design)
├── log_store.py          # Log storage and retrieval (json or sqlite)

main.py|最初に呼び出すモジュール

from gpt_helper import summarize_insight
from log_store import save_log
from datetime import datetime

if __name__ == "__main__":
    print("💬 入力してください(終了するには空のままEnter)")

    # ユーザーからの入力を受け取る/strip() で「先頭・末尾の無意味な空白」を取る
    user_input = input(">>> ").strip()

    # 入力が空でなければ、処理を開始
    if user_input:
        # GPTに入力文を送り、要約・タグ・リマインド判定を含む辞書型の構造データが返ってくる
        result = summarize_insight(user_input)

        # ログの記録日時(ISO形式で秒まで)
        result["timestamp"] = datetime.now().isoformat(timespec='seconds')

        # このログが main.py から来たことを示す
        result["source"] = "main.py"

        # CLI上に出力
        print("\n📅 Date:", result["date"])
        print("🌙 Summary (EN):", result["summary"]["en"])
        print("🌙 Summary (JA):", result["summary"]["ja"])
        print("🏷 Tags (EN):", result["tags"]["en"])
        print("🏷 Tags (JA):", result["tags"]["ja"])

        # ログの保存
        save_log(result)
        print("✅ 保存しました!")
    else:
        print("👋 Exiting")

gpt_helper.py|プロンプト設計

def summarize_insight(text: str) -> dict:
    from openai import OpenAI
    from dotenv import load_dotenv
    import os
    import datetime
    import json

    load_dotenv()
    client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

    def create_prompt(lang):
        return [
            {
                # GPTにキャラ設定と出力ルールを教える命令書
                "role": "system",

                # キーワード
                # "vague or tangled emotions":曖昧な感情(どうして?という疑問文も tangled emotionsとみなされる)
                # "into clear and insightful statements":明快な気づきを与えるような文章に変更する
                # "analyzing their emotional structure and patterns.":「その感情の背後にある思考パターン」を推論して答える

                # ("...""...") 定型の長い文字列
                "content": (
                    "You are an assistant that helps people translate vague or tangled emotions into clear and insightful statements"
                    "by analyzing their **emotional structure and patterns**."
                    f"Respond strictly in {'English' if lang == 'en' else 'Japanese'}, regardless of the input language. "

                    "Your role is not just to rephrase, but to help the writer discover the **underlying emotional mechanism or dynamic** "
                    "at play—what’s really happening internally."
                    "Return your result as a JSON object with the following fields:\n\n"
                    "- date: today's date (yyyy-mm-dd hh:mm:ss)\n"
                    "- summary: a single sentence that describes the emotional core of the log, written as if by the person themselves\n"
                    "- tags: a list of emotion, thought pattern, or psychological insight keywords\n\n"
                    "Avoid generic phrases like 'The user said...'; speak in the writer’s voice and help name what they could not."
                )
            },
            {
                # ユーザー側
                "role": "user",

                # f"""{...}""" とすることで、改行やフォーマットを見たまま維持できる(→ ドキュメント的にも読みやすい)
                "content": f"""
                    Please analyze the following emotional log and return a JSON response:

                    Text:
                    {text}
                    """
            }
        ]

    response_en = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=create_prompt("en"),
        temperature=0.3 # 0.3:講師タイプ/出力のブレを抑えて安定した返答を目指す
    )
    response_ja = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=create_prompt("ja"),
        temperature=0.3
    )

    try:
        # GPTが返したJSON文字列を → Pythonの辞書に変換
        data_en = json.loads(response_en.choices[0].message.content)
        data_ja = json.loads(response_ja.choices[0].message.content)
        return {
            "summary": {
                "en": data_en.get("summary"),
                "ja": data_ja.get("summary")
            },
            "tags": {
                "en": data_en.get("tags", []),
                "ja": data_ja.get("tags", [])
            },

            # GPTのdateは使わず、常にローカル日時を使用:GPTのdateは不正確のため
            "date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }

    # GPTがJSON形式で返してくれないこともあるから、安全のために例外処理:失敗時の代替返却
    except json.JSONDecodeError:
        return {
            "summary": {"en": "Failed to parse GPT (EN)", "ja": "Failed to parse GPT (JA)"},
            "tags": {"en": [], "ja": []} # 「空の構造」として後処理できるようにする
        }

temperatureとは|どれくらいの精度で答えを出すか

temperature特徴例えるなら
0.0完全ガチ正確マン公務員。マジメ。創造性ゼロ。
0.3事実ベースで穏やかに講師。筋は通ってるけど少し柔軟。
0.7バランス型友達。ちょっとユーモアあり。
1.0創造的だけどちょっと危なっかしい芸人。話はおもろいけど信憑性に欠けるときも。
1.5〜2.0カオス。詩人。妄想「宇宙ってさ、全部たまごやねん」みたいな感じ

temperatureはAIの性格スライダー
  • 数値が低い
    • 論理性・再現性
  • 数値が高い
    • 創造性・多様性
  • 用途に応じて設計者が調整すべき“構造変数”のひとつ

log_store.py |ログ保存機能

# Log storage and retrieval (json or sqlite)
# ログ設計における「記憶の中枢モジュール」

import json
import os
from datetime import datetime

def save_log(entry: dict):
    # ログファイル名用の日付文字列を生成
    date_str = datetime.now().strftime('%Y-%m-%d')
    log_dir = "logs"

    # logsフォルダがなければ自動で作る:存在しててもエラーにしない
    os.makedirs(log_dir, exist_ok=True)
    log_file = os.path.join(log_dir, f"{date_str}.json")

    # すでに当日のログファイルがあれば、中身を読み出して追記用に保持
    logs = []
    if os.path.exists(log_file):
        with open(log_file, 'r') as f:
            logs = json.load(f)

    # 今回のログ(entry)をリストに追加
    logs.append(entry)

    # 整形済JSON形式で保存(日本語も壊れないよう ensure_ascii=False)
    with open(log_file, 'w') as f:
        json.dump(logs, f, indent=2, ensure_ascii=False)

# 指定された日付のログファイル(例:2025-05-16.json)を読み込んでリストとして返す
def load_logs(date: str) -> list:
    # 日付をもとにファイルパスを生成
    log_file = os.path.join("logs", f"{date}.json")

    # ファイルがなければ空リストで返す(存在確認による安全性確保)
    if not os.path.exists(log_file):
        return []
    with open(log_file, 'r') as f:
        # JSONファイルを辞書リスト形式で返す
        return json.load(f)

main.pyを呼び出し→プロンプト発動→ログ保存

gpt-3.5-turbo での結果

python main.py
💬 入力してください(終了するには空のままEnter)

仕事が遅いと言われて悲しくなった

📅 Date: 2025-05-16 19:00:21
🌙 Summary (EN): Feeling sad after being told that I am slow at work.
🌙 Summary (JA): 自己評価が低くなり、悲しみを感じた。
🏷 Tags (EN): [‘sadness’, ‘criticism’, ‘self-doubt’, ‘workplace stress’]
🏷 Tags (JA): [‘自己評価’, ‘悲しみ’, ‘ストレス’]

gpt-4o での結果|より細かく分析的になる

🌙 Summary (EN): The sadness came not just from being told I was slow, but from the feeling that my unseen effort and careful process were dismissed as worthless.
🌙 Summary (JA): 「遅い」と言われた悲しさの本質は、見えない努力や丁寧なプロセスが無価値とされたように感じたことにある。
🏷 Tags (EN): [‘sadness’, ‘effort-dismissal’, ‘validation-seeking’]
🏷 Tags (JA): [‘悲しみ’, ‘努力の否定’, ‘承認欲求’]

さらに深ぼって
入力してみよう

2回目|gpt-3.5-turbo での結果

なぜ自己評価が低くなってしまうんだろう

🌙 Summary (EN): Why do I struggle with low self-esteem?
🌙 Summary (JA): 自分の価値を見出せずに自己評価が低くなってしまう自問
🏷 Tags (EN): [‘self-esteem’, ‘self-evaluation’, ‘confusion’, ‘introspection’]
🏷 Tags (JA): [‘自尊心’, ‘自己評価’, ‘混乱’, ‘内省’]

ちょっと
よくわからない

2回目|gpt-4o での結果

なぜ無価値だと感じてしまうんだろう

🌙 Summary (EN): Because when my process isn’t understood, I start to equate ‘not visible’ with ‘not valuable’, as if only speed proves my worth.
🌙 Summary (JA): 自分のプロセスが理解されないと、「見えないこと=価値がないこと」と思い込み、速さだけが価値だと錯覚してしまうから。
🏷 Tags (EN): [‘invisibility’, ‘self-worth”, ‘cognitive-distortion’]
🏷 Tags (JA): [‘不可視性’, ‘自己価値’, ‘認知のゆがみ’]

なんとなくの悲しみが
明確になった

分析|3.5 と 4o の違い

  • gpt-3.5
    • 書かれたことを整理する
  • gpt-4o
    • 書かれていない思いまで読み取ってくれる

ボットを使うことでわかること

  • 言った人に対する怒りではない
  • そもそも自分自身が、価値観の揺れに葛藤している
  • 言語化して、明確になることで対策がとれる
  • 自分の望みがわかる
  • 他人のせいにするのではなく、自分の感情を整理する

最終構成

Markdown形式での出力を追加

remind_bot/
├── main.py               # Entry point
├── gpt_helper.py         # GPT API interaction (prompt design)
├── log_store.py          # Log storage and retrieval (json or sqlite)
├── markdown_exporter.py  # Markdown output of daily insights
├── .env                  # Environment variables like API keys (excluded from git)
├── logs/                 # Folder for saved logs
│   └── 2025-05-16.json   # Example: log file by date
└── templates/            # Markdown templates, etc.
    └── log_template.md

著者

author
月うさぎ

編集後記:
この記事の内容がベストではないかもしれません。