career_app / docs /ARCHITECTURE_DESIGN.md
Youngger9765
fix: resolve all linting and formatting errors
efc08f7

職涯諮詢個案管理系統 - 架構設計文件

專案目標

打造一個差異化的助人者專業工具,相較於一般的「錄音 → AI 摘要」工具,我們提供:

核心價值主張

  1. 一鍵整合:錄音 → 逐字稿 → 專業報告 → 個案管理系統
  2. 專業標準:符合助人專業的報告結構(職游標準框架)
  3. 累積型檔案:支援個案歷程追蹤、跨次晤談分析
  4. 智能連動:報告自動與「來訪者歷程」「諮詢策略建議」「回訪提醒」連動
  5. 專業化 AI:經過專業顧問調教的對話設計(符合助人倫理、督導結構)
  6. 客製化知識庫:諮詢師可選擇特定文件進行 RAG 檢索

系統架構概覽

┌─────────────┐
│   iOS App   │ 錄音 → 轉逐字稿
└──────┬──────┘
       │ API Call
       ↓
┌─────────────────────────────────────────┐
│          Backend API Server              │
│  ┌────────────────────────────────────┐ │
│  │  Session Management (個案管理)     │ │
│  │  - 自動創建/更新個案檔案           │ │
│  │  - 晤談次數累計                   │ │
│  │  - 歷程追蹤                       │ │
│  └────────────────────────────────────┘ │
│  ┌────────────────────────────────────┐ │
│  │  AI Report Generation (報告生成)   │ │
│  │  - RAG 知識檢索                   │ │
│  │  - 結構化報告生成                 │ │
│  │  - 對話節錄提取                   │ │
│  └────────────────────────────────────┘ │
│  ┌────────────────────────────────────┐ │
│  │  Counselor Agent (客製化 AI)      │ │
│  │  - 文件選擇                       │ │
│  │  - 知識庫過濾                     │ │
│  └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
       │
       ↓
┌─────────────────────────────────────────┐
│          Supabase Database               │
│  - clients (個案基本資料)                │
│  - sessions (晤談紀錄)                   │
│  - session_reports (AI 生成報告)         │
│  - counselor_agents (諮詢師 AI 設定)     │
│  - documents + chunks + embeddings (RAG)  │
└─────────────────────────────────────────┘

核心使用情境

情境 1:首次晤談 (iOS → Backend)

iOS 端操作

  1. 諮詢師在 App 中錄音
  2. App 自動轉逐字稿
  3. 填寫個案基本資料(姓名、年齡、性別等)
  4. 點擊「生成報告」

API Request

POST /api/sessions/generate-report

{
  "counselor_code": "CO_張老師",
  "client": {
    "code": "CL_小明",
    "name": "小明",
    "age": 25,
    "gender": "男",
    "occupation": "軟體工程師",
    "education": "大學",
    "location": "台北"
  },
  "session": {
    "transcript": "Co: 你好,今天想聊什麼?\nCl: 我最近對工作感到很困惑...",
    "num_participants": 2,
    "session_date": "2025-01-15T10:00:00"
  }
}

Backend 處理流程

  1. ✅ 檢查 client_code 是否存在
    • 不存在 → 創建新個案檔案(clients 表)
    • 存在 → 略過
  2. ✅ 創建新的 Session 紀錄(sessions 表)
    • 自動累加 session_number (第 1 次)
    • 儲存逐字稿
  3. ✅ 呼叫 AI 生成報告
    • RAG 檢索相關理論(根據 counselor 的 Agent 設定選擇文件)
    • 生成結構化報告
  4. ✅ 儲存報告(session_reports 表)
  5. ✅ 更新個案統計(總晤談次數、最後晤談時間)

**API Response (SSE Stream)**:

data: {"step": 1, "status": "processing", "message": "正在分析逐字稿結構..."}
data: {"step": 2, "status": "completed", "message": "識別到 2 個關鍵議題"}
data: {"step": 3, "status": "processing", "message": "正在檢索相關理論..."}
...
data: {"step": 5, "status": "completed", "data": {"report": {...}}}

最終回傳完整報告 JSON


情境 2:第二次晤談 (簡化流程)

iOS 端操作

  1. 選擇既有個案「小明」
  2. 錄音 → 轉逐字稿
  3. 點擊「生成報告」

API Request

POST /api/sessions/generate-report

{
  "counselor_code": "CO_張老師",
  "client": {
    "code": "CL_小明"
    // 不需要再傳基本資料,後端自動取得
  },
  "session": {
    "transcript": "Co: 上次我們聊到職涯困擾...",
    "num_participants": 2,
    "session_date": "2025-01-22T10:00:00"
  }
}

Backend 處理

  1. ✅ 根據 client_code 取得既有個案資料
  2. ✅ 創建第 2 次 Session(session_number = 2
  3. ✅ 生成報告時可參考歷次晤談內容(optional,未來功能)
  4. ✅ 儲存並回傳報告

情境 3:查詢個案歷程 (iOS 或 Web)

API Request

GET /api/clients/CO_張老師/CL_小明/history

Response

{
  "client": {
    "code": "CL_小明",
    "name": "小明",
    "age": 25,
    "gender": "男",
    "total_sessions": 2,
    "first_session_date": "2025-01-15T10:00:00",
    "last_session_date": "2025-01-22T10:00:00",
    "status": "active"
  },
  "sessions": [
    {
      "session_id": 123,
      "session_number": 1,
      "session_date": "2025-01-15T10:00:00",
      "summary": "初次晤談,探索職涯困擾,使用生涯卡牌",
      "main_concerns": ["職涯轉換", "工作壓力"]
    },
    {
      "session_id": 124,
      "session_number": 2,
      "session_date": "2025-01-22T10:00:00",
      "summary": "深入探索價值觀,進行興趣測驗",
      "main_concerns": ["自我探索", "決策困難"]
    }
  ]
}

情境 4:取得特定報告詳情

API Request

GET /api/sessions/123/report

Response

{
  "session_id": 123,
  "client_info": {
    "name": "小明",
    "age": 25,
    "gender": "男"
  },
  "session_summary": {
    "content": "個案表達想要轉換跑道的想法...",
    "self_evaluation": "初次晤談,建立良好關係"
  },
  "conceptualization": "【主訴問題】\n個案表達對目前軟體工程師工作感到倦怠...",
  "main_concerns": ["職涯轉換", "工作生活平衡"],
  "counseling_goals": ["釐清職涯方向", "改善工作壓力"],
  "techniques": ["同理心回應", "生涯卡探索"],
  "theories": [
    {
      "text": "Super 的生涯發展理論指出...",
      "document": "生涯發展理論",
      "score": 0.85
    }
  ],
  "dialogue_excerpts": [
    {"speaker": "speaker1", "order": 1, "text": "聽起來你對目前的工作感到一些困擾?"},
    {"speaker": "speaker2", "order": 2, "text": "是的,我覺得每天寫程式很累"}
  ]
}

資料庫設計

1. clients - 個案基本資料

欄位 型別 說明
id SERIAL Primary Key
client_code VARCHAR(100) 唯一識別碼,格式:{counselor_code}_{alias}
counselor_code VARCHAR(100) 所屬諮詢師
name VARCHAR(200) 化名
gender VARCHAR(50) 性別
age INTEGER 年齡
occupation VARCHAR(200) 職業
education VARCHAR(200) 學歷
location VARCHAR(200) 居住地
economic_status VARCHAR(200) 經濟狀況
family_relations TEXT 家庭關係
status VARCHAR(50) active/inactive/completed
first_session_date TIMESTAMP 首次晤談時間
last_session_date TIMESTAMP 最後晤談時間
total_sessions INTEGER 總晤談次數
notes TEXT 諮詢師備註
tags TEXT[] 標籤
created_at TIMESTAMP 建檔時間
updated_at TIMESTAMP 更新時間

2. sessions - 晤談紀錄

欄位 型別 說明
id SERIAL Primary Key
client_id INTEGER FK → clients.id
counselor_code VARCHAR(100) 諮詢師代碼
session_number INTEGER 第幾次晤談
session_date TIMESTAMP 晤談日期
duration_minutes INTEGER 時長(分鐘)
session_type VARCHAR(50) individual/group/online/offline
transcript TEXT 原始逐字稿
num_participants INTEGER 會談人數(default 2)
status VARCHAR(50) draft/completed/reviewed
created_at TIMESTAMP 建立時間
updated_at TIMESTAMP 更新時間

Unique Constraint: (client_id, session_number)

3. session_reports - AI 生成報告

欄位 型別 說明
id SERIAL Primary Key
session_id INTEGER FK → sessions.id
client_info JSONB 個案基本資訊
session_summary JSONB 晤談摘要
conceptualization TEXT 概念化分析(完整文字)
main_concerns TEXT[] 主訴問題列表
counseling_goals TEXT[] 諮詢目標列表
techniques TEXT[] 使用技巧列表
dialogue_excerpts JSONB 對話節錄
theories JSONB 相關理論文獻
client_progress TEXT 個案進展(未來功能)
next_steps TEXT 後續建議
follow_up_date TIMESTAMP 回訪日期(未來功能)
supervision_notes TEXT 督導討論內容(未來功能)
generated_at TIMESTAMP 報告生成時間
updated_at TIMESTAMP 更新時間

4. counselor_agents - 諮詢師客製化 AI

欄位 型別 說明
id SERIAL Primary Key
counselor_code VARCHAR(100) 諮詢師代碼(Unique)
agent_name VARCHAR(200) Agent 名稱
selected_document_ids INTEGER[] 選擇的文檔 IDs
selected_tags TEXT[] 或按標籤篩選(未來)
system_prompt TEXT 客製化 prompt(未來)
temperature FLOAT 溫度參數(未來)
top_k INTEGER RAG top_k(default 5)
similarity_threshold FLOAT 相似度門檻(default 0.5)
specialties TEXT[] 專長領域(未來)
approach TEXT 諮詢取向(未來)
total_sessions INTEGER 使用次數
last_used_at TIMESTAMP 最後使用時間
created_at TIMESTAMP 建立時間
updated_at TIMESTAMP 更新時間

5. client_progress_timeline - 個案進展時間軸(未來功能)

欄位 型別 說明
id SERIAL Primary Key
client_id INTEGER FK → clients.id
session_id INTEGER FK → sessions.id
stage VARCHAR(100) 階段(探索期/工作期/結束期)
progress_score INTEGER 進展評分 1-10
mood_score INTEGER 情緒狀態 1-10
milestones TEXT[] 里程碑
breakthroughs TEXT 突破點
challenges TEXT 挑戰
counselor_notes TEXT 諮詢師觀察
created_at TIMESTAMP 建立時間

API 設計規格

核心 API(Phase 1 - 優先實作)

1. 生成並儲存報告

POST /api/sessions/generate-report
Content-Type: application/json

Request Body:
{
  "counselor_code": "CO_張老師",
  "client": {
    "code": "CL_小明",
    "name": "小明",              // 首次必填
    "age": 25,                    // 首次必填
    "gender": "男",               // 首次必填
    "occupation": "軟體工程師",   // 選填
    "education": "大學",          // 選填
    "location": "台北",           // 選填
    "economic_status": "穩定",    // 選填
    "family_relations": "已婚"    // 選填
  },
  "session": {
    "transcript": "Co: 你好...",
    "num_participants": 2,
    "session_date": "2025-01-15T10:00:00",
    "duration_minutes": 60        // 選填
  }
}

Response: SSE Stream
Content-Type: text/event-stream

data: {"step": 1, "status": "processing", "message": "正在分析逐字稿結構..."}
data: {"step": 1, "status": "completed", "message": "逐字稿分析完成", "data": {...}}
data: {"step": 2, "status": "processing", "message": "正在識別關鍵議題..."}
...
data: {"step": 5, "status": "completed", "message": "個案報告生成完成", "data": {"report": {...}, "session_id": 123}}
data: {"step": 6, "status": "completed", "message": "全部完成!"}

處理邏輯

  1. 檢查 client_code 是否存在於 clients
  2. 若不存在 → INSERT 新個案
  3. 若存在 → 取得 client_id
  4. INSERT 新的 session 紀錄(自動計算 session_number
  5. 呼叫 AI 生成報告(整合現有的 generate_report_stream
  6. INSERT session_reports 紀錄
  7. UPDATE clientstotal_sessions, last_session_date
  8. 回傳完整報告 + session_id

2. 查詢諮詢師的個案列表

GET /api/counselors/{counselor_code}/clients
Query Parameters:
  - status: active/inactive/completed (選填)
  - limit: 20 (選填)
  - offset: 0 (選填)

Response:
{
  "total": 15,
  "clients": [
    {
      "code": "CL_小明",
      "name": "小明",
      "age": 25,
      "gender": "男",
      "total_sessions": 3,
      "last_session_date": "2025-01-22T10:00:00",
      "status": "active",
      "tags": ["職涯困擾", "焦慮"]
    },
    ...
  ]
}

3. 查詢個案歷程

GET /api/clients/{counselor_code}/{client_code}/history

Response:
{
  "client": {
    "code": "CL_小明",
    "name": "小明",
    "age": 25,
    "gender": "男",
    "occupation": "軟體工程師",
    "total_sessions": 3,
    "first_session_date": "2025-01-15T10:00:00",
    "last_session_date": "2025-01-22T10:00:00",
    "status": "active"
  },
  "sessions": [
    {
      "session_id": 123,
      "session_number": 1,
      "session_date": "2025-01-15T10:00:00",
      "duration_minutes": 60,
      "summary": "初次晤談,探索職涯困擾",
      "main_concerns": ["職涯轉換", "工作壓力"]
    },
    ...
  ]
}

4. 取得特定報告

GET /api/sessions/{session_id}/report

Response:
{
  "session_id": 123,
  "session_number": 1,
  "session_date": "2025-01-15T10:00:00",
  "client_info": { ... },
  "session_summary": { ... },
  "conceptualization": "...",
  "main_concerns": [...],
  "counseling_goals": [...],
  "techniques": [...],
  "theories": [...],
  "dialogue_excerpts": [...]
}

Agent 設定 API(Phase 1 - 簡化版)

5. 設定諮詢師的 Agent

POST /api/agents/config

Request:
{
  "counselor_code": "CO_張老師",
  "agent_name": "職涯諮詢專用 AI",
  "selected_document_ids": [1, 3, 5, 7]  // 選擇特定文檔
}

Response:
{
  "success": true,
  "message": "Agent 設定已更新",
  "agent": {
    "id": 1,
    "counselor_code": "CO_張老師",
    "agent_name": "職涯諮詢專用 AI",
    "selected_document_ids": [1, 3, 5, 7],
    "top_k": 5,
    "similarity_threshold": 0.5
  }
}

6. 取得諮詢師的 Agent 設定

GET /api/agents/{counselor_code}/config

Response:
{
  "agent": {
    "id": 1,
    "counselor_code": "CO_張老師",
    "agent_name": "職涯諮詢專用 AI",
    "selected_document_ids": [1, 3, 5, 7],
    "top_k": 5,
    "similarity_threshold": 0.5,
    "total_sessions": 15,
    "last_used_at": "2025-01-22T10:00:00"
  }
}

身份識別策略

諮詢師識別(Counselor Code)

格式建議

CO_{姓名/暱稱}
例如:CO_張老師、CO_李心理師

特性

  • 不需要密碼(暫時)
  • 由 iOS 端儲存並自動帶入
  • 未來可考慮裝置綁定或簡易 PIN 碼

個案識別(Client Code)

格式建議

CL_{化名/代號}
或
{counselor_code}_CL_{化名}

例如:
- CL_小明
- CO_張老師_CL_小明

特性

  • 由諮詢師在 iOS 端輸入
  • 確保在該諮詢師下唯一即可
  • 不需要跨諮詢師唯一(未來若需轉介再調整)

資料儲存策略

✅ 儲存的資料

  1. 諮詢師基本資料

    • counselor_code, name
  2. 個案基本資料

    • 所有 clients 表欄位
  3. 晤談紀錄

    • 逐字稿(transcript
    • 晤談時間、次數、時長
  4. AI 生成報告

    • 完整的結構化報告內容
  5. Agent 設定

    • 文檔選擇設定

❌ 不儲存的資料

  1. 錄音檔

    • 由 iOS 本地儲存
    • 未來若需要可考慮 S3/Supabase Storage
  2. 諮詢師個人設定(暫時)

    • system_prompt, temperature 等進階設定
    • Phase 2 再實作

RAG 整合策略

現有 RAG 架構

documents → chunks → embeddings

Agent 如何選擇文件

方式 1:選擇特定文檔(Phase 1 實作)

# 從 counselor_agents 取得 selected_document_ids
agent = get_counselor_agent(counselor_code)
selected_doc_ids = agent.selected_document_ids  # [1, 3, 5, 7]

# RAG 查詢時過濾
WHERE c.doc_id IN :selected_doc_ids

方式 2:標籤篩選(未來)

# 文檔加上標籤
documents.tags = ["職涯諮詢", "生涯規劃"]

# Agent 設定選擇標籤
agent.selected_tags = ["職涯諮詢"]

# 查詢時過濾
WHERE d.tags && :selected_tags

Phase 1 實作優先順序

今天完成(核心功能)

  1. ✅ 資料庫 Migration(已完成 SQL)
  2. ⏳ 執行 Migration
  3. ⏳ 實作 POST /api/sessions/generate-report
    • 整合現有的 generate_report_stream
    • 加入個案創建/更新邏輯
    • 加入 Session 儲存邏輯
    • 加入 Report 儲存邏輯
  4. ⏳ 實作簡易 Agent 設定 API
    • POST /api/agents/config
    • GET /api/agents/{counselor_code}/config
  5. ⏳ 測試 iOS 串接

明天完成(查詢功能)

  1. GET /api/counselors/{counselor_code}/clients
  2. GET /api/clients/{counselor_code}/{client_code}/history
  3. GET /api/sessions/{session_id}/report

後天+(Web 管理介面)

  1. 諮詢師登入頁面(輸入 counselor_code)
  2. 個案列表頁面
  3. 個案詳情 + 歷程時間軸頁面
  4. Agent 設定頁面(選擇文檔)

未來擴充功能(Phase 2+)

進階 CRM 功能

  • 回訪提醒(follow_up_date
  • 個案進度評分(client_progress_timeline
  • 階段分析(探索期/工作期/結束期)

協作功能

  • 跨諮詢師轉介
  • 督導討論功能(supervision_notes
  • 團隊協作

進階 Agent 功能

  • 自訂 system_prompt
  • 調整 temperature
  • 專長領域標籤

資料分析

  • 諮詢師統計儀表板
  • 個案改善率分析
  • 熱門議題追蹤

技術棧

  • Backend: FastAPI + Python 3.10+
  • Database: Supabase (PostgreSQL + pgvector)
  • AI: OpenAI GPT-4o-mini + text-embedding-3-small
  • Frontend: Next.js 14 + React + TypeScript
  • iOS: Swift (由團隊其他成員開發)

資料隱私與安全

現階段(MVP)

  • 不儲存錄音檔
  • 逐字稿和報告儲存在 Supabase(已加密傳輸)
  • 簡易代碼識別(counselor_code)

未來規劃

  • 端對端加密(E2EE)
  • 符合 GDPR/HIPAA 規範
  • 資料匿名化選項
  • 定期資料備份
  • 資料刪除權(Right to be forgotten)

API 錯誤處理

標準錯誤格式

{
  "error": {
    "code": "CLIENT_NOT_FOUND",
    "message": "找不到該個案資料",
    "details": {
      "client_code": "CL_小明",
      "counselor_code": "CO_張老師"
    }
  }
}

常見錯誤碼

  • CLIENT_NOT_FOUND: 個案不存在
  • COUNSELOR_NOT_FOUND: 諮詢師不存在
  • SESSION_NOT_FOUND: 晤談紀錄不存在
  • INVALID_TRANSCRIPT: 逐字稿格式錯誤
  • AGENT_NOT_CONFIGURED: Agent 未設定
  • DOCUMENT_NOT_FOUND: 文檔不存在

測試策略

單元測試

  • 個案創建/更新邏輯
  • Session 編號自動累加
  • Report 資料解析

整合測試

  • iOS → API → Database 完整流程
  • RAG 文檔過濾功能
  • SSE 串流測試

E2E 測試

  • 首次晤談完整流程
  • 後續晤談流程
  • 歷程查詢功能

部署架構(未來)

┌──────────┐
│ iOS App  │
└────┬─────┘
     │
     ↓
┌──────────────────┐
│ Cloudflare / CDN │
└────┬─────────────┘
     │
     ↓
┌──────────────────┐
│ FastAPI Server   │  (Railway / Render / AWS)
│ (Load Balanced)  │
└────┬─────────────┘
     │
     ↓
┌──────────────────┐
│ Supabase         │
│ (PostgreSQL +    │
│  pgvector)       │
└──────────────────┘

變更歷史

日期 版本 變更內容
2025-01-XX 0.1 初版架構設計

文件作者: Claude + Young 最後更新: 2025-01-XX 狀態: 規劃中 → 準備實作