HackerRankのオープンソースATS、同じ履歴書のスコアが90点・74点・88点と揺れる
(danunparsed.com)- 同じ履歴書と同じコマンドでHackerRankのオープンソースATS採用エージェントを繰り返し実行したところ、スコアが66〜99点までぶれ、85点のカットオフでは65%が不合格になる結果が出た
- このツールはPDF履歴書をパースし、LLMを6回呼び出して基本情報、職歴、学歴、技術、プロジェクト、受賞歴を構造化した後、GitHub情報まで加えて100点+ボーナス20点で評価する
- 技術スキルは100回中98回が8/10点でほぼ一定だったが、プロジェクト評価は「アーキテクチャの複雑さが不足している」と「実際のデプロイを示している」の間を行き来し、大きな変動性を示した
- 基本モデルgemma3:4bのtemperature 0.1だけでなく、temperature 0でも非決定性が残り、Geminiに切り替えても60点カットオフ基準で28%の不合格率が発生した
- 職歴項目はインターンシップ1件しかない古い履歴書でも25/25点を取り、LLMによる点数化が候補者の質を見分けるより運によるフィルタリングになりうる
同じ履歴書が毎回違う点数を受け取る
- HackerRankのオープンソースATSであるhiring-agentがLinkedInとRedditで注目を集めた後、テスト対象になった
- 初回実行のスコアは90/100点で、デバッグ用の出力文を削除した後、同じ履歴書と同じコマンドで再実行すると74/100点が出た
DEVELOPMENT_MODEを無効化して100回繰り返し実行すると、スコア範囲は66〜99点まで広がった- 企業の通過基準が85点なら、同じ履歴書でも65%の確率で不合格になる
評価パイプラインと配点構造
- ツールはPDF履歴書をテキストとしてパースした後、LLMを複数回呼び出して応募者情報を構造化する
- 基本情報
- 職歴
- 学歴
- 技術
- プロジェクト
- 受賞歴
- GitHubプロフィールと上位リポジトリをスキャンし、この情報を追加コンテキストとして付けて、全情報をまとめてLLM評価に投入する
- 基本モデルはローカルで実行されるgemma3:4bで、temperatureは0.1に設定されている
- スコアは100点満点で、最大20点のボーナスが加算される
- オープンソース貢献: 35点
- 個人プロジェクト: 30点
- 業務経験: 25点
- 技術スキル: 10点
- スタートアップ経験、ポートフォリオサイト、技術ブログなど: 最大20点のボーナス
一貫した項目と揺れる項目
- 技術スキルは100回中98回で8/10点となり、ほぼ一貫していた
- Reactのような技術の保有有無はチェックリストに近く、LLMの主観的判断が入り込む余地が小さい
- 一方でプロジェクト項目は実行のたびに判断が大きく分かれた
- ある実行では、プロジェクトは「アーキテクチャの複雑さが不足している」と評価された
- 別の実行では、「実際のデプロイを示している」と評価された
- temperature 0.1は低い設定だが、temperature 0まで下げても問題は消えない
- 2025年10月に開かれたGitHub issueでも、temperature 0で6回連続のスコアが27、34、32、34、34、30と異なっていた
モデルを変えても残る不安定性
- gemma3:4bがローカルモデルであるため、モデルの影響もあわせて確認した
- Geminiを使うとスコア分布は48〜64点と、より狭くなった
- しかしカットオフが60点なら、応募者は自分の履歴書内容とは無関係に28%の確率で不合格になる
- オープンソースのスコアはより一貫して変化したが、プロジェクトのスコアは依然として大きく揺れた
職歴スコアの逆の問題: 一貫しているが役に立たない
- 職歴項目はすべての実行で25/25点だった
- 古い履歴書のようにインターンシップ1件しかない場合でも25/25点を取る
- 評価プロンプトのProduction項目は2行しかない
workとvolunteerセクションで実務、インターンシップ、プロダクション経験を分析- 創業者、共同創業者、スタートアップ初期エンジニアの役割には追加考慮
- 15点と25点を分ける基準、例、ベンチマークがない
- 結果として、ジュニアエンジニアのインターンシップ、10年の分散システム経験を持つprincipal engineer、テストに使われた履歴書のすべてが25/25点を取りうる
履歴書スクリーニングにおける実務上のリスク
- LLMで履歴書を構造化データとしてパースしたり、Pythonの保有有無を確認したりする作業は、比較的適した用途に近い
- 候補者の経験が18点か24点かを判断する作業は、vibe-checkに近い結果を生む
- オープンソースとプロジェクトが合わせて65%の比重を占める構造も、採用判断を歪める可能性がある
- 30年の経験でS3を作ったエンジニアより、インターンシップ2件とオープンソースプロジェクトがある応募者を高く評価する可能性がある
- GitHubに残っていない重要な仕事をしてきたエンジニアは、スコアの半分以上を失う可能性がある
- 履歴書スクリーニングにAIツールを導入する権限を持つエンジニアは、質を見分けられないツールが単に応募者をふるい落とす装置になりうることに注意すべきだ
訂正事項
- resume_evaluation_criteria.jinjaテンプレート1行目には「Software Intern」がある
- この文言は文書化されておらず、リポジトリの他の場所でも参照されていない
- 同じテンプレートは後段で、創業者、共同創業者、初期スタートアップエンジニアの役割にボーナスを与える
- 明示的にSenior SWEプロンプトを入れて再実行しても結果は同じで、スコア次元は職務レベルと無関係に動作した
1件のコメント
Hacker Newsの意見
LLMが純粋な確率過程として動作することを知らない人が驚くほど多いので、こうした掘り下げ記事はありがたい
求職中なのだが、最近コールバックをもらうのがこれほど難しい理由はこれなのかもしれない。履歴書がただ何らかのLLMブラックホールに投げ込まれ、実際にどう動いているのか誰も分かっていないように見える
記事の「temperature 0.1 — 低い値なので、モデルを決定的な出力の方向へ誘導すると考えられる」という説明は正確ではない。temperatureは決定性のスイッチではなく、サンプリング分布に影響する値であり、より尖った分布になるだけで、依然として分布である
より厳密に言うと、temperature 0そのものは存在しない。数学的にはtemperatureが0に近づくほど分布はどんどん尖り、最も可能性の高いサンプルはほぼ無限大に近づき、それ以外はほぼ0に近づく
実際の実装では、
temperature=0は0でない値に使う公式を使うのではなく、ゼロ除算を避けるためにif文の別分岐で最頻のサンプルを選ぶような形になるただし、バッチ処理や実装ごとの浮動小数点誤差のために、確率分布自体が実行ごとに変わることが多く、その結果サンプルも変わる
同じ履歴書を2人が見て同じ結論に至ることもあれば、同じ症状と臨床像を持つ2人の患者が異なる病気を持っていることもある
昔のAIが主に知識ベースの維持コストのせいで死んだという説明にはあまり納得できず、むしろ不確実性を扱う汎用的な推論体系の欠如が核心だったと思う
Spockが「船長、この任務で生存する確率は21%です」のようなことをいつも言っていたのは、個人的には繰り返しギャグのように感じる。ベイズの観点では確率分布にも確率分布があるので、「この任務で生存する可能性はβ(5,1)です」のほうがより適切な表現に近い
そういう意味では、履歴書をその機械に100回入れてスコアの確率分布を見るのも、それほど奇妙なことではない
[1] とはいえ私は、ベッドに横になってタブレットで画像整理をし、視覚系が壊れるまで続けてしまうタイプの狂人ではある
ただしMoEなら、重複入力がバッチ全体でも同一である必要がある。バッチ内の隣接要素がエキスパート選択に影響することがある
カーネルは決定的でなければならず、推論型モデルでクラスタ全体の負荷のようなものに反応するシステムレベルの努力量スイッチがあってはならない
結論として、既存のクラウドインフラではtemperature 0でもおそらく決定的ではないが、エッジ推論ではかなり安定して決定的になり得る
0.1のほうがより決定的だという表現については、かなり妥当な要約だと思う。temperature 0.9より0.1のほうが、「temperature 0の答え」側をずっと高頻度でサンプリングするのではないか?
「一貫した結果を得るにはtemperatureを0に設定しろ」という話を数え切れないほど聞いた
そうならない理由はいくつかあるが、筆者がやったようにローカルモデルを実行する場合には、それらの理由は当てはまらないと思う
AIがAIの生成したコードを「好む」傾向や、その他のバイアスまで合わせると、この方式はEUで差別禁止法にさまざまな形で違反し、かなり違法である可能性が高い
履歴書が多すぎるという理由でランダムにふるい落とすことは、概ね許されると思う。ただし、本当にランダムで、履歴書の内容から独立していなければならない
AIではランダム性が実際の履歴書評価から独立していないため、これには当てはまらない
一般に、AIが体系的なバイアスを適用していないと保証することはできず、実際にそうである可能性が高いという兆候も大きい
人間にはバイアスを無視するよう訓練し、指示することができる。もちろんそれも安定して機能するわけではないが、少なくとも違法なバイアスの責任が、指示に従わなかった採用担当者へ委ねられる構造にはなる
一方でAIを使う場合、何を指示していたとしても利用者が責任を負う。特定の文脈で特定のAIが強く偏っていることを技術的に「示したり証明したり」することもできる。人間の従業員についても技術的には可能だが、実務上はそれほど簡単ではない
結局、法的リスクは「個別的で、概ね否認可能」な水準から、体系的に立証可能なバイアスの領域へ移る。言い換えれば、採用にAIを使っていると分かれば、人々が法的に体系立てて攻撃できるようになる
つまり数学的に見ただけでも、これが米国の人種、性別、その他の保護対象集団と何らかの形で相関している可能性は高い
そのため米国でも、よい訴訟が1件あれば違法になり得る。必ず勝訴する必要すらなく、法廷で十分に持ちこたえるだけで、他社がこれを使うのを怖がるようにできる
自分のAI選別器がすべての採用関連法規を完全に遵守していると証明しなければならない被告の立場にはなりたくない。悪夢のようなものだろう
[1]: https://gwern.net/everything
山から4番目の履歴書を取り、その人に仕事をオファーするのは愚かだが、完全に公平な採用決定の方法ではある
しかしAIはバイアスの捕捉が非常に得意なので、履歴書をふるい落とせと指示すると、候補者名のように絶対に基準にしてはいけない要素を基準にしてしまっても不思議ではない
大規模なオープンソースプロジェクトでタイプミスを直したと書いた履歴書はすべて通過する一方で、自分のプロジェクトだけを書いた履歴書は60%の確率で落とされる、ということもあり得る。そうなると、悪い候補者より良い候補者を多く失うことになる
不合理なギャンブル機械のように動くため、望ましくない間接差別の効果があり得ることには同意する。しかし「宗教または信条、障害、年齢、性的指向」を理由に差別しているように見せるのは、おそらく簡単ではない。可能ではあるが、弁護士が法廷で立証するには多くの作業が必要になる
より興味深いのはEU AI Actの部分だ。この部分は2027年12月2日までまだ施行されていないが、採用または自然人の選抜に使われるAIシステム、とくにターゲティング型求人広告の配置、応募書類の分析・フィルタリング、候補者評価に使われるシステムは、明確に高リスクAIシステムである
禁止されるという意味ではないが、後にLLMが高リスクAIのユースケースから除外される可能性もある。例外なくArticle 6に該当し得るためだ
まだ標準が公開されていない状況で、このような作業にLLMを使う際、Article 10の次の部分をどう遵守させるのかまったく分からない
「(f) 人の健康と安全に影響を及ぼす、基本権に悪影響を与える、またはEU法で禁止された差別につながる可能性のあるバイアスを検討すること。特にデータ出力が将来の作業の入力に影響する場合
(g) (f)に従って特定された可能性のあるバイアスを検出、防止、緩和するための適切な措置」
現時点では、モデル提供者が全面的に協力したとしても、一般的なLLMでは技術的に不可能だと思う。小さなモデルなら意味のある監査が可能かもしれない
EU AI Actは最終的に、Annex IIIの高リスク用途で「LLMは使っているが、なぜ使っているのかはよく分からない」というバイブコーディング的なアプローチをすべて排除することになり得るし、それは妥当だと思う
https://eur-lex.europa.eu/eli/reg/2024/1689/oj/eng
「データ主体は、プロファイリングを含む自動化された処理のみに基づき、自己に法的効果を生じさせる、またはこれに類する重大な影響を及ぼす決定の対象とならない権利を有する」
22(2)の例外は適用されにくい。本当に必要だと主張するのは難しく、雇用の文脈では同意もほとんど成立しない。EUまたは加盟国の特定の法律が認める場合なら可能だろうが、別途の法的根拠が必要になる
ここまで来ると、「運の悪い人を採りたくないので、履歴書の半分を目をつぶって捨てる」という冗談をそのまま採用してもよさそうだ
この方式は、当時ますます複雑になっていた手作業の履歴書評価基準を攻略する余裕のある学生よりも、裕福でない背景を持つ資格ある学生に有利だった
「将来の医師をギャンブルで選ぶのか?」といった組織的なキャンペーンが起こり、結局、抽選方式はひっそり廃止された
残った半分の候補者は、今回の選抜ですでに運を一部使ってしまったので、平均的には捨てられた半分より運が悪いはずだ
過去数十年で訓練と教育は大きく拡大し、求職者は増え続けたが、雇用創出はその速度に追いつかなかった
1つの空席に何十人もの候補者が殺到すれば、雇用主は履歴書をいい加減に選別したり半分を捨てたりしても、それでも資格ある人を採用できる
「同じ履歴書なのに、運次第で65%の確率で落ちる」という結果は、ここ数年、技術職の採用パイプラインを運用してきた立場からすると、実は立派な数字だ
こう言うのは客観的に嫌なのだが、事実ではある
努力なしで技術人材を次の段階に進める確率が35%だって? ドメイン特化のスクリーニング質問を入れても、1時間に100人以上の応募者を見たことがある。そうなると、1時間に35人の「選別済み」応募者が出てくる計算だ
有効な候補者がふるい落とされたか? そうだ。それでも必要な人数の35倍の候補者プールを持つことになるのか? 残念ながら、それもそうだ
応募者数が多すぎるため、AIが介入しない場合、次の段階に進む確率はむしろはるかに低くなる。すぐに応募していなければ、それもAIボットを使って応募していなければ、前に50人以上がいて、疲れた技術リーダーがいつかあなたの履歴書までたどり着かなければならない
紹介ボーナスが存在するのには理由がある
最新技術によって、最高の*応募書類の上位1%だけを通す
*当社独自・非公開・非決定的な指標に基づくもので、
Math.randomかもしれないし、そうでないかもしれない履歴書の流入を減らすゲートは、その減少が品質と相関している場合にだけ有用だ。そうでなければ、採用プロセスをだらだら長引かせるか、結局は採用基準を不必要に下げさせるだけだ
「John Schmidt」「John J. Schmidt」「John J. J. Schmidt」「John Jacob J. Schmidt」「J. J. Jingleheimer Schmidt」のように
最初に応募した50人が全員ボットなら、なぜ応募順に履歴書を読んでいるのか?
さらに気になるのは、ほかのシステムもこのATSのように動作するなら、かなりまとも、あるいは優秀な候補者を大量に落とす要素で判断しているように見える点だ
たとえば、個人プロジェクトとオープンソース貢献の組み合わせに65点が割り当てられている。技術だけが唯一の関心事で、家族・扶養家族・2つ目や3つ目の仕事がないなら、よいかもしれない
だが、そのどれか一つでもあるなら、可能性はものすごく不利に見える
こうしたシステムのどれほど多くが、大学に通うことや希望業界の1社で働くこと以外に心配がなく、技術にほとんど特殊な関心レベルで没頭できる裕福な人々に有利になるよう設計されているのか、気になってくる
自分を例にすると、会社の外では個人プロジェクトをほとんどしない。実際のプログラミング経歴はすべて、勤務時間中に雇用主のためにやった仕事だけだ
趣味は3Dプリンティング、ハードウェア/Arduino、写真のような技術隣接領域だが、GitHubにプロジェクトを大量に作って上げるタイプではない
潜在的な雇用主に見せるために偽物のCRUDやSaaSアプリを作ることも絶対にしない。時間の無駄だ
私は意図的に、そうしたオンライン上の存在感をまったく作ってこなかった。私のGitHubには公開リポジトリがなく、ブログもやっていない
この傾向は、私が働いている運用/システム管理の方面にも広がっており、そちらではむしろさらにひどい。当然ながら、私のGitHubに環境特化のスクリプトを大量に置いてはいない。なぜそうする必要があるのか? 今の会社の私の部署で働いていない人には、何の意味もない
決定性という言葉には、触れたオンライン文章を歪めてしまう魔法のような効果がある
その言葉が聞こえたら、ほぼ間違った方向へ行くと保証できる。それでも今回は、同じ入力なら同じ出力という実際の決定性を扱っていて、的外れな無関係のものではない
決定性は再現性にとって重要だが、この場合、本当に出力が再現されることを望んでいるのか? LLMの出力を決定的にするのは比較的些細なことだ。バッチ処理を使うならバッチ不変カーネルを使い、temperatureを0に設定する――いや、そうではなく、ランダムサンプリングには理由があるのだから、もっとよいのはシードを固定することだ。いくつかのシステムではすでに可能だ
しかし、これで結果がより有用になるわけではない。エージェントが実際には確信を持てていないという事実を隠すだけだ。スコアの範囲を見てほしい。依然として何も予測できていないが、毎回スコアだけは同じになる。本当にそれを望むのか?
ここで起きているのは、提供される情報が少なすぎ、ほとんどノイズのような履歴書1枚だけを与えて、あまりに広い含意を持つ答えを期待しているということだ
LLMを使うかどうかにかかわらず、根本的な設計ミスだ。すべてのアンケート、試験、法律、投票システムは、少なすぎる情報で動作するため、フレーミングに極めて敏感だ。ただし、それらはこの代物と違って真空中に存在しているわけではない
ラスベガスアルゴリズムは非決定的だが100%正確だ。その代わり、正解に到達する時間が大きく変わるというトレードオフがある
ここでのミスは、非決定的システムを使ったことではない。ある意味では、使い方が少なすぎたことがミスかもしれない。同じ履歴書を5回再評価してスコアの分散が大きいことを見るほうが、1回だけ評価するよりも有用なシグナルだ
昼食直前の1時間にはより厳しい量刑が出る、という話は誰もが聞いたことがあるだろう
人々がフィルタリング過程に合わせて最適化できないようにするには、ある程度非決定的にする必要がある
たとえば上位100人でぴったり切るのではなく、より良い候補者ほどフィルターを通過する可能性を指数関数的に高めるようにする
そうすれば、フィルタリング過程をグッドハート的に攻略する価値は下がる。確率はほとんど増えず、時間をもっと有効に使える場所がはるかに多いからだ
デフォルトのモデルが
gemma3:4bなら、かなり小さなモデルです。どんな LLM も完璧で再現性のある審判ではないでしょうが、4B モデルをこのようなシステムに差し込むのは、乱数生成器をつなぐのに近いです。
実験全体が、誰かがオープンソースの ATS プロジェクトを作りたくて、バイブコーディングで ATS を作ったあと、テストが通る程度にだけ合わせたように感じます。
このモデルでもうまく動く履歴書分析のやり方は、おそらくあるはずです。ただし「おいポンコツ、この人はどんなプロジェクトをやったんだ?」というような方法ではありません。
抽出、整理、おそらく OCR による照合と追加整理、シグナルごとの複数回の LLM 分析、判定器などが必要です。
そのどれも大きなモデルである必要はありません。大きなモデルを使えば少しは良くなるでしょうが、コンテキストが非常に少ないため、正しく使えばこのようなモデルでもうまく動きます。
ATS を自分で動かしてみましたが、同じように奇妙な経験をしました。
私の GitHub プロフィールを見つけられず70点台になり、その次は私が作った有名な Ruby ライブラリのいくつかを気に入らなかったようです。
何度か回した後には適切に認識し始めましたが、正式な学歴については常に減点されました。
こういうのは気持ち悪いです。
認定資格や受賞歴も拾えません。人々が改善を提案した PR をいくつか適用してみました(https://github.com/Zem-0/hiring-agent)。助けにはなりましたが、全体としてこの ATS は大規模な GitHub オープンソース貢献がある人に大きく偏っています。
優秀なエンジニアは見つけにくいという理由でテック企業が30万ドル以上を支払う一方で、肝心のリクルーターは支援なしで働き、「良い候補者」に対する基準もまったく違うというのは、いつも驚きでした。
ATS はくだらないフィルタリングのヒューリスティックのせいで履歴書の50%以上をブラックホールに送り込み、採用チームは Google Gmail 連携のような理由で ATS を選び、その ATS のフィルタリング技術はエンジニアリングチームやデータチームの誰にもレビューされていませんでした。
自分の履歴書で試してみたところ、なぜか GSoC ボーナス点をもらいました。
BONUS POINTS: 5.0------------------------------Google Summer of Code (GSoC) participation: +5ところが私は GSoC に参加したこともなく、履歴書に参加したと書いたこともありません。
既知の幻覚です https://github.com/interviewstreet/hiring-agent/issues/240