目次
1. 解こうとしている問題
現在の運用フロー:
- LINE で 「増田が税理士を気に入っていない」 などの主題 (context) が入る
- manager がコメントを付ける
- コメントの中に subtask を手で分解 (「増田と話す」 「新しい税理士をタスク」 「XXX」)
- 誰が担当か、 関係者は誰か、 会議は必要か、 を都度判断
問題:
- 主題と task が同じレイヤーで扱われる → 主題が解決したのか、 個別 task が完了したのか曖昧
- 依存関係が消える → 「税理士候補を出してから増田に紹介」 という順序が manager の頭の中だけ
- 関係者の合意形成が分断 → 「増田」 「高野院長」 「経理担当」 が同じ画面で進捗を見られない
- 会議の調整に時間がかかる → 「話す」 task で全員のカレンダー突き合わせを手作業
2. 中核概念 — Issue / Task / Subtask の階層
3 レイヤーで階層化する。
| レイヤー | 定義 | 例 | 誰が完了判定するか |
|---|---|---|---|
| Issue (主題) | 解決すべき業務上の問題 / 状況。 1 つの Issue は 1 つの目的を持つ | 「増田が税理士を気に入っていない」 | executive (高野院長) / primary_owner |
| Task (タスク) | Issue を解決するための具体的な行動単位。 依存関係で順序が決まる | 「増田と意思確認の面談」 / 「新税理士候補リスト作成」 / 「既存税理士との解約交渉」 | 各 task の primary_owner |
| Subtask (サブタスク) | 1 つの Task を実行するための小さなアクション。 通常 30 分以内で完了 | 「面談日程を 3 候補メールで送る」 / 「候補税理士 5 件リスト化」 / 「契約書を確認」 | subtask 担当者本人 |
Issue は「閉じる (resolved)」 こと自体に意味がある。 個別 task が全部完了しても、 Issue が解決していないことがある (例: 増田が新税理士も気に入らない → 別 task tree が派生)。 → Issue を resolved にできるのは executive のみ。
3. 具体例 — 「増田が税理士を気に入っていない」
「最近、 増田さんが税理士の田中先生のことを気に入ってないみたいで。 確定申告のやり取りで何度かトラブル。 新しい税理士を探したほうがいいかも」
AI が抽出する Issue + Task Tree
元の LINE 一文から 1 Issue + 5 Tasks + 8 Subtasks + 依存関係 + 担当者 + 関係者 を AI が自動抽出。 全所要時間 30 秒。 manager は Slack で 「approve all」 押すだけ。
4. エンドツーエンド フロー (7 stage)
LINE message 受信 AUTO
LINE-MEMORY の既存 webhook が受信、 SQLite + Slack 既存通知に流れる。 さらに新規 path として extract_issues() が並列で走る。
Issue 抽出 (Claude opus-4-7) AUTO
会話 window (5-20 分) を Claude opus-4-7 で解析、 主題候補 を抽出。 出力: {title, topic_type, priority, urgency, stakeholders, talent_id, source_msg_indices}。 confidence ≥ 0.6 のみ採用。
Task Tree 提案 (Claude opus-4-7) AUTO
Issue を input に propose_task_tree() で 3-7 個の task と subtask を提案。 各 task に推奨担当者を handlers テーブルから推論 (role / specialty マッチング)、 依存関係を DAG (Directed Acyclic Graph) で記述。
Slack に通知 (Block Kit) AUTO
Tree 構造の Block を含む Slack message を post。 [ 全て承認 ] [ 編集 (modal) ] [ 却下 ] のボタン。 executive と primary_owner にメンション。
operator が承認 / 編集 SEMI
"全て承認" 押下 → 全 task が internal_tasks に転記。 "編集" → modal で個別 task の担当者・期限・依存変更が可能。 大体 30 秒で完結。
Calendar event 作成 (meeting type task のみ) AUTO
task_type=meeting の task は Google Calendar API で候補日時を 3 件提案 → manager が選択 → event 作成 + 関係者を invite。 candidate slot は freeBusy API で全 attendees の空き時間から自動算出。
LINE group に reply 投稿 SEMI
承認確定後、 LINE group に「✅ ご相談を 5 個のタスクに整理しました。 詳細は talent page で確認できます」 と push 通知 (前述の LINE Push 設計)。 meeting 確定後は「📅 面談を 5/15 14:00 で設定しました」 と続報。 audience=mixed の group は executive が承認後に投稿。
5. スキーマ追加
5.1 Issues テーブル (新規)
CREATE TABLE talent_mgmt.issues (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id text NOT NULL,
talent_id text REFERENCES talent_mgmt.talents(id),
thread_id text REFERENCES talent_mgmt.line_threads(id),
source_message_ids bigint[] NOT NULL DEFAULT '{}',
title text NOT NULL, -- 「増田が税理士を気に入っていない」
description text,
topic_type text NOT NULL, -- 'relationship' | 'business' | 'health' | 'career' | 'finance' | 'other'
priority text NOT NULL DEFAULT 'P2' -- P0/P1/P2/P3
CHECK (priority IN ('P0','P1','P2','P3')),
urgency text NOT NULL DEFAULT 'no_deadline'
CHECK (urgency IN ('immediate','this_week','this_month','no_deadline')),
status text NOT NULL DEFAULT 'open' -- open | in_progress | resolved | dropped
CHECK (status IN ('open','in_progress','resolved','dropped')),
primary_owner uuid REFERENCES talent_mgmt.handlers(id),
stakeholders uuid[] NOT NULL DEFAULT '{}', -- handler ids 配列
external_stakeholders jsonb DEFAULT '[]'::jsonb, -- [{ name, role, contact }] talent本人 / 第三者
confidence numeric(3,2),
rationale text,
ai_proposed boolean NOT NULL DEFAULT false,
approved_by uuid REFERENCES talent_mgmt.handlers(id),
approved_at timestamptz,
resolved_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX issues_status_idx ON talent_mgmt.issues(status);
CREATE INDEX issues_primary_owner_idx ON talent_mgmt.issues(primary_owner);
CREATE INDEX issues_talent_idx ON talent_mgmt.issues(talent_id);
5.2 internal_tasks 拡張
ALTER TABLE talent_mgmt.internal_tasks
ADD COLUMN IF NOT EXISTS issue_id uuid REFERENCES talent_mgmt.issues(id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS parent_task_id text REFERENCES talent_mgmt.internal_tasks(id) ON DELETE CASCADE,
ADD COLUMN IF NOT EXISTS task_type text NOT NULL DEFAULT 'action'
CHECK (task_type IN ('action','meeting','investigation','communication','decision')),
ADD COLUMN IF NOT EXISTS stakeholders uuid[] NOT NULL DEFAULT '{}',
ADD COLUMN IF NOT EXISTS depends_on text[] NOT NULL DEFAULT '{}', -- 他 task_id の依存
ADD COLUMN IF NOT EXISTS calendar_event_id text, -- Google Calendar event id
ADD COLUMN IF NOT EXISTS calendar_event_url text;
CREATE INDEX IF NOT EXISTS internal_tasks_issue_idx ON talent_mgmt.internal_tasks(issue_id);
CREATE INDEX IF NOT EXISTS internal_tasks_parent_idx ON talent_mgmt.internal_tasks(parent_task_id);
5.3 calendar integration 用 (tenant 単位)
CREATE TABLE talent_mgmt.tenant_calendar_integrations (
tenant_id text PRIMARY KEY,
provider text NOT NULL DEFAULT 'google_calendar',
service_account_email text, -- service account 経由の場合
oauth_refresh_token text, -- OAuth2 経由の場合 (暗号化)
default_calendar_id text,
timezone text NOT NULL DEFAULT 'Asia/Tokyo',
installed_by uuid REFERENCES talent_mgmt.handlers(id),
installed_at timestamptz NOT NULL DEFAULT now(),
active boolean NOT NULL DEFAULT true
);
-- handler の Google account を保存 (event 作成時の attendee 解決用)
ALTER TABLE talent_mgmt.handlers
ADD COLUMN IF NOT EXISTS google_account_email text;
6. AI prompt 設計
6.1 extract_issues() prompt (Claude opus-4-7)
あなたはマネージャー業務支援の AI です。
直近の LINE 会話 window から「主題 (Issue)」 を抽出してください。
Issue = 解決すべき業務上の問題 / 状況。 単発タスクではなく、 複数の行動を要する主題。
抽出基準 (1 つ以上):
1. 関係者の感情 / 意思の変化 (例: 「気に入っていない」「辞めたい」「やりたい」)
2. 構造的問題 (例: 「契約条件がおかしい」「料金が高すぎる」「業務分担が回らない」)
3. 中期的判断 (例: 「来年から方針変える」「事務所変更検討」)
返答は JSON のみ:
{
"issues": [
{
"title": "30 字以内、 名詞句で",
"description": "100 字以内、 背景情報",
"topic_type": "relationship | business | health | career | finance | other",
"priority": "P0 | P1 | P2 | P3",
"urgency": "immediate | this_week | this_month | no_deadline",
"primary_owner_hint": "高野院長 / 経理担当 / マネージャー田中 など (handlers.name に近い文字列)",
"stakeholders_hint": ["増田", "高野院長", "経理担当"],
"external_stakeholders": [{ "name": "田中先生", "role": "税理士 (外部)" }],
"confidence": 0.0,
"rationale": "なぜ Issue と判定したかの根拠 (80 字)",
"source_msg_indices": [0, 2]
}
]
}
6.2 propose_task_tree() prompt (Claude opus-4-7)
あなたは業務オペレーションの専門家です。
以下の Issue を解決するための Task Tree を提案してください。
入力:
- Issue: {title} / {description} / {topic_type} / {priority}
- primary_owner: {owner_handler}
- stakeholders: {list_of_handlers}
- external: {list_of_externals}
- 利用可能な handler 一覧: {handlers_with_role_and_specialty}
要件:
1. 3-7 個の Task に分解 (多すぎず少なすぎず)
2. 各 Task に primary_owner を 1 名 (handler.id) 割り当て
3. 各 Task に stakeholders を 0-3 名割り当て
4. 各 Task に task_type を割り当て:
- action: 具体的行動 (書類作成、 発送、 手続き)
- meeting: 会議 / 面談 / 1on1
- investigation: 調査 / リサーチ / 候補出し
- communication: 通知 / 連絡 / 報告
- decision: 意思決定ポイント
5. 依存関係を depends_on で表現 (他 task の id を参照)
6. 各 Task に subtasks を 0-3 個 (= 1 task = 30 分以内の細分化)
7. 全体が DAG (循環なし)、 起点 task は 1-2 個
返答は JSON のみ:
{
"tasks": [
{
"id": "t1",
"title": "増田と現状確認の 1on1 面談",
"task_type": "meeting",
"primary_owner_id": "h-tanaka-01",
"stakeholders": ["h-takano-00"],
"due_at_suggested": "2026-05-16",
"priority": "P1",
"depends_on": [],
"rationale": "本人の意思確認なくして次のステップに進めない",
"subtasks": [
{ "title": "面談候補日 3 件を提示", "task_type": "communication" }
]
}
]
}
7. Slack 承認 UI (Block Kit)
7.1 通知メッセージ (Block Kit 例)
{
"blocks": [
{ "type": "header", "text": "🎯 Issue 検出: 増田が税理士を気に入っていない" },
{ "type": "section", "fields": [
{ "type": "mrkdwn", "text": "*topic:* relationship + finance" },
{ "type": "mrkdwn", "text": "*priority:* P1 / urgency: this_week" },
{ "type": "mrkdwn", "text": "*primary owner:* 高野院長" },
{ "type": "mrkdwn", "text": "*stakeholders:* 増田、 経理担当、 (外部) 田中先生" }
]},
{ "type": "divider" },
{ "type": "section", "text": { "type": "mrkdwn", "text":
"*推奨タスク分解* (5 task / 8 subtask)\n" +
"├─ T1. 増田と意思確認の面談 [meeting] 担当: 田中マネ\n" +
"├─ T2. 新税理士候補リスト 5 件作成 [investigation] 担当: 経理\n" +
"├─ T3. 候補税理士との面談 [meeting] 担当: 高野院長 + 経理 (depends: T2)\n" +
"├─ T4. 増田に最終候補を紹介 [communication] 担当: 田中マネ (depends: T1, T3)\n" +
"└─ T5. 既存税理士との解約・引継ぎ [action] 担当: 経理 (depends: T4)"
}},
{ "type": "actions", "elements": [
{ "type": "button", "style": "primary", "text": "全て承認 + Calendar 設定", "action_id": "issue_approve_all" },
{ "type": "button", "text": "編集...", "action_id": "issue_edit" },
{ "type": "button", "style": "danger", "text": "却下", "action_id": "issue_reject" }
]}
]
}
7.2 操作フロー
| 操作 | 挙動 |
|---|---|
| 全て承認 | Issue を open で INSERT、 5 task + 8 subtask を internal_tasks に INSERT (depends_on の関係を保持)。 meeting type は Calendar 候補日提示 modal を pop up |
| 編集 | Modal を開いて task ごとに 担当者 / 期限 / type 変更 + 追加・削除可能。 確定で承認 |
| 却下 | Issue を dropped、 task は登録しない。 元の LINE thread に記録のみ残る |
7.3 Calendar 候補日 modal
meeting type task が approve されると、 各 meeting ごとに以下 modal が pop up:
📅 T1. 増田と意思確認の面談 — 日時候補
attendees: 田中マネ (h-tanaka-01) + 増田 (外部) + 高野院長 (option)
候補スロット (Google Calendar freeBusy API で算出):
⭕ 5/14 (火) 14:00-15:00
⭕ 5/15 (水) 16:00-17:00
⭕ 5/16 (木) 10:00-11:00
⭕ 他候補を見る...
[ 5/15 16:00 で確定 ] [ 編集 ] [ skip Calendar 連携 ]
確定 → Calendar event 作成 + attendees invite + Slack に確認 post + LINE group に push reply。
8. Google Calendar 連携
8.1 認証方式 (2 案)
- (a) Service Account + Domain-wide Delegation (推奨、 Google Workspace 環境)
- 1 service account で全 handler の Calendar 操作可能
- operator は GCP コンソールで 1 度設定するだけ
- handler 個別 OAuth 不要
- (b) Per-handler OAuth2 (Workspace 外の事務所向け)
- 各 handler が Talent Hub の設定 page で 「Google Calendar 接続」 ボタン
- refresh_token を
handlers.google_oauth_refresh_tokenに暗号化保存
8.2 freeBusy API による候補日算出
POST https://www.googleapis.com/calendar/v3/freeBusy
{
"timeMin": "2026-05-13T09:00:00+09:00",
"timeMax": "2026-05-20T18:00:00+09:00",
"items": [
{ "id": "tanaka@example.com" },
{ "id": "takano@example.com" }
]
}
→ レスポンスから全員空きの 1h スロットを 3 件抽出 → modal に表示
8.3 Event 作成
POST https://www.googleapis.com/calendar/v3/calendars/primary/events
{
"summary": "増田 — 税理士の件 1on1",
"description": "Issue: 増田が税理士を気に入っていない\n親 Task: T1\nLINE thread: line_t-masuda-91",
"start": { "dateTime": "2026-05-15T16:00:00+09:00" },
"end": { "dateTime": "2026-05-15T17:00:00+09:00" },
"attendees": [
{ "email": "tanaka@example.com" },
{ "email": "takano@example.com" }
],
"conferenceData": {
"createRequest": { "requestId": "uuid", "conferenceSolutionKey": { "type": "hangoutsMeet" } }
},
"reminders": { "useDefault": true }
}
→ event id を internal_tasks.calendar_event_id に保存
→ Google Meet URL は internal_tasks.calendar_event_url に保存
talent 本人 / 外部 stakeholder の Google account を持っていない場合、 attendees に email アドレスのみ追加 (任意)、 または LINE Push で「日時確定しました」 のみ通知して Calendar event は internal のみ。
9. 自動 / 半自動 の境界
operator から「自動化もしくは半自動化したい」 という要件。 各 stage の自動度を整理:
| stage | 自動度 | 人手介入 |
|---|---|---|
| LINE 受信 | 完全自動 | なし |
| Issue 抽出 | 完全自動 | なし (confidence ≥ 0.6 のみ) |
| Task Tree 提案 | 完全自動 | なし |
| Slack 通知 | 完全自動 | なし |
| 承認 / 編集 | 半自動 | operator 1 クリック (or modal 編集) |
| internal_tasks INSERT | 完全自動 | 承認後の処理 |
| Calendar event 作成 | 半自動 | 候補日選択 (1 クリック) |
| LINE group reply | 半自動 (audience による) | audience=mixed/external は executive 1 クリック確認 |
朝 Slack を開く → Issue 通知 5 件 → 各 Issue に対し 「全て承認」 (3 秒) → meeting 候補日選択 (10 秒) → 完了。 1 Issue あたり 30 秒、 5 Issue で 2.5 分。 既存運用 (1 Issue 10 分 × 5 = 50 分) と比べて 20 倍効率化。
10. 実装 Phase
| Phase | 時期 | 達成内容 |
|---|---|---|
| Phase 0 | 2026-06 | schema 追加 (issues + internal_tasks 拡張)、 mock UI に Issue list page 追加 |
| Phase 1 | 2026-06 〜 07 | analyzer.py に extract_issues() + propose_task_tree() 追加 (Claude opus-4-7)、 Slack Block Kit で承認 UI |
| Phase 2 | 2026-07 〜 08 | Google Calendar 連携 (service account + freeBusy + event 作成)、 候補日 modal、 attendees 解決ロジック |
| Phase 3 | 2026-08 〜 09 | LINE group push reply 連動 (LINE Push 設計 と統合)、 audience 別 投稿制御 |
| Phase 4 | 2026-09 以降 | Issue dashboard (open / in_progress / resolved の trend)、 SLA monitoring、 stakeholder graph 可視化 |