Synaplan
开源 AI 软件平台,面向企业:自托管 AI 代理,符合 GDPR 的聊天机器人小部件,RAG 知识库,WhatsApp 与 Outlook AI,MCP,多模型路由。支持 Docker、Kubernetes 或在 Germ 托管。
AI 翻译
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
「案件の空き」と「人材リソース」をAIで自動マッチングするシステム。 ログイン不要・ニックネーム制・即日利用可能。查看完整 README ↗
「案件の空き」と「人材リソース」をAIで自動マッチングするシステム。
ログイン不要・ニックネーム制・即日利用可能。
| 機能 | 説明 |
|---|---|
| マッチング | 案件と人材の相性スコアを算出。スコアは SQL 側のルール計算で完結(fetch_candidates_for_project RPC・スキル40/経験15/単価15/勤務地20/リモート10)。手動 (match-score) / バッチ (match-batch) / 自動 cron (auto-match) の 3 経路。上位のみ外部 AI で再採点する経路も実装されているが現在は使っていない(→ 下記「マッチングのスコアリング」) |
| 人材登録 | テキスト貼り付け・Excel・Word をアップロードするだけで自動解析・登録(PDF / 画像は現状未対応)。HTML テーブル形式メール / [氏名] ラベル / [氏名]男性:51 歳 等の混入パターンに対応 |
| 案件登録 | 同上。メール本文や要件定義書をそのまま貼り付け OK。【場所】【単価】【時期】【備考】等のブラケット形式・単金(税抜) 括弧付きラベル・■PR セクション・希望ラベルに対応 |
| メール自動取り込み | 専用 Outlook アドレスを 5 分ごとにポーリング → 取得 → DB 保存(AI 不使用・ルールベース)。営業/広告メール(メール配信解除・研修販売等)は自動スキップ |
| 人材マップ | 「人材」タブ内のサブ画面で日本地図上の都道府県別ヒートマップ表示。スキルフィルター(RPC 経由)・直近 7 日 / 全期間切替・都道府県クリックで受信メール一覧・都道府県クリックで地図ズームアニメーション |
| 改善案・バグメモ → GitHub Issue | 設定タブで自然文を貼り付けると、create-github-issue Edge Function 経由で GitHub Issue として自動登録。一覧表示・クローズ(PATCH)も同 UI から可能 |
| スコアウェイトのカスタムチューニング | match-batch / fetch_candidates_for_project の 5 観点(スキル / 経験 / 単価 / 勤務地 / リモート)のウェイトを実行時に変更可能(既定: 40/15/15/20/10 = 100pt) |
| スキル別経験年数の活用 | Excel スキルシートから skillYears を抽出し、必須スキルの実年数をスコアリングに反映(総経験年数より優先) |
| 人材ウォッチ通知 | 「通知」タブで条件(名前 / スキル / 最寄駅)を登録すると、合致する人材の登録時にメールで通知(notify-candidates cron・5分)。種類違いは AND、スキル欄の中だけ OR。スキルは語として照合(Java は JavaScript に一致しない) |
| AI による項目補正(社内PC) | Claude Code を使う常駐ワーカーが、新規人材の氏名・年数・スキル年数を読み直し、新規案件の抜けを埋め、提案ごとの所見を書く。マッチングの点数計算とは別系統 |
| 同一人材の検出 | 別の紹介会社から来た同じ人を検出して人材詳細に並べる(統合はしない。会社ごとに提示単価が違うため比較できるようにする) |
| 表示優先スキル | 一覧をどのスキルの人から読むかを設定できる。既定は全体設定、絞り込みから端末ごとに上書きでき、ブラウザに記憶される |
| デモ環境 | 本番データとは独立したデモ用データ環境(?demo=KEY でトグル + 設定タブの「デモモード」スイッチ)。本番→デモコピー・スコア別 5 人生成。デモ案件のリモートポリシーを考慮したスコア設計 |
flowchart TD
A[営業担当者<br/>ブラウザ] -->|テキスト・ファイル入力| B[React フロントエンド<br/>Vercel]
B -->|登録/再解析<br/>force=true| H[Edge Function<br/>inbound-email<br/>※ AI 不使用]
H -->|upsert| D[(Supabase<br/>PostgreSQL)]
B -->|upsert / fetch| D
B -->|手動マッチ要求| MS[Edge Function<br/>match-score<br/>単発スコア + duplicate判定]
B -->|高速/全件マッチ| MB[Edge Function<br/>match-batch<br/>ウェイト調整 + バッチ AI 採点]
B -->|SQL側で<br/>ルールスコア算出 + 上位500件取得| RPC[(RPC<br/>fetch_candidates_for_project<br/>p_weight_skill/exp/rate/location/remote)]
RPC --> D
MS -.->|AI再採点は現在未使用<br/>キー未設定なら ruleScore| AI[外部AIプロバイダ<br/>※未稼働]
MB -.->|AI再採点は現在未使用<br/>キー未設定なら ruleScore| AI
MS -->|upsert submissions| D
MB -->|upsert submissions<br/>ai_raw.ruleScore も保存| D
E[Outlook<br/>専用アカウント×4] -->|未読メール監視| F[pg_cron<br/>5分ごと起動]
F -->|HTTP POST| G[Edge Function<br/>poll-email]
G -->|OAuthトークン取得| M[Microsoft Graph API]
M -->|未読メール最大50件/アカウント| G
G -.->|メール種別分類<br/>Gemini バッチ| CLS[Gemini Flash Lite]
G -->|内部 POST| H
H -->|Drive/Sheets URL検出→fetch| I[Google Drive<br/>共有リンク]
H -->|デプロイ物に同梱した<br/>station_data.json を参照<br/>8,443駅名・実行時DB往復ゼロ| SM[station_data.json<br/>駅名+路線 → 都道府県]
H -->|skill_master DB照合<br/>+ regex + 文章スキャン<br/>+ HTML エンティティデコード| H
CR[pg_cron<br/>毎朝 JST 9:00] -->|HTTP POST| AM[Edge Function<br/>auto-match]
AM -->|match-batch を内部呼び出し| MB
AM -->|insert submissions| D
CR2[pg_cron<br/>毎日 JST 0:00] -->|HTTP POST| AR[Edge Function<br/>archive-candidates]
AR -->|7日以上経過した prod 人材を<br/>サマリー化してから削除| D
AR -->|prefecture/skills/name/subject 保存| AL[(candidates_archive_light<br/>軽量サマリーテーブル)]
B -->|人材マップ表示<br/>※「人材」タブ内サブ画面| HP[HeatmapPage<br/>d3-geo + japan.topojson<br/>都道府県ズームアニメ]
HP -->|prefecture_counts RPC<br/>candidates_by_prefecture RPC| D
HP -->|全期間モード時に合算| AL
B -->|改善案・バグメモ| GHI[Edge Function<br/>create-github-issue]
GHI -->|POST/GET/PATCH| GH[GitHub Issues API]
| レイヤー | 技術 |
|---|---|
| フロントエンド | React 19, Vite 8, TypeScript, Tailwind CSS v4, TanStack Query v5 |
| DB / バックエンド | Supabase(PostgreSQL, Edge Functions, pg_cron, pg_net) |
| AI(ブラウザ) | 未使用(Phase 4.11 で「AI で登録」廃止。VITE_GEMINI_* は設定不要) |
| マッチングのスコア | match-batch / match-score / auto-match。SQL 側のルール計算(スキル40/経験15/単価15/勤務地20/リモート10 = 100pt)。上位のみ外部 AI で再採点する経路も実装されているが現在は未使用(→「マッチングのスコアリング」) |
| メール種別分類 | poll-email の同一受信箱判別(任意・既定は無効) |
| メール解析 | AI 不使用。regex(extractCandidateFieldsRegex + flexLabel) + 文章スキャン(extractFromProse) + skill_master 照合 + station_master 照合(8,443 駅名。デプロイ物に同梱した station_data.json を読むので実行時の DB 往復はゼロ)。HTML テーブル形式メール・複数人材分割・送信者署名除去・営業/広告メールフィルタにも対応 |
| AI(社内PC・人材/案件の読み取り) | Claude Code(claude -p)。社内PCで常駐する scripts/llm_extract/shadow_worker.mjs が5分ごとに、新規人材の項目補正・新規案件の項目補完・マッチング提案の所見生成・Box 経歴書の取込を行う。マッチングの点数計算とは別物(点数はルール計算)。現在このシステムで実際に使っている唯一の AI。セットアップは docs/HandsOn_Setup.md 第1章・第8章 |
| ファイル解析 | xlsx(Excel)・mammoth(Word)。PDF と画像はテキスト解析対象外。Excel から skillYears(スキル別経験月数)を抽出してマッチングに活用 |
| 人材マップ | d3-geo(Mercator 投影) + topojson-client + public/japan.topojson(416KB・47 都道府県)。prefecture_counts / candidates_by_prefecture RPC で SQL 集計(スキルフィルタも RPC 側)。都道府県クリックでズームアニメーション |
| メール自動受信 | Microsoft Graph API + Supabase pg_cron(完全無料・Make.com 不要) |
| データアーカイブ | archive-candidates Edge Function(毎日 JST 0:00 cron)。7 日経過した prod 人材を candidates_archive_light にサマリー化してから DB 削除(人材マップ全期間集計用・Storage 書き込みは廃止) |
| Issue 連携 | create-github-issue Edge Function(POST 作成 / GET 一覧 / PATCH クローズ)。設定タブの「改善案・バグメモ」から自然文を貼り付けて GitHub Issues に登録 |
| Edge Function デプロイ事前検査 |
スコアは外部 AI を使わず、SQL 側のルール計算で出している。
| 観点 | 既定ウェイト |
|---|---|
| スキル一致 | 40 |
| 勤務地 | 20 |
| 経験年数 | 15 |
| 単価 | 15 |
| リモート可否 | 10 |
match-batch には「ルールで上位に来た候補だけ外部 AI に再採点させる」経路(Cerebras → Groq → Gemini の 3 段)が実装されているが、現在は稼働していない。
API キーが未設定なら 3 段とも失敗し、ai?.score ?? c.ruleScore で全件がルールスコアのまま返る。実測(ai_logs・直近 30 日):
| model | 件数 | 内訳 |
|---|---|---|
no-ai | 31,009 | inbound-email のメール解析(元から AI 不使用) |
gemini-2.5-flash-lite | 2 | poll-email のメール種別分類(既定 OFF) |
| Cerebras / Groq / Gemini(採点) | 0 | 使われていない |
有効化したい場合の手順は docs/HandsOn_Setup.md の付録A を参照。
実際に AI を使っているのは Claude Code のワーカーだけ(人材・案件の項目補正と提案の所見)。 スコアの計算とは別系統で、社内PCで動く。
node -v で確認)npm -v で確認)1. リポジトリをクローン
git clone https://github.com/kzmiyamura/akinavi-hr-ai.git
cd akinavi-hr-ai
2. 依存パッケージをインストール
npm install
3. 環境変数を設定
cp .env.example .env.local
.env.local を以下の内容で編集:
# Supabase(Dashboard → Settings → API から取得)
VITE_SUPABASE_URL=https://argizomylbolpqxgmvim.supabase.co
VITE_SUPABASE_ANON_KEY=(anon キーを貼り付け)
# デモ環境の解除キー(任意・未設定でもOK)
VITE_DEMO_KEY=(任意の文字列)
4. Supabase の DB を初期化
Supabase Dashboard → SQL Editor で以下を順番に実行:
supabase/schema.sqlsupabase/migrations/ 配下の SQL を ファイル名の昇順で全て実行
add_skill_master.sql / seed_skill_master.sql / add_relevance_keywords.sql / add_box_columns.sql / add_resume_url.sql / add_attachments_bucket.sqlfind_duplicate_candidates_rpc.sql / add_search_rpc.sqladd_email_polling_cron.sql / add_auto_match_cron.sql / add_skill_cleanup_cron.sql / add_enrich_cron.sql(YOUR_PROJECT_REF と YOUR_SERVICE_ROLE_KEY を実値に書き換え)20260520130000_add_work_phases.sql(IBM 系・ストレージ系・工程系 18 件)20260521000000_add_search_scope.sql(search_candidates(p_scope) 3 モード対応)20260521210000_add_bigquery_and_cloud_dwh.sql(DWH 系 10 件)20260522_add_error_logs.sql(フロント側エラーログ)20260522_add_fetch_candidates_for_matching.sql(MatchingPage RPC)20260522_add_fetch_candidates_for_project.sql(案件→人材 SQL 絞り込み RPC)20260523_add_process_skills.sql(テスト / 保守開発 / 保守運用 / 調査分析 4 件追加)20260523_archive_light_table.sql(人材マップ用 candidates_archive_light テーブル + 期間対応 prefecture_counts RPC)20260523_prefecture_counts_rpc.sql(人材マップ用 初版 RPC・後段で上書き)20260523_normalize_prefecture.sql(人材マップ用 normalize_prefecture 関数 + 最終版 prefecture_counts + candidates_by_prefecture)20260523_fix_heatmap_skill_filter.sql(人材マップ用 スキルフィルタを candidates.skills JSONB も参照するよう修正)20260525_fetch_candidates_with_rule_score.sql(マッチング SQL 化 fetch_candidates_for_project をルールスコア順に再定義)20260525_fix_matching_rpc_duplicate_filter.sql(duplicate_flag=true を SQL 側で除外 / fetch_candidates_for_matching 上限 2000 へ)20260526_fetch_candidates_with_weights.sql(ウェイト調整可能化 スキル/経験/単価/勤務地/リモート の 5 引数)
schema.sqlのcandidate_skills.check_categoryは 14 カテゴリへ更新済み。すべてのmigrations/を昇順で流すこと(新規環境構築時の必須手順)。 人材マップ機能の詳細仕様はdocs/Heatmap.mdを参照。
5. 開発サーバーを起動
npm run dev
ブラウザで http://localhost:5173 を開く。
Vercel Dashboard → Environment Variables に以下を設定してから main ブランチへ push:
| 変数名 | 値 |
|---|---|
VITE_SUPABASE_URL | Supabase の URL |
VITE_SUPABASE_ANON_KEY | Supabase の anon キー |
VITE_DEMO_KEY | デモ解除キー(任意) |
外部AI(Cerebras / Groq / Gemini)は 2026-10-02 に全廃したので、 AI プロバイダーを選ぶ環境変数は無くなった(以前はここに1つ載っていた)。
supabase functions deploy inbound-email
supabase functions deploy poll-email
supabase functions deploy auto-match
supabase functions deploy match-score
supabase functions deploy match-batch # Phase 4.10 新規(バッチ AI 採点)
supabase functions deploy archive-candidates # Phase 4.12 新規(7日アーカイブ・人材マップ用)
supabase functions deploy create-github-issue # Phase 4.14 新規(改善案・バグメモ → GitHub Issue)
supabase functions deploy microsoft-oauth
supabase functions deploy enrich-candidate
supabase functions deploy skill-master-cleanup
デプロイ前に型検査したい場合は
npm run check:edge <function>(scripts/check-and-deploy-edge.sh)を使うとdeno checkで TS2304(未定義変数)が出ていればデプロイを中止できる。npm run deploy:edge <function>で「型検査 + デプロイ」をまとめて実行。
Edge Functions Secrets(Supabase Dashboard → Edge Functions → Secrets)
| Secret 名 | 用途 | 必須 |
|---|---|---|
GRAPH_CLIENT_ID | Azure AD アプリのクライアント ID | ◎ |
GRAPH_CLIENT_SECRET | Azure AD アプリのクライアントシークレット | ◎ |
GRAPH_REFRESH_TOKEN_HUMAN | 人材用メール(prod)のリフレッシュトークン | ◎ |
GRAPH_REFRESH_TOKEN_PROJECT | 案件用メール(prod)のリフレッシュトークン | ◎ |
GRAPH_REFRESH_TOKEN_HUMAN_DEV | 人材用メール(demo)のリフレッシュトークン | 任意 |
GRAPH_REFRESH_TOKEN_PROJECT_DEV | 案件用メール(demo)のリフレッシュトークン | 任意 |
INBOUND_CALL_KEY | poll-email → inbound-email 呼び出し用 JWT(service_role キー) | ◎ |
GITHUB_TOKEN | create-github-issue 用の GitHub Personal Access Token(repo スコープ) | Issue 機能利用時 |
GOOGLE_SERVICE_ACCOUNT_JSON | Google Sheets/Drive(Box 連携キュー)アクセス用 | Box 連携時 |
BOX_SPREADSHEET_ID | Box 連携キュー用スプレッドシート ID | Box 連携時 |
INBOUND_WEBHOOK_SECRET | inbound-mail-webhook(転送メール経路)の認証。未設定なら全リクエストを 503 で拒否する | Webhook 経路利用時 |
外部AI の Secret(Cerebras / Groq / Gemini)は 2026-10-02 に不要になった。 以前この表に3つ載せていたが、外部AI は全廃した(理由と実測は CLAUDE.md 「技術スタック」)。マッチングはルールスコアだけで動く。
GitHub Issue 連携を使う場合は
GITHUB_TOKENを別途登録 (supabase/functions/create-github-issue/index.tsのREPO定数で対象リポジトリを指定)。
pg_cron スケジュール登録
以下の SQL 内 YOUR_PROJECT_REF と YOUR_SERVICE_ROLE_KEY を実際の値に書き換えて SQL Editor で実行:
supabase/migrations/add_email_polling_cron.sql(5 分ごと: poll-email)supabase/migrations/add_auto_match_cron.sql(毎朝 JST 9:00: auto-match)supabase/migrations/add_skill_cleanup_cron.sql(毎日 JST 3:00: skill-master-cleanup)supabase/migrations/add_archive_candidates_cron.sql(毎日 JST 0:00: archive-candidates。旧 delete-old-candidates を自動 unschedule)supabase/migrations/add_enrich_cron.sql(Box 連携時のみ)app_config テーブル)Edge Function 群の挙動はソースを書き換えずに app_config キーで切替できる。設定タブの UI から変更するか、Supabase SQL Editor で直接更新する。
| キー | 既定 | 内容 |
|---|---|---|
inbound_project_enabled | false | 案件メールの解析と DB 保存を有効化。'true' を設定すると inbound-email が type=project を処理。未設定=無効なので、案件用アドレスに転送しても登録されない(既定は人材メールのみ取り込み) |
auto_match_enabled | true | auto-match cron を有効化。'false' で毎朝のバッチ実行をスキップ。ここが false の間は毎朝の自動マッチングは走らない(画面からの手動マッチングは動く) |
email_poll_mode | incremental | incremental(未読のみ)か full(指定日以降全件) |
email_full_import_since | (未設定) | email_poll_mode=full 時に取得を開始する ISO 日時 |
email_classify_enabled | false | 同一受信箱に人材/案件が混在するとき、Gemini で candidate/project/other をバッチ分類 |
matching_run_mode | fast | マッチング既定実行モード (fast / full)。設定タブ「マッチング実行モード」で変更可能 |
matching_fast_max_candidates | 20 | 高速モード時の案件あたり候補者上限(1〜200 で UI から変更可) |
matching_fast_max_projects | 10 | 高速モード時の人材あたり案件上限(1〜200 で UI から変更可) |
candidate_retention_days | 7 | 人材データ保持日数(archive-candidates cron が参照) |
app_memo | (未設定) | 営業引き継ぎ用フリーテキストメモ |
llm_filter_skills | (未設定) | 常駐 AI ワーカーの解析対象を絞る優先スキル。人材一覧の「優先スキル」表示にも共有される(端末ごとの上書きは localStorage) |
graph_rt_human_prod ほか | — | Microsoft OAuth 連携で保存されるリフレッシュトークン(4 アカウント分) |
inbound-email の即時マッチング切替は環境変数(Supabase Secrets)で行う:
| Secret | 既定 | 内容 |
|---|---|---|
AUTO_MATCH_ENABLED | false | true で inbound-email 経由の即時自動マッチングも有効化(普段は cron 経由のみ) |
npm run test:run # 全テスト(CI向け)
npm run test # ウォッチモード(開発向け)
scripts/verify_email_extraction.mjs はメール解析の品質を一発でチェックする Node スクリプト(要 Node 20+)。Phoenix Technologies などの実メールフォーマットを使ったリグレッションケースを内蔵。
node scripts/verify_email_extraction.mjs
npm run check:edge inbound-email # deno check のみ(TS2304 検知)
npm run deploy:edge inbound-email # 型検査 + supabase functions deploy
scripts/check-and-deploy-edge.sh が deno check で TS2304(未定義変数)を検知したら deploy を中止する。引数省略時は inbound-email を対象とする。
.claude/skills/quality-check/SKILL.md / /quality-check コマンドの手順に従う:
[station_unmapped] を検索 → 集計station_master テーブルへ INSERT ... ON CONFLICT DO NOTHING で駅を追加(旧 STATION_TO_PREFECTURE ハードコードは保持しつつ DB 側で拡張)python3 scripts/skill_master_review.py → 怪しい source='ai' スキルの削除候補 SQL を出力[SKIP_IRRELEVANT] ログを確認(TRAINING_REPORT / PROJECT_SOLICITATION 等の誤投函パターン)ai_logs テーブル): モデル別・日次呼び出し数を集計。外部 AI は現在使っていないので、no-ai 以外が増えていたら設定を確認する5 分ごとに未読メールを自動取得・解析・DB 保存。処理完了後に既読マーク。
| 種別 | アドレス | data_env |
|---|---|---|
| 人材登録用(本番) | [email protected] | prod |
| 案件登録用(本番) | [email protected] | prod |
| 人材登録用(デモ) | dev 用アカウント | demo |
| 案件登録用(デモ) | dev 用アカウント | demo |
pg_cron(5分ごと)
↓ HTTP POST
poll-email Edge Function
├─ Graph API: リフレッシュトークン → アクセストークン取得(ローテーション保存)
├─ GET /me/messages?$filter=isRead eq false(最大50件/アカウント、ページネーション継続)
├─ メール種別分類(任意・既定 OFF): Gemini バッチで candidate/project/other 判定
│ → other はそのまま既読マークしてスキップ
├─ 各メール処理:
│ 1. markAsRead(二重処理防止)
│ 2. 添付ファイル取得
│ 3. inbound-email へ POST
│ 4. 失敗時は markAsUnread(次回再試行)
└─ 4アカウントを Promise.allSettled で並列処理
↓ 内部POST
inbound-email Edge Function(※ AI は呼ばない)
├─ TRAINING_REPORT / PROJECT_SOLICITATION フィルタ(人材メールボックスへの誤投函を 200 OK でスキップ)
├─ HTML → プレーンテキスト化 + HTML エンティティデコード(skill_master 照合前にも実施)
├─ URL 除去・送信者署名除去(誤マッチ対策。selfPR / agentComment に株式会社名が混入する問題も対策)
├─ Google Drive / Sheets / Docs URL 検出・自動取得
├─ Word / Excel 添付 → テキスト変換(PDF は Storage に保存するだけ)
├─ Excel から skillYears(スキル別経験月数)を抽出 → マッチングへ
├─ 複数人材検出: 区切り線(*****/─── 等)/◇形式で 1 メール = 複数候補者対応
├─ 案件メール / 営業/広告メール(配信解除リンク・研修販売等)を 200 OK でスキップ
├─ skill_master DB 照合(本文と添付で別ロジック、添付は上位 20 件・D/E評価除外)
├─ station_data.json(8,443駅名・ビルド時同梱)で駅名 → 都道府県を解決(実行時のDB往復なし)
├─ extractCandidateFieldsRegex: 氏名・最寄駅・都道府県・経験年数・希望単価(範囲・ラベルなし・月額対応)・参画時期・希望案件・国籍・自己PR
├─ extractFromProse: 役割・業界・リモート可否(フェーズ表ヘッダーは除外)
├─ 年齢・性別の抽出パターン 4 種(「男性:51 歳」混入パターンも対応)
├─ 駅 → 都道府県マッピングで送信者署名由来の誤判定を上書き
├─ 重複疑い: 名前一致 + スキル Jaccard ≥ 0.4 + 都道府県不一致・経験年数差 ≥ 5 年は別人扱い → duplicate_flag
└─ DB 保存(candidates / projects / candidate_skills / ai_logs ※ ai_logs.model='no-ai')
| 方式 | トリガー | 対象人材数上限 | AI フォールバック順 |
|---|---|---|---|
match-batch | UI ボタン(高速/全件)または auto-match から内部呼び出し | 高速モード: matching_fast_max_candidates(既定 20)全件モード: fetch_candidates_for_project 上限 500auto-match: 案件 1 件あたり最大 40 名 | ルールスコア(AI 再採点は未使用) |
match-score | 手動(個別スコア確認・duplicate 検出) | 1 ペア | ルールスコア(AI 再採点は未使用) |
auto-match | 毎朝 JST 9:00 cron。auto_match_enabled='false' でスキップ | 直近 25 時間以内に登録された prod 案件 ×最大 40 名 | match-batch 経由 |
SQL 側で計算(fetch_candidates_for_project RPC)→ ルールスコア降順で取得 → 上位 10 件のみ AI 採点。
| 観点 | 既定ウェイト | 加点ルール |
|---|---|---|
| スキル一致 | 40pt | 必須スキル合致比率 × 40。required 空時は 0.5 × 40 = 20pt。歓迎スキルは + 0.1 ボーナス(最大 40pt キャップ) |
| 経験年数 | 15pt | 10年=15 / 7年=12 / 5年=8 / 3年=4 / 1年=2 / 不明=5(中間点)。Excel skillYears あれば優先 / 必須スキル「希望」表明で 8/15 を付与 |
| 単価 | 15pt | 予算未設定 +15 / 範囲内 +15 / 上限+10% 内 +8 / 上限+20% 内 +3 |
| 勤務地 | 20pt | フルリモート +20 / 同一都道府県 +20 / 同一地方 +10 / 居住地不明 +5 / 不一致 0 |
| リモート | 10pt | リモート可 + 「リモート/remote/在宅」を含む案件で +10 |
required.length > 0 かつ hits === 0 の場合は合計を 35pt にキャップ(経験/単価/勤務地が良くてもスキル全不一致は上位に来させない)637152f): raw_profile.wantsFullRemote=true かつ案件側に リモート/remote/在宅/フルリモート 記述がない場合キャップ。breakdown 末尾に [フルリモート希望・常駐案件のため30pt上限] を付記p_weight_skill, p_weight_exp, p_weight_rate, p_weight_location, p_weight_remote) で実行時に変更可能c62bac2 以降): 旧 ±15pt クリップから、AI に「score をそのまま使うこと(変更禁止)」と指示する方針へ変更。AI は summary 生成のみを担当し、最終スコアはルール側の結果が採用されるファイル選択(Excel / Word)
├─ Excel → xlsx (SheetJS) で全シートを CSV 変換 → テキストエリアへ転記
└─ Word → mammoth で本文抽出 → テキストエリアへ転記
↓
「登録」ボタン
↓
inbound-email Edge Function(force=true で DEDUP/SENDER_DAILY_LIMIT バイパス)
↓
regex + skill_master DB 照合 → candidates / projects に upsert
PDF と画像は現状未対応。PDF を渡された場合は UI 側でエラー表示し処理を中断する。回避策: テキストを手動で貼り付ける、または PDF をページ画像化したうえで OCR テキストを手で貼り付ける。
同一 Supabase 内でデータを論理分離。data_env カラムでフィルタリング。
| 環境 | 用途 | 切替方法 |
|---|---|---|
prod | 本番データ(実際の人材・案件) | デフォルト |
demo | 営業デモ用サンプルデータ | URL に ?demo=<VITE_DEMO_KEY> を 1 回付加 → 以降は設定タブ「デモモード」スイッチでオン/オフ切替。トグル ON でヘッダの「データ」セレクタが出現 |
| テーブル | 用途 |
|---|---|
candidates | 人材マスタ(data_env で prod/demo 分離)。後述の主要カラム参照 |
projects | 案件マスタ(data_env で prod/demo 分離) |
submissions | マッチング提案履歴(スコア・AI要約・ai_raw に source タグ) |
candidate_skills | スキルのカテゴリ別管理(14カテゴリ・CHECK制約) |
candidates_archive_light | 7 日以上経過した人材のサマリー(prefecture / skills / name / subject)。人材マップの「全期間」モード集計用。Phase 4.12 新規 |
ai_logs | AI 解析実行ログ(モデル名・所要時間・結果・エラー。model='no-ai' でメール解析記録) |
error_logs | フロントエンド側クライアントエラー(page/message/stack/context/data_env/nickname) |
skill_master | スキル辞書(951 件)。aliases で表記ゆれ吸収、match_count で実績管理。更新すると skill_norm_map がトリガで貼り直される。AI では登録しない(シード SQL + 月次レビューのみ) |
station_master | 駅名・路線名 → 都道府県のマッピング(ekidata.jp 実データ・12,666 行 / 8,443 駅名)。同名駅は路線で判別する。inbound-email は実行時に DB を引かない(scripts/export_station_master.mjs で station_data.json に書き出してデプロイ物に同梱)。DB を更新したら再エクスポート + 再デプロイが必要 |
skill_norm_map | マテリアライズドビュー。skill_master の正式名 + 別名 → 正式名の正規化辞書。マッチングのスキル一致判定用 |
skill_implications | 「child を持つ人は parent 要件も満たす」向きのある包含関係(MySQL → SQL 等)。別名では表現できない関係を扱う |
notification_rules | 人材ウォッチ通知ルール(通知タブで CRUD)。条件は 名前 / スキル / 駅 |
notification_log | 通知送信済み記録(ルール × 人材で一意・二重通知防止) |
role_master | 役割の系統マスタ(マッチングの役割加点で使用) |
relevance_keywords | 関連度判定用キーワード(exclude / candidate / project の 3 種別。現状は未使用・Phase 4.9 で classifyInboundRelevance 削除済み) |
app_config | アプリ設定 / Graph API リフレッシュトークンのローテーション保存 |
candidates テーブルの主要カラム| カラム | 内容 |
|---|---|
data_env | prod または demo |
name | 候補者名(イニシャル可)。抽出失敗時は '不明' |
email / phone | 候補者本人の連絡先(あれば) |
skills (jsonb) | フラットなスキル名配列(skill_master 由来) |
experience_years | 経験年数(年単位) |
raw_profile (jsonb) | 元メール本文・添付テキスト・抽出メタデータ・スキル分類などを保持 |
desired_rate | 希望単価(例: "65万円以上") |
from_company | 営業会社名(送信者署名から抽出) |
box_url / box_status | Box 共有 URL と取り込みステータス(pending / enriched / failed) |
resume_url | Supabase Storage 上の履歴書 URL |
drive_url | Google Drive 共有 URL |
duplicate_flag | 重複候補と判定された場合 true |
merged_into | 重複マージ先の候補者 ID |
| カテゴリキー | 内容・例 |
|---|---|
languages | Python, TypeScript, SQL 等 |
frameworks | React, Laravel, Django 等 |
libraries | jQuery, NumPy 等 |
os | Linux, Windows, macOS 等 |
databases | PostgreSQL, Redis, MongoDB 等 |
dwh | Snowflake, BigQuery, Tableau 等 |
clouds | AWS, GCP, Azure 等 |
infrastructures | Docker, Kubernetes, Terraform 等 |
tools | Git, Jira, Slack, JP1, Teraterm 等 |
methodologies | アジャイル, スクラム, PM 等 |
certifications | AWS認定, 情報処理技術者, ITパスポート 等 |
design | Figma, Illustrator, Photoshop 等 |
marketing | SEO, SNS運用, Web広告 等 |
others | 上記以外 |
akinavi-hr-ai/
├── src/
│ ├── lib/
│ │ ├── ai/ # AI プロバイダー抽象化(ブラウザ用)
│ │ │ ├── types.ts # 型定義(リクエスト・レスポンス)
│ │ │ ├── geminiProvider.ts # Gemini 実装(マルチモーダル対応)
│ │ │ ├── openaiProvider.ts # OpenAI スタブ(未実装)
│ │ │ └── index.ts # プロバイダー切替ファクトリ
│ │ ├── db/ # DB 操作
│ │ │ ├── candidates.ts
│ │ │ ├── projects.ts
│ │ │ ├── submissions.ts
│ │ │ ├── emailSettings.ts
│ │ │ └── matchingSettings.ts
│ │ ├── fileParser.ts # Excel / Word テキスト抽出(PDF / 画像 非対応)
│ │ ├── dataEnv.ts # prod/demo 環境切替
│ │ └── supabase.ts
│ ├── pages/ # 各画面(Matching / Candidate / Project / Settings / Heatmap ほか)
│ │ # ※ 現在のナビは4タブ: マッチング・人材・案件・設定
│ │ # ※ 「人材マップ」は「人材」タブ内のサブ画面(ボタンから遷移)
│ │ # ※ History / Duplicate / Monitor は実装済みだがナビから非表示
│ └── components/ # 共通 UI(DemoSeedPanel / DemoProjectCandidateGen 等)
├── supabase/
│ ├── schema.sql # DB テーブル定義・RLS ポリシー
│ ├── migrations/ # 追加マイグレーション SQL(昇順で全て実行)
│ └── functions/
│ ├── inbound-email/ # メール解析 Edge Function(AI 不使用・regex + DB 照合・station_master/skill_master)
│ ├── poll-email/ # Outlook ポーリング Edge Function(5 分ごと cron)
│ ├── auto-match/ # 自動マッチング Edge Function(毎朝 JST 9:00 cron・match-batch を内部呼び出し)
│ ├── match-batch/ # バッチ AI 採点 Edge Function(fetch_candidates_for_project RPC + topN=10 を 1 コール採点・ウェイト可変)
│ ├── match-score/ # 単発スコア Edge Function(duplicate 検出付き・ルールスコア)
│ ├── archive-candidates/ # 7 日アーカイブ Edge Function(毎日 JST 0:00 cron・人材マップ全期間集計用)
│ ├── create-github-issue/ # GitHub Issue 登録 Edge Function(POST 作成 / GET 一覧 / PATCH クローズ)
│ ├── microsoft-oauth/ # Microsoft OAuth 認証 Edge Function
│ ├── enrich-candidate/ # Box 連携・再解析 Edge Function(毎日 JST 3:00 cron)
│ └── skill-master-cleanup/ # skill_master クリーンアップ Edge Function(毎日 cron)
├── public/
│ └── japan.topojson # 47 都道府県の TopoJSON(人材マップ用・約 416KB)
├── scripts/
│ ├── verify_email_extraction.mjs # メール解析の品質検証用 Node スクリプト
│ ├── check-and-deploy-edge.sh # deno check で TS2304 検知 → デプロイ
│ └── skill_master_review.py # source='ai' スキルの月次レビュー
└── docs/
├── Sales_Manual.md # 営業担当者向け操作マニュアル
├── HandsOn_Setup.md # 環境構築ガイド(後任エンジニア向け)
├── ai_fallback_flow.md # AI フォールバックフロー詳細
├── matching_candidate_selection.md # マッチング選定ロジック(SQL 側スコア計算・地方加点)
├── Heatmap.md # 人材マップ(ヒートマップ)機能仕様
├── DataEnv_Demo_Prod.md # データ環境(prod/demo)の使い分け(設定タブからの切替)
├── Outlook_AutoForward_Setup.md # Outlook 自動転送ルール設定
├── AWS_Account_Setup_Guide.md # AWS アカウント作成(参考資料)
├── AI_Freetier_Challenges.md # 無料枠と現状の限界(歴史的記述含む)
└── test-reports/ # テストレポート
argizomylbolpqxgmvimscripts/check-and-deploy-edge.sh(deno check で TS2304 を検知 → デプロイ中止) |
| デプロイ | Vercel(フロント・tsc 型チェックは Vercel ビルドから除外して高速化)/ Supabase(バックエンド) |
| テスト | Vitest, React Testing Library, MSW + scripts/verify_email_extraction.mjs(メール抽出リグレッション) |
20260526_fix_timeout.sql(CROSS JOIN LATERAL でルールスコアを 1 回だけ計算しタイムアウト解消)20260526_region_location_scoring.sql(勤務地に同一地方加点 get_region(prefecture_core) 関数。同一都道府県 20pt / 同一地方 10pt / 不明 5pt / 不一致 0pt)20260527_add_station_master.sql ほか station_master 系(駅名・路線名 → 都道府県。現在 12,666 行 / 8,443 駅名。更新したら node scripts/export_station_master.mjs で station_data.json を書き出して再デプロイする)20260527_fix_kyoto_bug.sql(「東京都 大森」に「京都」が部分一致するバグを修正 → 完全一致判定へ)add_archive_candidates_cron.sql(人材マップ用 7 日アーカイブ pg_cron。YOUR_PROJECT_REF / YOUR_SERVICE_ROLE_KEY 置換が必要。旧 delete-old-candidates を unschedule)ALTER TABLE candidates_archive_light ADD COLUMN IF NOT EXISTS name text, ADD COLUMN IF NOT EXISTS subject text;(archive-candidates Edge Function と candidates_by_prefecture RPC が両カラムを参照するため)