AI

AI採用・履歴書マッチングOSS比較:doccano vs OpenResume vs Claude APIで求人マッチングをセルフホスト

オープンソースラボ編集部2026年6月13日

AI採用・履歴書マッチングOSS比較:OpenCV vs doccano vs Manatal OSS で求人マッチングをセルフホスト

採用管理SaaSはGreenHouse月$599〜・Lever月$3,000〜と高額です。doccano(データラベリング)・OpenResume(AIレジュメパーサー)・セルフホスト型ATS(Applicant Tracking System)と、オープンソースのNLP・LLMを組み合わせることで、履歴書の自動解析・求人とのマッチングスコア計算・面接スケジューリングをコストゼロで構築できます。

AI採用ツールをOSSで構築する理由

  • コスト削減: 採用管理SaaSの月$600〜3,000を自社構築でほぼゼロに
  • データプライバシー: 応募者の個人情報・履歴書を外部SaaSに送らない(個人情報保護法・GDPR対応)
  • カスタムマッチング: 自社の採用基準・必要スキルに合わせたスコアリングロジック
  • LLM活用: Claude・GPT-4等でJDと履歴書の意味的マッチング(単純キーワード一致を超えた)
  • 継続学習: 採用実績データから精度を改善するファインチューニングパイプライン

OSSスタックの構成

doccano(データラベリング)

NERエンティティ抽出・テキスト分類のアノテーションツールです。GitHubスター9k+。履歴書・JDからスキル・職歴・学歴を自動抽出するNERモデルのトレーニングデータ作成に使います。アノテーターが履歴書テキストに対して「Python」→SKILL、「東京大学」→EDUCATION、「2020年〜2024年」→PERIOD等のラベルをつけ、NERモデルの学習データを作成できます。

# doccanoをDockerで起動
docker run -d   --name doccano   --restart always   -p 8000:8000   doccano/doccano:latest

# 初期設定
docker exec -it doccano python manage.py create_admin   --noinput --username admin --password admin@password

# API経由でラベリングデータをエクスポート(学習用)
curl -H "Authorization: Token your-api-token"   "http://localhost:8000/v1/projects/1/export?format=JSONL"   -o training_data.jsonl
# doccanoでラベル付けした履歴書データでNERモデルをトレーニング
# pip install spacy transformers datasets

import json
from datasets import Dataset
from transformers import AutoTokenizer, AutoModelForTokenClassification, TrainingArguments, Trainer
import torch

# doccanoのエクスポートデータを読み込む
def load_doccano_ner(jsonl_path: str):
    examples = []
    with open(jsonl_path, 'r', encoding='utf-8') as f:
        for line in f:
            data = json.loads(line)
            examples.append({
                'text': data['text'],
                'entities': data['label']  # [[start, end, label], ...]
            })
    return examples

# BIO形式に変換(NERトレーニングデータ)
def convert_to_bio(text: str, entities: list, tokenizer) -> dict:
    tokens = tokenizer(text, return_offsets_mapping=True, truncation=True, max_length=512)
    offsets = tokens['offset_mapping']

    LABEL_MAP = {
        'SKILL': 1, 'EXPERIENCE': 3, 'EDUCATION': 5, 'PERIOD': 7, 'COMPANY': 9
    }

    labels = [0] * len(offsets)  # 0 = O(Other)

    for start, end, label in entities:
        for i, (tok_start, tok_end) in enumerate(offsets):
            if tok_start == start:
                labels[i] = LABEL_MAP.get(label, 0)  # B-LABEL
            elif tok_start > start and tok_end <= end:
                labels[i] = LABEL_MAP.get(label, 0) + 1  # I-LABEL

    return {'input_ids': tokens['input_ids'], 'attention_mask': tokens['attention_mask'], 'labels': labels}

# モデルをロードして学習
MODEL_NAME = "cl-tohoku/bert-base-japanese-v3"  # 日本語BERT
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = AutoModelForTokenClassification.from_pretrained(MODEL_NAME, num_labels=11)

examples = load_doccano_ner('training_data.jsonl')
dataset = Dataset.from_list([convert_to_bio(e['text'], e['entities'], tokenizer) for e in examples])

training_args = TrainingArguments(
    output_dir="./resume-ner-model",
    num_train_epochs=10,
    per_device_train_batch_size=16,
    save_strategy="epoch",
    evaluation_strategy="epoch",
)

trainer = Trainer(model=model, args=training_args, train_dataset=dataset)
trainer.train()
trainer.save_model("./resume-ner-model/final")

LLMベースの求人マッチングシステム

Claude APIを使って履歴書とJD(Job Description)の意味的マッチングスコアを算出するシステムを構築できます。

# Claude APIを使った履歴書×JDマッチングスコアリング
# pip install anthropic pdfplumber

import anthropic
import pdfplumber
import json
from dataclasses import dataclass

client = anthropic.Anthropic()

@dataclass
class MatchScore:
    total: float        # 総合スコア(0〜100)
    skills: float       # スキルマッチ(0〜100)
    experience: float   # 経験年数マッチ(0〜100)
    culture: float      # カルチャーフィット(0〜100)
    summary: str        # マッチング理由の要約
    gaps: list          # 不足しているスキル・経験

def extract_resume_text(pdf_path: str) -> str:
    '''PDFから履歴書テキストを抽出'''
    with pdfplumber.open(pdf_path) as pdf:
        return '

'.join(page.extract_text() or '' for page in pdf.pages)

def match_resume_to_jd(resume_text: str, jd_text: str) -> MatchScore:
    '''履歴書とJDのマッチングスコアをClaude APIで算出'''
    prompt = f'''
以下の求人票と候補者の履歴書を分析して、マッチングスコアをJSON形式で返してください。

## 求人票
{jd_text}

## 候補者の履歴書
{resume_text}

## 出力形式(JSONのみ)
{{
  "total": 0〜100の総合スコア,
  "skills": 0〜100のスキルマッチスコア,
  "experience": 0〜100の経験年数マッチスコア,
  "culture": 0〜100のカルチャーフィットスコア,
  "summary": "マッチング理由の一言要約(日本語、50字以内)",
  "gaps": ["不足スキル1", "不足経験1", ...],
  "highlights": ["強みポイント1", "強みポイント2", ...]
}}

評価基準:
- スキルは必須・歓迎要件とのマッチ度で判断
- 経験年数は求められる年数と実績を比較
- カルチャーフィットは職務経歴書の文章スタイル・志向性から推測
'''

    response = client.messages.create(
        model='claude-sonnet-4-6',
        max_tokens=1024,
        messages=[{'role': 'user', 'content': prompt}]
    )

    result = json.loads(response.content[0].text)
    return MatchScore(**result)

# 複数の候補者を一括スコアリングしてランキング
def rank_candidates(jd_text: str, resume_paths: list) -> list:
    results = []
    for path in resume_paths:
        resume_text = extract_resume_text(path)
        score = match_resume_to_jd(resume_text, jd_text)
        results.append({'path': path, 'score': score})

    return sorted(results, key=lambda x: x['score'].total, reverse=True)

オープンソースATSとの統合

// Next.jsベースのシンプルなATS(Applicant Tracking System)
// Supabaseで応募者・求人・マッチングスコアを管理

// lib/ats.ts
import { createClient } from '@supabase/supabase-js';
import Anthropic from '@anthropic-ai/sdk';

const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_KEY!);
const claude = new Anthropic();

// 応募者登録 + AIスコアリング
export async function addApplicant(
  jobId: string,
  name: string,
  email: string,
  resumeText: string
) {
  // 求人票を取得
  const { data: job } = await supabase.from('jobs').select('description').eq('id', jobId).single();

  // AIマッチングスコアを計算
  const response = await claude.messages.create({
    model: 'claude-sonnet-4-6',
    max_tokens: 512,
    messages: [{
      role: 'user',
      content: `求人: ${job.description}

候補者: ${resumeText}

マッチスコア(0-100)と理由をJSON{"score":N,"reason":"..."}で返してください`,
    }],
  });

  const { score, reason } = JSON.parse(response.content[0].text);

  // Supabaseに保存
  const { data } = await supabase.from('applicants').insert({
    job_id: jobId,
    name,
    email,
    resume_text: resumeText,
    ai_score: score,
    ai_reason: reason,
    status: 'new',
    created_at: new Date().toISOString(),
  }).select().single();

  return data;
}

// 応募者を面接ステージに進める
export async function advanceStage(applicantId: string, newStatus: string, notes: string) {
  return supabase.from('applicants').update({
    status: newStatus,  // new → screening → interview → offer → hired/rejected
    notes,
    updated_at: new Date().toISOString(),
  }).eq('id', applicantId);
}

機能比較表

ツールライセンス用途特徴
doccanoMITNERデータラベリングWebアノテーションツール
OpenResumeMIT履歴書パーサーUINext.js製・PDF解析
HireBeat OSSMITATS(応募管理)Kanbanボード
Claude API商用マッチングスコア最高精度・柔軟

AI採用ツールと組み合わせるHRオートメーションはHRカテゴリ/categories/hrを参照してください。LLMを使った意味的テキスト分析のパターンはLLMツールカテゴリ/categories/llm-toolsにまとめています。

FAQ

Q. 履歴書のAI解析は個人情報保護法・GDPRの観点で問題ありませんか?

A. セルフホスト型AIを使う場合は外部への個人情報送信がないため、外部SaaSより安全ですが、いくつかの注意事項があります。個人情報保護法(日本): ①応募者から「AIによる自動評価を行う旨」の同意を取得する②採用選考でAIスコアのみを根拠に不合格とせず、人間のレビューを必ず挟む③履歴書データの保管期間・廃棄ポリシーを定める。GDPR(EU): GDPR第22条により「自動化された決定」のみで採用・不採用を決めることは原則禁止。AIスコアは「参考情報」として位置づけ、最終判断は人間が行う。Claude API等の商用LLMを使う場合: Anthropicのデータ処理契約(DPA)を締結し、プロンプトに含む個人情報のサーバー外処理について同意を確認する。

Q. 日本語の履歴書(JIS形式・市販の履歴書PDF)をOCRで解析する精度はどのくらいですか?

A. 解析精度は書類の種類によって大きく異なります。高精度(85%+): デジタル入力の履歴書(テキストとして保存されたPDF)・LinkedInからエクスポートしたPDF・Word/Googleドキュメントで作成した職務経歴書。中精度(60〜75%): スキャンしたJIS規格の手書き履歴書(OCRmyPDFで前処理が必要)。精度改善のアプローチ: ①pdfplumber(テキストPDF)+ OCRmyPDF(画像PDF)を自動判定して適用②固有名詞・スキル名はdoccanoでNERモデルを学習させると精度が大幅に向上③GPT-4 Vision(画像理解)でスキャン画像をそのままClaude/GPT-4に送る方法も有効。日本の新卒採用ではマイナビ・リクナビのデータ連携API(有償)が最も正確な構造化データを取得できます。

Q. 採用担当者なしで書類選考をAI全自動化するリスクは何ですか?

A. AI全自動化は推奨しません。主なリスク: ①バイアスの増幅: 過去の採用データに偏りがある場合(例: 特定の大学出身者ばかり採用していた)、AIがそのバイアスを学習して再現する②優秀な候補者の見落とし: 非線形なキャリア(転職多数・ブランクあり)をAIが低評価する③法的リスク: 差別的なフィルタリングは採用差別に該当する可能性④候補者体験の悪化: AI不合格を受けた候補者からのレビュー(GlassDoor等)が企業イメージを傷つける。推奨構成: AIは「スコアリングと優先度付け」に特化し、採用担当者が全応募者の書類を確認した上でAIスコアを参考にする。AIスコア80点以上を優先面接に呼ぶ等、AIをフィルターでなく補助ツールとして使う。

Q. OpenResume(GitHubスター18k+)はどんなツールですか?

A. OpenResumeはNext.js製の履歴書作成・解析ツールです。2つの機能を提供します: ①履歴書ビルダー: Webブラウザ上でリアルタイムプレビューしながら履歴書をPDF出力できる②履歴書パーサー: アップロードしたPDFから氏名・職歴・スキル・学歴等を自動抽出してJSONで出力する。パーサーのコードをそのまま自社ATSに統合できます(MIT License)。

// OpenResumeのパーサーをAPIとして使う
import { extractResumeFromPdf } from 'resume-parser-and-generator'; // OpenResumeベース

export async function POST(req: Request) {
  const formData = await req.formData();
  const pdfFile = formData.get('resume') as File;
  const pdfBuffer = Buffer.from(await pdfFile.arrayBuffer());

  const resume = await extractResumeFromPdf(pdfBuffer);
  // { name, email, phone, workExperiences, educations, skills, ... }
  return Response.json(resume);
}

まとめ

ユースケース推奨ツール
履歴書PDF解析OpenResume + pdfplumber
NERモデル構築doccano + BERT
意味的マッチングClaude API
応募者管理Supabase + Next.js ATS

関連外部リソース

他の記事も読む

Let's Build Together

OSS導入、自社だけで悩まない。

ツール選定から構築・運用・AI活用まで、オープンソースラボ運営元のClasslessが伴走します。初回のご相談は無料です。