![]()
上一課將代理人擴展到了雲端。本課則是將代理人帶回到單一機器上。結束時,你將擁有一個能推理、呼叫工具、閱讀你的文件,並搜尋你的文件資料的有效工程助理 — 整個過程中完全沒有呼叫過雲端推論。
為什麼你會想這麼做?在實際工程工作中常常會遇到三個原因:
代價是你用一個 小型語言模型(SLM) 來取代前沿雲端模型,並在 CPU、GPU 或 NPU 上運行。本課教你如何在此限制內建立表現 良好 的代理人,而不是假裝沒有限制存在。
本課將涵蓋:
完成本課後,你將知道如何:
本課假設你完成過早期課程,且熟悉:
你也需要:
requirements.txt 裡的套件,另加本課用的 foundry-local-sdk、openai 與 chromadb。前沿的雲端模型擁有數千億參數和資料中心作後盾。SLM 擁有數十億參數,必須能適合你的筆電記憶體。這差異設定了清楚的期望。
SLM 擅長的部分:
SLM 比較弱的部分:
因此本地代理人的致勝策略是:讓 SLM 負責協調,讓工具做重活。 模型不用真正 認識 你的程式碼庫 — 它需要知道何時呼叫 read_file 與 search_docs。這恰好符合 SLM 的強項。
flowchart LR
U[開發者] --> A[本地 SLM 代理]
A -->|決定使用哪個工具| T1[讀取檔案]
A -->|決定使用哪個工具| T2[搜尋文件 RAG]
A -->|決定使用哪個工具| T3[分析程式碼]
T1 --> A
T2 --> A
T3 --> A
A --> R[回答,完全在裝置端]
Microsoft Foundry Local 是一個輕量的執行時,它完全在你的機器上下載、管理和服務模型。我們最重要的特性是它暴露 OpenAI 相容 HTTP 端點 — 這代表使用 OpenAI SDK 與 Microsoft Agent Framework 的 OpenAI 客戶端只要改變 base_url 就能對它操作。你關於建立代理人學到的一切都能直接套用;唯一不同的是,端點從雲端變成了 localhost。
Foundry Local 還會自動為你的硬體選擇最佳模型版本 — CPU 版本、CUDA/GPU 版本或 NPU 版本 — 不必自己針對每台機器手動優化。
安裝 Foundry Local(請參閱你的作業系統相應的文件),然後確認它可正常運行:
# 安裝(範例;請遵循您平台的文件)
winget install Microsoft.FoundryLocal # Windows
# brew install microsoft/foundrylocal/foundrylocal # macOS
# 下載並運行 Qwen 模型,然後啟動本地服務
foundry model run qwen2.5-7b-instruct
foundry service status
服務啟動後,你就擁有了一個本地的 OpenAI 相容端點(通常是 http://localhost:PORT/v1)。筆記本使用 foundry-local-sdk 自動發現端點,故不需硬編碼埠號。
一個代理人只有在能呼叫工具時才是代理人。許多 SLM 可以聊天,但產生不可靠且格式錯誤的工具呼叫。Qwen 模型訓練成為能完成函數呼叫,且持續輸出格式良好的工具呼叫結構 — 這正是讓本地聊天模型成為本地 代理人 的關鍵。
流程是你已熟悉的標準工具呼叫循環,只是執行在裝置上:
sequenceDiagram
participant U as 使用者
participant A as Qwen 代理(本地)
participant T as 本地工具
U->>A: 「auth.py 做什麼?」
A->>A: 決定:呼叫 read_file
A->>T: read_file("auth.py")
T-->>A: 檔案內容
A->>A: 根據內容推理
A-->>U: 說明
文件搜尋是本地代理人發揮價值的地方。不用指望 SLM 記住你的框架文件,而是將這些文件嵌入到 本地向量資料庫,讓代理人按需檢索相關部分。
我們使用 Chroma,一個無需伺服器管理、可內嵌於程序中的向量存儲庫。管線完全本地:本地嵌入模型 → 本地向量 → 本地檢索 → 本地 SLM。
flowchart TB
D[您的文件/程式碼] --> E[本地嵌入模型]
E --> V[(Chroma 向量資料庫-磁碟上)]
Q[代理查詢] --> QE[本地嵌入查詢]
QE --> V
V -->|前 k 大片段| A[Qwen 代理]
A --> Ans[根據依據的答案]
這是與第 5 課相同的 Agentic RAG 模式 — 唯一不同是所有元件都在你的機器上運行。
MCP 是一種傳輸機制,不是雲端服務。MCP 伺服器可以作為本地程序在 stdio 上運行,通過標準協議向你的代理人暴露工具。這讓你能完全離線重用不斷增長的 MCP 伺服器生態系 — 檔案系統訪問、git 操作、資料庫查詢。
安全態勢與雲端不同,但並非不存在:本地 MCP 伺服器仍以你的使用者權限運行,故要限定它能接觸的範圍(例如一個專案目錄,而不是整個主目錄),並將其輸出視為輸入再進行驗證。
本地優先不代表只能本地。成熟系統會根據敏感性與難度做路由:
| 情況 | 運行地點 |
|---|---|
| 敏感程式碼/資料或離線狀態 | 本地 SLM |
| 簡單有限界任務 | 本地 SLM(便宜、快速) |
| 非敏感資料上的困難多跳推理 | 雲端模型 |
| 停電時的所有運作 | 本地 SLM(優雅降級) |
這映照第 16 課的 模型路由 概念 — 只是其中一個「模型」現在成了你自己的機器。一個健全設計會在雲端不可用時回落到本地,讓代理人品質降低,而不是完全失效。
flowchart LR
Q[請求] --> S{敏感或離線?}
S -->|是| L[本地 SLM]
S -->|否| C{需要深入推理?}
C -->|否| L
C -->|是| Cloud[雲端模型]
L --> Out[回應]
Cloud --> Out
開啟 code_samples/17-local-agent-foundry-local.ipynb 並跟著操作。你將建立一個本地工程助理,完全運行於你的工作站上,且能:
整個過程中皆不使用雲端推論。
助理透過 OpenAI 相容端點連接 Foundry Local,因此代理人程式碼與雲端課程幾乎相同 — 唯一不同是用戶端:
from foundry_local import FoundryLocalManager
from openai import OpenAI
# Foundry Local 會發現/下載模型並提供給我們一個本地端點。
manager = FoundryLocalManager(\"qwen2.5-7b-instruct\")
client = OpenAI(base_url=manager.endpoint, api_key=manager.api_key) # api_key 是本地佔位符
工具是普通的 Python 函數,作用範圍限縮在專案目錄:
def read_file(path: str) -> str:
\"\"\"Read a file, but only inside the sandboxed project directory.\"\"\"
full = (PROJECT_ROOT / path).resolve()
if PROJECT_ROOT not in full.parents and full != PROJECT_ROOT:
return \"Access denied: path is outside the project directory.\"
return full.read_text(encoding=\"utf-8\")
注意沙箱檢查 — 即使是本地,讀取任意路徑的工具仍是不安全的。筆記本將每個工具限定在單一專案根目錄。
在進入作業前檢測你的理解。
1. 請舉兩個在本地運行代理人,而非放在雲端的具體理由。
2. 本地代理人中,SLM 與工具之間推薦的分工是什麼?為什麼?
3. 為什麼能用 Foundry Local 重用雲端代理人程式碼?
4. 為什麼特別使用 Qwen 函數呼叫模型,而不是任何 SLM?
5. 在本地 RAG 管線中,哪些元件在機器上運行?
6. 本地 MCP 伺服器運行在你的機器上。這就代表它自動安全嗎?你還應採取什麼預防措施?
7. 請描述一個包含本地模型的合理混合路由規則。
8. 這課中執行本地代理人的實際最低 RAM 要求是多少?較多 RAM 有何助益?
將本地工程助理擴增成你選擇的小型專案的本地文件審查者(如果想,可使用本倉庫的某個課程資料夾)。
你的作業應該包括:
新增一個 find_todos 工具,掃描專案中的 TODO/FIXME 註解,並回傳含檔案與行號的位置 — 並保持與 read_file 一樣的沙箱檢查。
然後寫一段簡短的文字說明你會將哪些部分移至雲端,哪些會保留在本地,以及原因。你的評分標準為本地元件是否正確連接,以及你的混合推理是否合理 — 而非模型品質。
在本課中,你建立了完全在你自己電腦上執行的代理人:
本章完成了部署的進程:第 16 課將代理人大規模部署至 Microsoft Foundry,而本課則將其縮小部署到單台工作站。下一課將轉向保持已部署代理人的安全。
免責聲明: 此文件已使用 AI 翻譯服務 Co-op Translator 進行翻譯。雖然我們努力追求準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於關鍵資訊,建議採用專業人工翻譯。我們不對因使用此翻譯所產生的任何誤解或誤譯承擔責任。