iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
生成式 AI

打造基於 MCP 協議與 n8n 工作流的會議處理 Agent系列 第 26

Day 26 系統設定檔與環境變數管理 — 提升專案靈活性與安全性

  • 分享至 

  • xImage
  •  

昨天我們為 AI 會議助理加上了實用的檔案下載功能,然而我目前的程式碼中有些設定是寫死的也就是所謂的「硬編碼(Hardcoded)」,像是 API 的網址、模型的名稱等等。這種做法雖然在開發初期很方便,但會帶來幾個問題

  1. 缺乏靈活性:如果 LM Studio 的 IP 位址換了,或是我想換個模型測試,就必須去修改程式碼,非常麻煩。
  2. 安全性風險:如果未來加入了需要 API Key 的服務,將密鑰直接寫在程式碼裡並且發布至網路上,會有外洩的風險。
  3. 部署困難:在不同的環境通常會有不同的設定,硬編碼會讓環境切換變得複雜。

今天的目標就是解決以上所有的問題,讓這些設定抽離到獨立的設定檔中,讓我們的專案符合業界推崇的「十二因子應用(The Twelve-Factor App)」原則。

今天的目標與挑戰

  • 選擇並建立一個標準化的設定檔格式
  • 安裝 python-dotenv 套件來讀取設定檔
  • 重構 app.pymcp_agent.py,讓它們從設定檔讀取設定值
  • 更新 .gitignore 檔案,避免將敏感設定檔上傳到版本控制系統
  • 確保重構後的系統功能與之前完全一致

技術選型:以 .env 實踐設定管理的最佳實踐

在規劃如何管理我們專案的設定時,我考慮了幾種方案,例如傳統的 config.py 檔案、.ini 格式,或是 .env

最後選擇 .env 是以其簡潔、安全且符合現代化開發流程的特性脫穎而出,成為這次管理設定的最佳選擇,其主要理由有:

  1. 語法極簡,直觀易懂
    .env 檔案採用 KEY=VALUE 的純文字格式,完全沒有複雜的語法規則。這代表我們很快就能上手,大幅降低了因設定檔語法錯誤而導致程式出問題的風險。
  2. 安全性優先,實現設定與程式碼分離
    這是最重要的考量,將 API URL、金鑰等敏感資訊寫在程式碼中,就如同把家裡鑰匙掛在門上一樣危險。.env 檔案可以讓我們將這些「秘密」與程式碼本身徹底分開,再搭配 .gitignore 檔案,就能確保這些敏感資訊絕對不會被上傳到 GitHub 等版本控制系統中,是業界公認的資安基本功。
  3. 與容器化無縫接軌
    由於我的 n8n 已經執行在 Docker 中,因此將 Python 應用程式容器化是必然的下一步,Docker Compose 天生也能讀取 .env 檔案,並將其內容作為環境變數注入到容器中。選擇 .env 也可以為未來的「容器化」任務鋪平了道路,讓部署流程更加順暢。
  4. 符合雲原生應用的業界標準
    「十二因子應用(The Twelve-Factor App)」這個現代應用程式開發的黃金準則,明確提倡「在環境中儲存設定」, .env 正是在本機開發環境中實踐這一原則的最佳方式,採用它,代表我們的專案正朝著專業、可擴展的雲原生應用架構邁進。
  5. 提高靈活性與可維護性
    未來當我需要在開發環境、測試環境或正式環境之間切換時,只需更換不同的 .env 檔案,而無需修改任何一行程式碼,這樣子的靈活性能讓專案的維護變得非常輕鬆。

採用 .env 不僅僅是為了整理幾個變數,更是為我的 M2A Agent 導入了一套專業、安全且具備高度擴展性的設定管理模式。


Step 1:選擇與建立設定檔

在多種設定檔格式 .iniYAML.env 中,我選擇使用 .env 檔案,這是現代應用程式,特別是容器化(Docker)應用中,最主流且標準的做法,它語法簡單,且能與 Docker 環境無縫接軌。

1-1 建立 .env 檔案

在專案根目錄底下,建立一個名為 .env 的新檔案。

1-2 填寫設定內容

打開 .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 包起來。

Step 2:安裝與設定 python-dotenv 函式庫

要讓 Python 程式能讀取 .env 檔案,我們需要一個小幫手:python-dotenv 函式庫。它的功能很單純,就是在程式啟動時,自動去讀取 .env 檔案,並將裡面的設定值載入到系統的「環境變數」中。

2-1 將 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"]

2-2 安裝依賴套件

修改完 pyproject.toml 後,打開終端機(Terminal),並且確認已經在專案的虛擬環境裡,接著再執行以下的指令

pip install -e .

這個指令會根據 pyproject.toml 的內容,將所有需要的套件(包含我們剛剛新增的 python-dotenv ),安裝到虛擬環境中。


Step 3:修改 Python 程式碼以讀取環境變數

萬事俱備,現在我們要開始修改程式碼,讓它從「硬編碼」改成讀取「環境變數」。

3-1 修改 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

3-2 修改 mcp_agent.pyapp.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

Step 4:建立 .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 檔案,確保我們的敏感設定不會外洩。


Step 5:驗證與測試

所有修改都已完成了!現在要來驗證我們修改後的成果了。

5-1 測試修改成果

  1. 在終端機執行 python app.py 啟動 Gradio 介面
  2. 在「新會議處理」分頁上傳音訊並輸入指令
  3. 等待處理完成,觀察結果

結果驗證

結果如修改前一模一樣,這代表了我們的修改實作成功了。

5-2 驗證測試設定檔是否生效

  1. 打開 .env 檔案,修改 LM_STUDIO_API_URL 的值,改成錯誤的IP或Port號。
  2. 重新啟動 app.py

結果驗證

終端機有正確的報錯

ValueError: 錯誤:找不到 LM_STUDIO_API_URL 環境變數,請檢查 .env 檔案。

以上兩個簡單的測試,可以驗證我們的修改成功且正確。


今天的成果總結

完成項目

  • 成功建立了 .env 檔案,將所有硬編碼的設定值(API URL、模型名稱、Webhook URL)做集中管理。
  • 學會了使用 pyproject.toml 來管理專案的依賴套件,並安裝了 python-dotenv
  • 重構了 app.pymcp_agent.py,現在它們會透過標準的 os.getenv() 方式來讀取設定。
  • 實踐了依賴注入(Dependency Injection)的最佳實踐,讓程式碼的耦合度降低。
  • 建立了 .gitignore 檔案,確保了專案的安全性,避免敏感資訊外洩。

心得

今天雖然沒有增加新功能或強化,但我讓整個專案變得更健康、更專業了,將設定與程式碼分離,是軟體工程中一個至關重要的原則。這不僅讓維護和修改變得輕而易舉,更重要的是,它為我們下一步的「容器化」鋪平了道路。

當我可以透過簡單地更換 .env 檔案就切換不同環境的設定時,整個系統的靈活性與可擴展性就提升到了一個新的層次,這些讓我感到我持續朝著專業前進了。

🎯 明天計劃

將整個 Python 應用程式打包成一個獨立的 Docker 映像檔,並修改 docker-compose.yml,實現「一鍵啟動」整個 AI 會議助理系統(n8n + Python App)的目標!


上一篇
Day 25 實用性升級 — 新增會議記錄與任務清單下載功能
下一篇
Day 27 應用程式容器化 — 使用 Docker 實現一鍵啟動
系列文
打造基於 MCP 協議與 n8n 工作流的會議處理 Agent28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言