구조화된 출력

구조화된 출력 (Structured outputs)

Gemini 모델이 제공된 JSON Schema를 따르는 응답을 생성하도록 구성할 수 있어요. 이를 통해 예측 가능하고 타입 안전한 결과를 보장하고, 비구조화된 텍스트에서 구조화된 데이터를 추출하는 작업을 단순화할 수 있어요.

구조화된 출력은 다음과 같은 경우에 이상적이에요:

  • 데이터 추출(Data extraction): 텍스트에서 이름, 날짜 같은 특정 정보를 뽑아내기.
  • 구조화된 분류(Structured classification): 텍스트를 미리 정의된 카테고리로 분류하기.
  • 에이전트 워크플로(Agentic workflows): 도구나 API용 구조화된 입력 생성하기.

REST API에서 JSON Schema를 지원하는 것에 더해, Google GenAI SDK는 Pydantic(Python)과 Zod(JavaScript)로 스키마를 쉽게 정의하게 해 줘요.

출처: 원문

본문

구조화된 출력 예시 (Structured output examples)

레시피 추출기 (Recipe Extractor)

이 예시는 object, array, string, integer 같은 기본 JSON Schema 유형으로 텍스트에서 구조화된 데이터를 추출하는 방법을 보여줘요.

from google import genai
from pydantic import BaseModel, Field
from typing import List, Optional

class Ingredient(BaseModel):
    name: str = Field(description="Name of the ingredient.")
    quantity: str = Field(description="Quantity of the ingredient, including units.")

class Recipe(BaseModel):
    recipe_name: str = Field(description="The name of the recipe.")
    prep_time_minutes: Optional[int] = Field(description="Optional time in minutes to prepare the recipe.")
    ingredients: List[Ingredient]
    instructions: List[str]

client = genai.Client()

prompt = """
Please extract the recipe from the following text.
The user wants to make delicious chocolate chip cookies.
They need 2 and 1/4 cups of all-purpose flour, 1 teaspoon of baking soda,
1 teaspoon of salt, 1 cup of unsalted butter (softened), 3/4 cup of granulated sugar,
3/4 cup of packed brown sugar, 1 teaspoon of vanilla extract, and 2 large eggs.
For the best part, they'll need 2 cups of semisweet chocolate chips.
First, preheat the oven to 375°F (190°C). Then, in a small bowl, whisk together the flour,
baking soda, and salt. In a large bowl, cream together the butter, granulated sugar, and brown sugar
until light and fluffy. Beat in the vanilla and eggs, one at a time. Gradually beat in the dry
ingredients until just combined. Finally, stir in the chocolate chips. Drop by rounded tablespoons
onto ungreased baking sheets and bake for 9 to 11 minutes.
"""

response = client.models.generate_content(
    model="gemini-3.8-flash",
    contents=prompt,
    config={
        "response_format": {"text": {"mime_type": "application/json", "schema": Recipe.model_json_schema()}},
    },
)

recipe = Recipe.model_validate_json(response.text)
print(recipe)
import { GoogleGenAI } from "@google/genai";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const ingredientSchema = z.object({
  name: z.string().describe("Name of the ingredient."),
  quantity: z.string().describe("Quantity of the ingredient, including units."),
});

const recipeSchema = z.object({
  recipe_name: z.string().describe("The name of the recipe."),
  prep_time_minutes: z.number().optional().describe("Optional time in minutes to prepare the recipe."),
  ingredients: z.array(ingredientSchema),
  instructions: z.array(z.string()),
});

const ai = new GoogleGenAI({});

const prompt = `
Please extract the recipe from the following text.
The user wants to make delicious chocolate chip cookies.
They need 2 and 1/4 cups of all-purpose flour, 1 teaspoon of baking soda,
1 teaspoon of salt, 1 cup of unsalted butter (softened), 3/4 cup of granulated sugar,
3/4 cup of packed brown sugar, 1 teaspoon of vanilla extract, and 2 large eggs.
For the best part, they'll need 2 cups of semisweet chocolate chips.
First, preheat the oven to 375°F (190°C). Then, in a small bowl, whisk together the flour,
baking soda, and salt. In a large bowl, cream together the butter, granulated sugar, and brown sugar
until light and fluffy. Beat in the vanilla and eggs, one at a time. Gradually beat in the dry
ingredients until just combined. Finally, stir in the chocolate chips. Drop by rounded tablespoons
onto ungreased baking sheets and bake for 9 to 11 minutes.
`;

const response = await ai.models.generateContent({
  model: "gemini-3.8-flash",
  contents: prompt,
  config: {
    responseFormat: { text: { mimeType: "application/json", schema: zodToJsonSchema(recipeSchema) } },
  },
});

const recipe = recipeSchema.parse(JSON.parse(response.text));
console.log(recipe);
package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
)

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }

    prompt := `
  Please extract the recipe from the following text.
  The user wants to make delicious chocolate chip cookies.
  They need 2 and 1/4 cups of all-purpose flour, 1 teaspoon of baking soda,
  1 teaspoon of salt, 1 cup of unsalted butter (softened), 3/4 cup of granulated sugar,
  3/4 cup of packed brown sugar, 1 teaspoon of vanilla extract, and 2 large eggs.
  For the best part, they'll need 2 cups of semisweet chocolate chips.
  First, preheat the oven to 375°F (190°C). Then, in a small bowl, whisk together the flour,
  baking soda, and salt. In a large bowl, cream together the butter, granulated sugar, and brown sugar
  until light and fluffy. Beat in the vanilla and eggs, one at a time. Gradually beat in the dry
  ingredients until just combined. Finally, stir in the chocolate chips. Drop by rounded tablespoons
  onto ungreased baking sheets and bake for 9 to 11 minutes.
  `
    config := &genai.GenerateContentConfig{
        ResponseMIMEType: "application/json",
        ResponseJsonSchema: map[string]any{
            "type": "object",
            "properties": map[string]any{
                "recipe_name": map[string]any{
                    "type":        "string",
                    "description": "The name of the recipe.",
                },
                "prep_time_minutes": map[string]any{
                    "type":        "integer",
                    "description": "Optional time in minutes to prepare the recipe.",
                },
                "ingredients": map[string]any{
                    "type": "array",
                    "items": map[string]any{
                        "type": "object",
                        "properties": map[string]any{
                            "name": map[string]any{
                                "type":        "string",
                                "description": "Name of the ingredient.",
                            },
                            "quantity": map[string]any{
                                "type":        "string",
                                "description": "Quantity of the ingredient, including units.",
                            },
                        },
                        "required": []string{"name", "quantity"},
                    },
                },
                "instructions": map[string]any{
                    "type":  "array",
                    "items": map[string]any{"type": "string"},
                },
            },
            "required": []string{"recipe_name", "ingredients", "instructions"},
        },
    }

    result, err := client.Models.GenerateContent(
        ctx,
        "gemini-3.8-flash",
        genai.Text(prompt),
        config,
    )
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result.Text())
}
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
    -H "x-goog-api-key: *** \
    -H 'Content-Type: application/json' \
    -X POST \
    -d '{
      "contents": [{
        "parts":[
          { "text": "Please extract the recipe from the following text.\nThe user wants to make delicious chocolate chip cookies.\nThey need 2 and 1/4 cups of all-purpose flour, 1 teaspoon of baking soda,\n1 teaspoon of salt, 1 cup of unsalted butter (softened), 3/4 cup of granulated sugar,\n3/4 cup of packed brown sugar, 1 teaspoon of vanilla extract, and 2 large eggs.\nFor the best part, they will need 2 cups of semisweet chocolate chips.\nFirst, preheat the oven to 375°F (190°C). Then, in a small bowl, whisk together the flour,\nbaking soda, and salt. In a large bowl, cream together the butter, granulated sugar, and brown sugar\nuntil light and fluffy. Beat in the vanilla and eggs, one at a time. Gradually beat in the dry\ningredients until just combined. Finally, stir in the chocolate chips. Drop by rounded tablespoons\nonto ungreased baking sheets and bake for 9 to 11 minutes." }
        ]
      }],
      "generationConfig": {
        "responseFormat": {
          "text": {
            "mimeType": "application/json",
            "schema": {
          "type": "object",
          "properties": {
            "recipe_name": {
              "type": "string",
              "description": "The name of the recipe."
            },
            "prep_time_minutes": {
                "type": "integer",
                "description": "Optional time in minutes to prepare the recipe."
            },
            "ingredients": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": { "type": "string", "description": "Name of the ingredient."},
                  "quantity": { "type": "string", "description": "Quantity of the ingredient, including units."}
          }
        }
      },
                "required": ["name", "quantity"]
              }
            },
            "instructions": {
              "type": "array",
              "items": { "type": "string" }
            }
          },
          "required": ["recipe_name", "ingredients", "instructions"]
        }
      }
    }'

예시 응답 (Example Response):

{ "recipe_name" : "Delicious Chocolate Chip Cookies" , "ingredients" : [ { "name" : "all-purpose flour" , "quantity" : "2 and 1/4 cups" }, { "name" : "baking soda" , "quantity" : "1 teaspoon" }, { "name" : "salt" , "quantity" : "1 teaspoon" }, { "name" : "unsalted butter (softened)" , "quantity" : "1 cup" }, { "name" : "granulated sugar" , "quantity" : "3/4 cup" }, { "name" : "packed brown sugar" , "quantity" : "3/4 cup" }, { "name" : "vanilla extract" , "quantity" : "1 teaspoon" }, { "name" : "large eggs" , "quantity" : "2" }, { "name" : "semisweet chocolate chips" , "quantity" : "2 cups" } ], "instructions" : [ "Preheat the oven to 375°F (190°C)." , "In a small bowl, whisk together the flour, baking soda, and salt." , "In a large bowl, cream together the butter, granulated sugar, and brown sugar until light and fluffy." , "Beat in the vanilla and eggs, one at a time." , "Gradually beat in the dry ingredients until just combined." , "Stir in the chocolate chips." , "Drop by rounded tablespoons onto ungreased baking sheets and bake for 9 to 11 minutes." ] }

콘텐츠 중재 (Content Moderation)

이 예시는 조건부 스키마를 위한 anyOf와 분류를 위한 enum을 보여줘요. 콘텐츠에 따라 출력 구조가 달라질 수 있죠.

from google import genai
from pydantic import BaseModel, Field
from typing import Union, Literal

class SpamDetails(BaseModel):
    reason: str = Field(description="The reason why the content is considered spam.")
    spam_type: Literal["phishing", "scam", "unsolicited promotion", "other"] = Field(description="The type of spam.")

class NotSpamDetails(BaseModel):
    summary: str = Field(description="A brief summary of the content.")
    is_safe: bool = Field(description="Whether the content is safe for all audiences.")

class ModerationResult(BaseModel):
    decision: Union[SpamDetails, NotSpamDetails]

client = genai.Client()

prompt = """
Please moderate the following content and provide a decision.
Content: 'Congratulations! You''ve won a free cruise to the Bahamas. Click here to claim your prize: www.definitely-not-a-scam.com'
"""

response = client.models.generate_content(
    model="gemini-3.8-flash",
    contents=prompt,
    config={
        "response_format": {"text": {"mime_type": "application/json", "schema": ModerationResult.model_json_schema()}},
    },
)

result = ModerationResult.model_validate_json(response.text)
print(result)
import { GoogleGenAI } from "@google/genai";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const spamDetailsSchema = z.object({
  reason: z.string().describe("The reason why the content is considered spam."),
  spam_type: z.enum(["phishing", "scam", "unsolicited promotion", "other"]).describe("The type of spam."),
});

const notSpamDetailsSchema = z.object({
  summary: z.string().describe("A brief summary of the content."),
  is_safe: z.boolean().describe("Whether the content is safe for all audiences."),
});

const moderationResultSchema = z.object({
  decision: z.union([spamDetailsSchema, notSpamDetailsSchema]),
});

const ai = new GoogleGenAI({});

const prompt = `
Please moderate the following content and provide a decision.
Content: 'Congratulations! You''ve won a free cruise to the Bahamas. Click here to claim your prize: www.definitely-not-a-scam.com'
`;

const response = await ai.models.generateContent({
  model: "gemini-3.8-flash",
  contents: prompt,
  config: {
    responseFormat: { text: { mimeType: "application/json", schema: zodToJsonSchema(moderationResultSchema) } },
  },
});

const result = moderationResultSchema.parse(JSON.parse(response.text));
console.log(result);
package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
)

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }

    prompt := `
  Please moderate the following content and provide a decision.
  Content: 'Congratulations! You''ve won a free cruise to the Bahamas. Click here to claim your prize: www.definitely-not-a-scam.com'
  `
    config := &genai.GenerateContentConfig{
        ResponseMIMEType: "application/json",
        ResponseJsonSchema: map[string]any{
            "type": "object",
            "properties": map[string]any{
                "decision": map[string]any{
                    "anyOf": []map[string]any{
                        {
                            "type":        "object",
                            "title":       "SpamDetails",
                            "description": "Details for content classified as spam.",
                            "properties": map[string]any{
                                "reason": map[string]any{
                                    "type":        "string",
                                    "description": "The reason why the content is considered spam.",
                                },
                                "spam_type": map[string]any{
                                    "type":        "string",
                                    "enum":        []string{"phishing", "scam", "unsolicited promotion", "other"},
                                    "description": "The type of spam.",
                                },
                            },
                            "required": []string{"reason", "spam_type"},
                        },
                        {
                            "type":        "object",
                            "title":       "NotSpamDetails",
                            "description": "Details for content classified as not spam.",
                            "properties": map[string]any{
                                "summary": map[string]any{
                                    "type":        "string",
                                    "description": "A brief summary of the content.",
                                },
                                "is_safe": map[string]any{
                                    "type":        "boolean",
                                    "description": "Whether the content is safe for all audiences.",
                                },
                            },
                            "required": []string{"summary", "is_safe"},
                        },
                    },
                },
            },
            "required": []string{"decision"},
        },
    }

    result, err := client.Models.GenerateContent(
        ctx,
        "gemini-3.8-flash",
        genai.Text(prompt),
        config,
    )
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result.Text())
}
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
    -H "x-goog-api-key: *** \
    -H 'Content-Type: application/json' \
    -X POST \
    -d '{
      "contents": [{
        "parts":[
          { "text": "Please moderate the following content and provide a decision.\nContent: ''Congratulations! You have won a free cruise to the Bahamas. Click here to claim your prize: www.definitely-not-a-scam.com''" }
        ]
      }],
      "generationConfig": {
        "responseFormat": {
          "text": {
            "mimeType": "application/json",
            "schema": {
          "type": "object",
          "properties": {
            "decision": {
              "anyOf": [
                {
                  "type": "object",
                  "title": "SpamDetails",
                  "description": "Details for content classified as spam.",
                  "properties": {
                    "reason": { "type": "string", "description": "The reason why the content is considered spam." },
                    "spam_type": { "type": "string", "enum": ["phishing", "scam", "unsolicited promotion", "other"], "description": "The type of spam." }
          }
        }
      },
                   "required": ["reason", "spam_type"]
                 },
                 {
                   "type": "object",
                   "title": "NotSpamDetails",
                   "description": "Details for content classified as not spam.",
                   "properties": {
                     "summary": { "type": "string", "description": "A brief summary of the content." },
                     "is_safe": { "type": "boolean", "description": "Whether the content is safe for all audiences." }
                   },
                   "required": ["summary", "is_safe"]
                 }
               ]
             }
           },
           "required": ["decision"]
         }
       }
     }'

예시 응답 (Example Response):

{
"decision": {
 "reason": "The content is an unsolicited prize notification attempting to trick the user into clicking a suspicious link.",
 "spam_type": "scam"
}
}

재귀 구조 (Recursive Structures)

이 예시는 조직도 같은 재귀 스키마를 정의하는 방법을 보여줘요.

from google import genai
from pydantic import BaseModel, Field
from typing import List

class Employee(BaseModel):
    """Represents an employee in an organization."""
    name: str
    employee_id: int
    reports: List["Employee"] = Field(
        default_factory=list,
        description="A list of employees reporting to this employee."
    )

client = genai.Client()

prompt = """
Generate an organization chart for a small team.
The manager is Alice, who manages Bob and Charlie. Bob manages David.
"""

response = client.models.generate_content(
    model="gemini-3.8-flash",
    contents=prompt,
    config={
        "response_format": {"text": {"mime_type": "application/json", "schema": Employee.model_json_schema()}},
    },
)

employee = Employee.model_validate_json(response.text)
print(employee)
import { GoogleGenAI } from "@google/genai";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const employeeSchema = z.object({
  name: z.string(),
  employee_id: z.number().int(),
  reports: z.lazy(() => z.array(employeeSchema)).describe("A list of employees reporting to this employee."),
});

const ai = new GoogleGenAI({});

const prompt = `
Generate an organization chart for a small team.
The manager is Alice, who manages Bob and Charlie. Bob manages David.
`;

const response = await ai.models.generateContent({
  model: "gemini-3.8-flash",
  contents: prompt,
  config: {
    responseFormat: { text: { mimeType: "application/json", schema: zodToJsonSchema(employeeSchema) } },
  },
});

const employee = employeeSchema.parse(JSON.parse(response.text));
console.log(employee);
package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
)

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }

    prompt := `
  Generate an organization chart for a small team.
  The manager is Alice, who manages Bob and Charlie. Bob manages David.
  `
    config := &genai.GenerateContentConfig{
        ResponseMIMEType: "application/json",
        ResponseJsonSchema: map[string]any{
            "type": "object",
            "properties": map[string]any{
                "name":        map[string]any{"type": "string"},
                "employee_id": map[string]any{"type": "integer"},
                "reports": map[string]any{
                    "type":        "array",
                    "description": "A list of employees reporting to this employee.",
                    "items": map[string]any{
                        "$ref": "#",
                    },
                },
            },
            "required": []string{"name", "employee_id", "reports"},
        },
    }

    result, err := client.Models.GenerateContent(
        ctx,
        "gemini-3.8-flash",
        genai.Text(prompt),
        config,
    )
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result.Text())
}
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
    -H "x-goog-api-key: *** \
    -H 'Content-Type: application/json' \
    -X POST \
    -d '{
      "contents": [{
        "parts":[
          { "text": "Generate an organization chart for a small team.\nThe manager is Alice, who manages Bob and Charlie. Bob manages David." }
        ]
      }],
      "generationConfig": {
        "responseFormat": {
          "text": {
            "mimeType": "application/json",
            "schema": {
          "type": "object",
          "properties": {
            "name": { "type": "string" },
            "employee_id": { "type": "integer" },
            "reports": {
              "type": "array",
              "description": "A list of employees reporting to this employee.",
              "items": {
                "$ref": "#"
              }
          }
        }
      }
          },
          "required": ["name", "employee_id", "reports"]
        }
      }
    }'

예시 응답 (Example Response):

{ "name" : "Alice" , "employee_id" : 101 , "reports" : [ { "name" : "Bob" , "employee_id" : 102 , "reports" : [ { "name" : "David" , "employee_id" : 104 , "reports" : [] } ] }, { "name" : "Charlie" , "employee_id" : 103 , "reports" : [] } ] }

스트리밍 (Streaming)

구조화된 출력을 스트리밍할 수 있어요. 전체 출력이 완료될 때까지 기다리지 않고, 응답이 생성되는 대로 처리를 시작할 수 있죠. 애플리케이션의 체감 성능을 개선할 수 있어요.

스트리밍된 청크는 유효한 부분 JSON 문자열이며, 이어 붙이면 최종 완전한 JSON 객체를 형성할 수 있어요.

from google import genai
from pydantic import BaseModel, Field
from typing import Literal

class Feedback(BaseModel):
    sentiment: Literal["positive", "neutral", "negative"]
    summary: str

client = genai.Client()
prompt = "The new UI is incredibly intuitive and visually appealing. Great job. Add a very long summary to test streaming!"

response_stream = client.models.generate_content_stream(
    model="gemini-3.8-flash",
    contents=prompt,
    config={
        "response_format": {"text": {"mime_type": "application/json", "schema": Feedback.model_json_schema()}},
    },
)

for chunk in response_stream:
    print(chunk.candidates[0].content.parts[0].text)
import { GoogleGenAI } from "@google/genai";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const ai = new GoogleGenAI({});
const prompt = "The new UI is incredibly intuitive and visually appealing. Great job! Add a very long summary to test streaming!";

const feedbackSchema = z.object({
  sentiment: z.enum(["positive", "neutral", "negative"]),
  summary: z.string(),
});

const stream = await ai.models.generateContentStream({
  model: "gemini-3.8-flash",
  contents: prompt,
  config: {
    responseFormat: { text: { mimeType: "application/json", schema: zodToJsonSchema(feedbackSchema) } },
  },
});

for await (const chunk of stream) {
  console.log(chunk.candidates[0].content.parts[0].text)
}

도구와 구조화된 출력 (Structured outputs with tools)

미리보기: 이 기능은 Gemini 3 시리즈 모델, gemini-3.1-pro-preview, gemini-3.8-flash에서만 사용할 수 있어요.

Gemini 3에서는 구조화된 출력을 내장 도구와 결합할 수 있어요. Google 검색 접지, URL 컨텍스트, 코드 실행, 파일 검색, 함수 호출이 포함돼요.

from google import genai
from pydantic import BaseModel, Field
from typing import List

class MatchResult(BaseModel):
    winner: str = Field(description="The name of the winner.")
    final_match_score: str = Field(description="The final match score.")
    scorers: List[str] = Field(description="The name of the scorer.")

client = genai.Client()

response = client.models.generate_content(
    model="gemini-3.1-pro-preview",
    contents="Search for all details for the latest Euro.",
    config={
        "tools": [
            {"google_search": {}},
            {"url_context": {}}
        ],
        "response_format": {"text": {"mime_type": "application/json", "schema": MatchResult.model_json_schema()}},
    },  
)

result = MatchResult.model_validate_json(response.text)
print(result)
import { GoogleGenAI } from "@google/genai";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

const ai = new GoogleGenAI({});

const matchSchema = z.object({
  winner: z.string().describe("The name of the winner."),
  final_match_score: z.string().describe("The final score."),
  scorers: z.array(z.string()).describe("The name of the scorer.")
});

async function run() {
  const response = await ai.models.generateContent({
    model: "gemini-3.1-pro-preview",
    contents: "Search for all details for the latest Euro.",
    config: {
      tools: [
        { googleSearch: {} },
        { urlContext: {} }
      ],
      responseFormat: { text: { mimeType: "application/json", schema: zodToJsonSchema(matchSchema) } },
    },
  });

  const match = matchSchema.parse(JSON.parse(response.text));
  console.log(match);
}

run();
curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-pro-preview:generateContent" \
  -H "x-goog-api-key: *** \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{
    "contents": [{
      "parts": [{"text": "Search for all details for the latest Euro."}]
    }],
    "tools": [
      {"googleSearch": {}},
      {"urlContext": {}}
    ],
    "generationConfig": {
        "responseFormat": {
          "text": {
            "mimeType": "application/json",
            "schema": {
            "type": "object",
            "properties": {
                "winner": {"type": "string", "description": "The name of the winner."},
                "final_match_score": {"type": "string", "description": "The final score."},
                "scorers": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "The name of the scorer."
                }
          }
        }
      },
            "required": ["winner", "final_match_score", "scorers"]
        }
    }
  }'

JSON 스키마 지원 (JSON schema support)

JSON 객체를 생성하려면 생성 구성에서 response_format을 설정하세요. 스키마는 원하는 출력 형식을 설명하는 **유효한 JSON Schema**여야 해요.

그러면 모델은 제공된 스키마와 일치하는 구문적으로 유효한 JSON 문자열인 응답을 생성해요. 구조화된 출력을 사용하면 모델은 스키마의 키 순서와 같은 순서로 출력을 생성해요.

Gemini의 구조화된 출력 모드는 JSON Schema 사양의 하위 집합을 지원해요.

다음 type 값이 지원돼요:

  • string: 텍스트용.
  • number: 부동소수점 숫자용.
  • integer: 정수용.
  • boolean: true/false 값용.
  • object: 키-값 쌍이 있는 구조화된 데이터용.
  • array: 항목 목록용.
  • null: 속성을 null로 허용하려면 유형 배열에 "null"을 포함하세요(예: {"type": ["string", "null"]}).

다음 설명적 속성들이 모델을 안내하는 데 도움을 줘요:

  • title: 속성에 대한 짧은 설명.
  • description: 속성에 대한 더 길고 상세한 설명.

유형별 속성 (Type-specific properties)

객체 값(object values)의 경우:

  • properties: 각 키가 속성 이름이고 각 값이 해당 속성의 스키마인 객체.
  • required: 필수 속성을 나열하는 문자열 배열.
  • additionalProperties: properties에 나열되지 않은 속성이 허용되는지 제어. 불리언 또는 스키마일 수 있어요.

문자열 값(string values)의 경우:

  • enum: 분류 작업을 위한 특정 가능한 문자열 집합을 나열.
  • format: date-time, date, time 같은 문자열 구문을 지정.

숫자 및 정수 값(number and integer values)의 경우:

  • enum: 특정 가능한 숫자 값 집합을 나열.
  • minimum: 최소 포함 값.
  • maximum: 최대 포함 값.

배열 값(array values)의 경우:

  • items: 배열의 모든 항목에 대한 스키마 정의.
  • prefixItems: 처음 N개 항목에 대한 스키마 목록을 정의해 튜플형 구조를 허용.
  • minItems: 배열의 최소 항목 수.
  • maxItems: 배열의 최대 항목 수.

모델 지원 (Model support)

다음 모델들이 구조화된 출력을 지원해요:

모델 구조화된 출력
Gemini 3.1 Flash-Lite ✔️
Gemini 3.1 Pro Preview ✔️
Gemini 3.5 Flash ✔️
Gemini 3.1 Flash-Lite Preview ✔️
Gemini 2.5 Pro ✔️
Gemini 2.5 Flash ✔️
Gemini 2.5 Flash-Lite ✔️
Gemini 2.0 Flash ✔️*
Gemini 2.0 Flash-Lite ✔️*

** 참고: Gemini 2.0은 선호 구조를 정의하기 위해 JSON 입력 안에 명시적인 propertyOrdering 목록이 필요해요. 이 쿡북에서 예시를 찾을 수 있어요.*

구조화된 출력 vs 함수 호출 (Structured outputs vs. function calling)

구조화된 출력과 함수 호출 모두 JSON 스키마를 사용하지만, 목적은 서로 달라요:

기능 주요 사용 사례
구조화된 출력(Structured Outputs) 사용자에게 보내는 최종 응답 형식 지정. 모델 답변을 특정 형식으로 만들고 싶을 때 사용(예: 문서에서 데이터를 추출해 데이터베이스에 저장).
함수 호출(Function Calling) 대화 중 조치 취하기. 모델이 최종 답변을 주기 전에 작업을 요청해야 할 때 사용(예: "현재 날씨 가져오기").

모범 사례 (Best practices)

  • 명확한 설명(Clear descriptions): 스키마의 description 필드를 사용해 각 속성이 무엇을 나타내는지 모델에 명확한 지시를 주세요. 모델 출력을 안내하는 데 중요해요.
  • 강력한 타이핑(Strong typing): 가능하면 구체적인 유형(integer, string, enum)을 사용하세요. 매개변수의 유효한 값 집합이 제한적이라면 enum을 사용하세요.
  • 프롬프트 엔지니어링(Prompt engineering): 프롬프트에 모델이 무엇을 하길 원하는지 명확히 말하세요. 예: "텍스트에서 다음 정보를 추출하세요..." 또는 "제공된 스키마에 따라 이 피드백을 분류하세요...".
  • 검증(Validation): 구조화된 출력이 구문적으로 올바른 JSON을 보장하지만, 값이 의미적으로 올바르다는 것을 보장하지는 않아요. 사용 전에 항상 애플리케이션 코드에서 최종 출력을 검증하세요.
  • 오류 처리(Error handling): 스키마를 준수하는 모델 출력이라도 비즈니스 로직 요구를 충족하지 못할 수 있으므로, 애플리케이션에서 견고한 오류 처리를 구현해 우아하게 관리하세요.

제한 사항 (Limitations)

  • 스키마 하위 집합(Schema subset): JSON Schema 사양의 모든 기능이 지원되지는 않아요. 모델은 지원되지 않는 속성을 무시해요.
  • 스키마 복잡성(Schema complexity): API는 매우 크거나 깊게 중첩된 스키마를 거부할 수 있어요. 오류가 발생하면 속성 이름을 줄이고, 중첩을 줄이거나, 제약 수를 제한해 스키마를 단순화해 보세요.

더 알아보기 (Learn more)

  • JSON Schema — 스키마 명세.
  • Pydantic — Python 스키마 정의.
  • Zod — JavaScript 스키마 정의.