在昨天的 [Day 10] 中,我們透過 Structured Outputs 成功為 Gemini 戴上了 JSON Schema 緊箍咒,保證了輸出資料的確定性與前端渲染的穩定性。
然而,大語言模型(LLM)即便擁有了百萬 Token 的記憶體與精確的格式控制,在本質上依然是一個「封閉世界的靜態大腦」。它面臨兩個致命弱點:
如果我們的 OmniVibe AI 只能總結用戶上傳的現有內容,那它只是一個進階版的文字處理器;但如果它能自主判斷何時需要調用外部工具,獲取即時資料補足上下文,它就正式進化為具備決策能力的 AI Agent(智慧代理)。
今天,我們將深入 Google Gemini 的核心超能力 —— Function Calling(工具調用 / 函數調用),並用 Next.js 打造一套完整的雙向調用閉環!
許多初學者對 Function Calling 有一個誤解,以為「大模型會直接去幫我發 HTTP Request 調用外部 API」。
事實並非如此,也不該如此(因為會有極大的安全性風險)。
Function Calling 的本質是「語意決策與參數萃取」。模型只負責當大腦,真正的執行權依然完全掌握在我們的後端伺服器手中:
sequenceDiagram
autonumber
participant User as 使用者
participant Server as Next.js 後端 (Controller)
participant Gemini as Gemini 1.5 大腦
participant ExternalAPI as 外部工具 / 資料庫
User->>Server: 提出請求:「幫我分析這篇文章,並結合今天的最新 AI 趨勢產出貼文」
Server->>Gemini: 帶著 [用戶問題] + [可用工具宣告清單 (Tools)] 發起請求
Note over Gemini: 模型分析後判定需要最新趨勢,決定調用 fetchTrendingTopics
Gemini-->>Server: 回傳 FunctionCall 物件:{ name: "fetchTrendingTopics", args: { category: "AI" } }
Server->>ExternalAPI: 後端代為執行真實函式 fetchTrendingTopics("AI")
ExternalAPI-->>Server: 回傳真實外部資料:["Google 發布新模型", "AI 代理架構普及"]
Server->>Gemini: 將函式執行結果 (FunctionResponse) 餵回模型
Gemini-->>Server: 模型結合外部真實數據,產出最終分析成果
Server-->>User: 回傳完整內容
整個架構呈現一個優雅的 4 步閉環:
在內容提煉場景中,若用戶上傳了一份技術分析報告,我們希望 AI 能主動調用一個名為 fetchSocialTrending 的工具,獲取今日社群最夯的 HashTags 與討論主題,使產出的社群貼文更具擴散性。
src/lib/gemini/tools.ts)我們使用 @google/generative-ai 提供的 FunctionDeclaration 規格來宣告工具:
// src/lib/gemini/tools.ts
import { FunctionDeclaration, SchemaType } from '@google/generative-ai';
// 1. 定義真實執行的本機/外部函式 (Mock 或對接真實搜尋 API)
export async function fetchSocialTrending(args: { topic: string; platform: string }) {
console.log(`[執行外部工具] 正在檢索 ${args.platform} 上關於 ${args.topic} 的即時熱點...`);
// 此處可對接 Tavily、SerpAPI 或內部熱門關鍵字資料庫
// 這裡回傳結構化的模擬即時數據
return {
timestamp: new Date().toISOString(),
topic: args.topic,
platform: args.platform,
trendingKeywords: ['#VibeCoding', '#GoogleAIStudio', '#Nextjs15', '#Gemini15'],
hottestAngle: '開發者熱烈討論如何用自然語言直接生成可部署的生產級全端架構',
};
}
// 2. 提供給 Gemini 大腦識別的工具宣告 Schema
export const socialTrendingToolDeclaration: FunctionDeclaration = {
name: 'fetchSocialTrending',
description: '獲取指定社群平台(如 threads, twitter, linkedin)上特定主題的當日即時熱門關鍵字與討論風向',
parameters: {
type: SchemaType.OBJECT,
properties: {
topic: {
type: SchemaType.STRING,
description: '想檢索的技術或產業主題,例如:AI 開發、SaaS 創業、前端技術',
},
platform: {
type: SchemaType.STRING,
enum: ['threads', 'twitter', 'linkedin'],
description: '目標檢索的社群平台',
},
},
required: ['topic', 'platform'],
},
};
src/app/api/ai/agent-transform/route.ts)現在我們在後端實作具備 Agent 決策能力的 API Route。如果模型回傳了 functionCalls,程式碼會自動截獲、執行對應邏輯,再將結果回灌給模型:
// src/app/api/ai/agent-transform/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { fetchSocialTrending, socialTrendingToolDeclaration } from '@/lib/gemini/tools';
import { OMNIVIBE_SYSTEM_INSTRUCTION } from '@/lib/gemini/prompts';
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || '');
export async function POST(req: NextRequest) {
try {
const { prompt } = await req.json();
if (!prompt) {
return NextResponse.json({ error: '請提供提示詞內容' }, { status: 400 });
}
// 1. 初始化模型,並掛載可用工具
const model = genAI.getGenerativeModel({
model: 'gemini-1.5-flash',
systemInstruction: OMNIVIBE_SYSTEM_INSTRUCTION,
tools: [{ functionDeclarations: [socialTrendingToolDeclaration] }],
});
// 2. 開啟多輪會話 (Chat Session) 來自然處理工具調用的上下文遞送
const chat = model.startChat();
// 3. 發送初次請求
let result = await chat.sendMessage(prompt);
let response = await result.response;
// 4. 檢查模型是否發出 Function Call 請求
const functionCalls = response.functionCalls();
if (functionCalls && functionCalls.length > 0) {
const call = functionCalls[0];
console.log(`[Gemini Agent 決策] 觸發工具調用: ${call.name}`, call.args);
if (call.name === 'fetchSocialTrending') {
// 5. 在後端執行真實函式
const toolResult = await fetchSocialTrending(call.args as any);
// 6. 將函式執行結果以 functionResponse 角色回灌給 Gemini
result = await chat.sendMessage([
{
functionResponse: {
name: 'fetchSocialTrending',
response: { content: toolResult },
},
},
]);
response = await result.response;
}
}
// 7. 回傳最終經過外部即時數據增強後的輸出
return NextResponse.json({
success: true,
data: {
output: response.text(),
wasToolCalled: Boolean(functionCalls && functionCalls.length > 0),
usageMetadata: response.usageMetadata,
},
});
} catch (error: any) {
console.error('[Agent Transform Error]:', error);
return NextResponse.json(
{ error: 'Agent 執行失敗', message: error.message },
{ status: 500 }
);
}
}
我們使用 curl 發送一個需要「即時時事與熱點結合」的複雜 Prompt:
curl -X POST http://localhost:3000/api/ai/agent-transform \
-H "Content-Type: application/json" \
-d '{
"prompt": "我剛寫完一篇關於 Next.js 與 Firebase 整合的心得。請幫我寫一篇適合發在 Threads 上的爆款短文,請務必先查詢今天 Threads 上關於 AI 開發的最熱風向,並將熱門關鍵字自然融入貼文中!"
}'
[Gemini Agent 決策] 觸發工具調用: fetchSocialTrending { platform: 'threads', topic: 'AI 開發' }
[執行外部工具] 正在檢索 threads 上關於 AI 開發 的即時熱點...
### 📱 Threads 爆款貼文(結合今日熱門風向)
別再爭論前後端架構了!今天 Threads 上最火的討論全都在聊 **#VibeCoding**。
大家都在問:怎麼可能用自然語言直接做出可商業化的全端 SaaS?
答案很簡單:把重複的膠水代碼交給 Google AI,把資料庫交給 Firebase,你的生產力直接翻十倍。
🔥 今日熱議切入點:
不再花兩週刻後端 API,現代開發者都在用 Gemini 1.5 的多模態與 Next.js 15 極速縮短驗證週期!
#VibeCoding #GoogleAIStudio #Nextjs15 #Gemini15
💬 你今天也開始用自然語言寫程式了嗎?底下留下你的看法!
fetchSocialTrending 工具。platform: "threads" 與 topic: "AI 開發"。#VibeCoding、#GoogleAIStudio)被自然且精確地編織進最終文案中。今天我們完成了從「純文本大模型」到「智慧型 Agent」的關鍵躍遷:
隨著我們塞給 Gemini 的上下文越來越龐大(數十萬字的白皮書、數小時的影片、再加上外部即時檢索數據),一個嚴肅的問題浮現了:「每一次重複對這份長文件提問,都要重新收費,錢包怎麼吃得消?」
👉 明天(Day 12),我們將進入第二篇章的收官之作【快取省成本篇】:實戰 Context Caching(上下文快取)機制!看我們如何用代碼將大檔案快取在雲端,直接砍掉 75% 的重複輸入成本與顯著降低延遲!
我們明天見!🔥