SmartHR Tech Blog

SmartHR 開発者ブログ

「AI履歴書読み取り機能」開発の舞台裏 ── ぜんぶ見せます!

こんにちは、AIインテグレーションユニットの木村です。

この記事では、AIインテグレーションユニットで行ってきた「AI履歴書読み取り機能」開発の舞台裏を、検証プロセスや得られた知見を中心に紹介します。

目次

「AI履歴書読み取り機能」の開発理由

「AI履歴書読み取り機能」はAIインテグレーションユニットとして最初にリリースした機能となります。 AIインテグレーションユニットとは、2024年8月に新設されたAI専門のチームです。このチームはSmartHRのプロダクト全体にAI機能を実装することを目的とし、各プロダクトチームと連携しながらAIを活用した機能の実験、実装、運用を行っています。

「AI履歴書読み取り機能」の開発は、最初から決まっていたわけではありません。AIインテグレーションユニットの発足後、まずは各プロダクトチームに対してヒアリングを行い、AIで解決できそうな課題やアイデアの吸い上げから行っていきました。

そして課題の「ユーザー数」「頻度」「事業価値」「実現可能性」並びに 「現在その課題がどのように人間によって解決されているか」 について確認しました。人間が全くアプローチできていない課題は、AIでも解決が難しいとされています。「コストがかかっていても人間が何らかの形で解決できていること」を重要視し、そこから解決策の具体化を行っていきました。

また、AIインテグレーションユニットは、SmartHRの強みである豊富な企業・従業員データを最大限に活かし、 ハーベストループ(AIが業務課題を解決し、その結果得られたデータを活用してAIの精度や機能をさらに強化し、最終的な価値や顧客体験を向上させるというサイクル)を実現することをビジョンの一つとしているため、そういった観点も合わせて解決すべき課題の優先順位付けを行ってきました。

ヒアリング時点で50個近い課題が集まりましたが、それらの内容や優先順位を整理した上で、「SmartHRの中でコアな機能であること」「従業員というデータの重要性」「実現可能性」の観点から、まずは 「AI履歴書読み取り機能」 に着手することが決まりました。

「AI履歴書読み取り機能」のコンセプト

「AI履歴書読み取り機能」とは、履歴書のPDFを「SmartHR」にアップロードすることで、履歴書に記載された項目(姓名・生年月日・メールアドレス・住所など)をAI-OCR(文字認識)が読み取り、従業員データの登録に必要な情報を自動入力することができる機能です。

従業員情報の入力画面(姓名・生年月日・メールアドレス・住所など)

smarthr.jp

OCRの技術自体は昔からあるものですが、「AI履歴書読み取り機能」ではマルチモーダルLLMによってOCRを実現しました。 従来のOCRでは画像から読み取ったテキスト情報の位置や近くにある文字列などから中身を識別して処理する必要がありましたが、マルチモーダルLLMを用いることにより AIが読み取った文字列の中身を理解しているかのように、各文字列の意味付けを持ったまま扱うことができます。

これにより、読み取る履歴書の書き方やフォーマットが少し変わっていたり独自のものであったとしても、従来よりも高い精度で読み取りが可能になるという狙いです。

「AI履歴書読み取り機能」の技術検証

実装を始める前に、複数のマルチモーダルLLMモデルに対して実験と検証を行うことから始めました。

検証対象としたマルチモーダルLLMモデルは下記のとおりです。

  • GPT-4o
  • GPT-4o mini
  • Gemini 1.5 Pro
  • Gemini 1.5 Flash
  • Claude 3.5 Sonnet
  • Claude 3.0 Haiku

これらの6モデルに対して、「履歴書(PDFファイル)を読み取り、そこから記載してある下記の項目を読み取ること」ができるか、その精度・コスト・処理速度を検証していきました。

- 姓
- 名
- 姓ヨミガナ(カタカナ)
- 名ヨミガナ(カタカナ)
- 生年月日(YYYY/MM/DD形式)
- 郵便番号
- 住所(都道府県)
- 住所(市区町村)
- 住所(丁目・番地)
- 住所(建物名・部屋番号)
- 住所(ヨミガナ)
- メールアドレス
- 電話番号
- 性別
- 学歴(配列)
- 職歴(配列)
- 資格(配列)

また履歴書のみならず、別の書類のOCRも同様の技術を用いて実現することも当初から視野に入れていたため、履歴書を含むそれらの書類群に対して検証を実施しました。

技術検証のやり方

検証にあたり、まず実際に履歴書などの書類データを収集するところから始めました。結果として80枚以上のデータが集まったため、その書類の1枚1枚に対して、人力で「正解データ」をJSON形式で作成していきました。

正解データは例えば下記のようなものです。こういったデータを履歴書の枚数分(80枚以上)用意しました。

{
  "lastName": "佐藤",
  "firstName": "一郎",
  "lastNameKana": "サトウ",
  "firstNameKana": "イチロウ",
  "birthday": "1989/04/01",
  "zipCode": "106-0032",
  "pref": "東京都",
  "city": "港区",
  "street": "六本木3-2-1",
  "building": "住友不動産六本木グランドタワー 17F",
  "literalYomi": "トウキョウト ミナトク ロッポンギ",
  "email": "hoge@gmail.com",
  "phone": "03-1234-5678)",
  "gender": "男性",
  "education": [
    {
      "period": {
        "startMonth": "200804",
        "endMonth": "201203"
      },
      "details": "東京大学 工学部"
    }
  ],
  "employment": [
    {
      "period": {
        "startMonth": "201204",
        "endMonth": "202409"
      },
      "details": "株式会社SmartHRにて、MLエンジニアとして従事"
    }
  ],
  "qualifications": [
    {
      "obtainedMonth": "201807",
      "qualificationName": "TOEIC 900点"
    }
  ]
}

正解データが用意できたら、各モデルに対して実際に履歴書データを読み取らせ、OCRの結果をJSON形式で返させます。(このときに処理時間や、トークン数とモデルの価格から推定コストも一緒に出しておきます)

そして「OCRの結果として返ってきたJSON」と「用意していた正解データのJSON」の2つをLLMに渡し、その精度について5段階で評価をしてもらいます。(このようにLLMに評価させる手法を、LLM-as-a-Judgeといいます)

こうして出てきた下記項目に対して、「書類 ✕ モデル」分のデータをまとめていきました。

  • OCRの結果として返ってきたJSON
  • 用意していた正解データのJSON
  • 処理時間
  • 推定コスト(トークン数とモデルごとの価格から算出)
  • 精度(LLM-as-a-Judgeで出した5点満点の点数)
    • 経歴を含めた文章の精度はLLM-as-a-Judgeの点数で見つつ、それ以外の項目は文字列の完全一致でも精度を見た

これらによって出てきた項目を、書類と各モデル毎にまとめていきます。

つまり今回は履歴書だけでも「書類数(約80枚) ✕ モデル数(6モデル)」分の実行を行い、その結果をもとにどのモデルを選定すべきかを検証していきました。

各LLMモデルへの期待値と検証後の評価

ここでは検証を行った各モデルに対しての、検証前の期待値と検証後の評価についてまとめていきます。(あくまで当時(2024年秋)の評価であり、またSmartHRで求めていた履歴書OCR要件に対しての内容となります。)

GPT-4o ── 高い期待値で始めたものの、思ったほどの精度が出ず

  • 期待値
    • 画像を添付できる
    • Azure OpenAIは社内の別プロジェクトで実績があった
    • 当時最もメジャーな印象もあり、6モデルの中で検証前の期待値はNo.1
  • 検証後の評価
    • PDFをそのまま添付できないため画像への変換処理を挟んだが、履歴書によってはその段階でテキストの一部が抜け落ちてしまったり、処理時間も加算されてしまい、思ったように精度や速度が伸びなかった
    • 金額もそれなりに高い
    • 出力トークンが大きくなると全文取得できない場合があった

GPT-4o mini ── 画像を添付した際にトークン数が跳ね上がり、そこまで安くならなかった

  • 期待値
    • GPT-4oの低価格・軽量版として、こちらも高い期待値で実施
    • 精度面は検証が必要
    • バッチでの処理もできるためそこも期待
  • 検証後の評価
    • GPT-4oと同様に、画像への変換による精度や速度面の影響があった
    • 画像を添付したときにトークン数が跳ね上がり、結果としてそこまで安くならなかった

Gemini 1.5 Pro ── PDFをそのまま添付できるのが便利。サーバー高負荷エラーがしばしば

  • 期待値
    • 画像だけでなくPDFも添付することができる
    • 扱えるトークン数も多い
    • GPT-4oと同程度の精度までいけるかくらいの期待値
  • 検証後の評価
    • 今回のケースではPDFを直接送れるのが便利
    • 扱えるトークン数が多く、精度も高い
    • サーバー高負荷でエラーになることがしばしば

Gemini 1.5 Flash ── 十分な精度と速度と使い勝手、圧倒的なコストパフォーマンス

  • 期待値
    • Gemini 1.5 Proの軽量版として、こちらも精度面に検証の必要性を感じていた
    • トークン数も多いし、動作も早い
    • GPT-4o miniかGemini 1.5 Flashで精度が出せると最高
  • 検証後の評価
    • こちらも今回のケースではPDFを直接送れるのが便利
    • 低コスト・高速処理が可能
    • 精度もProとそこまで差がなかった
    • サーバー負荷によるエラーも少ない

Claude 3.5 Sonnet ── JSONモード未対応で、思ったほどの精度が出ず

  • 期待値
    • 画像を添付できる
    • 日本語の処理に期待し検討対象に追加
  • 検証後の評価
    • Vertex AI経由だとJSONモードが使えず(プロンプトで指示していても)テキスト出力になることがある
    • 精度は期待していたものよりは低く、思ったほどの点数が出なかった

Claude 3.0 Haiku ── 個人情報の取扱について怒られることも何度か

  • 期待値
    • Claude 3.5 Sonnetの低価格、軽量版
  • 検証後の評価
    • VertexAI経由だとJSONモードが使えず(プロンプトで指示していても)テキスト出力になることがある
    • 「申し訳ありませんが、添付された画像には個人情報が含まれているため、その内容を抜き出してJSONで出力することはできません。個人情報の取り扱いには十分注意が必要です。」というテキストだけが出力されることがある

結論

「精度」「コスト」「処理速度」を包括的に判断し、今回は Gemini 1.5 Flash を採用することにしました。

モデル選定後の精度改善

モデルの選定が終わると、そのモデルで更に読み取り精度が上がるように改善を行いました。

プロンプトチューニング

まずプロンプトのチューニングを実施しました。

プロンプトチューニングでは、仮説をもとにプロンプトを変更しては80枚の履歴書全てにOCRを再実行し、精度の平均点がどう変わったかを確認していきました。 そうしてプロンプトの変更とOCRの実行を何度も繰り返すことで、点数が上がるようなポイントを探っていきます。 様々な修正を実施しては、トライ&エラーで試行錯誤を繰り返していきましたが、結果として今回特に効果があったのは以下のようなポイントでした。

  • フォーマットの記載と、出力に際しての条件文の記載は分ける
  • フォーマットの記載にはすでにある規格に習った書き方にする(今回はJSON Schemaを使用)
  • 例はフォーマット内のdescriptionに入れる
    • Structured output(Geminiではresponse_schema)のような仕組みも検討していたが、各スキーマのdescriptionを読んでくれるかが不明だったなどの理由で当時は見送った
  • 長すぎず短すぎないようにする(塩梅が難しい)

結果的に、最初35行程度だったプロンプトは、180行程度となりました。

下記はプロンプトの一部です。

FORMAT = """
{
  "type": "object",
  "properties": {
    "lastName": {
      "type": "string",
      "description": "姓 <例>佐藤"
    },
    "firstName": {
      "type": "string",
      "description": "名 <例>一郎"
    },
    ...(省略)
  },
  "required": [
    "lastName",
    "firstName",
    ...(省略)
  ]
}
"""

PROMPT = f"""
  添付ファイルから下記の値を抜き出し、{フォーマット(JSON Schema)}で指定するJSON形式で返してください。この際、{条件}に従ってください。

  # フォーマット(JSON Schema)
  {FORMAT}

  # 条件
  - 内容は要約/改変せずにそのままのかたちで記載してください。
  ...(省略)
"""

ルールベースでの処理

プロンプトをいくら修正しても、なかなか精度が上がらないケースがあります。そういった場合にはルールベース(通常のプログラムによる処理)によってデータを加工することで精度を上げます。

例えば「学歴や職歴を新しいものから順にソートする処理」は、プロンプトを改善しても思ったように正しくソートされない場合が多く、JSONで受け取った後にそれをプログラムによってソートした方が精度が上がりました。

また、履歴書によっては「入学/卒業」「入社/退社」の表記がありますが、今回は学校名や会社名のみの表記で扱いたかったため、そういった文字列を除外する必要がありました。プロンプトチューニングだけでは指定しきれず、履歴書に「入学」などが表記されているとそれを含めたテキストで返ってきてしまうケースが多くあったため、末尾に該当の文字列があった場合は取り除く処理を入れています。

こういったいくつかのケースでは、AIではOCRの結果を出すまでにとどめ、その後のプログラムによって出力後のJSONを改修した方が効果的でした。

LLMの併用

今回OCRのメインの処理は Gemini 1.5 Flash を採用することとなりましたが、場合によっては処理の中で別のモデルのLLMを併用することもあります。

今回のケースでは「GeminiでJSONモードを指定していても正しいJSONの形にならない場合が稀にあった」ため、そこに対して GPT-4o mini を併用しています。JSONへの変換処理でエラーになった場合のみそれをcatchし「正しいJSONに直してください」というプロンプトとともに GPT-4o mini にJSONを渡して、正しいJSON形式への修正のみしてもらっています。

これは現状効果的に動作しており、 GPT-4o mini の懸念であった「画像ファイルを扱うことによるトークン数増加」などの影響は受けず、また Gemini 1.5 Flash よりも精度が高い状態で、十分な速度とコストをもって処理できています。

処理の内容によってモデルの最適解が変わるという学びであると同時に、今後も必要があれば処理によって異なるモデルの併用も手段の一つとして検討をしていく予定です。

検証やチューニングの振り返り、学び

モデル間の検証やチューニングを行うにあたって得た学びや反省点を共有します。

AI開発はやってみないとわからないことだらけ

プロンプトチューニングは予想と結果にギャップがあることが非常に多かったです。 「これで精度が上がるだろう」と思って試してみても思うように精度が上がらなかったり、逆に「どうなるかわからないけど試しに」とやってみたものが精度に影響したりと、予想と結果に乖離があることもしばしばありました。

またモデルごとの仕様や特徴に対しても実際に試してみて明らかになったことが多くありました。 「各LLMモデルへの期待値と検証後の評価」でまとめて記載しましたが、中でも「GPT-4o miniで画像を扱うとトークン数が跳ね上がる問題」などは費用面にも直接関わってくることなので、モデル選定の前に気付くことができてよかったです。

正解データとLLM-as-a-Judgeの重要性

やってみないとわからないことが多く、不確実性の高い状況下で、正解データやLLM-as-a-Judgeによる精度の点数は、暗闇の中の一筋の光でした。

正解データの作成は地道な作業ではあったものの、各種チューニングを行っていくにあたり「どこがうまくいっていないのか」を確認して試行錯誤するために大いに役に立ちました。

またチューニングをいつまで続けるか、どのレベルまで実施すればいいのかについても、点数があったことにより目標値を決めて取り組むことができるなどの行動基準を設けられることにもつながり、チーム内のコミュニケーションにも役に立ちました。

AIチューニングは地道な作業

チューニング作業は、修正しては約80件のOCRを実行し、点数を確認してはまた修正してOCRを実行しての繰り返しで、かなり地道な作業でした。

また正解データは人力ですべて作っているので、ときには正解データが間違っていることもあり、精度検証をしながら正解データ側を手動で直していくということもしばしばありました。

「AI履歴書読み取り機能」のこれから

「AI履歴書読み取り機能」は無事2月にリリースすることができましたが、リリースして終わりではありません。今後の取り組みについてもご紹介します。

OCR精度改善

OCRの精度向上はリリースをして終わりではなく、リリース後も継続的に実施したいと思っています。

そのための仕組みもすでに取り入れており、今後も引き続きプロンプトチューニングや、モデルのアップデート、ルールベースの処理の改善、response_schemaのような仕組みの利用などを通して、OCRの読み取り精度を改善していく予定です。

特にモデルは、次々に新しいモデルが出てきて精度も速度も上がっていくことがわかっているため、現時点でもアップデートを予定していますし、積極的に検討を進めていきたいと思っています。

使い勝手の向上

AIインテグレーションユニットも社内の他プロダクトチームと同様、AI機能を出して終わりではなく、最終的に顧客に価値を届けるところまでを責務としています。

AIに直接は関連しない改善であったとしても、それによって生まれる価値があればそういった改善も続けていく予定です。

「AIインテグレーションユニット」のこれから

最後に、今後のAIインテグレーションユニットの展望についてもご紹介したいと思います。

別の書類でのAI-OCR機能の提供

今回は「履歴書」にフォーカスして最初のリリースをしましたが、まだまだユーザーがデータを入力しているところは多くあります。AI-OCRによって自動入力されることで、手間やコストが減るだけでなく、入力間違いのリスクも減らすことができます。

今回開発したOCRの基盤を流用することで、履歴書以外にもフォーマットが多様な書類、または一般的でなかったり複雑な書類であっても一定の精度でOCRができる可能性があります。

進め方の改善

正解データの作成は大変有用ではありましたが、チーム内のみで完結させるのは大変な面も多くありました。この辺りは今後のAI機能の開発に向けて別のやり方も検討していく予定です。他にも、他チームとの連携周りなど進め方の面で細かい反省や知見は多くあったため、今後の開発ではそれらを活かして改善していきたいと思っています。

またMLエンジニアの採用も積極的に進めており、チームにも多様なメンバーが増えていく予定です。今回は外部のLLMを利用しましたが、内製化することも検討していますし、より多くの選択肢から最適な手法を選んで進めていけるようにする予定です。

その他のAI機能の提供

冒頭に記載したように、各プロダクトから挙がってきた課題は数多くあり、AIによって解決したい問題は山ほどあります。

今回リリースした「AI履歴書読み取り機能」は、そのほんの一部に過ぎません。今後もOCRに限らず、様々なAI機能を開発していくことで、ユーザーに価値を届けていきたいと思っています。

We Are Hiring!

SmartHR のAIチームでは、本記事で紹介したようにAIを中心に様々なチャレンジを行っていく予定です。まずはカジュアル面談からでも大歓迎です。ご応募お待ちしております!

tech.smarthr.jp