Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "AI Learning Notes Dev Environment",
"image": "mcr.microsoft.com/devcontainers/python:3.11",

"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},

"postCreateCommand": "bash scripts/setup.sh || pip install -r requirements.txt && pip install -r requirements-dev.txt",

"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.debugpy",
"ms-toolsai.jupyter",
"ms-toolsai.jupyter-keymap",
"ms-toolsai.jupyter-renderers",
"charliermarsh.ruff",
"ms-python.black-formatter",
"github.copilot",
"github.copilot-chat",
"eamodio.gitlens",
"yzhang.markdown-all-in-one",
"bierner.markdown-mermaid",
"redhat.vscode-yaml",
"tamasfe.even-better-toml",
"mechatroner.rainbow-csv"
],
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.formatting.provider": "none",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
},
"editor.rulers": [100],
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"jupyter.askForKernelRestart": false,
"jupyter.notebookFileRoot": "${workspaceFolder}"
}
}
},

"mounts": [
"source=${localWorkspaceFolder}/.env,target=/workspaces/My-AI-Learning-Notes/.env,type=bind,consistency=cached"
],

"forwardPorts": [8000, 8501, 7860, 8888],

"portsAttributes": {
"8000": {"label": "FastAPI", "onAutoForward": "notify"},
"8501": {"label": "Streamlit", "onAutoForward": "notify"},
"7860": {"label": "Gradio", "onAutoForward": "notify"},
"8888": {"label": "Jupyter", "onAutoForward": "notify"}
},

"remoteUser": "vscode",

"containerEnv": {
"PYTHONPATH": "/workspaces/My-AI-Learning-Notes",
"PYTHONDONTWRITEBYTECODE": "1"
}
}
23 changes: 17 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,29 @@ jobs:

- name: 🔐 Bandit - 安全漏洞掃描
run: |
bandit -r . -f json -o bandit-report.json --exit-zero
# 產出報告但不阻擋 CI,讓開發者檢視結果
# 掃描高危漏洞(高嚴重性會阻擋CI)
bandit -r . -f json -o bandit-report.json -ll
# -ll 只報告中等及以上嚴重性的問題

- name: 🛡️ Safety - 依賴安全檢查
run: |
safety check --json --continue-on-error || true
# 依賴安全問題需要人工評估
# 檢查已知漏洞,高危問題會阻擋CI
safety check --json --output safety-report.json
echo "✅ 依賴安全檢查通過"

- name: 🔍 Pip Audit - 依賴審計
run: |
pip-audit --desc
continue-on-error: true # 依賴審計供參考
# 審計依賴,發現漏洞會阻擋CI
pip-audit --desc --strict

- name: 📤 上傳安全報告
if: always()
uses: actions/upload-artifact@v4
with:
name: security-reports
path: |
bandit-report.json
safety-report.json

# ==================== 構建檢查 ====================
build:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
class AIAssistant:
"""AI 助手基類"""

def __init__(self, api_key: Optional[str] = None, model: str = "gpt-3.5-turbo"):
def __init__(self, api_key: Optional[str] = None, model: str = "gpt-4o-mini"):
"""
Args:
api_key: API 密鑰
Expand All @@ -30,20 +30,29 @@ def generate(self, prompt: str, **kwargs) -> str:
class OpenAIAssistant(AIAssistant):
"""OpenAI GPT 助手"""

def __init__(self, api_key: Optional[str] = None, model: str = "gpt-3.5-turbo"):
def __init__(self, api_key: Optional[str] = None, model: str = "gpt-4o-mini"):
"""
Args:
api_key: OpenAI API 密鑰 (如果為 None,從環境變量獲取)
model: 模型名稱
model: 模型名稱 (推薦: gpt-4o-mini, gpt-4o)
"""
super().__init__(api_key, model)
self.api_key = api_key or os.getenv('OPENAI_API_KEY')
self._client = None

if not self.api_key:
print("⚠️ 警告: 未設置 OPENAI_API_KEY")
print("請設置環境變量: export OPENAI_API_KEY='your-key'")
print("或直接傳入 api_key 參數")

@property
def client(self):
"""延遲初始化 OpenAI 客戶端"""
if self._client is None and self.api_key:
from openai import OpenAI
self._client = OpenAI(api_key=self.api_key)
return self._client

def generate(
self,
prompt: str,
Expand Down Expand Up @@ -72,10 +81,10 @@ def generate(
return "❌ 錯誤: 未設置 API 密鑰"

try:
import openai
openai.api_key = self.api_key
if self.client is None:
return "❌ 錯誤: 無法初始化 OpenAI 客戶端"

response = openai.ChatCompletion.create(
response = self.client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
temperature=temperature,
Expand All @@ -86,15 +95,15 @@ def generate(
return response.choices[0].message.content.strip()

except ImportError:
return "❌ 錯誤: 請安裝 openai 套件 (pip install openai)"
return "❌ 錯誤: 請安裝 openai 套件 (pip install openai>=1.0)"
except Exception as e:
return f"❌ 錯誤: {str(e)}"


class AnthropicAssistant(AIAssistant):
"""Anthropic Claude 助手"""

def __init__(self, api_key: Optional[str] = None, model: str = "claude-3-sonnet-20240229"):
def __init__(self, api_key: Optional[str] = None, model: str = "claude-3-5-sonnet-20241022"):
"""
Args:
api_key: Anthropic API 密鑰
Expand Down Expand Up @@ -140,7 +149,7 @@ def generate(
def generate_with_gpt(
prompt: str,
api_key: Optional[str] = None,
model: str = "gpt-3.5-turbo",
model: str = "gpt-4o-mini",
temperature: float = 0.7,
) -> str:
"""
Expand Down
Loading
Loading