打造 AI SaaS 最令人沮喪的時刻,莫過於當你的前端 UI 刻得美輪美奐,後端卻因為大語言模型(LLM)多吐了一個字元而當場崩潰。
在過往的開發經驗中,我們常在 Prompt 裡千叮嚀萬萬囑咐:
「請務必回傳標準 JSON 格式,不要加入任何額外解釋或 Markdown 標籤(如 ```json)...」
結果呢?大模型心情好的時候很配合,偶爾卻會在開頭加上「好的,這是為您生成的 JSON:」,或是在最後一筆陣列元素後手滑加了一個逗號(Trailing Comma),甚至是直接漏掉核心欄位。這會直接導致後端或前端的 JSON.parse() 拋出語法錯誤(SyntaxError),讓使用者看到尷尬的白畫面(White Screen of Death)。
在商業級 SaaS 應用中,「不可預測的格式」就是嚴重的系統 Bug。
今天我們將深入探討 Gemini 1.5 提供的殺手級特性 —— Structured Outputs(結構化輸出保證),並用完整的 TypeScript 代碼為 OmniVibe AI 構建強固的資料契約,保證模型輸出的每一筆資料 100% 符合型別定義!
為什麼以往僅靠 Prompt 要求回傳 JSON 總是會破功?這牽涉到底層的生成機制:
graph TD
subgraph 傳統 Prompt 做法 (靠運氣)
A1[下達 Prompt: 請回傳 JSON] --> B1[模型自由預測下一個 Token]
B1 --> C1[可能產生 Markdown 標籤 或 口語文字]
C1 --> D1[後端 JSON.parse 隨機崩潰]
end
subgraph 原生 Structured Outputs (語法導向約束)
A2[傳入 JSON Schema 定義] --> B2[解碼器限制 (Constrained Decoding)]
B2 --> C2[強制 Token 只能沿著合法的 JSON 語法樹生成]
C2 --> D2[100% 絕對合法的結構化資料]
end
在 OmniVibe AI 的規格中,我們需要模型一次性產出三大結構化資產:
takeaways:核心論點清單。socialPosts:各社群平台的貼文陣列(標明平台、吸引人的 Hook、內文與 CTA)。shortClips:適合剪輯短影音的時間戳記分鏡腳本。executiveSummary:供高層決策的摘要物件。在 Google Gen AI SDK 中,我們使用 SDK 內建的 SchemaType 列舉來定義精確的 JSON Schema。
src/lib/gemini/schema.ts)// src/lib/gemini/schema.ts
import { ResponseSchema, SchemaType } from '@google/generative-ai';
// 1. 定義 TypeScript 介面,供前端與後端共用
export interface OmniVibeExtractionResult {
takeaways: string[];
socialPosts: Array<{
platform: 'threads' | 'x' | 'linkedin';
hook: string;
body: string;
hashtags: string[];
callToAction: string;
}>;
shortClips: Array<{
startTime: string;
endTime: string;
headline: string;
visualCue: string;
script: string;
}>;
executiveSummary: {
coreThesis: string;
actionItems: string[];
};
}
// 2. 定義給 Gemini 解碼器使用的強制 Schema
export const omniVibeResponseSchema: ResponseSchema = {
type: SchemaType.OBJECT,
properties: {
takeaways: {
type: SchemaType.ARRAY,
description: '3 到 5 個關鍵的核心洞見與事實論點',
items: { type: SchemaType.STRING },
},
socialPosts: {
type: SchemaType.ARRAY,
description: '針對不同社群平台客製化的爆款貼文',
items: {
type: SchemaType.OBJECT,
properties: {
platform: {
type: SchemaType.STRING,
enum: ['threads', 'x', 'linkedin'],
description: '目標發布社群平台',
},
hook: { type: SchemaType.STRING, description: '前 3 行極具吸睛效果的開頭鉤子' },
body: { type: SchemaType.STRING, description: '條列式重點與乾貨論述' },
hashtags: {
type: SchemaType.ARRAY,
items: { type: SchemaType.STRING },
description: '熱門關聯標籤',
},
callToAction: { type: SchemaType.STRING, description: '引導互動留言的文案' },
},
required: ['platform', 'hook', 'body', 'hashtags', 'callToAction'],
},
},
shortClips: {
type: SchemaType.ARRAY,
description: '適合剪成 60 秒短影音的分鏡腳本',
items: {
type: SchemaType.OBJECT,
properties: {
startTime: { type: SchemaType.STRING, description: '格式如 01:23' },
endTime: { type: SchemaType.STRING, description: '格式如 02:15' },
headline: { type: SchemaType.STRING, description: '該片段的吸睛標題' },
visualCue: { type: SchemaType.STRING, description: '畫面剪輯或視覺動作提示' },
script: { type: SchemaType.STRING, description: '口播台詞精華' },
},
required: ['startTime', 'endTime', 'headline', 'visualCue', 'script'],
},
},
executiveSummary: {
type: SchemaType.OBJECT,
description: '高層決策筆記',
properties: {
coreThesis: { type: SchemaType.STRING, description: '核心論點總結' },
actionItems: {
type: SchemaType.ARRAY,
items: { type: SchemaType.STRING },
description: '具體可落地的行動方針',
},
},
required: ['coreThesis', 'actionItems'],
},
},
required: ['takeaways', 'socialPosts', 'shortClips', 'executiveSummary'],
};
在 src/app/api/ai/transform-structured/route.ts 中,我們在呼叫 Gemini 1.5 Flash 時,透過 generationConfig 注入兩項最關鍵的屬性:
responseMimeType: 'application/json':告知模型回傳標準 JSON 字串。responseSchema: omniVibeResponseSchema:強制約束資料骨架。// src/app/api/ai/transform-structured/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { OMNIVIBE_SYSTEM_INSTRUCTION } from '@/lib/gemini/prompts';
import { omniVibeResponseSchema, OmniVibeExtractionResult } from '@/lib/gemini/schema';
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || '');
export async function POST(req: NextRequest) {
try {
const { content } = await req.json();
if (!content || typeof content !== 'string') {
return NextResponse.json({ error: '請提供合法的文字內容' }, { status: 400 });
}
// 1. 初始化模型並套用約束 Schema
const model = genAI.getGenerativeModel({
model: 'gemini-1.5-flash',
systemInstruction: OMNIVIBE_SYSTEM_INSTRUCTION,
generationConfig: {
temperature: 0.3,
responseMimeType: 'application/json',
responseSchema: omniVibeResponseSchema, // 關鍵:鎖定 Schema
},
});
const userPrompt = `請分析以下輸入素材,嚴格按照 Schema 要求產出多模態知識矩陣:\n\n${content}`;
// 2. 呼叫模型
const result = await model.generateContent(userPrompt);
const rawText = result.response.text();
// 3. 安全解析:因為有 Schema 保證,此處的 parse 具備 100% 確定性
const parsedData: OmniVibeExtractionResult = JSON.parse(rawText);
return NextResponse.json({
success: true,
data: parsedData,
usageMetadata: result.response.usageMetadata,
});
} catch (error: any) {
console.error('[Structured Output Error]:', error);
return NextResponse.json(
{ error: '資料結構化轉換失敗', details: error.message },
{ status: 500 }
);
}
}
使用 curl 對這支全新端點發送測試文本:
curl -X POST http://localhost:3000/api/ai/transform-structured \
-H "Content-Type: application/json" \
-d '{
"content": "在打造 AI SaaS 的過程中,很多團隊死於過度工程化。第一個月應該先專注在用戶是否願意為核心功能付費,而不是一開始就花大錢搭建高併發微服務。善用 Google AI 的 Gemini API 與 Firebase,單兵作戰就能在兩週內完成具備付費驗證的 MVP。"
}'
{
"success": true,
"data": {
"takeaways": [
"AI SaaS 初期的致命傷在於過度工程化,而非技術深度不夠。",
"MVP 的核心指標是付費意願驗證,而非過早追求高併發微服務。",
"採用 Google AI (Gemini) 與 Firebase 可以在兩週內快速完成可商業化的產品閉環。"
],
"socialPosts": [
{
"platform": "threads",
"hook": "90% 的 AI 新創團隊,其實都死在『過度工程化』這把雙面刃下。",
"body": "很多工程師一上來就規劃 Kubernetes、微服務架構,結果上線第一天根本沒人造訪。\n\n聰明開發者這樣做:\n1. 用 Gemini 1.5 解決核心推論\n2. 搭配 Firebase App Hosting 快速部署\n3. 兩週上線驗證真實付費意願",
"hashtags": ["#獨立開發", "#VibeCoding", "#SaaS創業"],
"callToAction": "你在開發初期踩過最大的坑是什麼?歡迎在下方分享!"
}
],
"shortClips": [
{
"startTime": "00:00",
"endTime": "00:45",
"headline": "為什麼你的 AI 專案不需要一開始就上微服務?",
"visualCue": "對著鏡頭敲桌強調,後方背景畫面展示複雜的架構圖叉叉",
"script": "停!不要再花一個月去架設沒人造訪的高併發架構了..."
}
],
"executiveSummary": {
"coreThesis": "新創 AI 產品應以最精簡架構(Gemini + Firebase)實現極速市場驗證。",
"actionItems": [
"凍結非必要的微服務重構計畫",
"兩週內完成整合 Stripe 的端到端 MVP",
"建立第一批付費種子用戶反饋渠道"
]
}
}
}
platform 欄位精準命中 threads、x、linkedin 之一,絕不自創未定義的字串。required 陣列中宣告的鍵值全部被完整填補,前端 UI 元件可以直接進行 data.socialPosts.map(...) 渲染,永不觸發 undefined 錯誤!今天我們為 OmniVibe AI 奠定了最堅實的資料骨架:
@google/generative-ai 的 SchemaType 精確繪製了多模態知識提煉的資料契約。現在,我們的 AI 已經具備了讀取長文件、辨識影音以及穩定回傳結構化資料的強大能力。但如果我們希望 AI 能更主動 —— 例如發現輸入內容提到了即時股市行情、或是需要即時搜尋 Google 取得最新趨勢呢?
👉 明天(Day 11),我們將進入【工具調用篇】:實戰 Function Calling(工具調用)!看我們如何讓 Gemini 突破知識庫的時間限制,主動聯網搜尋與調用外部 API,讓靜態模型變身自主 Agent!
敬請期待,我們明天見!🔥