昨天讓 Gemini 在終端機裡學會「打字」。
今天碰到一個更現實的問題:
如果 Gemini 的回答不是拿來給人看的,而是要交給程式繼續處理呢?
假設 DevPulse 要的是:
{
"issue": "變數可能未初始化",
"suggestion": "使用前先確認資料是否存在"
}
但 Gemini 偏偏回你:
這段程式有一個問題,我先幫你分析一下:
```json
{
"issue": "變數可能未初始化",
"suggestion": "使用前先確認資料是否存在"
}
希望這個說明對你有幫助!
對人來說,這樣的回答完全沒問題。
但對程式來說,這就很麻煩了。
因為我真正想做的是:
```javascript
const result = JSON.parse(response);
多了一句「希望這個說明對你有幫助」,JSON.parse() 就直接爆炸。
所以今天的問題不是「Gemini 會不會回答」,而是:
怎麼讓 Gemini 的輸出格式,變成程式可以放心接手的資料?
最直覺的方法,大概會先寫:
請只回傳 JSON。
不要使用 Markdown。
不要加入任何額外說明。
這招有時候有效,但問題是,我其實還是在「拜託 AI 自己遵守規則」。
而且格式越複雜,越容易出現一些小變化。
例如我希望永遠都有:
{
"issue": "...",
"suggestion": "..."
}
結果某次可能變成:
{
"problem": "...",
"fix": "..."
}
JSON 語法本身完全合法。
但對 DevPulse 來說,problem 根本不是我定義的欄位。
因此真正需要控制的,不只是「我要 JSON」,而是:
我要什麼樣子的 JSON。
Google 的 Interactions API 提供了 Structured Output,可以直接在 response_format 裡告訴 Gemini:
「我要的是 JSON,而且欄位就只有這些。」
目前新版 Interactions API 的寫法,是在 response_format 裡指定:
response_format: {
type: "text",
mime_type: "application/json",
schema: {
...
}
}
也就是把「輸出格式」從 Prompt 裡的文字要求,提升成 API 層級的規格。
這個差異很重要。
Prompt 是:
請你盡量照我的規則回答。
Structured Output 則比較像:
我要的資料結構就是這個。
今天先不要搞太複雜。
DevPulse 的程式碼分析結果,只需要兩個欄位:
{
"issue": "",
"suggestion": ""
}
對應到 Schema:
const responseSchema = {
type: "object",
properties: {
issue: {
type: "string",
description: "程式碼問題的簡短說明"
},
suggestion: {
type: "string",
description: "具體修正建議"
}
},
required: ["issue", "suggestion"]
};
這裡第一次出現 schema 的概念。
現在先把它理解成:
告訴 Gemini:「你最後交給我的資料,應該長什麼樣子。」
真正更進一步的型別驗證,明天再來處理。
延續前幾天的 index.js:
import 'dotenv/config';
import { GoogleGenAI } from "@google/genai";
const ai = new GoogleGenAI({});
async function main() {
const responseSchema = {
type: "object",
properties: {
issue: {
type: "string",
description: "程式碼問題的簡短說明"
},
suggestion: {
type: "string",
description: "具體修正建議"
}
},
required: ["issue", "suggestion"]
};
const interaction = await ai.interactions.create({
model: "gemini-3-flash-preview",
input: `
請分析下面這段程式碼。
\`\`\`javascript
const user = {};
console.log(user.profile.name);
\`\`\`
找出最主要的問題,並提出修正建議。
`,
generation_config: {
thinking_level: "minimal",
},
response_format: {
type: "text",
mime_type: "application/json",
schema: responseSchema
}
});
const result = JSON.parse(interaction.output_text);
console.log(result);
}
main();
這次最值得注意的不是 Prompt,而是這一段:
response_format: {
type: "text",
mime_type: "application/json",
schema: responseSchema
}
它告訴 API:
application/json
Google 目前的官方文件也是以這種方式實作 Structured Output。
JSON.parse()執行:
node index.js
理想結果會是類似:
{
issue: '嘗試存取一個不存在的物件屬性中的子屬性,導致 TypeError。',
suggestion: '在存取深層屬性前,應先確保中間層級 (user.profile) 存在。建議使用可選連鎖運算子 (Optional Chaining) `user.profile?.name` 或先為屬性賦值。'
}
這時候最重要的變化是:
Gemini 的回答已經不只是「一段文字」。
它開始變成:
程式可以接手的資料。
例如下一步就可以直接:
console.log(result.issue);
console.log(result.suggestion);
甚至之後可以把它存成檔案、傳進另一個函式,或交給其他 API。
這也是 DevPulse 從「聊天小工具」開始往「真正的程式工具」前進的一個小轉折。
json_mode看到今天的標題,很容易直覺寫出:
json_mode: true
但目前新版 Interactions API 並不是這樣設計。
新版 API 已經把輸出格式控制整合到 response_format,而舊版的 response_mime_type 等寫法已經隨 2026 年的 Interactions API 更新而調整。
所以今天真正應該記住的不是某一個「神奇參數」,而是一個觀念:
不要只告訴模型「我要 JSON」,而是直接定義「我要什麼結構的 JSON」。
另外,昨天剛學會 Streaming,今天可能會忍不住想:
「那我能不能一邊收到 JSON,一邊直接 JSON.parse()?」
先不要。
Structured Output 確實可以搭配 Streaming,官方文件也說明串流收到的是可以累積的部分 JSON;但單獨拿其中一小段來 JSON.parse(),本來就不一定是完整 JSON。
所以今天先採用最單純的方式:
等完整結果 → 取得 output_text → JSON.parse()
把「串流+結構化資料」這個問題留給之後再處理。
Day 09 解決的是:
「AI 回答太慢,看不到進度怎麼辦?」
所以我們加入 Streaming。
Day 10 解決的是:
「AI 回答有了,但程式不知道該怎麼接怎麼辦?」
所以我們開始使用 Structured Output。
這兩個功能解決的是完全不同的問題:
Streaming
↓
讓人更快看到內容
Structured Output
↓
讓程式更容易理解內容
而今天的 { issue, suggestion },其實已經開始很像 DevPulse 真正需要的資料結構了。
只是還有最後一個問題:
Gemini 說自己會照 Schema 輸出,不代表我們的程式就可以永遠毫無防備地相信它。
欄位會不會缺少?
型別會不會錯?
資料到底有沒有真的符合規則?
這些問題,就留給明天。
Day 11:讓格式絕對可靠——Zod Schema 登場。