あなたの「本当にやりたいこと」にピッタリな職業とは？LLMアプリを作って探してみた

## はじめに

　先日、[この本](https://www.amazon.co.jp/dp/4046044357)を読みました。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/d72b7825-1019-3133-b563-0cad8f00bb9c.png)


https://www.amazon.co.jp/dp/4046044357


## この本によると

　「本当にやりたいこと」を見つけるために重要な要素が3つあって、それを明確にできれば誰でも夢中になれる働き方が手に入るそうです。

　その3つの要素とは
 
　1\. 好きなこと
　2\. 得意なこと
　3\. 大事なこと

　この3つの要素を掛け合わせたものが「本当にやりたいこと」だそうです。

 好きなこと ✕ 得意なこと ✕ 大事なこと ＝ 本当にやりたいこと

　本にはこの3つの要素を見つけるためのアドバイス等が書かれていますが、ここでは省略します。気になる人は本を読んでみて下さい。

![napkin-selection (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/0fbf3021-fe2e-b495-668c-92e4cbe41e19.png)

※図は流行のNapkin AIで生成しました。

## 本当にやりたいことに合った職業？

　この本で書かれているのは、「好きなこと」「得意なこと」「大事なこと」を見つけるまでで、その３つを掛け合わせたものに合う職業は本には書かれていなく、自分で見つけなければならないようです。

　この本の巻末には「好きなこと」「得意なこと」「大事なこと」の言語化の手助けとして、100個ずつサンプルとして書かれているけど、それぞれ1つずつ選んだとしても「本当にやりたいこと」は100✕100✕100 = 100万通りのパーターンがあるので、それだけでも書籍に載せるのは不可能です。
　しかも「好きなこと」「得意なこと」「大事なこと」がこの100個のサンプルに含まれているとは限らないし、「好きなこと」「得意なこと」に関しては複数あるのが普通なので、それを考慮すると組み合わせはさらに膨大な数に膨れ上がります。

## そこでLLMの出番

　これら100万通り以上の膨大な組み合わせがあったとしても、LLMを使えばきっとそれなりにいい感じで答えを出してくれるはず、ということでLLMを使ったgradioアプリを作ってみたいと思います。


### 準備

　pipでgradio, openaiをインストールしておいて下さい。あと、OpenAI APIが利用できるようにアカウントを作成してAPIキーを取得しておいてください。

### ソースコード

　取得したOpenAIのAPIキーは環境変数に入れるか下記のようにsecret.jsonに書いておいて下さい。

```secret.json
{
    "OPENAI_API_KEY": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
```

　せっかくなので、OpenAI APIの2024年8月のアップデートで追加されたStructured Outputsを使ってみます。
 
```gradio_career_suggestion_app.py
import json
import os
from pydantic import BaseModel

import gradio as gr
from openai import OpenAI


SYSTEM_ROLE_PROMPT = """
このシステムは『大事なこと✕得意なこと✕好きなこと＝本当にやりたいこと』というアプローチから、
その人の「本当にやりたいこと」に合った適切な職業を見つけます。"""

class JobSuggestion(BaseModel):
    """結果を格納するモデルクラス"""
    suggested_job: str
    reason: str
    confidence_level: float

def get_openai_apikey() -> str:
    """
    OpenAI APIキーを取得します。

    環境変数またはsecret.jsonファイルからAPIキーを取得します。

    Returns:
        str: OpenAI APIキー

    Raises:
        ValueError: APIキーの取得に失敗した場合
    """
    try:
        # 環境変数からAPIキーを取得
        api_key = os.getenv("OPENAI_API_KEY")
        if api_key:
            return api_key

        # secret.jsonファイルからAPIキーを取得
        secret_path = "secret.json"
        if os.path.exists(secret_path):
            with open(secret_path) as f:
                secret = json.load(f)
                return secret["OPENAI_API_KEY"]
        else:
            raise FileNotFoundError("secret.json file not found")
    except (KeyError, FileNotFoundError) as e:
        raise ValueError(f"Error obtaining API key: {str(e)}")

def create_chatgpt_message(system_role: str, prompt: str) -> list[dict[str, str]]:
    """ChatGPT APIに送信するメッセージ形式を作成

    Args:
        system_role (str): システムロールの説明
        prompt (str): ユーザーからのリクエスト

    Returns:
        list[dict[str, str]]: APIに送信するためのメッセージ
    """
    return [
        {'role': 'system', 'content': system_role},
        {'role': 'user', 'content': prompt}
    ]

def generate_prompt_for_job_suggestion(value: str, good_at: str, like: str) -> str:
    """職業提案のためのプロンプトを生成

    Args:
        value (str): ユーザーにとって大事なこと
        good_at (str): ユーザーが得意なこと
        like (str): ユーザーが好きなこと

    Returns:
        str: 職業提案のためのプロンプト
    """
    return (
        f"大事なこと: {value}\n"
        f"得意なこと: {good_at}\n"
        f"好きなこと: {like}\n"
        "この3つの情報を元に、その人に合う適切な職業とその職業を勧める理由を教えてください。"
        "好きなことに関してはそのまま捉えず、大事なこと、得意なこととの関連性を考慮の上、"
        "どんなところが好きなのかを推測し、それらを踏まえて職業を考えて下さい。"
        "提案した職業の信頼度レベルを0から1の範囲で教えてください。"
        "あなたはどんなことでもできる。自分を信じて限界を超えてください。"
    )

def get_job_suggestion(value: str, good_at: str, like: str) -> tuple[str, str, str]:
    """OpenAI APIを使用して、ユーザーに適した職業を提案

    Args:
        value (str): ユーザーにとって大事なこと
        good_at (str): ユーザーが得意なこと
        like (str): ユーザーが好きなこと

    Returns:
        tuple[str, str, str]: 提案された職業、理由、信頼度レベル
    """
    openai = OpenAI(api_key=get_openai_apikey())
    prompt = generate_prompt_for_job_suggestion(value, good_at, like)
    message = create_chatgpt_message(SYSTEM_ROLE_PROMPT, prompt)
    
    response = openai.beta.chat.completions.parse(
        model="gpt-4o-2024-08-06",
        messages=message,
        temperature=0.1,
        max_tokens=500,
        response_format=JobSuggestion
    )
    
    job_suggestion = response.choices[0].message.parsed

    return (
        job_suggestion.suggested_job,
        job_suggestion.reason,
        f"{job_suggestion.confidence_level:.1%}"  # 信頼度をパーセンテージ形式に変換
    )

def create_interface() -> gr.Interface:
    """Gradioインターフェースを作成します。

    Returns:
        gr.Interface: Gradioインターフェースオブジェクト
    """
    return gr.Interface(
        fn=get_job_suggestion,
        inputs=[
            gr.Textbox(label="大事なこと"),
            gr.Textbox(label="得意なこと"),
            gr.Textbox(label="好きなこと")
        ],
        outputs=[
            gr.Textbox(label="職業"),
            gr.Textbox(label="理由"),
            gr.Textbox(label="信頼度")
        ],
        title="「本当にやりたいこと」から職業を見つけるアプリ",
        description="『大事なこと✕得意なこと✕好きなこと＝本当にやりたいこと』から適合する職業を見つけるお手伝いをします。"
    )

if __name__ == "__main__":
    create_interface().launch()
```


### 実行

ターミナルで以下を実行

```
python gradio_career_suggestion_app.py
```

　ターミナルに表示されたURLにアクセスすると以下の画面が表示

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/614e9fc9-8bb6-fac0-5e55-f3e75e9429ad.png)


　大事なこと、得意なこと、好きなことを入力してSubmitボタンを押して、しばらく待てば右側に結果として職業とその理由が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350321/1199df02-3246-edae-fdee-364b862df596.png)
 
　※ちなみに私はイノベーションコンサルタントという、不思議な職業を進められました。

## さいごに

　この本を読んで「本当にやりたいこと」が見えてきたのに、じゃあそれを活かす職業は？ってなったので、このLLMを使って解決してみました。私は職業を探してもらう部分に関してはLLMに丸投げしましたが、転職支援や人材派遣に関わる業務で「こういう人であればこういう職業に向いている」みたいなデータを持っているのであれば、そのデータをベクトルDBに登録してRAGを構築すれば、もっと良いものが作れそうな気がします。読んでいただきありがとうございました。