iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
Build on Google AI

30 天玩轉 Google AI 全家桶:初學者的隨身 Coding 助理養成記系列 第 10 篇

Day 10:拒絕廢話連篇:強制 Gemini 回傳標準 JSON

  • 分享至 

  • xImage
  •  

昨天讓 Gemini 在終端機裡學會「打字」。

今天碰到一個更現實的問題:

如果 Gemini 的回答不是拿來給人看的,而是要交給程式繼續處理呢?

假設 DevPulse 要的是:

{
  "issue": "變數可能未初始化",
  "suggestion": "使用前先確認資料是否存在"
}

但 Gemini 偏偏回你:

這段程式有一個問題,我先幫你分析一下:

```json
{
  "issue": "變數可能未初始化",
  "suggestion": "使用前先確認資料是否存在"
}

希望這個說明對你有幫助!


對人來說,這樣的回答完全沒問題。

但對程式來說,這就很麻煩了。

因為我真正想做的是:

```javascript
const result = JSON.parse(response);

多了一句「希望這個說明對你有幫助」,JSON.parse() 就直接爆炸。

所以今天的問題不是「Gemini 會不會回答」,而是:

怎麼讓 Gemini 的輸出格式,變成程式可以放心接手的資料?


一、光靠 Prompt 說「請回傳 JSON」其實不夠

最直覺的方法,大概會先寫:

請只回傳 JSON。
不要使用 Markdown。
不要加入任何額外說明。

這招有時候有效,但問題是,我其實還是在「拜託 AI 自己遵守規則」。

而且格式越複雜,越容易出現一些小變化。

例如我希望永遠都有:

{
  "issue": "...",
  "suggestion": "..."
}

結果某次可能變成:

{
  "problem": "...",
  "fix": "..."
}

JSON 語法本身完全合法。

但對 DevPulse 來說,problem 根本不是我定義的欄位。

因此真正需要控制的,不只是「我要 JSON」,而是:

我要什麼樣子的 JSON。


二、讓 API 幫忙限制輸出格式

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 要的格式

今天先不要搞太複雜。

DevPulse 的程式碼分析結果,只需要兩個欄位:

{
  "issue": "",
  "suggestion": ""
}

對應到 Schema:

const responseSchema = {
  type: "object",
  properties: {
    issue: {
      type: "string",
      description: "程式碼問題的簡短說明"
    },
    suggestion: {
      type: "string",
      description: "具體修正建議"
    }
  },
  required: ["issue", "suggestion"]
};

這裡第一次出現 schema 的概念。

現在先把它理解成:

告訴 Gemini:「你最後交給我的資料,應該長什麼樣子。」

真正更進一步的型別驗證,明天再來處理。


四、完整程式:讓 Gemini 只交 JSON

延續前幾天的 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:

  1. 我要文字形式的輸出
  2. MIME Type 是 application/json
  3. JSON 必須符合我提供的 Schema

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

看到今天的標題,很容易直覺寫出:

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 登場。


上一篇
Day 09:終端機打字機效果:Gemini 串流響應(Streaming)實作
下一篇
Day 11:讓格式更可靠:用 Zod 幫 Gemini 回應加上一道型別防線
系列文
30 天玩轉 Google AI 全家桶:初學者的隨身 Coding 助理養成記 共 12 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言