📚職涯停看聽・知識庫← 總部儀表板
📅最後更新:2026/05/25
📑 目錄

職涯諮詢客戶 8 維度 JSON Schema v1.0

建立日期:2026-05-25 | 作者:Tim + Claude | 狀態:草稿(待 L210 實作驗證) 用途:為 SYS-10 LINE Bot Phase 4 個人化諮詢打底;定義客戶資料結構、KV 儲存規格、自動化機制


一、用途與範圍

本文件服務對象

對象 用途
L210 實作者 Phase 4 Claude Tool Use 客戶資料萃取的完整規格
諮詢完整輸出 SKILL Stage 1 KV 寫入的欄位定義依據
Tim 了解哪些客戶資料會被收集與如何使用

明確不在範圍內

  • LINE Bot 的 Gemini/Claude prompt 設計(→ L210)
  • 諮詢完整輸出 SKILL 的程式碼修改(→ L210)
  • line-webhook.js 的程式碼修改(→ L210)

個人化用途示例(讓 L210 知道為何收集這些資料)

career_status.employment_status = "求職中"
  → LINE Bot 回應求職類問題時,主動提供履歷/面試資源,不問「你現在有在找工作嗎?」

pain_pattern.p_type_primary = "P3"
  → 涉及面試問題時,優先調用 six-consulting-frameworks.md P3 相關框架

service_progress.follow_up_date 距今 < 7 天
  → 對話開頭自然帶出:「你上次提到 [next_milestone],有進展嗎?」

engagement_cycle.session_count = 1(首次諮詢後)
  → 使用較引導性的語氣,提供更多背景說明

salary_expectation.target_monthly_ntd 已知
  → 討論薪資議題時,直接對應目標數字,不需重新詢問

二、完整 JSON Schema

{
  "schema_version": "1.0",
  "metadata": {
    "client_id": "C-YYYYMM-XXX",
    "line_user_id": "Uxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "last_updated": "YYYY-MM-DD",
    "consent_at": null,
    "consent_revoked_at": null,
    "data_deletion_requested_at": null
  },
  "dimensions": {
    "career_status": {
      "employment_status": "在職|求職中|待業|自由接案|創業中",
      "current_role": "string",
      "company_size": "新創(<50)|中小(50-500)|大型(500+)|外商|政府/學術",
      "tenure_current_role_years": 0
    },
    "job_seeking_intent": {
      "intent_level": "主動求職|被動觀望|暫不考慮",
      "timeline_months": 0,
      "trigger": "薪資|發展|環境|興趣|被裁|其他"
    },
    "work_experience_years": {
      "total_years": 0,
      "main_function": "string"
    },
    "industry_target": {
      "current_industry": "string",
      "target_industry": "string",
      "open_to_cross_industry": true
    },
    "salary_expectation": {
      "current_monthly_ntd": 0,
      "target_monthly_ntd": 0,
      "negotiation_readiness": "已有底線|模糊|未思考"
    },
    "engagement_cycle": {
      "last_contact": "YYYY-MM-DD",
      "session_count": 0,
      "preferred_channel": "LINE|Meet|Email"
    },
    "pain_pattern": {
      "p_type_primary": "P1|P2|P3|P4|P5|P6",
      "p_type": ["P1"],
      "pain_description": "string"
    },
    "service_progress": {
      "active_service": "S4|S6|D1|W6|其他",
      "current_phase": "string",
      "next_milestone": "string",
      "follow_up_date": "YYYY-MM-DD"
    }
  }
}

欄位說明

metadata

欄位 類型 說明
schema_version string 固定 "1.0";schema 升版時用於 migration 判斷
client_id string CRM ID(格式:C-YYYYMM-XXX);SKILL 觸發 key
line_user_id string | null LINE User ID(格式:Uxxxxxxxx);可選,初始化後填入
last_updated date Stage 1 或 Stage 2 最後寫入日期
consent_at date | null 客戶同意個人化服務的日期;未同意則不啟動個人化
consent_revoked_at date | null 客戶撤回同意的日期;撤回後停止 Stage 2 更新
data_deletion_requested_at date | null 客戶請求刪除資料的日期;L210 需在 24 小時內執行 DEL

dimensions.pain_pattern(修正說明)

  • p_type_primary:主要類型(單值),Stage 1 從逐字稿判定
  • p_type:陣列,允許複數(如 P3 + P6 混合型)
  • pain_description:自由文字,補充 P-type 無法涵蓋的細節
  • P-type 定義來源knowledge/methodology/career-problem-patterns.md
    • P1 履歷無效率 / P2 方向不清晰 / P3 面試表現差
    • P4 薪資不到位 / P5 職場困境 / P6 轉職焦慮

三、自動化難度標記

維度 欄位 難度 來源
career_status employment_status, current_role 🟡 逐字稿(Stage 1);LINE 訊息偶爾可偵測
career_status company_size, tenure_current_role_years 🔴 幾乎只能從 Meet 逐字稿取得
job_seeking_intent intent_level, timeline_months, trigger 🟡 Stage 1 主要來源;Stage 2 偵測信號弱但可補充
work_experience_years total_years, main_function 🔴 逐字稿(Stage 1);LINE 訊息極少提及
industry_target current_industry, target_industry 🟡 Stage 1 主要;Stage 2 偶可偵測
industry_target open_to_cross_industry 🟡 Stage 1
salary_expectation current/target_monthly_ntd 🔴 逐字稿(Stage 1);LINE 訊息偶爾提及數字
salary_expectation negotiation_readiness 🟡 Stage 1
engagement_cycle last_contact 🟢 全自動:每次 LINE webhook → 更新
engagement_cycle session_count 🟢 全自動:Stage 1 執行 +1
engagement_cycle preferred_channel 🟡 Stage 1 判定
pain_pattern p_type_primary, p_type 🟡 Stage 1;後續 LINE 訊息可補強信號
pain_pattern pain_description 🔴 Stage 1
service_progress active_service, current_phase 🟡 Stage 1;LINE 訊息中提到課名可偵測
service_progress next_milestone 🟡 Stage 1
service_progress follow_up_date 🟢 Stage 1 設定;可以 LINE 訊息觸發更新

🟢 全自動(LINE Bot webhook)| 🟡 半自動(Stage 1 主導,Stage 2 補充)| 🔴 純 Stage 1(需逐字稿)


四、Stage 1 vs Stage 2 欄位權限

Stage 1(諮詢完整輸出 SKILL — 深度寫入)

觸發:每次 Tim 執行 諮詢完整輸出 [客戶代號] SKILL 可更新:所有欄位(完整覆蓋,包含 🔴 欄位) 機制:4 步驟寫入(見 Section 六)

Stage 2(LINE Bot webhook — 輕量更新)

觸發:客戶每次傳 LINE Bot 訊息(前提:consent_at 已設定) 僅可更新以下欄位

可更新欄位 偵測方式
engagement_cycle.last_contact 每次訊息自動更新(無需 AI 萃取)
engagement_cycle.preferred_channel 每次訊息確認為 LINE
job_seeking_intent.intent_level Pattern B + C 偵測(見 Section 五)
job_seeking_intent.timeline_months Pattern C 偵測
salary_expectation.current_monthly_ntd Pattern B 數字偵測
salary_expectation.target_monthly_ntd Pattern B 數字偵測
service_progress.next_milestone Pattern D 偵測
service_progress.follow_up_date Pattern D 偵測

Stage 2 禁止更新work_experience_years.*pain_pattern.*career_status.company_size(需逐字稿確認,LINE 訊息不可靠)


五、Stage 2 Gemini 偵測框架

完整 Gemini prompt 設計 → L210 實作時定義。本節提供偵測分類框架供 L210 參考。

Pattern 偵測目標 觸發信號範例 更新欄位
A — 求職狀態信號 employment_status 變化 「我最近辭職了」「剛被裁員」「現在在找工作」 career_status.employment_status, job_seeking_intent.intent_level
B — 薪資數字 薪資期望值 「希望薪資 6 萬」「現在月薪 4 萬 5」「目標年薪 80K」 salary_expectation.current/target_monthly_ntd
C — 時程表達 求職時程 「希望 3 個月內找到」「年底前想換工作」「下個月要面試」 job_seeking_intent.timeline_months, follow_up_date
D — 服務進度更新 服務進展 提到課名、諮詢預約、階段完成 service_progress.next_milestone, follow_up_date
E — 互動(自動) 最後接觸時間 每次訊息(無需 AI 偵測) engagement_cycle.last_contact

萃取失敗處理:Pattern A-D 無法信心確定時(低置信度)→ 靜默跳過,不更新 KV


六、KV Key 結構

Upstash Redis 命名空間:

client:{crmId}:profile                    ← 最新 profile(主要讀取 key)
client:{crmId}:profile:{YYYY-MM-DD}       ← 歷史備份(每次 Stage 1 寫入前保存)
mapping:{lineUserId}                       ← lineUserId → crmId 對照表

範例

client:C-202604-001:profile              = {...完整 JSON...}
client:C-202604-001:profile:2026-05-25   = {...Stage 1 執行前備份...}
mapping:U8a9b2c3d4e5f6a7b8c9d0e1f2a3b4c5  = C-202604-001

TTL 規則

  • client:*:profile:無 TTL(永久保存)
  • client:*:profile:*(歷史備份):TTL 365 天
  • mapping:*:無 TTL(永久保存)
  • 若 consent_revoked_at 或 data_deletion_requested_at 已設定 → 執行 DEL 清除所有相關 key

七、Stage 1 寫入機制(Bash curl SOP)

前提:Tim 本機需有以下環境變數(與 SYS-10 Vercel 相同的 Upstash 實例):

KV_REST_API_URL=https://XXXXX.upstash.io
KV_REST_API_TOKEN=YYYYY

Stage 1 完整寫入流程(4 步驟)

# Step 1:讀取現有 profile(GET)
CURRENT=$(curl -s "$KV_REST_API_URL/get/client%3A${CLIENT_ID}%3Aprofile" \
  -H "Authorization: Bearer $KV_REST_API_TOKEN" | jq -r '.result // empty')

# Step 2:備份舊版本(SET with TTL 365 days)
TODAY=$(date +%Y-%m-%d)
BACKUP_KEY="client:${CLIENT_ID}:profile:${TODAY}"
curl -s -X POST "$KV_REST_API_URL/pipeline" \
  -H "Authorization: Bearer $KV_REST_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "[[ \"SET\", \"${BACKUP_KEY}\", $(echo $CURRENT | jq -R '.'), \"EX\", \"31536000\" ]]"

# Step 3:合併新資料與舊資料(Python merge)
# (由 SKILL 中的 Python 腳本執行 JSON merge)

# Step 4:寫入最新 profile(使用 pipeline 避免 URL 長度問題)
NEW_PROFILE=$(python3 -c "import json,sys; print(json.dumps(json.load(sys.stdin)))" < new_profile.json)
curl -s -X POST "$KV_REST_API_URL/pipeline" \
  -H "Authorization: Bearer $KV_REST_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "[[ \"SET\", \"client:${CLIENT_ID}:profile\", ${NEW_PROFILE} ]]"

⚠️ 使用 pipeline(POST body)而非 /set/{key}/{value} 路徑格式,避免大 JSON 觸發 URL 長度限制

mapping 建立(初始化時執行)

# 建立 lineUserId → crmId 對照(由 L210 SKILL 整合)
curl -s -X POST "$KV_REST_API_URL/pipeline" \
  -H "Authorization: Bearer $KV_REST_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "[[ \"SET\", \"mapping:${LINE_USER_ID}\", \"${CLIENT_ID}\" ]]"

八、初始化與同意機制

初始化流程

Tim 執行諮詢完整輸出 SKILL [客戶代號]
  → SKILL 提示:「此客戶是否有 LINE Bot 帳號?(可選,格式:Uxxxxxxxx,從 LINE Bot Vercel log 查詢)」
  → Tim 輸入 lineUserId(或跳過)
  → 若輸入 → 建立 mapping:{lineUserId} → crmId
  → 寫入 client:{crmId}:profile(含 consent_at: null)

客戶下次傳 LINE 訊息(首次 mapping 命中)
  → LINE Bot 偵測到 mapping 存在但 consent_at = null
  → 發送同意請求訊息(⚠️ 訊息文字 → LEG 審查後定稿,見 Section 九)
  → 客戶回覆同意 → LINE Bot 設定 consent_at = 今日日期
  → 個人化功能啟動

lineUserId 取得方式

  • 從 SYS-10 Vercel log 查詢(客戶曾傳訊才有記錄)
  • 若客戶從未傳訊給 LINE Bot → lineUserId 不存在 → 跳過 mapping 建立
  • 此情況 client profile 仍會建立,但個人化功能對此客戶無效

個人化啟動條件(LINE Bot 每次收訊時檢查)

mapping:{lineUserId} 存在 → 取得 crmId
AND client:{crmId}:profile 存在
AND metadata.consent_at ≠ null
AND metadata.consent_revoked_at = null
→ 啟動個人化(讀取 profile 注入 Gemini/Claude prompt)
→ 同時執行 Stage 2 輕量更新(async,先回覆後更新)

以上任一條件不符 → 靜默降級為通用回覆(不告知客戶,不報錯)

九、法律合規注意事項(⚠️ L210 實作前必讀)

PDPA 個資法合規要求

項目 規範 實作要求
同意告知 個資法第 8 條 客戶首次啟用個人化前,必須明確告知「收集哪些資料、用途為何」
撤回同意 個資法第 3 條 客戶傳「退出個人化」或類似指令 → 設定 consent_revoked_at,停止 Stage 2
刪除請求 個資法第 11 條 設定 data_deletion_requested_at → 24 小時內 DEL 所有 client:* 和 mapping:* key
同意訊息文字 ⚠️ 待 LEG 審查 禁止在 LEG 審查完成前設定 consent_at;不得在客戶未明確回覆前視為同意

⛔ 同意訊息設計(L210 實作前必做)

  1. Tim 草擬同意告知文字
  2. 送 LEG 部門審查(legal/CLAUDE.md 路由)
  3. 確認客戶觸發動作(回覆「同意」/ 點選快速回覆按鈕)
  4. 確認後才實作 consent_at 設定邏輯

Stage 2 資料處理聲明

Stage 2 使用 Gemini API 分析 LINE 訊息內容以萃取客戶職涯資訊。同意訊息必須包含此說明。


十、Fallback 規則(L210 實作規範)

失敗情境 處理方式
Upstash GET timeout / 錯誤 靜默降級為通用回覆,不報錯給客戶
mapping:{lineUserId} 不存在 靜默降級,繼續通用服務
consent_at 未設定 靜默降級,發送同意請求(若尚未發送)
Stage 2 Gemini 萃取失敗 跳過 KV 更新,不影響回覆
Stage 2 Gemini 置信度低 跳過 KV 更新(不寫入不確定資料)
Stage 2 寫入失敗 fail-open:仍返回回覆,背景記錄 console.error
Stage 1 寫入失敗 報告 Tim(非背景忽略),需手動重試

核心原則:Stage 2 任何環節失敗 = 靜默降級,不影響客戶收到回覆


十一、Schema 版本管理

Migration 策略(v1.0 → v1.x)

  • 新增欄位:讀取時若欄位不存在,L210 使用 null 或預設值,不報錯
  • 移除欄位:舊 KV 記錄中保留舊欄位直到下次 Stage 1 覆蓋(不影響功能)
  • 重大結構改變(v1.x → v2.0):需建立 migration script,重寫所有 client:*:profile key

未來整合項目(Phase 5+)

  • SYS-03 診斷整合:官網診斷結果的 P-type 流入 schema → Phase 5 待規劃
  • 知識庫健康儀表板:SYS-07 顯示已個人化客戶數、consent 率 → Phase 6 待規劃
  • 管理端讀取工具:Tim 可查詢特定客戶的 profile 現況 → L210 可納入

十二、L210 實作清單(本文件的下游任務)

在 L210 實作前需完成:

  • LEG 審查:同意訊息文字定稿(阻塞項,L210 不可在此完成前實作 consent_at 設定)
  • 環境設定:Tim 本機 .env 新增 KV_REST_API_URL + KV_REST_API_TOKEN(與 Vercel 相同值)
  • SYS-10 程式碼:line-webhook.js 新增 profile 讀取邏輯 + Stage 2 萃取 + consent 流程
  • 諮詢完整輸出 SKILL:新增 LINE User ID 輸入提示 + Stage 1 KV 寫入步驟
  • Stage 2 Gemini prompt:依 Section 五偵測框架設計 5 個 Pattern 的 prompt + 置信度門檻
  • Fallback 測試:模擬 Upstash 失敗、Gemini 失敗,確認靜默降級正常運作

來源:10 輪嚴謹設計自查(2026-05-25)|前置任務:tasks.md L209 | 後置任務:tasks.md L210

← 返回 方法論