タスク推定アルゴリズム
:LiTarget: 用途
タスク内容から推定時間を自動算出するロジック。過去実績から学習。
:LiSparkle: 特徴
- 推定アルゴリズム
- 実績学習
- カテゴリ別係数
:LiCode: 実コード(SCALE Base より自動抽出)
:LiInfo:
lib/task-estimate.tsの中身そのもの。コピペ即可。
// タスク名からざっくり所要時間を推定するヒューリスティック
// 100%正解ではないが、全タスク1時間固定よりは遥かにマシ。
// ユーザーが上書き可能。
export interface EstimateRule {
keywords: string[];
minutes: number;
label: string;
}
// 優先順に評価(早いものマッチが勝つので、具体的なものを先に)
const RULES: EstimateRule[] = [
{ keywords: ['MTG', 'mtg', '打合せ', '打ち合わせ', 'ミーティング', '会議', '商談', '面談', '1on1', 'オンボーディング'], minutes: 60, label: 'MTG/面談' },
{ keywords: ['資料作成', '資料の作成', '提案書', 'プレゼン', 'スライド', 'パワポ'], minutes: 90, label: '資料作成' },
{ keywords: ['分析', 'リサーチ', '調査', '競合分析', '市場調査'], minutes: 90, label: '分析・リサーチ' },
{ keywords: ['制作', 'クリエイティブ', 'デザイン', 'バナー', 'LP', 'HP修正', 'サイト修正'], minutes: 90, label: '制作' },
{ keywords: ['ブラッシュアップ', 'レビュー', '振り返り', '検討'], minutes: 45, label: 'レビュー系' },
{ keywords: ['メール', 'DM', '返信', '連絡', '送信', '送付', 'Slack'], minutes: 15, label: 'メール・連絡' },
{ keywords: ['確認', 'チェック', '閲覧'], minutes: 15, label: '確認' },
{ keywords: ['決定', '決め', '承認'], minutes: 15, label: '決定' },
{ keywords: ['リスト作成', 'リストアップ', '一覧作成'], minutes: 60, label: 'リスト作成' },
{ keywords: ['整理', '片付け', 'アーカイブ', '削除'], minutes: 30, label: '整理' },
{ keywords: ['架電', 'テレアポ', 'コール'], minutes: 60, label: '架電' },
{ keywords: ['投稿作成', '投稿', 'ポスト作成'], minutes: 30, label: '投稿作成' },
{ keywords: ['日報', '報告'], minutes: 10, label: '日報' },
{ keywords: ['記入', '入力'], minutes: 15, label: '入力' },
];
export const DEFAULT_ESTIMATE_MINUTES = 30;
/**
* タスク名から所要時間を推定(キーワードベース)
* 数値付き(例: "300件メール作成")は数値ベースで調整
*/
export function estimateTaskMinutes(taskName: string): { minutes: number; reason: string } {
if (!taskName) return { minutes: DEFAULT_ESTIMATE_MINUTES, reason: 'デフォルト' };
const name = taskName.toLowerCase();
// 数値パターン(例: 300件, 60社)→ 件数ベースで加算
const countMatch = taskName.match(/(\d+)\s*(件|社|人|本|通)/);
if (countMatch) {
const count = parseInt(countMatch[1], 10);
if (taskName.match(/(メール|連絡|DM|送信|送付)/)) {
return { minutes: Math.min(180, Math.max(15, Math.ceil(count / 50) * 15)), reason: `件数: ${count}${countMatch[2]} × メール系` };
}
if (taskName.match(/(架電|コール|電話)/)) {
return { minutes: Math.min(240, Math.max(30, Math.ceil(count / 20) * 30)), reason: `件数: ${count}${countMatch[2]} × 架電` };
}
if (taskName.match(/(リスト|作成|ピック)/)) {
return { minutes: Math.min(120, Math.max(20, Math.ceil(count / 30) * 15)), reason: `件数: ${count}${countMatch[2]} × リスト` };
}
}
// キーワードマッチ
for (const rule of RULES) {
for (const kw of rule.keywords) {
if (taskName.includes(kw)) {
return { minutes: rule.minutes, reason: rule.label };
}
}
}
return { minutes: DEFAULT_ESTIMATE_MINUTES, reason: '推定なし' };
}
// 分→時間表示
export function fmtMinutes(mins: number): string {
if (mins < 60) return `${mins}分`;
const h = Math.floor(mins / 60);
const m = mins % 60;
return m === 0 ? `${h}時間` : `${h}時間${m}分`;
}
// 想定時間のクイック選択プリセット(Math.maxで下限15分)
export const TIME_PRESETS = [15, 30, 45, 60, 90, 120, 180];
:LiFolder: ソースファイルのパス
/Users/oogushiyuuki/Library/CloudStorage/GoogleDrive-y-ogushi@scale-group.co.jp/マイドライブ/AI/scale-base/lib/task-estimate.ts
:LiHandPointer: 使い方
対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。
:LiAlertCircle: 注意事項
- 依存パッケージを忘れず追加