---
name: linebot-dev
description: LINE Bot 智慧股票聊天助手的開發技能指南，定義 AI Agent 在開發、除錯與擴展此專案時應遵守的規範，涵蓋環境設定、專案結構、模組職責、開發流程與常見問題處理。

---

## 一、專案概覽

| 欄位     | 說明                                         |
| -------- | -------------------------------------------- |
| 專案名稱 | LINE Bot 智慧股票聊天助手                     |
| 技術棧   | Python 3.11+ / FastAPI / aiosqlite / OpenAI SDK |
| AI 模型  | 小米 MiMo v2 Flash（OpenAI 相容 API）         |
| 股價資料 | twstock（台灣證交所公開資料）                  |
| 關聯文件 | PRD（`.agents/skills/prd/skill.md`）          |
|          | ADD（`.agents/skills/architecture/skill.md`） |

---

## 二、環境設定（Environment Setup）

### 2.1 建立虛擬環境

```bash
# 建立虛擬環境
python -m venv .venv

# 啟動虛擬環境
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate

# 安裝依賴
pip install -r requirements.txt
```

### 2.2 環境變數

複製 `.env.example` 為 `.env` 並填入實際金鑰：

```bash
cp .env.example .env
```

必要的環境變數：

| 變數名稱                  | 必填 | 說明                          |
| ------------------------- | ---- | ----------------------------- |
| `LINE_CHANNEL_SECRET`     | ✅   | LINE Bot Channel Secret       |
| `LINE_CHANNEL_ACCESS_TOKEN` | ✅ | LINE Bot Channel Access Token |
| `MIMO_API_KEY`            | ✅   | 小米 MiMo API Key             |
| `MIMO_BASE_URL`           | ❌   | MiMo API 端點（預設已設定）    |
| `MIMO_MODEL`              | ❌   | 模型名稱（預設 `mimo-v2-flash`）|
| `CONTEXT_WINDOW_SIZE`     | ❌   | 對話上下文輪數（預設 10）      |
| `CONVERSATION_EXPIRE_DAYS`| ❌   | 對話紀錄保留天數（預設 30）    |
| `DATABASE_PATH`           | ❌   | SQLite 路徑（預設 `data/conversations.db`）|

> **安全規則**：`.env` 與 `data/*.db` 已納入 `.gitignore`，**絕對不可提交至版本控制**。

### 2.3 啟動開發伺服器

```bash
uvicorn app.main:app --reload --port 8000
```

> 開發時需搭配 ngrok 等工具暴露 HTTPS 端點，供 LINE Webhook 使用：
> ```bash
> ngrok http 8000
> ```
> 將產生的 HTTPS URL 設定至 LINE Developers Console 的 Webhook URL（`https://xxx.ngrok.io/callback`）。

---

## 三、專案目錄結構與模組職責

```
午安/
├── app/
│   ├── __init__.py          # 套件初始化
│   ├── main.py              # FastAPI 入口、lifespan 管理
│   ├── config.py            # pydantic-settings 環境變數
│   ├── database.py          # SQLite CRUD（aiosqlite）
│   ├── webhook.py           # LINE Webhook handler
│   ├── router.py            # 訊息意圖路由
│   ├── services/
│   │   ├── ai_chat.py       # MiMo AI 聊天 Service
│   │   ├── stock.py         # twstock 股價查詢
│   │   └── line_reply.py    # LINE 回覆封裝
│   └── utils/
│       └── formatters.py    # 訊息格式化模板
├── data/                    # SQLite 資料庫（不進版控）
├── .env.example             # 環境變數範本
├── .gitignore
└── requirements.txt
```

### 模組依賴關係

```
webhook.py → router.py → ai_chat.py → database.py
                        → stock.py
                        → database.py（清除對話）
           → line_reply.py
           → formatters.py
```

> **核心原則**：模組之間單向依賴，`database.py` 和 `formatters.py` 是最底層，不依賴其他 app 模組（除 `config.py`）。

---

## 四、請求處理流程

```
LINE 使用者 → LINE Platform → POST /callback
                                  ↓
                          webhook.py（驗證簽章、回 200）
                                  ↓ asyncio.create_task()
                          router.py（意圖判斷）
                           ├── /help → formatters.py
                           ├── /reset → database.py
                           ├── 股價查詢 → stock.py
                           └── 一般聊天 → ai_chat.py → database.py + MiMo API
                                  ↓
                          line_reply.py（回覆使用者）
```

**重要**：Webhook handler 立即回傳 HTTP 200，訊息處理以 `asyncio.create_task()` 在背景執行。這是 LINE Platform 的要求，不可阻塞 Webhook 回應。

---

## 五、開發規範（Coding Standards）

### 5.1 程式碼風格

- **語言版本**：Python 3.11+，使用 type hints
- **非同步**：所有 I/O 操作使用 `async/await`
- **命名**：函式與變數用 `snake_case`，類別用 `PascalCase`
- **Docstring**：所有公開函式必須有 docstring，說明 Args 與 Returns
- **日誌**：使用 `logging` 模組，不使用 `print()`

### 5.2 錯誤處理原則

每個外部呼叫（MiMo API、twstock、LINE API）都必須包在 try/except 中，並：
1. 記錄錯誤日誌（`logger.exception()`）
2. 回傳友善的降級訊息給使用者
3. **不讓例外傳播到 Webhook handler 導致 500 錯誤**

```python
# ✅ 正確
try:
    result = await external_api_call()
except Exception:
    logger.exception("外部 API 呼叫失敗")
    return "系統忙碌中，請稍後再試 ⏳"

# ❌ 錯誤：例外會導致無回覆
result = await external_api_call()  # 爆了就沒有回覆了
```

### 5.3 資料庫操作規範

- 每次操作使用 `async with aiosqlite.connect()` 取得連線，操作完立即釋放
- 寫入操作必須呼叫 `await db.commit()`
- 不要在模組層級持有長期連線（避免 SQLite 鎖定問題）
- 所有 SQL 使用參數化查詢（`?` 佔位符），防止 SQL Injection

### 5.4 新增功能的擴展方式

如要新增工具型功能（如匯率查詢），遵循以下步驟：

1. 在 `app/services/` 新增 Service 模組（如 `currency.py`）
2. 在 `app/utils/formatters.py` 新增對應的格式化函式
3. 在 `app/router.py` 新增正則匹配與路由邏輯
4. **不需要修改** `webhook.py`、`main.py`、`database.py`

---

## 六、訊息路由規則

router.py 的意圖判斷優先順序（由上而下，先匹配先處理）：

| 順序 | 正則模式                                    | 處理對象     |
| ---- | ------------------------------------------ | ------------ |
| 1    | `^(幫助\|help\|/help)$`                    | 幫助訊息     |
| 2    | `^(清除對話\|重置\|/reset)$`                | 清除對話紀錄 |
| 3    | `^(股價\|查詢\|stock)\s+(.+)$`             | 股價查詢     |
| 4    | 其餘所有文字                                | AI 聊天      |

> **注意**：所有匹配皆 `re.IGNORECASE`。新增指令時確保不與現有模式衝突。

---

## 七、常見開發情境

### 7.1 修改 System Prompt

位置：`app/services/ai_chat.py` 的 `SYSTEM_PROMPT` 常數。
修改後不需重啟（`--reload` 會自動偵測），但建議實測對話品質。

### 7.2 調整對話上下文長度

修改 `.env` 中的 `CONTEXT_WINDOW_SIZE`（預設 10 輪）。
增加會消耗更多 token，減少會降低對話連貫性。

### 7.3 本地測試 Webhook

不需部署即可測試核心邏輯：

```python
# 直接測試 router
import asyncio
from app.router import route_message

result = asyncio.run(route_message("test_user", "幫助"))
print(result)
```

### 7.4 查看 / 清理資料庫

```python
import asyncio
from app import database

async def main():
    await database.init_db()
    
    # 查看某使用者的對話
    history = await database.get_history("USER_ID", limit=20)
    for h in history:
        print(f"[{h['role']}] {h['content']}")
    
    # 清理過期資料
    cleaned = await database.cleanup_expired(days=30)
    print(f"清理了 {cleaned} 筆")

asyncio.run(main())
```

---

## 八、部署注意事項

### 8.1 啟動指令

```bash
uvicorn app.main:app --host 0.0.0.0 --port $PORT
```

### 8.2 部署平台設定

- **環境變數**：在平台 Dashboard 設定，不放入版本控制
- **SQLite 持久化**：確保部署平台支援持久化磁碟（如 Render 的 Persistent Disk）
- **健康檢查**：設定 `GET /health` 作為 health check URL
- **冷啟動**：免費方案可能有 30 秒冷啟動延遲，可設定 keep-alive 排程

### 8.3 LINE Webhook 設定

1. 前往 [LINE Developers Console](https://developers.line.biz/)
2. 設定 Webhook URL：`https://your-domain/callback`
3. 開啟 **Use webhook**
4. 關閉 **Auto-reply messages**（避免自動回覆干擾 Bot）

---

## 九、疑難排解（Troubleshooting）

| 問題                               | 原因                          | 解決方式                                    |
| ---------------------------------- | ----------------------------- | ------------------------------------------- |
| Webhook 回傳 400                   | LINE Signature 驗證失敗        | 確認 `LINE_CHANNEL_SECRET` 正確             |
| Bot 收到訊息但不回覆               | `create_task` 中發生未捕獲例外 | 檢查日誌，確認 try/except 有包住外部呼叫    |
| AI 回覆「暫時無法回應」            | MiMo API 呼叫失敗或逾時       | 檢查 `MIMO_API_KEY` 與網路連線              |
| 股價查詢失敗                       | twstock 無法連線證交所         | 確認網路；非交易時段可能無即時資料          |
| 資料庫錯誤 `database is locked`    | 多進程同時寫入 SQLite          | 確保單進程部署；或遷移至 PostgreSQL          |
| Windows 終端機 emoji 亂碼          | `cp950` 編碼不支援 emoji       | 設定 `chcp 65001` 或使用 Windows Terminal    |

---

## 十、開發自我檢查清單

完成功能開發後，提交前確認：

- [ ] 所有外部呼叫有 try/except 與降級回覆
- [ ] 新增的函式有 docstring 與 type hints
- [ ] 使用 `logger` 記錄關鍵操作（不用 `print`）
- [ ] 資料庫操作使用參數化查詢
- [ ] `.env` 中的新變數已同步更新至 `.env.example`
- [ ] `.gitignore` 已包含敏感檔案
- [ ] 本地手動測試通過（router、service 層級）
- [ ] Commit 訊息符合 Conventional Commits 格式
