昨天我們為 AI 會議助理加上了實用的檔案下載功能,然而我目前的程式碼中有些設定是寫死的也就是所謂的「硬編碼(Hardcoded)」,像是 API 的網址、模型的名稱等等。這種做法雖然在開發初期很方便,但會帶來幾個問題
今天的目標就是解決以上所有的問題,讓這些設定抽離到獨立的設定檔中,讓我們的專案符合業界推崇的「十二因子應用(The Twelve-Factor App)」原則。
python-dotenv
套件來讀取設定檔app.py
與 mcp_agent.py
,讓它們從設定檔讀取設定值.gitignore
檔案,避免將敏感設定檔上傳到版本控制系統在規劃如何管理我們專案的設定時,我考慮了幾種方案,例如傳統的 config.py
檔案、.ini
格式,或是 .env
。
最後選擇 .env
是以其簡潔、安全且符合現代化開發流程的特性脫穎而出,成為這次管理設定的最佳選擇,其主要理由有:
.env
檔案採用 KEY=VALUE
的純文字格式,完全沒有複雜的語法規則。這代表我們很快就能上手,大幅降低了因設定檔語法錯誤而導致程式出問題的風險。.env
檔案可以讓我們將這些「秘密」與程式碼本身徹底分開,再搭配 .gitignore
檔案,就能確保這些敏感資訊絕對不會被上傳到 GitHub 等版本控制系統中,是業界公認的資安基本功。.env
檔案,並將其內容作為環境變數注入到容器中。選擇 .env
也可以為未來的「容器化」任務鋪平了道路,讓部署流程更加順暢。.env
正是在本機開發環境中實踐這一原則的最佳方式,採用它,代表我們的專案正朝著專業、可擴展的雲原生應用架構邁進。.env
檔案,而無需修改任何一行程式碼,這樣子的靈活性能讓專案的維護變得非常輕鬆。採用 .env
不僅僅是為了整理幾個變數,更是為我的 M2A Agent 導入了一套專業、安全且具備高度擴展性的設定管理模式。
在多種設定檔格式 .ini
、YAML
、.env
中,我選擇使用 .env
檔案,這是現代應用程式,特別是容器化(Docker)應用中,最主流且標準的做法,它語法簡單,且能與 Docker 環境無縫接軌。
.env
檔案在專案根目錄底下,建立一個名為 .env
的新檔案。
打開 .env
檔案,將整理出來的硬編碼設定值,依照 KEY=VALUE
的格式填寫進去,一行一筆。
# LM Studio 相關設定
LM_STUDIO_API_URL=http://<ip>:1234/v1/chat/completions
LM_STUDIO_MODEL=mradermacher/Qwen2.5-Taiwan-7B-Instruct-i1-GGUF
# n8n Webhook 相關設定
N8N_WEBHOOK_URL=http://localhost:5678/webhook/m2a-test
LM_STUDIO_API_URL
讓人一看就知道這個設定的用途。.env
檔案也支援用 #
來加上註解,方便我對設定進行分類說明。VALUE
包起來。python-dotenv
函式庫要讓 Python 程式能讀取 .env
檔案,我們需要一個小幫手:python-dotenv
函式庫。它的功能很單純,就是在程式啟動時,自動去讀取 .env
檔案,並將裡面的設定值載入到系統的「環境變數」中。
python-dotenv
加入專案依賴我們不應該隨意地使用 pip install
,應該採取一個更標準的做法,就是將專案的依賴套件明確地定義在 pyproject.toml
檔案中。
打開先前寫好的 pyproject.toml
檔案,在 [project]
區塊下方新增一個 dependencies
列表,並將 python-dotenv
加進去。
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "m2a-agent"
version = "0.1.0"
description = "A meeting processing agent"
requires-python = ">=3.8"
dependencies = [
"python-dotenv",
"faster-whisper",
"requests",
"pydub",
"gradio"
]
[tool.setuptools.packages.find]
where = ["src"]
修改完 pyproject.toml
後,打開終端機(Terminal),並且確認已經在專案的虛擬環境裡,接著再執行以下的指令
pip install -e .
這個指令會根據 pyproject.toml
的內容,將所有需要的套件(包含我們剛剛新增的 python-dotenv
),安裝到虛擬環境中。
萬事俱備,現在我們要開始修改程式碼,讓它從「硬編碼」改成讀取「環境變數」。
app.py
app.py
是專案的主要進入點,是最適合載入環境變數的地方,因此打開 app.py
,在檔案最上方的 import
區塊加入以下內容
# ... 其他import ...
from dotenv import load_dotenv
# 載入 .env 檔案中的環境變數
load_dotenv()
接著用 os.getenv()
這個函式來讀取我們在 .env
中設定好的值,並在 app.py
中呼叫 LM Studio API 的地方將它改成
# 在初始化區塊,從環境變數讀取模型的相關資訊
lm_studio_api_url = os.getenv("LM_STUDIO_API_URL")
lm_studio_model = os.getenv("LM_STUDIO_MODEL")
# 在原有的呼叫程式碼裡,改為剛剛定義的常數
payload = {
"model": lm_studio_model,
# ... 其他 payload 內容 ...
}
response = requests.post(
lm_studio_api_url,
headers={"Content-Type": "application/json"},
json=payload,
timeout=60,
)
load_dotenv()
:這行程式碼是最重要的核心,它會自動尋找並載入專案根目錄下的 .env
檔案。os.getenv("KEY")
:這是 Python 內建的函式,用來讀取一個環境變數的值,如果該變數不存在,它會回傳 None
。mcp_agent.py
與 app.py
的整合接下來處理 mcp_agent.py
中的 webhook_url
,最好的方式是由主程式 app.py
讀取設定值後,再透過參數「注入(Inject)」給 MCPAgent
類別,而不是讓 MCPAgent
自己去讀取,這能讓 MCPAgent
的職責更單純。
mcp_agent.py
打開 mcp_agent.py
,修改 MCPAgent
的 __init__
方法。我們移除掉硬編碼的 URL ,並利用 Python 的型別提示(Type Hinting)明確要求 webhook_url
必須是一個字串。
class MCPAgent:
def __init__(self, webhook_url: str, model="medium"):
self.whisper = WhisperService(model)
self.webhook_url = webhook_url
.gitignore
確保安全性.env
檔案通常包含 API 金鑰、資料庫密碼等敏感資訊,絕對不能上傳到 Git 版本控制系統中,因此我們需要編輯 .gitignore
檔案。
在專案根目錄下建立 .gitignore
檔案,並在裡面撰寫
# Python 虛擬環境
venv/
.venv/
# 編譯後的 Python 檔案
__pycache__/
*.py[cod]
# 日誌檔案
*.log
# Docker 相關檔案
docker-compose.override.yml
# 環境變數檔案
.env
# Gradio 憑證
.gradio/
# 資料庫檔案
n8n_data/database.sqlite
# 臨時檔案
*.tmp
*.bak
*.swp
# 音訊處理的檔案
recording/**/*.mp3
recording/**/*.wav
recording/**/*.m4a
# Setuptools 生成的檔案
src/m2a_agent.egg-info/
# 臨時測試檔案
test_*.pyc
這樣一來,Git 就會自動忽略 .env
檔案,確保我們的敏感設定不會外洩。
所有修改都已完成了!現在要來驗證我們修改後的成果了。
python app.py
啟動 Gradio 介面結果如修改前一模一樣,這代表了我們的修改實作成功了。
.env
檔案,修改 LM_STUDIO_API_URL
的值,改成錯誤的IP或Port號。app.py
。終端機有正確的報錯
ValueError: 錯誤:找不到 LM_STUDIO_API_URL 環境變數,請檢查 .env 檔案。
以上兩個簡單的測試,可以驗證我們的修改成功且正確。
✅ 完成項目
.env
檔案,將所有硬編碼的設定值(API URL、模型名稱、Webhook URL)做集中管理。pyproject.toml
來管理專案的依賴套件,並安裝了 python-dotenv
。app.py
和 mcp_agent.py
,現在它們會透過標準的 os.getenv()
方式來讀取設定。.gitignore
檔案,確保了專案的安全性,避免敏感資訊外洩。今天雖然沒有增加新功能或強化,但我讓整個專案變得更健康、更專業了,將設定與程式碼分離,是軟體工程中一個至關重要的原則。這不僅讓維護和修改變得輕而易舉,更重要的是,它為我們下一步的「容器化」鋪平了道路。
當我可以透過簡單地更換 .env
檔案就切換不同環境的設定時,整個系統的靈活性與可擴展性就提升到了一個新的層次,這些讓我感到我持續朝著專業前進了。
🎯 明天計劃
將整個 Python 應用程式打包成一個獨立的 Docker 映像檔,並修改 docker-compose.yml
,實現「一鍵啟動」整個 AI 會議助理系統(n8n + Python App)的目標!