🎯 核心議題:MCP 直接工具呼叫的效能瓶頸
Model Context Protocol (MCP) 已成為連接 AI 代理與外部系統的事實標準。然而,當代理連接到數百甚至數千個工具時,傳統的「直接工具呼叫」模式會遇到嚴重的效能問題。Anthropic 於 2025 年 11 月發表的技術文章提出了革命性的解決方案:用程式碼執行環境取代直接工具呼叫。
- 工具定義佔用大量上下文:連接 1,000 個工具 = 處理 100,000+ Token(每次對話都要重新載入)
- 中間結果重複流經模型:2 小時會議逐字稿(50,000 Token)可能處理 2-3 次
本文精煉 Anthropic 官方文章的5 個核心突破,展示如何將 Token 使用降低 98.7%、實現按需載入、資料過濾與技能持久化。
(150K → 2K)
(不佔用上下文)
→ 只回傳 5 列
問題根源:直接工具呼叫的 Token 浪費
瓶頸 1:工具定義佔用上下文
傳統 MCP 客戶端會將所有工具定義預先載入模型上下文。每個工具定義包含:
gdrive.getDocument
描述:從 Google Drive 取得文件
參數:
documentId (必填, string): 文件 ID
fields (選填, string): 要回傳的特定欄位
回傳:文件物件(標題、內容、權限等)
salesforce.updateRecord
描述:更新 Salesforce 記錄
參數:
objectType (必填, string): Salesforce 物件類型
recordId (必填, string): 記錄 ID
data (必填, object): 要更新的欄位
回傳:更新確認
當連接到 1,000 個工具時,每次對話開始前就需要處理 100,000+ Token。這直接導致:
- 回應延遲增加:模型需要先處理所有工具定義才能開始推理
- 成本暴增:即使只用 2 個工具,也要為 1,000 個工具定義付費
- 擴展受限:上下文視窗有限,無法連接更多工具
瓶頸 2:中間結果重複流經模型
假設任務:「從 Google Drive 下載會議逐字稿,並附加到 Salesforce 潛在客戶」
1. LLM 呼叫:
gdrive.getDocument("abc123")
2. 回傳 50,000 Token 逐字稿
→ 載入模型上下文
3. LLM 再次呼叫:
salesforce.updateRecord({
Notes: "[完整逐字稿內容]"
})
→ 模型需重新輸出 50,000 Token
總計:逐字稿處理 2 次
1. LLM 撰寫程式碼:
const transcript =
await gdrive.getDocument("abc123");
await salesforce.updateRecord({
Notes: transcript.content
});
2. 在執行環境中執行
→ 逐字稿從未進入模型上下文
總計:逐字稿處理 0 次
2 小時會議逐字稿 = 50,000 Token。傳統模式處理 2 次 = 100,000 Token 成本。
大型文件甚至可能超過上下文視窗限制,導致工作流程中斷。
核心解決方案:將 MCP 伺服器呈現為程式碼 API
Anthropic 的突破性方法:不要讓模型直接呼叫工具,而是讓模型撰寫程式碼來呼叫工具。
技術實作:檔案系統式的工具組織
將所有 MCP 伺服器的工具以檔案系統形式呈現:
servers/
├── google-drive/
│ ├── getDocument.ts
│ ├── listFiles.ts
│ └── index.ts
├── salesforce/
│ ├── updateRecord.ts
│ ├── query.ts
│ └── index.ts
└── ... (其他伺服器)
每個工具對應一個檔案:
// ./servers/google-drive/getDocument.ts
import { callMCPTool } from "../../../client.js";
interface GetDocumentInput {
documentId: string;
}
interface GetDocumentResponse {
content: string;
}
/* 從 Google Drive 讀取文件 */
export async function getDocument(
input: GetDocumentInput
): Promise {
return callMCPTool(
'google_drive__get_document',
input
);
}
代理的工作方式轉變
模型收到:
- 1,000 個工具定義(100K Token)
- 使用者問題
模型回應:
TOOL_CALL: gdrive.getDocument(...)
↓
等待結果
↓
TOOL_CALL: salesforce.updateRecord(...)
模型收到:
- 檔案系統結構(1K Token)
- 使用者問題
模型回應:
```typescript
import * as gdrive from './servers/google-drive';
import * as sf from './servers/salesforce';
const doc = await gdrive.getDocument({
documentId: 'abc123'
});
await sf.updateRecord({
objectType: 'Lead',
recordId: 'xyz',
data: { Notes: doc.content }
});
```
↓
程式碼在執行環境中執行
- LLM 擅長寫程式碼:現代 LLM(如 Claude、GPT-4)在程式設計任務表現極佳
- 軟體工程的最佳實踐:模組化、可重複使用、易於測試
- 擴展性:新增工具只需新增檔案,不影響現有程式碼
突破 1:按需載入(Progressive Disclosure)
模型只載入需要的工具定義,而非一次載入全部。
工作流程
- 探索階段:模型列出
./servers/目錄,發現可用的伺服器 - 選擇階段:根據任務,選擇相關的伺服器(如
google-drive、salesforce) - 載入階段:只讀取需要的工具檔案(如
getDocument.ts) - 執行階段:撰寫並執行程式碼
實際效益
Token 使用量
- 1,000 個工具定義:150,000 Token
- 使用者問題:500 Token
- 總計:150,500 Token
Token 使用量
- 檔案系統結構:500 Token
- 2 個工具定義:1,000 Token
- 使用者問題:500 Token
- 總計:2,000 Token
Token 節省:150,500 → 2,000(降低 98.7%)
- 成本降低:每次對話節省 $0.30(以 Claude Sonnet 3.5 計價)
- 延遲降低:處理時間從 15 秒降至 2 秒
- 擴展性:支援連接 10,000+ 工具,不影響效能
進階技巧:search_tools 工具
為 MCP 伺服器新增 search_tools 工具,支援關鍵字搜尋:
// 代理撰寫程式碼
const tools = await search_tools({
query: "salesforce",
detail: "name_and_description" // name_only | name_and_description | full_schema
});
// 回傳相關工具清單
// → updateRecord, query, createLead, ...
這讓代理可以快速找到需要的工具,而不需要瀏覽整個檔案系統。
突破 2:資料在執行環境中處理
最強大的功能:資料可以在程式碼執行環境中過濾、轉換、聚合,再回傳給模型。
實戰案例 1:過濾大型試算表
TOOL_CALL:
gdrive.getSheet("abc123")
回傳:10,000 列 × 20 欄
= 200,000 Token 載入上下文
模型手動過濾:
「找出狀態為 pending 的訂單」
const allRows =
await gdrive.getSheet("abc123");
const pending = allRows.filter(
row => row.Status === 'pending'
);
console.log(
`找到 ${pending.length} 筆訂單`
);
console.log(pending.slice(0, 5));
回傳給模型:5 列(500 Token)
效益:從 200,000 Token 降至 500 Token(降低 99.75%)
實戰案例 2:迴圈與條件判斷
任務:「等待 Slack 的部署完成通知」
輪次 1:
TOOL_CALL: slack.getMessages(...)
回傳:未找到 → 5,000 Token
輪次 2:
TOOL_CALL: sleep(5000)
TOOL_CALL: slack.getMessages(...)
回傳:未找到 → 5,000 Token
輪次 3-10:
... 重複 8 次 ...
總計:10 輪次 × 5,000 Token
= 50,000 Token
let found = false;
while (!found) {
const messages =
await slack.getChannelHistory({
channel: 'C123456'
});
found = messages.some(
m => m.text.includes('部署完成')
);
if (!found) {
await sleep(5000);
}
}
console.log('收到部署通知');
回傳給模型:1 行(20 Token)
效益:從 50,000 Token 降至 20 Token(降低 99.96%),並大幅降低延遲(不需等待模型推理)。
實戰案例 3:隱私保護
任務:「從試算表匯入客戶聯絡資訊到 Salesforce」
// 代理撰寫程式碼(看不到真實資料)
const sheet = await gdrive.getSheet({ sheetId: 'abc123' });
for (const row of sheet.rows) {
await salesforce.updateRecord({
objectType: 'Lead',
recordId: row.salesforceId,
data: {
Email: row.email, // 實際值:john@example.com
Phone: row.phone, // 實際值:+886912345678
Name: row.name // 實際值:張三
}
});
}
console.log(`已更新 ${sheet.rows.length} 筆潛在客戶`);
MCP 客戶端的自動標記化(Tokenization):
// 代理看到的資料(已脫敏)
[
{
salesforceId: '00Q...',
email: '[EMAIL_1]',
phone: '[PHONE_1]',
name: '[NAME_1]'
},
{
salesforceId: '00Q...',
email: '[EMAIL_2]',
phone: '[PHONE_2]',
name: '[NAME_2]'
}
]
當資料寫入 Salesforce 時,MCP 客戶端會自動將 [EMAIL_1] 還原為 john@example.com。
- 零曝光:敏感資料永不進入模型上下文
- 合規性:符合 GDPR、HIPAA 等隱私法規
- 可稽核:資料流向清晰(Google Sheets → Salesforce,不經過模型)
突破 3:技能持久化(Skills)
程式碼執行環境允許代理將成功的實作儲存為可重複使用的函式,建立「技能庫」。
技能儲存範例
// ./skills/save-sheet-as-csv.ts
import * as gdrive from './servers/google-drive';
import { writeFile } from 'fs/promises';
/**
* 將 Google Sheets 試算表儲存為 CSV 檔案
* @param sheetId - Google Sheets 文件 ID
* @returns CSV 檔案路徑
*/
export async function saveSheetAsCsv(
sheetId: string
): Promise {
const data = await gdrive.getSheet({ sheetId });
const csv = data
.map(row => row.join(','))
.join('\n');
const path = `./workspace/sheet-${sheetId}.csv`;
await writeFile(path, csv);
return path;
}
技能重複使用
// 未來任何代理執行都可以呼叫
import { saveSheetAsCsv } from './skills/save-sheet-as-csv';
const csvPath = await saveSheetAsCsv('abc123');
console.log(`CSV 已儲存至 ${csvPath}`);
結合 SKILL.md 建立結構化技能
./skills/save-sheet-as-csv/
├── index.ts # 技能程式碼
├── SKILL.md # 技能文件
└── test.ts # 測試範例
// SKILL.md 內容
# 儲存試算表為 CSV 技能
## 用途
將 Google Sheets 試算表下載並轉換為 CSV 格式。
## 何時使用
- 需要離線存取試算表資料
- 準備匯入其他系統(如資料庫、Excel)
- 備份重要資料
## 範例
```typescript
const path = await saveSheetAsCsv('1a2b3c4d');
// → ./workspace/sheet-1a2b3c4d.csv
```
- 學習能力:代理從經驗中學習,不需重複「發明輪子」
- 一致性:相同任務使用相同實作,降低錯誤
- 可維護性:技能有文件、測試,可由人類審查與改進
- 團隊共享:技能庫可在組織內共享,加速開發
狀態持久化
代理可將中間結果寫入檔案,實現跨對話的狀態維護:
// 第一次執行:匯出資料
const leads = await salesforce.query({
query: 'SELECT Id, Email FROM Lead LIMIT 1000'
});
const csv = leads
.map(l => `${l.Id},${l.Email}`)
.join('\n');
await writeFile('./workspace/leads.csv', csv);
// 第二次執行:從檔案讀取(無需重新查詢)
const saved = await readFile('./workspace/leads.csv', 'utf-8');
const leads = saved.split('\n').map(line => {
const [id, email] = line.split(',');
return { id, email };
});
這對長時間任務(如批次處理 10,000 筆記錄)特別有用:即使中斷,也可從上次進度繼續。
⚖️ 權衡考量:程式碼執行的複雜度
Anthropic 在文章中也誠實指出,程式碼執行模式並非沒有代價:
- 安全隔離:需要沙箱環境執行代理生成的程式碼(如 Docker、Firecracker)
- 資源限制:需設定 CPU/記憶體/網路限制,防止濫用
- 監控與稽核:需記錄程式碼執行、資料流向,符合合規要求
- 基礎設施成本:執行環境需要伺服器資源(相對於直接工具呼叫的無狀態模式)
決策標準
| 場景 | 使用直接工具呼叫 | 使用程式碼執行 |
|---|---|---|
| 工具數量 | < 10 個工具 | 100+ 個工具 |
| 資料處理 | 小型結果(< 1,000 Token) | 大型資料集、需過濾/轉換 |
| 控制流 | 單一工具呼叫 | 迴圈、條件判斷、錯誤處理 |
| 隱私需求 | 資料可進入模型 | 敏感資料不可曝光 |
| 基礎設施 | 無狀態、輕量 | 可承擔執行環境成本 |
📝 5 個關鍵要點總結
- 問題:直接工具呼叫會浪費大量 Token(工具定義佔用上下文、中間結果重複處理)
- 解決方案:將 MCP 伺服器呈現為程式碼 API,讓代理撰寫程式碼呼叫工具
- 突破 1:按需載入工具定義,Token 使用降低 98.7%(150K → 2K)
- 突破 2:資料在執行環境中過濾/轉換,隱私保護、大型資料集處理
- 突破 3:技能持久化,代理可儲存並重複使用成功的實作
- 政府機關:隱私保護、合規稽核、大量文件處理
- 企業 IT 部門:整合數百個內部系統、降低 AI 成本
- AI 產品開發者:建立可擴展的代理平台、支援外掛生態系統
🔗 延伸閱讀
Anthropic 官方文章:Code execution with MCP - Building more efficient agents
本文精煉自 Anthropic 於 2025 年 11 月發表的官方技術文章,保留核心技術要點並加入臺灣在地化說明。