2026年10月23日 星期五

從傳統資安掃描到 AI Agent 審計:GitHub 專案的安全檢查工具與實務

從 DevSecOps 到 AI Agent Security Review:重新思考 GitHub 專案的資安掃描

一、資安掃描的下一步,不只是找到更多漏洞

在現代軟體開發流程中,資安檢查已經逐漸融入 CI/CD Pipeline。

開發者提交程式碼後,系統可以自動執行 SAST、Secret Scanning、Dependency Scanning 與 Container Scanning,在程式碼合併或部署之前,找出潛在的安全風險。

這些工具解決了許多傳統人工審查難以持續執行的問題。

然而,自動化掃描結果仍然需要判讀。

工具可能找到一段可疑的程式碼,但它是否真的構成漏洞?攻擊者是否能滿足利用條件?系統其他位置是否已經存在防護機制?修補之後,又是否會破壞原有的業務邏輯?

這些問題往往需要結合程式碼、執行流程、權限設計與系統架構才能回答。

因此,我認為值得關注的發展方向,是從傳統的自動化資安掃描,進一步走向 AI Agent Security Review。

Cloudflare 開源的 Security Audit Skill,提供了一個值得研究的案例:它不只是要求 AI 尋找漏洞,而是將架構分析、漏洞搜尋、對抗式驗證、結構化輸出與獨立檢查納入同一套審計流程。

這背後真正值得探討的問題是:

當 AI 能夠協助尋找漏洞時,我們要如何確認它找到的問題確實成立?

二、傳統 DevSecOps:不同工具負責不同的安全風險

DevSecOps 的核心不是把所有資安工具塞進 Pipeline,而是將安全檢查整合到軟體開發生命週期,讓問題能夠及早被發現、追蹤與修復。

常見的 GitHub 專案安全檢查,可以分成以下幾個類別。

1. SAST:靜態應用程式安全測試

代表工具:CodeQL、Semgrep。

SAST 在不需要實際執行完整應用程式的情況下,分析原始碼中的潛在安全問題。

CodeQL 可以利用程式碼資料模型與查詢規則分析資料流,尋找符合特定模式的漏洞。Semgrep 則適合使用既有或自訂規則,檢查不安全的程式碼模式與團隊規範。

這類工具能協助發現 SQL Injection、不安全的輸入處理,以及其他受分析規則涵蓋的漏洞。

但靜態分析的結果取決於語言支援、分析能力、規則品質與程式碼上下文。沒有掃描結果,不代表沒有漏洞。

參考:CodeQL、Semgrep

2. Secret Scanning:機密資訊檢查

代表工具:Gitleaks。

開發者可能不小心將 API Key、資料庫密碼、Token 或私鑰提交到 Git Repository。

Gitleaks 可以掃描儲存庫內容,尋找疑似機密資訊,協助團隊在問題進入正式環境之前發現風險。

需要注意的是,從最新版本刪除機密資訊,不代表 Git 歷史紀錄中的內容已經消失。若憑證曾經外洩,通常還需要撤銷或輪替,並檢查相關存取紀錄。

參考:Gitleaks

3. Dependency Scanning:第三方依賴套件檢查

代表工具:Dependabot、Trivy。

現代應用程式大量依賴第三方套件。即使自行撰寫的程式碼沒有明顯問題,依賴套件仍可能存在已知漏洞。

Dependabot 可以追蹤支援的依賴套件與安全公告,並依照設定提出版本更新。Trivy 則可以掃描語言套件、作業系統套件及其他支援的目標,找出已知漏洞。

不過,發現有漏洞的依賴,不代表它在目前系統中一定能被利用。仍需考慮受影響版本、實際使用方式、攻擊可達性與修補相容性。

參考:Dependabot、Trivy

4. Container Scanning:容器與部署設定檢查

代表工具:Trivy。

對採用 Docker 的專案而言,安全問題不只存在於應用程式原始碼,也可能來自基底映像、作業系統套件、容器設定與部署配置。

Trivy 可以針對支援的容器映像與設定檔進行檢查,協助團隊在部署之前發現風險。

這類掃描能補足純粹檢查應用程式程式碼的不足,但仍不能取代正式環境的權限管理、網路隔離與執行時期防護。

傳統工具的共同限制

這些工具各有專長,卻不能單獨代表完整的資安審計。

它們通常能回答:

  • 哪些程式碼符合已知的不安全模式?

  • 哪些依賴套件存在已知漏洞?

  • 哪些提交可能包含機密資訊?

  • 哪些容器或部署設定存在風險?

但對於跨模組的權限邏輯、業務流程中的安全假設,以及一個候選漏洞是否真的可以被利用,仍可能需要更深入的分析。

這正是 AI Agent Security Review 可以嘗試補足的部分。

三、為什麼掃描工具可能發現不了真正的業務邏輯漏洞?

以企業系統常見的多租戶 API 為例。

假設系統提供以下端點:

GET /supplier/123/materials

登入使用者可以查看供應商 123 的物料資料。當使用者將網址中的 ID 改成 456:

GET /supplier/456/materials

如果後端只檢查使用者是否已登入,卻沒有確認該使用者是否有權限存取供應商 456 的資料,就可能造成跨租戶資料外洩。

這類漏洞稱為 BOLA(Broken Object Level Authorization,物件層級授權失效)。

OWASP API Security Top 10 2023 將 BOLA 列為 API1:2023,並指出,API 在根據使用者提供的物件識別碼存取資料時,必須正確執行物件層級的授權檢查。

參考:OWASP API1:2023 — Broken Object Level Authorization

這個案例說明了為什麼安全檢查不能只看某一行程式碼。

假設某個 Controller 沒有直接檢查供應商歸屬權限,不能因此立即認定存在漏洞。權限檢查可能已經在 Middleware、共用依賴或資料存取層執行。

反過來說,即使程式碼沒有 SQL Injection,也不代表 API 的存取控制是安全的。

真正需要追蹤的是完整的請求路徑:

  1. 使用者如何通過身分驗證?

  2. API 如何取得請求中的物件 ID?

  3. 系統在哪個位置判斷使用者的存取權限?

  4. 資料查詢是否受到租戶或所有權條件限制?

  5. 未授權的請求是否真的能讀取或修改其他人的資料?

這種需要理解多個模組與業務規則的問題,正是單純依賴掃描規則可能不足的地方。

四、Cloudflare Security Audit Skill:將安全審計流程化

專案網址:cloudflare/security-audit-skill

Cloudflare Security Audit Skill 是一套用於 Coding Agent 的安全審計工作流程,也是 Cloudflare 公開漏洞發現系統的起點版本。

它的價值不在於取代所有既有掃描器,而在於把 AI Agent 的安全審計拆成明確的階段,並要求候選漏洞經過驗證與獨立檢查。

六個主要階段

1. Reconnaissance:架構分析

先理解程式庫的架構、信任邊界、輸入來源與主要攻擊面,建立後續審計的範圍與紀錄。

2. Coverage-led Hunting:依涵蓋範圍搜尋漏洞

將審計範圍拆成不同工作單位,交由不同的 Hunter Agent 檢查。除了搜尋漏洞,也要追蹤已檢查的範圍,並找出可能被遺漏的部分。

3. Candidate Validation:候選漏洞驗證

將候選漏洞交給新的 Verifier Agent,要求它嘗試推翻原本的判斷。

驗證者需要檢查程式碼路徑、必要條件、防護機制與實際影響,而不是直接接受發現者的結論。

4. Structured Output:結構化結果

將結果整理到 findings.json,並使用不同狀態區分已確認、待驗證與已被推翻的問題。

這讓後續流程可以根據明確的資料結構處理結果,而不是只依賴一份自然語言報告。

5. Independent Record Verification:獨立檢查報告證據

由新的 Agent 重新核對結構化紀錄中的程式碼位置與事實陳述,確認最終報告是否仍有足夠的原始碼證據支持。

6. Target-neutral Reporting:產生審計報告

依照經過驗證的紀錄,產生人類可閱讀的報告、詳細發現紀錄,以及尚待驗證的問題清單。

以上階段與輸出設計,可以直接參考官方專案的 README。

五、真正值得研究的設計:Verification Chain

如果只把 Cloudflare Security Audit Skill 理解成「用 AI 找漏洞」,就容易忽略它更重要的工程設計。

我認為它最值得研究的是 Verification Chain(驗證鏈):讓一個安全發現從提出、驗證到正式寫入報告,每個階段都必須留下可追蹤的依據。

概念上可以表示為:

程式碼與架構分析
        |
        v
Hunter Agent
提出候選漏洞與證據
        |
        v
Verifier Agent
嘗試推翻候選漏洞
        |
        v
結構化 Findings
記錄狀態與證據
        |
        v
Independent Verification
重新核對來源與結論
        |
        v
安全審計報告
        |
        v
人工審查、修補與回歸測試

這是概念上的責任流程,不代表所有實作都只能依照單一線性順序執行。

為什麼要讓不同 Agent 負責不同工作?

假設 Hunter 發現某個 API 可能存在越權存取。

如果同一個 Agent 直接替自己的發現背書,就可能沿用原先的假設,忽略其他模組已經存在的防護。

Verifier 的任務應該不同:它不是幫忙把原本的說法寫得更完整,而是主動尋找反證。

例如:

  • 是否有 Middleware 已完成授權?

  • 查詢是否已限制在目前使用者所屬的租戶?

  • 攻擊者是否真的能控制目標物件的識別碼?

  • 請求成功後,是否確實能讀取或修改未授權的資料?

如果證據不足,就應該保留待驗證狀態,而不是直接定義為已確認漏洞。

這種責任分離與一般程式碼審查的概念相似:開發者提交修改,Reviewer 挑戰設計與實作,QA 驗證行為,最後才進入交付流程。

但要注意,使用不同 Agent 並不會自動消除錯誤。不同 Agent 可能共享相似的模型弱點或錯誤假設。因此,對高風險問題仍應搭配可重現的測試、人工審查與安全的執行環境。

真正的重點不是讓 AI 多回答一次,而是讓驗證階段有明確的反證責任。

六、如何將傳統掃描與 AI 審計整合?

傳統工具與 AI Agent 並不是二選一,而是適合處理不同層次的問題。

以下是一個概念性的 GitHub 安全檢查流程:

GitHub Push / Pull Request
            |
            v
   ┌─────────────────────┐
   │ CodeQL / Semgrep    │
   │ 靜態程式碼分析      │
   └─────────────────────┘
            |
            v
   ┌─────────────────────┐
   │ Gitleaks            │
   │ 機密資訊掃描        │
   └─────────────────────┘
            |
            v
   ┌─────────────────────┐
   │ Trivy / Dependabot  │
   │ 依賴與容器風險      │
   └─────────────────────┘
            |
            v
   ┌─────────────────────┐
   │ Security Audit Skill│
   │ 深入分析與獨立驗證  │
   └─────────────────────┘
            |
            v
       Human Review
            |
            v
     修補與回歸測試
            |
            v
         Deploy

這張圖展示的是一種可能的整合方式,而不是 Cloudflare Skill 的內建工具清單,也不代表所有專案都應將完整 AI 審計放在每一次 Pull Request 中。

實際導入時,可以依照成本與風險決定執行頻率:

  • 每次 Pull Request: 執行速度較快、適合高頻率執行的靜態分析與機密掃描。

  • 定期或重大變更時: 執行較深入的 AI Agent 安全審計。

  • 修補完成後: 執行相關安全測試、原有功能測試與必要的回歸檢查。

  • 正式部署前: 由負責人確認高風險問題的處理狀態與交付條件。

另外,安全審計涉及不受信任的程式碼時,必須限制執行環境與權限。不能為了驗證漏洞,就讓 Agent 在沒有適當隔離的環境中執行任意程式碼,或直接探測未經授權的正式系統。

七、AI Agent Security Review 的限制

即使採用多 Agent 驗證流程,仍然有幾個問題需要正視。

第一,驗證不等於形式上的安全證明。

Agent 找到證據、重現問題,能提高判斷的可信度,但無法證明整個系統不存在其他漏洞。

第二,覆蓋率仍然重要。

如果某個模組根本沒有被檢查,再好的驗證流程也無法確認該模組是否安全。架構分析與涵蓋範圍紀錄因此很重要。

第三,AI 審計有額外成本。

多階段分析、平行 Agent 與獨立驗證會增加 Token、工具呼叫與執行時間。是否值得投入,應取決於系統風險與審計目標。

第四,發現漏洞不等於修補完成。

修補可能引入新的錯誤,尤其是權限、租戶隔離與資料完整性等涉及業務不變條件的功能。

因此,AI 安全審計的成果應該進入正常的工程流程,包含問題追蹤、人工審核、修補、測試與重新驗證,而不是以一份 AI 產生的報告作為安全保證。

八、結語:Security as Process,而不只是 Security as Tool

從 CodeQL、Semgrep、Gitleaks、Trivy 到 Dependabot,傳統資安工具已經能在程式碼、依賴套件、機密資訊與部署設定等層面提供自動化檢查。

Cloudflare Security Audit Skill 則展示了另一種值得研究的方向:讓 AI Agent 參與更完整的安全審計,並將候選漏洞的驗證、證據紀錄與獨立檢查納入工作流程。

對 AI 輔助開發而言,這個方向尤其值得關注。

當 AI Agent 越來越常參與程式碼撰寫,工程團隊不能只關心產生程式碼的速度,也必須建立相應的驗證機制,確認程式碼是否符合安全要求、修改是否破壞既有行為,以及審計結論是否有足夠證據支持。

未來資安工具的競爭,可能不再只是誰能找到更多漏洞,而是誰能建立更可信的 Verification Chain,讓漏洞發現、驗證、修復與追蹤形成可持續運作的工程流程。

這也是從傳統 DevSecOps 走向 AI Agent Security Review 時,最值得深入思考的工程問題。

安全不應只是工具執行完畢後的一份掃描報告,而應該是整個軟體交付流程中,能持續驗證、追蹤與改善的工程能力。

從 API 呼叫到工程閉環:以 Laravel 12 打造可追蹤、可恢復的 AI Agent 迴圈工程

本文以 Laravel 12 專案 RentFlow 為實作背景,探討如何將 AI 從單次 API 呼叫,設計成具備狀態管理、測試驗證、錯誤處理與終止條件的可控修復迴圈。

一、前言:真正困難的不是讓 AI 寫出程式碼

當我們開始在軟體開發流程中導入大型語言模型(LLM),最容易想到的應用,就是把錯誤訊息交給 AI,請它分析根因,再產生一份修復建議。

這樣的流程很容易實現。

例如,當 PHPUnit 測試失敗時,將錯誤輸出傳送給 Gemini,要求模型找出問題,並回傳可能的修正方式。只要完成 API 串接,就能得到一個看起來具備程式碼分析能力的工具。

但這還不是完整的 AI Agent 工程。

因為 AI 回答了,不代表問題已經解決;AI 提出了 Patch,不代表 Patch 能夠套用;Patch 成功套用,也不代表系統行為正確。

真正的工程問題在於:

  • 如何讓 Agent 知道自己目前正在執行哪一個任務?

  • 如何確認測試真的執行過,而不是只根據文字推測?

  • 如何判斷應該繼續、暫停、重試,還是終止?

  • 如何避免 AI 無限制地重複呼叫 API?

  • 如何保存每一輪的執行結果,讓任務可以在不同命令之間恢復?

  • 如何將 AI 產生的修復建議,與實際套用的程式碼變更區分開來?

  • 當 API 配額不足、測試程序失敗或修復無效時,系統應該如何處理?

這些問題並不能單靠一個 Prompt 解決。

因此,在我的 Laravel 12 專案 RentFlow 中,我將 AI Agent 的設計重點放在 Loop Engineering(迴圈工程):把測試執行、結果分析、修復提案、狀態管理與後續驗證,組合成一個具有明確控制條件的工程流程。

本文會以專案中的兩種 Agent 工作方式為例:

  1. 原有 Queue AI Agent:支援純分析與測試驅動的自動修復流程,並可透過設定決定是否自動套用修復。

  2. Autonomous AI Code Repair Agent Demo:在既有 Repository 中新增的隔離展示案例,採用離線模擬、可選擇的 Gemini 分析,以及人工審核修復提案的流程。

兩者共享迴圈工程的核心概念,但用途與執行方式不同,不應混為一談。


二、什麼是 Loop Engineering?

我將 Loop Engineering 定義為:

將一個需要反覆嘗試才能完成的工程任務,拆解成有狀態、有邊界、有觀測資料,而且每一輪結果都能決定下一步行動的執行閉環。

傳統的一次性 AI 呼叫通常是:

輸入問題
   ↓
呼叫 LLM
   ↓
取得回答
   ↓
結束

這種模式適合解釋錯誤訊息、提供程式碼建議,或回答一般技術問題。

但當任務需要實際執行測試、依據測試結果修正程式碼,再重新執行測試時,單次呼叫就不夠了。

我們需要的是:

建立任務
   ↓
執行測試
   ↓
取得真實執行結果
   ↓
判斷是否成功
   ├── 成功 → 結束任務
   │
   └── 失敗
         ↓
      分析失敗原因
         ↓
      產生修復提案
         ↓
      驗證提案與執行條件
         ↓
      套用修復或等待人工審核
         ↓
      再次執行測試
         ↓
      判斷是否繼續

這個流程的核心,不是「重複呼叫 AI」,而是讓每一輪都建立在上一輪的實際結果之上。

如果沒有測試結果,Agent 就無法可靠地判斷問題是否解決;如果沒有狀態管理,Agent 就無法知道自己應該從哪裡繼續;如果沒有停止條件,Agent 就可能持續消耗資源,卻無法產生有意義的結果。

因此,一個可靠的 Agent Loop 至少需要四個基本元素:

  1. Execution:執行——實際執行測試或其他工程操作。

  2. Observation:觀測——保存退出碼、輸出、錯誤資訊與執行狀態。

  3. Decision:決策——根據觀測結果決定下一步,而不是只根據 AI 的主觀判斷。

  4. Control:控制——限制迴圈次數、管理失敗狀態、處理配額與控制任務終止。

這四個元素共同構成迴圈工程的基本骨架。

三、為什麼選擇 Laravel 12 作為實作環境?

RentFlow 本身是一個以 Laravel 12 建置的 Multi-Tenant Rental SaaS Platform,包含租戶隔離、車輛管理、預約流程、定價規則、JWT 會員 API,以及 Filament 後台管理功能。

這是一個適合展示工程型 AI Agent 的環境,因為它不只有模型呼叫,還有既有的應用程式結構、資料庫、Queue、測試套件與明確的商業規則。

Agent 必須在這些既有條件中工作,而不是建立一個獨立的範例專案,再假裝問題已經解決。

目前的實作設計涉及以下元件:

元件在迴圈工程中的責任
Laravel Artisan Command接收任務、設定執行參數、建立或恢復 Loop
Queue Job執行任務處理流程,將長時間工作與命令入口分離
Agent Loop 資料模型保存任務狀態、目前輪次與執行設定
PHPUnit提供可重現的測試結果
Symfony Process在獨立子程序中執行指定測試
Gemini API在真實模式下分析測試失敗資訊
SQLite Demo Database保存 Demo 任務及逐輪執行歷史
Patch 驗證流程檢查修復提案是否符合允許的修改範圍與替換條件

需要強調的是,這些元件各自負責不同的事情。

Gemini 負責分析,不負責證明分析正確;PHPUnit 負責執行測試,不負責決定整個任務的生命週期;資料庫負責保存狀態,不代表狀態轉移一定正確。

Agent 的可靠性,來自這些元件之間清楚的責任分工。

四、迴圈工程的第一個核心:任務必須有明確狀態

如果一個 Agent 只有一段不斷執行的 while 迴圈,那麼它很難處理中斷、恢復、配額不足與多輪測試等情境。

因此,我將任務生命週期視為一個需要明確管理的狀態機。

以 Demo 的工作流程為例,狀態可能是:

pending
   ↓
processing
   ↓
pending
   │
   │ 等待人工檢視、修改 Fixture 或恢復任務
   ↓
processing
   ↓
completed

如果測試持續失敗,也可能進入:

processing
   ↓
failed

如果真實 Gemini 分析遇到配額不足或 HTTP 429,則可能進入:

processing
   ↓
waiting_quota

而在原有 Agent 的工作流程中,任務還可能依據測試結果、修復套用結果與重試條件,進入不同的後續狀態。

這些狀態不能只當成資料表中的文字欄位。

每個狀態都應該代表一組清楚的語義與允許操作。

例如:

  • pending:任務等待執行或等待下一輪處理。

  • processing:任務目前正在執行。

  • waiting_quota:任務因配額限制而暫停,等待後續處理。

  • completed:任務已依照對應模式的完成條件結束。

  • failed:任務已無法依照目前流程繼續完成。

這裡最容易犯的錯誤,是把 completed 理解成「程式碼已經修好」。

實際上,狀態的意義必須與任務模式一起判讀。

例如,純分析任務可能在 Gemini 成功回應後結束。這只能證明分析流程完成,不代表程式碼已被修改,更不代表 PHPUnit 已經通過。

同樣地,Demo 中的離線分析也可能產生修復提案,但這些內容是固定展示資料,不是模型真正推論的結果。

因此,狀態設計必須回答的問題是:

這個任務完成了哪一件事,而不是只問它是否完成。

五、第二個核心:每一輪都必須有可驗證的觀測結果

AI Agent 很容易產生一種假象:只要回傳了一段看似合理的分析文字,就好像已經完成了工程工作。

但在真正的程式碼修復流程中,文字不是最終證據。

最重要的證據之一,是測試程序的實際退出碼。

以 PHPUnit 為例:

  • 退出碼為 0:代表該次測試程序成功結束。

  • 非 0:代表該次程序沒有以成功狀態結束,必須進一步檢查輸出及失敗原因。

不能只看終端機裡是否出現某個關鍵字,也不能只看 AI 是否說「問題已修復」。

Demo 使用 Symfony Process 執行指定的 PHPUnit Fixture,並將測試程序的輸出與退出碼保存下來。

逐輪歷史則可保存:

  • 測試退出碼。

  • 測試完整輸出。

  • 分析內容。

  • 分析來源,例如 offline simulation 或真實模型分析。

  • Token 使用量。

  • 本輪狀態與執行結果。

這讓每一輪不再只是一次不可追蹤的函式呼叫,而是一筆可以回頭檢視的工程紀錄。

5.1 為什麼不能只保存 AI 分析?

假設 Agent 回傳:

Root cause:
The discount calculation is missing.

Suggested fix:
Apply the discount rate before returning the total.

這段文字看起來合理,但我們仍然不知道:

  1. 測試是否真的執行過?

  2. 測試是否確實因折扣計算失敗?

  3. 修復提案是否符合目前的程式碼?

  4. 修復是否真的套用?

  5. 套用後的測試是否通過?

因此,分析結果必須與實際執行結果分開保存。

AI 的輸出是候選解釋,測試的輸出是工程觀測;兩者不能互相取代。

六、第三個核心:用有限迴圈避免無止境的重試

AI Agent 的迴圈如果沒有上限,就可能在相同錯誤中不斷重複。

例如:

  1. 測試失敗。

  2. Gemini 產生修復提案。

  3. Patch 無法套用,或套用後測試仍失敗。

  4. Agent 再次分析相同問題。

  5. 模型又產生另一個無法有效解決問題的提案。

  6. 流程不斷重複。

這不只是 API 費用問題,也可能造成無效的 Queue 工作、日誌膨脹,以及難以追查的任務狀態。

因此,迴圈必須具有明確的預算。

在原有 Agent 中,預設最大迴圈次數為 3 次,並可透過 --max-loops 調整。

Demo 同樣提供迴圈上限設定,但採用不同的執行方式:每次命令執行一輪,開發者檢視並修改 Fixture 後,再使用 --resume=ID 恢復任務。

這兩種方式展示了不同的控制策略。

自動迴圈與人工確認迴圈

比較項目原有 AgentDemo Agent
核心用途測試驅動的分析與修復展示受控的修復提案流程
輪次推進依既有 Job 流程與終止條件決定每次命令執行一輪
修復套用可依設定選擇是否自動套用不自動套用
人工介入依任務設定與修復流程決定每輪由人員檢視並決定是否修改
停止條件測試成功、迴圈上限或其他終止條件測試成功、迴圈上限或其他失敗條件

有限迴圈的目的,不是保證 Agent 一定能修好問題。

它的目的,是保證 Agent 不會因為沒有解決問題,就無限制地繼續執行。

可控的失敗,往往比不可預測的持續重試更有工程價值。

七、用一個可重現的 PHPUnit 案例展示整個閉環

為了讓迴圈工程可以被實際觀察,而不是只停留在架構圖,我在 Demo 中設計了一個刻意失敗的折扣計算案例。

假設一筆租車訂單的小計為 100,折扣率為 10%。

預期金額為:

[
100 \times (1 - 0.1) = 90
]

但刻意錯誤的實作直接回傳原始小計:

$total = $subtotal; // Intentional defect

測試預期為 90,實際卻得到 100,因此 PHPUnit 會回報失敗。

這個案例的優點是簡單、可重現,而且失敗原因容易理解。

它不需要連接正式租車資料庫,也不需要準備大量種子資料,就能展示 Agent 如何取得失敗結果。

7.1 第一輪:建立任務並執行測試

執行:

php artisan ai:demo

在預設離線模式下,Demo 會建立或執行一個 Agent Loop,並使用 Symfony Process 啟動指定的 PHPUnit Fixture。

這一輪的重點不是 AI 分析,而是確認測試真的失敗。

預期可觀察到:

PHPUnit: FAIL (exit 1)
Failed asserting that 100 is identical to 90.

這是測試程序的輸出,不是 AI 自己生成的錯誤敘述。

接下來,Demo 才進入分析階段。

7.2 第二步:產生修復提案

在離線模式中,Demo 使用固定的展示分析。

因此,輸出必須明確標示為模擬分析,而不能把它描述成 Gemini 的真實回應。

例如:

Offline simulated analysis (not Gemini):
Fix status: proposed; not applied.
Human review required.

這個設計可以在不消耗 API 配額、不依賴網路的情況下,展示 Agent 的控制流程。

如果要使用真實模型,則必須明確指定:

php artisan ai:demo --real

真實模式會將測試輸出送給 Gemini 分析,可能消耗 API 配額,也可能受到速率限制、認證失敗或服務端錯誤影響。

7.3 第三步:由人員檢視並修改 Fixture

Demo 不會直接修改檔案。

開發者必須檢視提案,決定是否接受,再手動修改:

$total = (int) ($subtotal * (1 - $discountRate));

這裡刻意保留人工審核。

因為即使 AI 的提案看起來合理,仍然可能存在型別、精度、商業規則或其他邏輯問題。

在真實商業系統中,金額計算還需要依照系統的精度與捨入規則設計。這個案例只用來展示測試驅動的閉環,不代表所有折扣金額都應採用相同的整數轉型方式。

7.4 第四步:恢復同一個任務

修改 Fixture 後,使用第一輪輸出的 Loop ID:

php artisan ai:demo --resume=ID

Demo 會恢復原有任務,並再次執行測試。

當測試通過時,預期輸出為:

PHPUnit: PASS
Final status: completed — Tests passed successfully

這時,整個流程才取得新的測試證據。

但仍要精確描述成果:我們證明的是指定 Fixture 在修改後通過了指定測試,而不是證明整個 RentFlow 專案所有功能都沒有問題。

7.5 第五步:展示迴圈上限

如果不修改 Fixture,而是設定:

php artisan ai:demo --max-loops=1

在符合該 Demo 的迴圈上限處理條件時,任務會以失敗狀態結束,原因為:

Final status: failed | Max loops exceeded

這個案例展示了一項重要的工程能力:

Agent 不一定要成功修復問題,但必須能夠辨識自己已經無法在預算內完成任務,並且正常終止。

八、為什麼 Demo 需要離線模式?

如果每一次展示都必須呼叫 Gemini,會產生幾個問題:

  • 受網路連線影響。

  • 受 API 金鑰與帳號設定影響。

  • 受速率限制與每日配額影響。

  • 模型回應可能隨時間與輸入內容改變。

  • 每次展示的 Token 用量與回應時間不一定一致。

因此,Demo 將離線模擬與真實模型分析分開。

離線模式

php artisan ai:demo

特性:

  • 不發送 Gemini HTTP 請求。

  • 使用固定的展示分析。

  • Token 用量固定為 0。

  • 可以重現預先設計的測試失敗與修復提案流程。

  • 適合展示狀態管理、逐輪紀錄與人工審核。

真實 Gemini 模式

php artisan ai:demo --real

特性:

  • 依照真實 PHPUnit 輸出呼叫 Gemini。

  • Token 用量取決於模型實際回報。

  • 可能發生配額不足、速率限制、認證或請求錯誤。

  • 分析結果具有模型推論的不確定性。

這兩種模式的差異,不只是開發方便與否,而是展示流程的可重現性與真實模型推論之間的責任分離。

離線模式能證明流程在固定情境下如何運作,但不能證明模型具有正確分析能力。

真實模式能觀察模型實際輸出,但也不能單憑一次成功回應,證明整個 Agent 的修復能力。

九、第四個核心:隔離執行環境,避免測試污染應用程式

AI Agent 的危險不只來自錯誤的修復提案,也可能來自測試程序本身。

例如,當 Agent 執行 PHPUnit 時,如果測試程序誤用應用程式的預設資料庫,可能造成不必要的資料變更。

因此,Demo 採用兩個用途不同的 SQLite 環境。

9.1 Demo 狀態資料庫

檔案位置:

database/agent-demo.sqlite

用途:

  • 保存 Agent Loop 任務狀態。

  • 保存逐輪測試與分析歷史。

  • 支援跨命令恢復任務。

Demo 使用既有的 agent_loops 表保存任務狀態,並使用 agent_loop_demo_runs 保存每輪執行紀錄。

9.2 PHPUnit 子程序資料庫

測試子程序使用:

DB_CONNECTION=sqlite
DB_DATABASE=:memory:

用途是隔離該次測試的資料庫環境。

Demo 透過 Symfony Process 啟動獨立測試程序,並在啟動前檢查有效的 Laravel 資料庫設定;若不是指定的 SQLite 記憶體資料庫,就拒絕建立 PHPUnit 程序。

這兩個環境不能混為一談。

前者負責保存任務狀態,後者負責隔離測試執行。

這種設計也提醒我們:把測試放進子程序,不會自動讓測試安全;仍然需要明確控制子程序的環境變數、資料庫設定與執行邊界。

同時,這項隔離措施不能被解讀為對所有主機故障、外部副作用或未來程式碼變更的絕對保證。環境檢查只能保護它實際涵蓋的範圍。

十、第五個核心:修復提案不等於已套用的 Patch

在 AI 程式碼修復系統中,有一個非常容易混淆的概念:

AI 產生修復方案,不代表系統已經修改程式碼。

在 RentFlow 中,原有 Agent 與 Demo 對此採用不同策略。

原有 Agent 可以依照設定選擇是否自動套用修復,例如使用 --auto-apply-fixes。在自動套用模式下,仍需要符合允許的檔案範圍、精確搜尋片段與其他修復驗證條件。

Demo 則不自動套用修復。開發者需要檢視提案,手動修改 Fixture,再恢復任務執行測試。

這兩種策略分別適合不同的風險情境。

人工審核模式

優點:

  • 可以在修改前檢視 Patch。

  • 可以拒絕不符合商業規則的建議。

  • 可以降低自動修改造成意外變更的風險。

  • 適合展示、教學與尚未成熟的修復流程。

代價是每輪都需要人工介入,無法完全自動化。

自動套用模式

優點是能減少人工操作,並讓 Agent 在符合條件時自動進入下一輪測試。

但自動套用不應只依賴 AI 的自我判斷。

至少需要考慮:

  1. 修復檔案是否在允許清單中。

  2. 搜尋片段是否與目前檔案完全相符。

  3. Patch 是否只修改允許的內容。

  4. 套用後是否確實重新執行測試。

  5. 測試失敗時是否能停止或回報。

  6. 是否保存實際修改的檔案與修復歷史。

在工程上,我更重視這個順序:

AI 提案
   ↓
檢查提案
   ↓
確認允許修改的範圍
   ↓
套用 Patch
   ↓
重新執行測試
   ↓
依據測試結果判斷

真正的成功條件應該來自驗證結果,而不是模型宣告「修復完成」。

十一、第六個核心:配額、重試與終止必須是流程的一部分

當 Agent 開始使用真實模型,外部 API 就成為整個迴圈的一個不確定因素。

即使 PHPUnit 已經產生正確的失敗輸出,Gemini 仍可能因為配額不足或速率限制而無法完成分析。

如果系統沒有清楚的錯誤分類,就可能把 API 問題誤判成程式碼問題,或者在同一個錯誤上反覆重試。

RentFlow 的設計將配額相關失敗與一般失敗區分處理。

例如,Demo 在真實模式下遇到包含 quota 原因或 HTTP 429 的分析失敗時,可以將任務標記為 waiting_quota,並停止當次命令的後續流程。

這比無條件重試更容易控制。

11.1 為什麼不能把所有錯誤都當成同一種錯誤?

可以將錯誤概念上區分為:

錯誤類型需要關注的問題
PHPUnit 失敗程式行為是否違反測試預期?
測試程序啟動失敗路徑、執行環境或 Process 設定是否有問題?
Patch 無法套用搜尋片段是否過時,或修改範圍是否不合法?
Gemini 配額不足是否應暫停等待,而不是立即繼續呼叫?
HTTP 429是否觸發速率限制?
認證或請求錯誤API Key、模型設定或請求格式是否正確?
迴圈達上限是否應結束任務並保存原因?

這些錯誤需要不同的處理策略。

尤其要注意,waiting_quota 不等於任務已經修復,也不等於系統會自動恢復。

它代表任務因為特定限制而暫停。何時恢復、由誰恢復,以及恢復後從哪個階段繼續,都必須由明確的控制流程決定。

同樣地,最大迴圈次數與 Queue 重試次數也不是同一件事。

  • 迴圈上限限制一個工程任務可以進行多少輪。

  • Queue 重試機制處理工作執行失敗後是否再次交由 Queue 執行。

如果把兩者混為一談,即使每個設定值看起來合理,整個系統仍可能產生超出預期的 API 呼叫或重複工作。

十二、持久化與恢復:Agent 不應只存在於一次命令中

一般腳本的生命週期通常是:

啟動 → 執行 → 結束

但工程型 Agent 可能需要跨越多次命令執行。

例如,Demo 的第一輪產生修復提案後,開發者需要檢視程式碼、修改 Fixture,才能進行下一輪。

如果所有狀態只保存在記憶體中,第一次命令結束後,任務進度就很難被可靠地延續。

因此,Demo 將任務狀態與逐輪歷史保存到獨立 SQLite 資料庫,並提供:

php artisan ai:demo --resume=ID

這讓任務恢復不再依賴原本的命令程序仍然存在。

但要注意,真正可靠的恢復不只是把 status 改回 pending。

恢復流程還應該能夠辨識:

  • 任務目前進行到哪一輪。

  • 原本設定的迴圈上限。

  • 上一輪測試是否已經執行。

  • 上一輪分析是否成功。

  • 任務是否處於等待配額或人工審核的狀態。

  • 恢復後應該重跑哪個階段。

Demo 在恢復任務時會沿用資料庫中保存的迴圈上限。這能避免使用者在恢復時無意間改變原有任務的預算。

這也說明了為什麼任務狀態、輪次與歷史紀錄應該被視為整體設計,而不是各自獨立的欄位。

十三、可觀測性:如何判斷 Agent 到底做了什麼?

對工程型 Agent 而言,可觀測性不是附加功能,而是判斷它是否可靠的必要條件。

如果一個任務最後只留下:

status = completed

我們仍然無法回答:

  • 測試到底有沒有執行?

  • 執行了幾輪?

  • 哪一輪產生了修復提案?

  • AI 分析是否成功?

  • Token 使用量是多少?

  • 有哪些檔案被修改?

  • 最後為什麼結束?

因此,RentFlow 的 Agent 工作流程會使用任務欄位與逐輪紀錄保存相關資訊。

原有 Agent 的紀錄可能涉及:

  • current_loop

  • max_loops

  • test_path

  • last_test_exit_code

  • last_test_output

  • ai_analysis

  • modified_files

  • patch_history

  • failure_reason

  • termination_reason

Demo 則另外保存每輪執行輸出、分析來源、狀態與 Token 數。

這些資訊讓我們可以從結果反向追查整個流程。

例如,若 test_path 為 NULL,就必須注意這可能是純分析任務,而不是測試驅動的修復任務。

此時即使 ai_analysis 有內容,也不能推論 PHPUnit 已經執行。

同樣地,Token 使用量為 0 可能代表離線模擬,也可能是提供者未回報使用量中繼資料。必須結合分析來源與執行紀錄判斷,不能只看單一欄位。

這就是可觀測性的價值:讓系統的實際行為能夠被檢查,而不是只能相信 Agent 自己的敘述。

十四、用 CustomerAuth 註冊問題理解真正的工程驗證

除了 Demo 中的折扣案例,RentFlow 的手動驗證指南也使用了更接近真實 API 的情境:會員註冊時,User 已建立,但 JWT 產生失敗。

假設註冊流程依序執行:

驗證請求
   ↓
建立 User
   ↓
產生 JWT
   ↓
回傳註冊結果

如果 JWTAuth::fromUser($user) 拋出 JWTException,Controller 回傳 HTTP 500,但 User 已經寫入資料庫,就可能出現:

  • 使用者收到註冊失敗的回應。

  • 資料庫中卻已經存在該帳號。

  • 使用者再次註冊時,可能遇到 Email 重複問題。

這是一個值得測試的資料一致性風險。

但在工程上,不能只憑這個描述就宣稱缺陷已經被驗證。必須實際讓 JWT 產生流程拋出例外,再檢查資料庫中的結果。

對應的測試應驗證:

  1. 提供有效的註冊資料。

  2. 讓 User 建立成功。

  3. 模擬 JWT 產生時拋出 JWTException。

  4. 確認 API 回傳預期的 HTTP 500。

  5. 確認交易確實回滾,資料庫中不存在該筆新 User。

如果程式碼的修正方向是將 User 建立與 JWT 產生放入同一個 DB::transaction(),就必須確保 JWT 例外能夠離開交易 callback,讓 Laravel 回滾交易。

如果交易只包住 User 建立,卻在交易結束後才產生 JWT,就無法達到這個回滾目的。

這個例子說明了迴圈工程最重要的一點:

Agent 必須根據可重現的測試結果判斷問題,而不是只根據錯誤描述生成一段看起來合理的修補程式碼。

對這種情境而言,真正有價值的不是 AI 是否寫出了 DB::transaction(),而是修正後的程式是否通過了可以重現該例外的測試。

十五、工程型 AI Agent 最容易出現的六種誤判

在設計這類系統時,我認為有幾種情況特別值得注意。

15.1 把分析完成當成修復成功

純分析模式可能完全沒有執行測試。

因此,completed 必須搭配任務模式與終止原因判讀。

15.2 把 Patch 產生當成 Patch 套用

AI 回傳 JSON 中包含 fixes,只代表模型提出了修復建議。

仍然需要確認檔案路徑、搜尋片段、替換內容與允許修改的範圍。

15.3 把 Patch 套用成功當成測試通過

檔案成功修改,只代表變更已經發生。

它可能引入其他錯誤,也可能根本沒有解決原始問題。

15.4 把一次測試通過當成整個專案正確

測試通過只能證明指定測試在指定環境中通過。

如果只執行一個 Fixture,就不能推論整個應用程式的功能都已經正確。

15.5 把 Queue 重試當成 Agent 的下一輪

Queue 重試與工程迴圈具有不同語義。

如果沒有清楚區分工作重試、業務迴圈與模型 API 重試,就可能增加非預期的執行次數。

15.6 把離線模擬當成真正的 AI 推論

固定展示資料有助於測試流程,但不能用來證明 Gemini 的推理品質。

因此,分析來源與 Token 紀錄必須清楚標示。

這些誤判的共同問題,是把不同層次的成功條件混在一起。

真正可靠的 Agent,應該能夠分別回答:

  • 任務是否成功執行?

  • AI 是否成功回應?

  • 修復是否實際套用?

  • 測試是否通過?

  • 任務是否符合終止條件?

只有把這些問題分開,才能避免用一個模糊的「成功」掩蓋實際狀態。

十六、迴圈工程的邊界:它不是無限制的自主程式碼修改器

我並不認為工程型 Agent 的成熟度,應該用它能修改多少檔案、能夠執行多少輪,或能否完全不需要人類參與來衡量。

一個允許 AI 無限制修改程式碼的系統,即使看起來非常自主,也可能因為錯誤的假設、錯誤的 Patch 或錯誤的執行環境而產生更大的風險。

因此,RentFlow 的 Demo 明確保留以下限制:

  • 每次命令只執行一輪。

  • 離線模式不呼叫 Gemini。

  • 真實模型分析需要明確指定 --real。

  • 修復提案由開發者檢視。

  • Fixture 由人員手動修改。

  • 迴圈具有上限。

  • 測試程序使用獨立的 SQLite 記憶體資料庫。

  • 任務狀態與逐輪結果保存於獨立的 Demo 資料庫。

這些限制不是為了讓系統看起來比較保守,而是為了讓整個流程的行為可以被理解、檢查與重現。

未來若要擴展成更完整的自動修復系統,還需要持續處理更多問題,例如:

  • Patch 的語法與語義驗證。

  • 測試覆蓋率與回歸測試策略。

  • 多任務併發時的狀態競爭。

  • Queue 重複投遞與冪等處理。

  • 外部 API 的退避策略。

  • 中斷後的任務恢復一致性。

  • 修改檔案前後的版本控制與差異檢查。

  • 自動修復失敗時的回復策略。

目前的 Demo 不宣稱已經解決所有這些問題,也不宣稱具備通用的自主程式碼修復能力。

它要展示的是:在加入更複雜的能力之前,先建立一個有邊界、有紀錄、有驗證條件的工程閉環。

十七、如何評估一個 Agent Loop 是否值得信任?

如果要評估一個 AI Agent,我不會只問它能不能呼叫 Gemini,或能不能產生一段 PHP 程式碼。

我會先確認以下幾件事。

任務控制

  • 是否有明確的任務狀態?

  • 是否有迴圈上限?

  • 是否有清楚的成功與失敗條件?

  • 是否能在中斷後恢復?

執行驗證

  • 是否真的執行指定測試?

  • 是否保存退出碼與輸出?

  • 是否能區分測試失敗與程序啟動失敗?

  • 修復後是否重新執行測試?

修復安全

  • 是否限制可修改的檔案?

  • 是否驗證搜尋片段與替換內容?

  • 是否能區分提案與已套用的變更?

  • 是否保留實際 Diff 供人工檢視?

資源控制

  • 是否限制迴圈次數?

  • 是否處理 API 配額與速率限制?

  • 是否能避免無意義的重複呼叫?

  • 是否記錄 Token 用量及分析來源?

可觀測性

  • 是否保存每輪歷史?

  • 是否能查出任務為何結束?

  • 是否能辨識純分析、離線模擬與真實測試?

  • 是否能根據資料庫紀錄與實際測試輸出重建執行過程?

這些問題比單純展示一段 AI 回答,更能反映 Agent 的工程成熟度。

十八、結語:把 AI 放進工程閉環,而不是讓工程依賴 AI 的自我宣告

回到最初的問題:如何打造一個能夠持續分析、修復與驗證程式碼的 AI Agent?

答案不只是增加 Prompt、換一個更強的模型,或讓 Agent 多執行幾輪。

真正的核心,是建立一套清楚的控制流程:

Task
  ↓
Execution
  ↓
Observation
  ↓
Decision
  ↓
Controlled Action
  ↓
Verification
  ↓
Next Iteration or Termination

在這個流程中,AI 是重要的分析元件,但不是唯一的判斷依據。

測試結果負責提供工程證據;狀態機負責管理任務生命週期;資料庫負責保存執行歷史;迴圈上限負責控制成本與執行範圍;Patch 驗證負責限制修改行為;人工審核則在需要時提供額外的決策邊界。

RentFlow 的 AI Agent Demo 是我用來探索這套設計的工程案例。

它不追求宣稱 AI 能夠自動修復所有問題,而是把任務狀態、測試執行、分析來源、修復提案、人工確認與再次驗證,組成一個可以觀察的閉環。

對我而言,這才是 Loop Engineering 的重點:

不是讓 AI 無止境地嘗試,而是讓每一次嘗試都有目的、每一個結果都有證據、每一個狀態都有意義,而且每一個迴圈都有明確的停止條件。

當 AI 開始參與真實的軟體工程時,我們需要的不只是更聰明的模型,更需要能夠約束、觀測、驗證與恢復的工程系統。

而這正是我希望透過 Laravel 12 與 RentFlow 展示的方向。


專案與展示

  • 專案:RentFlow — Multi-Tenant Rental SaaS Platform

  • 技術基礎:Laravel 12、PHPUnit、Symfony Process、Laravel Queue、SQLite、Google Gemini API

  • Demo 入口:php artisan ai:demo

  • 真實 Gemini 分析:php artisan ai:demo --real

  • 恢復任務:php artisan ai:demo --resume=ID

  • 迴圈上限展示:php artisan ai:demo --max-loops=1

注意: 上述命令與流程以專案 README 所描述的實作為基礎。實際發布前,仍應確認目前分支中的命令參數、測試結果與狀態轉移實作一致;架構描述本身不代表所有測試情境都已在實際環境通過驗證。

2026年10月20日 星期二

SMTMS 技術分享:從 AI Coding Agent 開發到資料一致性,打造供應商品質與材料追溯系統

 

SMTMS 技術分享:從 AI Coding Agent 開發到資料一致性,打造供應商品質與材料追溯系統

一、為什麼要做 SMTMS?

在製造業的供應商品質管理中,管理供應商、物料與進料檢驗只是基本需求。真正困難的地方,是當物料發生品質異常時,系統能否提供足夠的資料,說明物料從哪裡來、經過哪些檢驗,以及目前處於什麼狀態。

例如,一批物料收到工廠後,會經過收料、內部批次拆分與 IQC(Incoming Quality Control,進料品質檢驗)等流程。如果後續發現品質問題,就必須能夠回頭查明來源,而不是只找到一筆孤立的檢驗紀錄。

因此,我以 SMTMS(Supplier Material Traceability Management System)作為專案,嘗試整合供應商管理、物料管理、進料作業、品質檢驗、異常處理與追溯相關功能。

這個專案的重點不是單純完成 CRUD,而是思考如何讓系統中的資料具有可追溯性、業務規則具有一致性,以及權限控制不會因為 AI 生成程式碼而被忽略。

二、技術架構與選型

SMTMS 採用前後端分離的架構,後端負責業務規則、資料驗證與 API,前端負責操作介面與資料呈現。

技術用途
FastAPI後端 API 與請求處理
SQLAlchemy 2.xORM 與資料存取
Alembic資料庫結構版本管理
MySQL業務資料儲存
RedisJWT Token 撤銷等相關狀態管理
React 19 + TypeScript前端介面
Vite前端開發與建置
Tailwind CSS介面樣式
Docker Compose開發環境服務編排
Nginx前端與服務入口相關配置
JWT + RBAC身分驗證與角色權限控制
uvPython 依賴管理

資料庫使用既有的外部 MySQL,Redis 則透過 Docker Compose 提供開發環境服務。這樣的安排也讓資料庫與應用程式容器的生命週期可以分開管理。

在資料存取層,使用 SQLAlchemy 管理 ORM 與資料庫操作,透過 Alembic 管理 Schema 變更,避免開發過程中只修改程式模型,卻忘記同步資料庫結構。

三、核心設計:資料關聯必須反映真實業務流程

在材料追溯系統中,最重要的不是能查出多少筆資料,而是查詢結果是否有實際紀錄支持。

SMTMS 的核心資料關係包含:

Supplier Lot → Material Receipt → Internal Material Lot → IQC Record

各個環節分別代表供應商批次、實際收料、工廠內部批次,以及進料檢驗紀錄。

這些關係必須透過實際建立的資料與關聯取得,不能只依照物料編號或 BOM(Bill of Materials,物料清單)推測某批物料實際使用在哪裡。

這個設計原則可以歸納為:

  • 資料關聯必須有明確來源。

  • 批次與收料紀錄必須保持一致。

  • 檢驗結果與批次放行狀態必須符合業務規則。

  • 缺少實際紀錄時,系統應明確反映資料不足,而不是自行推論。

這些原則比單純增加查詢畫面更重要,因為它們決定了系統提供的資訊能否被信任。

四、資料一致性:不能只依賴前端驗證

在收料與批次管理中,數量錯誤可能直接影響後續的品質管理與庫存資料。

例如,假設某次收料的數量為 100 件,系統允許建立內部批次時拆分數量,但所有拆分數量的總和不能超過實際可分配的收料數量。

如果只在前端檢查數量,使用者仍可能透過直接呼叫 API、重複送出請求,或同時執行多筆操作,造成資料不一致。

因此,這類規則必須在後端進行驗證,並根據操作的資料依賴關係設計交易與併發控制。

在開發過程中,我特別關注以下問題:

  1. 收料與內部分批之間的數量限制。

  2. 同一物料與供應商批次之間的關聯一致性。

  3. IQC 檢驗結果與放行狀態的同步。

  4. 多筆資料更新失敗時,是否可能留下部分完成的結果。

  5. 重複請求或併發操作是否可能突破既有驗證。

這些問題不能只靠增加欄位驗證解決,還需要檢查資料庫約束、交易邊界及實際的資料更新順序。

尤其在併發情境下,單元測試通過不代表真實 MySQL 多連線操作一定安全。涉及資料競爭的功能,仍需要適當的整合測試與併發測試,才能進一步確認其正確性。

五、權限設計:驗證身分不等於授權成功

SMTMS 使用 JWT 進行身分驗證,並透過 RBAC(Role-Based Access Control)管理角色與權限。

除了確認使用者是否登入,還必須判斷使用者能否執行特定操作,以及是否有權存取指定供應商的資料。

例如,供應商使用者不應只因為知道其他供應商的資料 ID,就能讀取或修改對方的品質紀錄。

因此,權限檢查不能只存在於前端選單或按鈕。即使前端隱藏了某個功能,後端 API 仍然必須獨立驗證權限與資料範圍。

在這個架構中,需要分別考慮:

  • Authentication: 確認使用者身分。

  • Authorization: 確認使用者是否具有所需權限。

  • Data Isolation: 確認使用者只能存取被授權的資料範圍。

  • Token Revocation: 在登出等情境下,透過 Redis 等機制管理已撤銷的 Token。

這些概念彼此相關,但不能互相取代。通過登入驗證,不代表自動擁有所有 API 的存取權限。

六、品質異常管理:讓異常處理成為可追蹤流程

品質異常管理不只是建立一筆異常紀錄,還涉及調查、處置、確認與結案等不同階段。

如果系統只保留異常描述與目前狀態,卻沒有記錄處置內容或操作歷程,就很難理解異常是如何被處理的。

因此,品質異常相關設計需要考慮:

  • 異常紀錄及其關聯資料。

  • 調查與處置內容。

  • 狀態變更及歷程。

  • 附件上傳、下載與刪除。

  • 使用者的操作權限與供應商資料隔離。

附件也不只是檔案上傳問題。系統必須確認檔案與業務紀錄的關聯、存取權限,以及刪除操作的授權範圍。

不過,品質異常管理與完整 CAPA(Corrective and Preventive Action,矯正與預防措施)並不是同一件事。若尚未具備根因分析、矯正措施、預防措施及有效性驗證等完整流程,就不應把一般異常處理功能描述成已完成的 CAPA 系統。

七、AI Coding Agent:開發速度之外,更重要的是限制錯誤

這個專案的另一個重點,是使用 AI Coding Agent 協助開發。

AI 可以協助建立 API、整理 ORM、撰寫前端頁面、補充測試與處理錯誤,但產出程式碼並不代表業務邏輯一定正確。

尤其當需求涉及資料一致性、批次關聯或權限隔離時,AI 可能產生語法正確、卻違反業務規則的實作。

因此,我採取的開發思路不是讓 Agent 自由擴大功能,而是盡可能明確定義修改範圍與驗證條件。

1. 先理解現有實作,再修改程式

面對既有程式或錯誤訊息,先確認實際執行流程、資料表結構、API 路由與相關測試,再判斷問題出在哪裡。

不應只根據錯誤訊息就直接新增欄位、服務類別或抽象層。

2. 明確定義不可違反的業務規則

例如:

  • 收料數量不可超過允許範圍。

  • 內部分批總數不可超過可分配數量。

  • 供應商資料不可跨租戶或授權範圍存取。

  • IQC 檢驗結果與放行狀態必須一致。

  • 追溯結果必須由實際資料關聯取得。

這些規則應該成為 Agent 修改程式時的明確限制,而不是依賴模型自行猜測。

3. 控制修改範圍

採取 KISS 與最小修改原則,優先重用既有的程式模式,避免為了單一需求就引入不必要的 DTO、Service、Helper 或新的抽象層。

修改範圍越大,回歸測試與理解成本通常也越高。

4. 讓驗證結果有明確依據

透過自動化測試、型別檢查、Lint、Migration 檢查與前端建置,確認修改至少符合已定義的技術條件。

但也必須區分測試通過與功能完整:測試只能證明已覆蓋的情境,不能自動證明所有業務規則都正確。

八、為什麼要拆分規格文件?

隨著專案功能增加,單一 spec.md 容易同時包含需求、目前實作、已知缺陷、資料表結構、測試結果與未來規劃。

這會讓 AI Coding Agent 難以判斷哪些是必須遵守的規則、哪些只是目前的程式行為,以及哪些功能尚未實作。

因此,SMTMS 的文件整理方向是將不同責任分開:

  • 01_SRS.md:系統應該提供什麼功能。

  • 02_DOMAIN_RULES.md:業務上不能違反的規則。

  • 03_ADR.md:架構與技術決策。

  • 04_API_CONTRACT.md:API 契約。

  • 05_DATABASE_SCHEMA.md:資料庫結構。

  • 06_CURRENT_STATE.md:目前的實作狀態與已知限制。

  • 07_ACCEPTANCE.md:如何驗證功能是否符合需求。

  • 08_ROADMAP.md:未來的開發順序與優先級。

這樣的拆分不只是文件管理,而是把原本依賴開發者記憶的知識,轉換成 Agent 可以明確引用的工程約束。

九、目前仍需持續驗證的部分

SMTMS 的開發不能只以功能頁面是否存在作為完成標準。

後續仍需要針對以下項目持續確認:

  • 真實 MySQL 多連線下的併發正確性。

  • 所有關鍵業務規則是否都有負向測試。

  • 追溯查詢是否能完整呈現實際存在的關聯鏈。

  • 品質異常是否具備完整的稽核歷程與 CAPA 流程。

  • 報表與匯出是否符合完整資料集的需求。

  • 部署、安全性與效能是否經過實際環境驗證。

這些項目必須依照實際完成的實作與測試逐步確認,不應因為規格已經列出,就認定功能已經完成。

十、結語

SMTMS 對我而言,不只是練習 FastAPI、React 或資料庫操作的專案,更是一個思考 AI 輔助工程開發方式的實例。

AI Coding Agent 可以加速程式碼產出,但工程上的責任並沒有因此消失。資料是否正確、交易是否一致、權限是否完整,以及系統是否真正符合業務規則,仍然需要透過明確的設計、可驗證的測試與持續的人工判斷來確保。

我認為 AI 輔助開發最有價值的地方,不是讓 AI 一次寫出大量程式碼,而是讓它在明確的規則與邊界下完成工作,並讓每一次修改都能被理解、被驗證,也能在出問題時被追查。

這也是 SMTMS 希望呈現的工程方向:以 AI 提升開發效率,以領域規則控制風險,以測試與證據確保系統的可信度。

2026年10月18日 星期日

【技術分享】解構 DeepSeek Harness EAC:打造解放 AI Agent 生態的 IDE & 桌面終端全戰力


在 AI Agent 落地於軟體開發流程(Agentic Coding Workflows)的浪潮中,DeepSeek Harness (DSH) 憑藉強大的 Cordis 微模組架構與長文本重構能力,成為不少工程師的桌面端首選。然而,原生環境在多專案切換、環境依賴污染、以及跨 IDE 整合上,往往存在一定的配置門檻。

由 Ebony-Vinyl 團隊維護與推廣的 DeepSeek Harness EAC (Embracing All Creation) 生態系,正是為了了解決這些實踐痛點而生。本文將從系統架構、環境隔離機制到 IDE 側邊欄整合,深入剖析 EAC 版本的技術亮點與實戰配置。

💡 什麼是 DeepSeek Harness EAC?

EAC 代表 Embracing All Creation(攬盡万象)。Ebony-Vinyl 團隊將 DeepSeek Harness 的原生 Agent 編排引擎與桌面殼層(Tauri 2 + Rust)深度融合,並搭配專屬的 EAC-Pack 套件 與 dsh-our-free-model 機制:

  1. 開箱即用的跨平台環境:內建 Node.js / DSH 專屬 Runtime,免去開發者手動配置環境變數或修復 pnpm lockfile 衝突的麻煩。

  2. 多通道免 Key 整合(Our Free Model):整合了前沿模型(如 DeepSeek V4.1 Flash、Kimi K3)以及多帳號管道轉發,支援本地 OpenAI 相容網域與局域網轉發,大幅降低 AI 輔助開發的代價。

  3. 極致的沙盒與安全隔離:提供唯讀沙盒(Read-only Sandbox)、項目寫入鎖定(Workspace-write)與完整權限模式,並具備自愈面板(Self-healing Panel)與 ACL 授權機制。

🛠 EAC 版本的四大核心技術亮點

1. 雙層架構設計:最小殼化與皮膚外遷 (M1-M5 Refactoring)

EAC 在 v6 架構演進中採用了「最小 Core-Shell 隔離」設計。桌面端 Rust 殼體僅專注於 window management、系統托盤、通知與 ACL 存取 control;而 UI 界面、主題皮膚與外掛模組則各自解耦,確保在進行大規模 Code Diffusion 與 Diff 渲染時,UI 線程不會卡死。

2. 專案級別的配置與憑據單向隔離

在團隊開發情境下,最忌諱不同專案的 Agent 上下文相互污染。EAC-IDE 外掛實現了 按專案(Workspace)隔離數據目錄 的能力。每個 IDE 視窗開啟時,會獨立綁定當前專案的 Root Path,並單向繼承宿主的基礎憑據,實現真正的多專案並行開發。

3. Native IDE VCS Base Line Diff 整合

傳統 Web UI 的 AI 修改程式碼往往需要手動複製貼上。EAC IDE 插件將文件變更直接接軌 IDE 原生的 VCS Diff 機制, Agent 的修改會以 Git / IDE Native Diff 的形式呈現在編輯器內,工程師可以逐行 Review 審閱後一鍵 Accept 或 Revert。

4. 離線裝配與 Managed Distribution 依賴守護

針對企業內網或離線開發環境,EAC-Pack 提供了 managed 升級分配機制與離線 tarball 裝配。當插件設定為受管狀態時,系統會自動切斷可能引發 EINTEGRITY 或 EPERM 的網路下載鏈,確保系統運作的確定性與冪等性。

🚀 實戰配置:在 IDE / VS Code 中掛載 EAC 免費模型通道

若您已下載或部署 Ebony-Vinyl EAC 套件,可以透過以下兩種方式輕鬆對接:

方案 A:手動配置 settings.json

在 VS Code 或 IDE 外掛的設定檔中,貼入 EAC 的專屬通道配置:

JSON
{
  // 對接 Ebony-Vinyl 本地網域或轉發端點
  "deepseekHarness.connection.apiBase": "http://127.0.1.1:18900/v1",
  "deepseekHarness.connection.model": "dsh-our-free-model",
  
  // EAC 隔離與沙盒安全性預設
  "deepseekHarness.sandbox.mode": "read-only",
  "deepseekHarness.workspace.isolation": true,
  
  // 最佳化多步代碼重構排程
  "deepseekHarness.agent.timeout": 120,
  "deepseekHarness.agent.maxSteps": 20
}

方案 B:使用 .vsix 或 IDE Marketplace 原生載入

  1. 在 IDE 擴充功能選單中選擇 Install from VSIX...。

  2. 啟動後點擊側邊欄 EAC 圖示,進入 Connection Settings。

  3. 勾選 Our Free Model 啟用,系統即會自動同步帳號池與免費車道,實現免 API Key 直接呼叫!

🎯 總結

Ebony-Vinyl 團隊維護的 DeepSeek Harness EAC 為開發者社群提供了一個兼具高效能、高安全性與無限擴充性的 Agent 開發環境。不論是全鍵盤操作的 TUI 流派,還是結合 IDE 原生 Diff 的可視化重構,EAC 都讓 AI 真正從「聊天機器人」演進為「離線可控的自動化架構夥伴」。

歡迎前往 Ebony-Vinyl/Deepseek-Harness-EAC GitHub 倉庫提交 Issue 或參與社群插件貢獻!

這支影片詳細示範了 DeepSeek Harness 的安裝與基本配置流程,有助於理解 Harness 核心的運作邏輯與插件機制。

從傳統資安掃描到 AI Agent 審計:GitHub 專案的安全檢查工具與實務

從 DevSecOps 到 AI Agent Security Review:重新思考 GitHub 專案的資安掃描 一、資安掃描的下一步,不只是找到更多漏洞 在現代軟體開發流程中,資安檢查已經逐漸融入 CI/CD Pipeline。 開發者提交程式碼後,系統可以自動執行 SAS...