自然言語で検索できる求人サービスをGPTのFunction Callingで作ってみた。


結果

こんな感じのものができた。
ちなみに、求人情報はQiita Jobsからスクレイピングした。
(登録しなくても、求人情報全部見れたから)

質問内容

私は現在インフラエンジニアをやっています。
勤務地が東京で年収1100万円以上の求人を探しています。
AWSやKubernetesを利用してる会社だと嬉しいです。
また、リモートワークが可能で、交通費が支給されるとなおうれしいです。

GPTによって生成された検索用パラメーター

{
    "required_skills": "AWS,Kubernetes",
    "minimum_salary": 1100,
    "job_positions": "インフラエンジニア",
    "locations": "東京都",
    "welfare_benefits": "交通費支給",
    "remote_work_option": True,
}

作ってみた感想

正直、求人検索に関しては、一般的な求人サイトの検索用のフォームがあれば、それで十分だから、今回作ったものは需要なさそうw
あと転職エージェントの代替を目指すなら、普通に結果として求人情報を返すのではなく、自然言語で返してくれたほうがそれっぽい。
だから、別の方法も色々試してみようと思う。
GPT-3.5とかGPT-4でファインチューニングできるようになるらしいし、できるようになったら試してみようと思う。
LangChainも触ってみる。

本当はAWS上にデプロイしようと思ってて、ドメインも取得して、terraformでリソースも生成してたんだけど、求人情報を定期的に取得して更新するバッチ処理とか、その他いろいろなものをガチで作ろうとすると、しんどいなと思ったのでやめた。w

まあ、Function Callingの実験はできたから、良しとする。
ただ、FunctionCallingのおかげで、様々なものとのインテグレーションが楽になったのは間違いない。これから、どんどん使われていくと思う。

詳細

作ろうと思った背景

最近、ハードコアに働ける場所を探していて、そのために正社員転職しようと思い、転職活動をしている。
その時、転職エージェントを使う機会もあったが、転職エージェントのやっていることは、求職者からヒアリングして、それに合った求人を紹介すること。

あれ?転職エージェントの仕事って、LLMでもできんじゃね?て思ったのがきっかけ。

使用したGPTのモデル

  • gpt-3.5-turbo-16k-0613

    • スクレイピングしたデータを構造化(MySQLに入れるデータに変換)するために使った。function callingの実験をするために使ったけど、スクレイピングしてる時点である程度構造化できてるので、別に使わなくてもよかった気がした。ただ、求人情報のデータ(今回はmarkdown形式)をそのまま入れるだけで、いい感じにしてくれるので、使い道は結構ありそう。GPT-4のほうが精度良かったけど、コストが高くて現実的ではなかった。あとトークン足りなかった。

  • gpt-4-0613

    • ユーザーが入力した情報から、OpenSearchの検索用のパラメータを作成する(Function Calling)のに使った。GPT-3でもよかったが、精度が低かったのでgpt-4にした。

技術スタック

  • python

  • django

    • 見た目とか、もろもろの機能とかをDjangoで実装した。大した内容じゃないから、flaskでやればよかったかも。

  • MySQL

    • 求人サイトからスクレイピングしたデータいれてる

  • OpenSearch

    • 検索用。基本的に、MySQLのデータをそのままコピーしただけ。

  • その他ライブラリ

    • scrapy

      • スクレイピングのためにいれた。

    • openai

      • 今回の主役となるGPTのAPI(Function Calling)を使うために導入

プロンプト

実験的にやってみただけなので、参考程度にw
ちなみに、enumのオプションは一切使ってない。GPTの可能性を狭めるかなと思ったのと、検索はOpenSearchのほうがいい感じにやってくれると思ったから。
もちろん、正確な値を求めるようなケースでは、設定したほうが絶対いい。

求人情報解析(非構造化データから構造化データへの変換)のプロンプト

functions = [
    {
        "name": "insert_job_table",
        "description": "markdown形式の求人情報を解析して、テーブルに登録する",
        "parameters": {
            "type": "object",
            "properties": {
                "job_title": {
                    "type": "string",
                    "description": "求人タイトル",
                },
                "required_skills": {
                    "type": "string",
                    "description": "必要スキル(技術)。複数ある場合はカンマ区切り。ex. React,Angular,AWS",
                },
                "minimum_salary": {
                    "type": "integer",
                    "description": "最小年収(単位は万)。 ex. 650",
                },
                "maximum_salary": {
                    "type": "integer",
                    "description": "最大年収(単位は万)。 ex. 1400",
                },
                "positions": {
                    "type": "string",
                    "description": "募集ポジション・職種。複数ある場合はカンマ区切り。 ex. バックエンドエンジニア,インフラエンジニア",
                },
                "locations": {
                    "type": "string",
                    "description": "勤務地。複数ある場合はカンマ区切り。 ex. 東京都,北海道/札幌市",
                },
                "qualifications": {
                    "type": "string",
                    "description": "応募資格・あると望ましいスキル・求める人物像。複数ある場合はカンマ区切り。 ex. データベースの使用経験,コードレビューの経験,能動的に作業できる人",
                },
                "welfare_benefits": {
                    "type": "string",
                    "description": "待遇・福利厚生。複数ある場合はカンマ区切り。 ex. 住宅手当,交通費支給",
                },
                "company_cultures": {
                    "type": "string",
                    "description": "企業文化(特徴・風習)。複数ある場合はカンマ区切り。 ex. スタートアップ文化,エンジニアファースト",
                },
                "employment_categories": {
                    "type": "string",
                    "description": "雇用区分。複数ある場合はカンマ区切り。 ex. 正社員,契約社員",
                },
                "work_system": {
                    "type": "string",
                    "description": "勤務制度。複数ある場合はカンマ区切り。 ex. フレックスタイム制",
                },
                "remote_work_option": {
                    "type": "boolean",
                    "description": "リモートワークが可能かどうか。不明な場合は[false]。 ex. true",
                },
                "company_name": {
                    "type": "string",
                    "description": "会社名。 ex. 株式会社Google",
                },
                "company_capital": {
                    "type": "integer",
                    "description": "会社資本金。単位は円。 ex. 120000000",
                },
                "company_founded_date": {
                    "type": "string",
                    "description": "会社設立年月日。「YYYY-MM-DD」のフォーマット。 ex. 2019-04-01",
                },
                "company_employee_count": {
                    "type": "integer",
                    "description": "会社従業員数。 ex. 30",
                },
            },
            "required": [
                "job_title",
                "required_skills",
                "minimum_salary",
                "maximum_salary",
                "positions",
                "locations",
                "qualifications",
                "welfare_benefits",
                "company_cultures",
                "employment_categories",
                "work_system",
                "remote_work_option",
                "company_name",
                # "company_capital",
                # "company_founded_date",
                # "company_employee_count",
            ],
        },
    }
]

completion = openai.ChatCompletion.create(
    model="gpt-3.5-turbo-16k-0613",
    messages=[
        {
            "role": "system",
            "content": "ユーザーから、与えられた求人情報(markdown形式)の解析をして、テーブルに登録してください。",
        },
        {"role": "user", "content": job_markdown_raw_data}, # 求人情報の元データ
    ],
    functions=functions,
    function_call={"name": "insert_job_table"},
)

求人検索時(検索用のパラメーター生成)のプロンプトは下記

functions = [
    {
        "name": "search_jobs",
        "description": "ユーザーから与えられた情報をもとに求人を検索する。",
        "parameters": {
            "type": "object",
            "properties": {
                "required_skills": {
                    "type": "string",
                    "description": "求人で必要と明記されているスキル(技術)。複数ある場合はカンマ区切り。ex. React,Angular,AWS",
                },
                "minimum_salary": {
                    "type": "integer",
                    "description": "最小年収(単位は万円)。 ex. 650",
                },
                "maximum_salary": {
                    "type": "integer",
                    "description": "最大年収(単位は万円)。 ex. 1400",
                },
                "job_positions": {
                    "type": "string",
                    "description": "募集ポジション・職種。複数ある場合はカンマ区切り。 ex. バックエンドエンジニア,インフラエンジニア",
                },
                "locations": {
                    "type": "string",
                    "description": "勤務地。複数ある場合はカンマ区切り。 ex. 東京都,北海道",
                },
                "qualifications": {
                    "type": "string",
                    "description": "ユーザーが保持している資格や、今までの経験。 ex. AWS Solution Architect Professional保有,データベースの使用経験,コードレビューの経験,能動的に作業できる人",
                },
                "welfare_benefits": {
                    "type": "string",
                    "description": "待遇・福利厚生。複数ある場合はカンマ区切り。 ex. 住宅手当,交通費支給",
                },
                "company_cultures": {
                    "type": "string",
                    "description": "企業文化(特徴・風習)。複数ある場合はカンマ区切り。 ex. スタートアップ文化,エンジニアファースト",
                },
                "employment_categories": {
                    "type": "string",
                    "description": "雇用区分。複数ある場合はカンマ区切り。 ex. 正社員,契約社員",
                },
                "work_system": {
                    "type": "string",
                    "description": "勤務制度。複数ある場合はカンマ区切り。 ex. フレックスタイム制",
                },
                "remote_work_option": {
                    "type": "boolean",
                    "description": "リモートワークが可能かどうか。デフォルトは[true]。 ex. true",
                },
                "company_name": {
                    "type": "string",
                    "description": "会社名の検索で使うキーワード。 ex. 株式会社Google",
                },
                "minimum_company_capital": {
                    "type": "integer",
                    "description": "最小会社資本金。単位は円。 ex. 120000000",
                },
                "maximum_company_capital": {
                    "type": "integer",
                    "description": "最大会社資本金。単位は円。 ex. 120000000",
                },
                "start_company_founded_date": {
                    "type": "string",
                    "description": "検索範囲の開始日。会社設立年月日。「YYYY-MM-DD」のフォーマット。 ex. 2019-04-01",
                },
                "end_company_founded_date": {
                    "type": "string",
                    "description": "検索範囲の終了日。会社設立年月日。「YYYY-MM-DD」のフォーマット。 ex. 2019-04-01",
                },
                "minimum_company_employee_count": {
                    "type": "integer",
                    "description": "最小会社従業員数。 ex. 30",
                },
                "maximum_company_employee_count": {
                    "type": "integer",
                    "description": "最大会社従業員数。 ex. 30",
                },
            },
        },
    }
]

completion = openai.ChatCompletion.create(
    model="gpt-4-0613",
    messages=[
        {
            "role": "system",
            "content": "ユーザーから、情報をもとに、求人を検索してください。",
        },
        {"role": "user", "content": question}, # ユーザーからの質問
    ],
    functions=functions,
    function_call={"name": "search_jobs"},
)

この記事が気に入ったらサポートをしてみませんか?