📚 Claude AI 整合系列
- 架構選擇:Claude CLI 整合與對話設計
- Token 管理:估算、警告與自動壓縮
- System Prompt 設計:打造專屬 AI 助手人格 ← 目前閱讀
📖 延伸閱讀:Git 入門:版本控制基礎指令
這篇文章要解決什麼問題?
你有沒有覺得,直接用 Claude/ChatGPT 的回答有點「通用」?比如:
- 回答太冗長,公司習慣簡潔風格
- 用語不符合公司慣例(繁體/簡體、專有名詞)
- 沒有考慮公司特定的知識背景
- 對某些問題應該拒絕回答(商業機密相關)
System Prompt 就是 AI 的「人設」,決定了它的:
- 個性和語氣
- 專長領域
- 回答風格
- 行為限制
業務:「客戶問 AI 助手我們跟競爭對手的差別,它竟然說對手也不錯?」
老闆:「這怎麼行!而且回答風格也太冗長,我們公司習慣簡潔有力。」
後端工程師:「這是因為 AI 預設是中立的。我們需要設定 System Prompt,就像 AI 的員工守則。」
老闆:「可以規定它不能說什麼嗎?」
後端工程師:「當然,可以設定:回答要簡潔、用公司專有名詞、不評論競爭對手、遇到敏感問題要導向業務。」
業務:「這樣就不怕 AI 亂講話了!」
技術概念
System Prompt vs User Prompt
┌──────────────────────────────────────────────────┐
│ Claude │
│ ┌────────────────────────────────────────────┐ │
│ │ System Prompt(設定人格) │ │
│ │ "你是 ChingTech AI 助手,擎添工業的..." │ │
│ └────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────┐ │
│ │ User Prompt(使用者訊息) │ │
│ │ "幫我寫一份報告" │ │
│ └────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────┐ │
│ │ 回應 │ │
│ │ (根據 System Prompt 設定的風格回答) │ │
│ └────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
System Prompt 是「常駐設定」,每次對話都會帶入;User Prompt 是「單次訊息」。
Prompt 設計的四大要素
好的 System Prompt 包含:
| 要素 | 說明 | 範例 |
|---|---|---|
| 角色定義 | AI 是誰?為誰服務? | “你是 ChingTech AI 程式碼助手” |
| 個性特徵 | 說話風格、態度 | “專業友善、回答簡潔” |
| 能力範圍 | 能做什麼、擅長什麼 | “撰寫程式碼、除錯、架構設計” |
| 行為限制 | 不能做什麼、注意事項 | “不提供可能有害的內容” |
跟著做:Step by Step
Step 1:建立 Prompt 檔案結構
data/
└── prompts/
├── default.md # 預設通用助手
├── code-assistant.md # 程式碼助手
├── pm-assistant.md # 專案管理助手
└── summarizer.md # 對話壓縮(內部使用)
Step 2:設計預設助手 Prompt
<!-- data/prompts/default.md -->
# 預設助手
你是 ChingTech AI 助手,擎添工業的智慧工作中樞助理。
## 你的個性
- 專業且友善
- 回答簡潔明瞭
- 樂於協助解決各種問題
## 對話語氣
- 使用繁體中文
- 保持專業但不失親切
- 適時使用條列式整理重點
## 能力範圍
- 回答一般問題
- 協助文件撰寫
- 提供建議和想法
- 解釋概念和流程
## 注意事項
- 如果不確定答案,請誠實說明
- 避免提供可能有害或不當的內容
- 保持客觀中立的立場
Step 3:設計程式碼助手 Prompt
<!-- data/prompts/code-assistant.md -->
# 程式碼助手
你是 ChingTech AI 程式碼助手,專門協助軟體開發相關任務。
## 你的個性
- 技術導向,注重實作細節
- 提供可執行的程式碼範例
- 解釋程式碼邏輯和最佳實踐
## 對話語氣
- 使用繁體中文解釋
- 程式碼註解可使用英文
- 技術術語保持一致性
## 能力範圍
- 撰寫和審查程式碼
- 除錯和問題排解
- 解釋演算法和資料結構
- 推薦開發工具和框架
- 協助設計系統架構
## 程式碼風格
- 提供完整可執行的範例
- 加入適當的錯誤處理
- 遵循該語言的慣例和最佳實踐
- 必要時加入註解說明
## 注意事項
- 優先考慮程式碼的可讀性和維護性
- 提醒潛在的安全風險
- 建議適當的測試策略
Step 4:設計專案管理助手 Prompt
<!-- data/prompts/pm-assistant.md -->
# 專案管理助手
你是 ChingTech AI 專案管理助手,專門協助專案規劃和管理任務。
## 你的個性
- 組織能力強,注重結構化
- 善於拆解複雜任務
- 關注時程和風險管理
## 對話語氣
- 使用繁體中文
- 條理分明,善用列表
- 提供具體可行的建議
## 能力範圍
- 協助制定專案計畫
- 拆解任務和估算工時
- 識別風險和依賴關係
- 追蹤進度和里程碑
- 協助撰寫需求文件
- 提供敏捷/Scrum 實踐建議
## 輸出格式偏好
- 使用表格整理資訊
- 提供清晰的任務清單
- 標示優先順序和負責人
- 包含時間軸和里程碑
## 注意事項
- 提醒潛在的風險和阻礙
- 建議適當的溝通策略
- 關注資源配置和瓶頸
- 鼓勵定期回顧和調整
Step 5:後端動態載入 Prompt
# claude_agent.py
from pathlib import Path
# Prompts 目錄路徑
PROMPTS_DIR = Path("data/prompts")
def get_prompt_content(prompt_name: str) -> str | None:
"""讀取 prompt 檔案內容
Args:
prompt_name: prompt 名稱(不含 .md)
Returns:
prompt 內容,找不到則回傳 None
"""
prompt_file = PROMPTS_DIR / f"{prompt_name}.md"
if not prompt_file.exists():
return None
return prompt_file.read_text(encoding="utf-8")
def list_available_prompts() -> list[dict]:
"""列出所有可用的 prompts
Returns:
[{"name": "default", "display_name": "預設助手", "description": "..."}]
"""
prompts = []
for file in PROMPTS_DIR.glob("*.md"):
# 跳過內部使用的 prompts
if file.stem == "summarizer":
continue
content = file.read_text(encoding="utf-8")
# 從第一行取得顯示名稱
first_line = content.split("\n")[0]
display_name = first_line.replace("#", "").strip()
# 取得描述(第一個段落)
lines = content.split("\n")
description = ""
for line in lines[1:]:
if line.strip() and not line.startswith("#"):
description = line.strip()
break
prompts.append({
"name": file.stem,
"display_name": display_name,
"description": description
})
return prompts
Step 6:API 路由
# api/ai_router.py
from fastapi import APIRouter
from services.claude_agent import list_available_prompts
router = APIRouter(prefix="/api/ai", tags=["AI"])
@router.get("/prompts")
async def get_prompts():
"""取得所有可用的 prompt 列表"""
return list_available_prompts()
Step 7:前端選擇器
// ai-assistant.js
let availablePrompts = [];
/**
* 載入可用的 prompts
*/
async function loadPrompts() {
try {
availablePrompts = await APIClient.getPrompts();
} catch (e) {
console.error('Failed to load prompts:', e);
availablePrompts = [
{ name: 'default', display_name: '預設助手', description: '' }
];
}
}
/**
* 建立 prompt 選擇器 UI
*/
function buildPromptSelector() {
return `
<div class="ai-prompt-selector">
<label>助手:</label>
<select class="ai-prompt-select input">
${availablePrompts.map(p =>
`<option value="${p.name}">${p.display_name}</option>`
).join('')}
</select>
</div>
`;
}
/**
* 綁定 prompt 選擇事件
*/
function bindPromptSelector() {
const select = document.querySelector('.ai-prompt-select');
if (select) {
select.addEventListener('change', async (e) => {
const chat = getChatById(currentChatId);
if (chat) {
chat.prompt_name = e.target.value;
// 更新到伺服器
await APIClient.updateChat(chat.id, {
prompt_name: e.target.value
});
}
});
}
}
Step 8:整合到 Claude CLI 呼叫
# claude_agent.py
async def call_claude(
prompt: str,
model: str = "sonnet",
history: list[dict] | None = None,
system_prompt: str | None = None, # 可以傳入內容或名稱
timeout: int = 120,
) -> ClaudeResponse:
"""非同步呼叫 Claude CLI"""
# 如果傳入的是 prompt 名稱,讀取檔案內容
if system_prompt and not system_prompt.startswith("#"):
loaded_prompt = get_prompt_content(system_prompt)
if loaded_prompt:
system_prompt = loaded_prompt
# 建立命令
cmd = ["claude", "-p", full_prompt, "--model", model]
if system_prompt:
cmd.extend(["--system-prompt", system_prompt])
# ... 其餘程式碼 ...
進階技巧與踩坑紀錄
1. Prompt 結構化模板
建議所有 Prompt 遵循統一結構:
# [角色名稱]
[一句話描述]
## 你的個性
- 特點 1
- 特點 2
## 對話語氣
- 風格指引
## 能力範圍
- 能做什麼
## [領域特定區塊]
- 根據角色需要
## 注意事項
- 限制和提醒
2. Few-shot Learning:提供範例
讓 AI 看範例學習期望的輸出格式:
## 輸出範例
使用者:我想寫一個 Python 函數來計算費波那契數列
助手:好的,這是一個基本的費波那契函數:
\`\`\`python
def fibonacci(n):
"""計算第 n 個費波那契數"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
\`\`\`
**說明:**
- 使用遞迴方式實作
- 時間複雜度 O(2^n)
- 建議 n > 30 改用迭代或記憶化
3. 負面指令:明確說「不要」
## 注意事項
### 不要做的事
- 不要編造不確定的資訊
- 不要提供可能有害的程式碼(如惡意腳本)
- 不要洩露公司內部系統細節
- 不要給出法律、醫療、財務建議
### 遇到這些情況
- 如果被要求提供敏感資訊,禮貌地說明無法協助
- 如果問題超出能力範圍,建議尋求專業人員協助
4. 動態 Prompt 組合
可以根據上下文動態加入額外指令:
async def get_dynamic_system_prompt(chat_id: str, base_prompt_name: str) -> str:
"""根據對話上下文動態組合 prompt"""
# 讀取基礎 prompt
base_prompt = get_prompt_content(base_prompt_name)
# 如果有對話摘要,加入上下文
chat = await get_chat(chat_id)
if chat and chat.get("summary"):
base_prompt += f"\n\n## 對話背景\n{chat['summary']}"
# 如果是特定時段,加入額外提醒
now = datetime.now()
if now.hour >= 22 or now.hour < 6:
base_prompt += "\n\n(提醒:現在是深夜/凌晨,建議使用者注意休息)"
return base_prompt
5. Prompt 版本管理
用 Git 管理 Prompt 檔案,追蹤改動:
data/prompts/
├── default.md
├── code-assistant.md
└── CHANGELOG.md # 記錄改動
# CHANGELOG.md 內容:
# ## 2024-01-15
# - code-assistant.md: 新增安全風險提醒
#
# ## 2024-01-10
# - default.md: 調整語氣更友善
6. A/B Testing
可以準備多個版本測試效果:
# 隨機選擇 prompt 版本(用於 A/B 測試)
import random
def get_prompt_for_ab_test(base_name: str) -> str:
versions = [
f"{base_name}",
f"{base_name}_v2", # 測試版本
]
selected = random.choice(versions)
return get_prompt_content(selected) or get_prompt_content(base_name)
小結
這篇我們完成了:
- Prompt 結構設計:角色、個性、能力、限制
- 多角色切換:不同助手有不同專長
- 動態載入:後端讀取 .md 檔案
- 前端整合:選擇器讓使用者切換
System Prompt 設計的黃金法則:
1. 明確角色:「你是誰」比「你要做什麼」更重要
2. 正負並行:既說「要做什麼」也說「不要做什麼」
3. 提供範例:Few-shot 比長篇說明更有效
4. 持續迭代:根據實際回答效果調整
到這裡,Claude AI 整合系列就完成了!你現在有了:
- 對話歷史管理
- Token 估算和壓縮
- 可客製化的 AI 人格
完整程式碼
Prompt 載入模組
"""Prompt 管理模組"""
from pathlib import Path
PROMPTS_DIR = Path("data/prompts")
def get_prompt_content(prompt_name: str) -> str | None:
"""讀取 prompt 檔案內容"""
prompt_file = PROMPTS_DIR / f"{prompt_name}.md"
if not prompt_file.exists():
return None
return prompt_file.read_text(encoding="utf-8")
def list_available_prompts() -> list[dict]:
"""列出所有可用的 prompts"""
prompts = []
for file in PROMPTS_DIR.glob("*.md"):
# 跳過內部使用的
if file.stem == "summarizer":
continue
content = file.read_text(encoding="utf-8")
# 解析標題和描述
lines = content.split("\n")
display_name = lines[0].replace("#", "").strip() if lines else file.stem
description = ""
for line in lines[1:]:
stripped = line.strip()
if stripped and not stripped.startswith("#"):
description = stripped
break
prompts.append({
"name": file.stem,
"display_name": display_name,
"description": description
})
return prompts
def validate_prompt(content: str) -> bool:
"""驗證 prompt 格式是否正確"""
required_sections = ["你的個性", "能力範圍", "注意事項"]
for section in required_sections:
if section not in content:
return False
return True
Prompt 範本:通用助手
# 通用助手
你是 [公司名稱] AI 助手,[公司描述] 的智慧工作助理。
## 你的個性
- 專業且友善
- 回答簡潔明瞭
- 樂於協助解決各種問題
## 對話語氣
- 使用繁體中文
- 保持專業但不失親切
- 適時使用條列式整理重點
## 能力範圍
- 回答一般問題
- 協助文件撰寫
- 提供建議和想法
- 解釋概念和流程
## 注意事項
- 如果不確定答案,請誠實說明
- 避免提供可能有害或不當的內容
- 保持客觀中立的立場
- 不提供法律、醫療、財務建議
Prompt 範本:專業領域助手
# [領域] 助手
你是 [公司名稱] AI [領域]助手,專門協助 [領域] 相關任務。
## 你的個性
- [領域特有的個性特點]
- [領域特有的工作風格]
## 對話語氣
- 使用繁體中文
- [領域特有的溝通方式]
## 能力範圍
- [能力 1]
- [能力 2]
- [能力 3]
## [領域特定區塊名稱]
- [領域特定的指引]
## 輸出格式偏好(可選)
- [格式偏好 1]
- [格式偏好 2]
## 注意事項
- [限制 1]
- [限制 2]
- 如果問題超出能力範圍,建議尋求專業人員協助