Interactions API로 마이그레이션하기

Interactions API로 마이그레이션하기

이 가이드는 generateContent API에서 Interactions API로 마이그레이션하는 방법을 도와줘요.

Interactions API는 Gemini 모델과 에이전트로 구축하는 가장 간단하고 좋은 방법이에요. generateContent는 계속 완전히 지원되지만, 모든 새 개발에는 Interactions API를 권장해요.

코딩 에이전트로 이 마이그레이션을 자동화하세요. 스킬을 지원하는 코딩 에이전트(Gemini CLI나 Jules 같은)를 사용한다면, Gemini API 개발 스킬을 설치하고 다음을 실행하세요.

 translate="no" dir="ltr" is-upgraded>/gemini-api-dev migrate my app to the interactions api

이 스킬은 이 가이드에서 설명하는 API 변경 사항을 적용해요.

출처: 문서

본문

왜 마이그레이션해야 하나요?

Interactions API는 Gemini 모델과 에이전트로 구축하는 가장 간단하고 좋은 방법이에요.

  • 서버 측 히스토리 관리: previous_interaction_id를 통한 단순화된 멀티 턴 흐름. 서버가 기본으로 상태를 활성화(store=true)하지만, store=false로 설정해 무상태 동작을 선택할 수도 있어요.
  • 관찰 가능한 실행 단계: 타입이 지정된 단계(step)는 복잡한 흐름을 디버깅하고 중간 이벤트(사고나 검색 위젯 같은)에 대한 UI를 렌더링하기 쉽게 해 줘요.
  • 도구 사용과 에이전트 워크플로: 타입이 지정된 실행 단계를 통한 다단계 도구 사용, 오케스트레이션, 복잡한 추론 흐름의 기본 지원.
  • 장기 실행·백그라운드 작업: background=true를 사용해 Deep Think나 Deep Research 같은 시간 집약적 작업을 백그라운드 프로세스로 오프로드 지원.

기본 입출력

이 섹션은 간단한 텍스트 생성 요청을 마이그레이션하는 방법을 보여줘요.

Before (generateContent)

generateContent API는 무상태이며 응답을 직접 반환해요. 응답 구조는 출력을 candidates 목록으로 감싸고, 각각은 파싱할 parts 목록이 있는 content를 포함해요.

Python

from google import genai

client = genai.Client()

response = client.models.generate_content(
    model="gemini-2.5-flash-lite", contents="Tell me a joke."
)
print(response.text)

JavaScript

import { GoogleGenAI } from '@google/genai';

const ai = new GoogleGenAI({});

const response = await ai.models.generateContent({
  model: "gemini-2.5-flash-lite",
  contents: "Tell me a joke.",
});
console.log(response.text);

Java

import com.google.genai.Client;
import com.google.genai.types.GenerateContentResponse;

Client client = new Client();

GenerateContentResponse response =
    client.models.generateContent("gemini-2.5-flash-lite", "Tell me a joke.", null);
System.out.println(response.text());

Go

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)
    }

    response, err := client.Models.GenerateContent(
        ctx,
        "gemini-2.5-flash-lite",
        genai.Text("Tell me a joke."),
        nil,
    )
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(response.Text())
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "contents": [{
        "parts": [{
            "text": "Tell me a joke."
        }]
    }]
}'

# Response
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "Why did the chicken cross the road? To get to the other side!"
          }
        ],
        "role": "model"
      },
      "finishReason": "STOP",
      "index": 0
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 4,
    "candidatesTokenCount": 12,
    "totalTokenCount": 16
  }
}

Interactions API는 steps 타임라인이 있는 저장된 상호작용 리소스를 반환해요. steps 배열을 수동으로 검사해 중간 이벤트를 찾을 수 있지만, Google GenAI SDK는 반환된 Interaction 객체에 직접 편의 속성(convenience properties)을 제공해 최종 출력에 접근할 수 있어요.

가장 흔한 편의 속성은 .output_text(String)로, 모델 응답 끝에서 연속된 TextContent 블록을 자동으로 추출해 결합해요. 간단한 응답에는 완벽하게 작동하지만, 비텍스트 콘텐츠(사고, 이미지, 오디오, 도구 호출 등)로 분리된 앞선 텍스트 블록은 포함하지 않아요. 복잡하거나 문장과 이미지가 교차하는 멀티모달 응답은 대신 steps를 수동으로 순회해야 해요.

참고: 다른 SDK 편의 속성(.output_image, .output_audio 포함)에 대한 자세한 내용은 개요의 SDK 편의 속성으로 출력 접근을 참조하세요.

Python

from google import genai

client = genai.Client()

interaction = client.interactions.create(
    model="gemini-3.8-flash", input="Tell me a joke."
)

print(interaction.output_text)

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

let interaction = await client.interactions.create({
    model: 'gemini-3.8-flash',
    input: 'Tell me a joke.'
});

console.log(interaction.output_text);

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.CreateModelInteraction;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Model;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;

Client client = new Client();

CreateModelInteraction request =
    CreateModelInteraction.builder()
        .model(Model.of("gemini-3.8-flash"))
        .input(InteractionsInput.of("Tell me a joke."))
        .build();

Interaction interaction =
    client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();

System.out.println(interaction.outputText().orElse(""));

Go

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

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

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.8-flash"),
            Input: interactions.NewInteractionsInput("Tell me a joke."),
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
    if res.Interaction.OutputText != nil {
        fmt.Println(*res.Interaction.OutputText)
    }
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "model": "gemini-3.8-flash",
    "input": "Tell me a joke."
}'

# Response
{
  "id": "int_123",
  "status": "completed",
  "steps": [
    {
      "type": "user_input",
      "status": "done",
      "content": [
        {
          "type": "text",
          "text": "Tell me a joke."
        }
      ]
    },
    {
      "type": "model_output",
      "status": "done",
      "content": [
        {
          "type": "text",
          "text": "Why did the chicken cross the road?"
        }
      ]
    }
  ]
}

멀티 턴 대화

Interactions API는 상호작용을 기본적으로 저장해 멀티 턴 대화를 위한 서버 측 상태 관리를 가능하게 해요.

Before (generateContent)

generateContent에서는 contents 배열이나 클라이언트 측 채팅 헬퍼를 사용해 대화 히스토리를 수동으로 관리해야 해요.

Python

채팅 헬퍼 사용(권장)

from google import genai

client = genai.Client()

chat = client.chats.create(model="gemini-2.5-flash-lite")
response1 = chat.send_message("Hi, my name is Phil.")
print(response1.text)

response2 = chat.send_message("What is my name?")
print(response2.text)

히스토리 수동 관리

from google import genai
from google.genai import types

client = genai.Client()

response = client.models.generate_content(
    model="gemini-2.5-flash-lite",
    contents=[
        types.Content(
            role="user", parts=[types.Part.from_text(text="Hi, my name is Phil.")]
        ),
        types.Content(
            role="model",
            parts=[types.Part.from_text(text="Hi Phil, how can I help you?")],
        ),
        types.Content(
            role="user", parts=[types.Part.from_text(text="What is my name?")]
        ),
    ],
)
print(response.text)

JavaScript

채팅 헬퍼 사용(권장)

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const chat = client.chats.create({ model: 'gemini-2.5-flash-lite' });
let response = await chat.sendMessage({ message: 'Hi, my name is Phil.' });
console.log(response.text);

response = await chat.sendMessage({ message: 'What is my name?' });
console.log(response.text);

히스토리 수동 관리

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const response = await client.models.generateContent({
    model: 'gemini-2.5-flash-lite',
    contents: [
        { role: 'user', parts: [{ text: 'Hi, my name is Phil.' }] },
        { role: 'model', parts: [{ text: 'Hi Phil, how can I help you?' }] },
        { role: 'user', parts: [{ text: 'What is my name?' }] }
    ]
});
console.log(response.text);

Java

import com.google.genai.Chat;
import com.google.genai.Client;
import com.google.genai.types.Content;
import com.google.genai.types.GenerateContentResponse;
import com.google.genai.types.Part;
import java.util.Arrays;

Client client = new Client();

// Using the chat helper (recommended)
Chat chat = client.chats.create("gemini-2.5-flash-lite");
GenerateContentResponse response1 = chat.sendMessage("Hi, my name is Phil.");
System.out.println(response1.text());

GenerateContentResponse response2 = chat.sendMessage("What is my name?");
System.out.println(response2.text());

// Manually managing history
GenerateContentResponse manualResponse =
    client.models.generateContent(
        "gemini-2.5-flash-lite",
        Arrays.asList(
            Content.builder()
                .role("user")
                .parts(Arrays.asList(Part.fromText("Hi, my name is Phil.")))
                .build(),
            Content.builder()
                .role("model")
                .parts(Arrays.asList(Part.fromText("Hi Phil, how can I help you?")))
                .build(),
            Content.builder()
                .role("user")
                .parts(Arrays.asList(Part.fromText("What is my name?")))
                .build()),
        null);
System.out.println(manualResponse.text());

Go

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)
    }

    // Using the chat helper (recommended)
    chat, err := client.Chats.Create(ctx, "gemini-2.5-flash-lite", nil, nil)
    if err != nil {
        log.Fatal(err)
    }
    response1, err := chat.SendMessage(ctx, genai.Part{Text: "Hi, my name is Phil."})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(response1.Text())

    response2, err := chat.SendMessage(ctx, genai.Part{Text: "What is my name?"})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(response2.Text())

    // Manually managing history
    history := []*genai.Content{
        genai.NewContentFromText("Hi, my name is Phil.", genai.RoleUser),
        genai.NewContentFromText("Hi Phil, how can I help you?", genai.RoleModel),
        genai.NewContentFromText("What is my name?", genai.RoleUser),
    }
    manualResponse, err := client.Models.GenerateContent(ctx, "gemini-2.5-flash-lite", history, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(manualResponse.Text())
}

REST

# Request (the second turn requires sending the entire history)
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "contents": [
        {"role": "user", "parts": [{"text": "Hi, my name is Phil."}]},
        {"role": "model", "parts": [{"text": "Hi Phil, how can I help you?"}]},
        {"role": "user", "parts": [{"text": "What is my name?"}]}
    ]
}'

# Response
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "Your name is Phil."
          }
        ],
        "role": "model"
      },
      "finishReason": "STOP",
      "index": 0
    }
  ]
}

After (Interactions API)

Interactions API는 서버에서 상태를 관리해요. previous_interaction_id를 참조해 대화를 계속해요.

Python

from google import genai

client = genai.Client()

interaction1 = client.interactions.create(
    model="gemini-3.8-flash", input="Hi, my name is Phil."
)
print("Response 1:", interaction1.output_text)

interaction2 = client.interactions.create(
    model="gemini-3.8-flash",
    previous_interaction_id=interaction1.id,
    input="What is my name?",
)
print("Response 2:", interaction2.output_text)

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

let interaction = await client.interactions.create({
    model: 'gemini-3.8-flash',
    input: 'Hi, my name is Phil.'
});
console.log("Response 1:", interaction.output_text);

interaction = await client.interactions.create({
    model: 'gemini-3.8-flash',
    previous_interaction_id: interaction.id,
    input: 'What is my name?'
});
console.log("Response 2:", interaction.output_text);

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.CreateModelInteraction;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Model;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;

Client client = new Client();

CreateModelInteraction req1 =
    CreateModelInteraction.builder()
        .model(Model.of("gemini-3.8-flash"))
        .input(InteractionsInput.of("Hi, my name is Phil."))
        .build();

Interaction interaction1 =
    client.interactions.create(CreateInteractionRequestBody.of(req1)).interaction().get();
System.out.println("Response 1: " + interaction1.outputText().orElse(""));

CreateModelInteraction req2 =
    CreateModelInteraction.builder()
        .model(Model.of("gemini-3.8-flash"))
        .previousInteractionId(interaction1.id().orElse(""))
        .input(InteractionsInput.of("What is my name?"))
        .build();

Interaction interaction2 =
    client.interactions.create(CreateInteractionRequestBody.of(req2)).interaction().get();
System.out.println("Response 2: " + interaction2.outputText().orElse(""));

Go

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

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

    res1, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.8-flash"),
            Input: interactions.NewInteractionsInput("Hi, my name is Phil."),
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
    if res1.Interaction.OutputText != nil {
        fmt.Println("Response 1:", *res1.Interaction.OutputText)
    }

    res2, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model:                 interactions.Model("gemini-3.8-flash"),
            PreviousInteractionID: res1.Interaction.ID,
            Input:                 interactions.NewInteractionsInput("What is my name?"),
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
    if res2.Interaction.OutputText != nil {
        fmt.Println("Response 2:", *res2.Interaction.OutputText)
    }
}

REST

# First Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "model": "gemini-3.8-flash",
    "input": "Hi, my name is Phil."
}'

# Second Request (using ID from first response)
curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "model": "gemini-3.8-flash",
    "previous_interaction_id": "int_123",
    "input": "What is my name?"
}'

# Response to Second Request
{
  "id": "int_123",
  "steps": [
    {
      "type": "user_input",
      "status": "done",
      "content": [{ "type": "text", "text": "Hi, my name is Phil." }]
    },
    {
      "type": "model_output",
      "status": "done",
      "content": [{ "type": "text", "text": "Hello Phil! How can I help you today?" }]
    },
    {
      "type": "user_input",
      "status": "done",
      "content": [{ "type": "text", "text": "What is my name?" }]
    },
    {
      "type": "model_output",
      "status": "done",
      "content": [{ "type": "text", "text": "Your name is Phil." }]
    }
  ]
}

멀티모달 입력

두 API 모두 멀티모달 입력(텍스트, 이미지, 비디오 등)을 지원해요.

Before (generateContent)

generateContent에서는 contents 배열 안에 parts 목록을 전달해요. 응답은 첫 번째 candidate의 parts에서 출력을 반환해요.

Python

from google import genai
from google.genai import types

client = genai.Client()

with open("sample.jpg", "rb") as f:
    image_bytes = f.read()

response = client.models.generate_content(
    model="gemini-2.5-flash-lite",
    contents=[
        types.Part.from_bytes(data=image_bytes, mime_type="image/jpeg"),
        "Describe this image.",
    ],
)
print(response.text)

JavaScript

import { GoogleGenAI } from '@google/genai';
import * as fs from 'fs';

const client = new GoogleGenAI({});

const imageBytes = fs.readFileSync('sample.jpg').toString('base64');

const response = await client.models.generateContent({
    model: 'gemini-2.5-flash-lite',
    contents: [
        {
            inlineData: {
                data: imageBytes,
                mimeType: 'image/jpeg',
            },
        },
        'Describe this image.',
    ],
});
console.log(response.text);

Java

import com.google.genai.Client;
import com.google.genai.types.Content;
import com.google.genai.types.GenerateContentResponse;
import com.google.genai.types.Part;
import java.nio.file.Files;
import java.nio.file.Paths;

Client client = new Client();

byte[] imageBytes = Files.readAllBytes(Paths.get("sample.jpg"));

GenerateContentResponse response =
    client.models.generateContent(
        "gemini-2.5-flash-lite",
        Content.fromParts(
            Part.fromBytes(imageBytes, "image/jpeg"), Part.fromText("Describe this image.")),
        null);
System.out.println(response.text());

Go

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "google.golang.org/genai"
)

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

    imageBytes, err := os.ReadFile("sample.jpg")
    if err != nil {
        log.Fatal(err)
    }

    contents := []*genai.Content{
        genai.NewContentFromParts([]*genai.Part{
            genai.NewPartFromBytes(imageBytes, "image/jpeg"),
            genai.NewPartFromText("Describe this image."),
        }, genai.RoleUser),
    }

    response, err := client.Models.GenerateContent(ctx, "gemini-2.5-flash-lite", contents, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(response.Text())
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "contents": [{
        "parts": [
            {
                "inlineData": {
                    "mimeType": "image/jpeg",
                    "data": "..."
                }
            },
            {
                "text": "Describe this image."
            }
        ]
    }]
}'

# Response
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "This is a picture of a beautiful sunset."
          }
        ],
        "role": "model"
      }
    }
  ]
}

After (Interactions API)

Interactions API에서는 input 필드에 배열을 전달해요. 타임라인에서 model_output 단계를 찾아 출력 콘텐츠를 가져와요.

Python

import base64
from google import genai

client = genai.Client()

with open("sample.jpg", "rb") as f:
    image_bytes = f.read()
image_b64 = base64.b64encode(image_bytes).decode("utf-8")

interaction = client.interactions.create(
    model="gemini-3.8-flash",
    input=[
        {
            "type": "image",
            "mime_type": "image/jpeg",
            "data": image_b64,
        },
        {"type": "text", "text": "Describe this image."},
    ],
)
print(interaction.output_text)

JavaScript

import { GoogleGenAI } from '@google/genai';
import * as fs from 'fs';

const client = new GoogleGenAI({});

const imageBytes = fs.readFileSync('sample.jpg').toString('base64');

const interaction = await client.interactions.create({
    model: 'gemini-3.8-flash',
    input: [
        {
            type: 'image',
            mime_type: 'image/jpeg',
            data: imageBytes
        },
        {
            type: 'text',
            text: 'Describe this image.'
        }
    ]
});
console.log(interaction.output_text);

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.CreateModelInteraction;
import com.google.genai.gaos.models.interactions.ImageContent;
import com.google.genai.gaos.models.interactions.ImageContentMimeType;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Model;
import com.google.genai.gaos.models.interactions.TextContent;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Base64;

Client client = new Client();

byte[] imageBytes = Files.readAllBytes(Paths.get("sample.jpg"));
String base64ImageData = Base64.getEncoder().encodeToString(imageBytes);

CreateModelInteraction request =
    CreateModelInteraction.builder()
        .model(Model.of("gemini-3.8-flash"))
        .input(
            InteractionsInput.ofContent(
                Arrays.asList(
                    ImageContent.builder()
                        .mimeType(ImageContentMimeType.IMAGE_JPEG)
                        .data(base64ImageData)
                        .build(),
                    TextContent.builder().text("Describe this image.").build())))
        .build();

Interaction interaction =
    client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();
System.out.println(interaction.outputText().orElse(""));

Go

package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "log"
    "os"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

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

    imageBytes, err := os.ReadFile("sample.jpg")
    if err != nil {
        log.Fatal(err)
    }
    base64ImageData := base64.StdEncoding.EncodeToString(imageBytes)

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.8-flash"),
            Input: interactions.NewInteractionsInput([]interactions.Content{
                interactions.NewContent(interactions.ImageContent{
                    MimeType: interactions.ImageContentMimeType("image/jpeg").ToPointer(),
                    Data:     genai.Ptr(base64ImageData),
                }),
                interactions.NewContent(interactions.TextContent{
                    Text: "Describe this image.",
                }),
            }),
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
    if res.Interaction.OutputText != nil {
        fmt.Println(*res.Interaction.OutputText)
    }
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "model": "gemini-3.8-flash",
    "input": [
        {
            "type": "image",
            "mime_type": "image/jpeg",
            "data": "..."
        },
        {
            "type": "text",
            "text": "Describe this image."
        }
    ]
}'

# Response
{
  "id": "int_multimodal",
  "steps": [
    {
      "type": "user_input",
      "status": "done",
      "content": [
        {
          "type": "image",
          "mime_type": "image/jpeg",
          "data": "..."
        },
        {
          "type": "text",
          "text": "Describe this image."
        }
      ]
    },
    {
      "type": "model_output",
      "status": "done",
      "content": [
        {
          "type": "text",
          "text": "This is a picture of a beautiful sunset over the mountains."
        }
      ]
    }
  ]
}

구조화된 출력

모델이 특정 스키마와 일치하는 JSON을 반환하게 하려면 응답 형식을 구성하세요.

Before (generateContent)

generateContent에서는 config(또는 generationConfig) 객체 안에 중첩된 response_mime_type과 response_schema 필드를 사용해 출력 형식을 구성해요.

Python

from google import genai
from google.genai import types
from pydantic import BaseModel

client = genai.Client()

class Recipe(BaseModel):
    recipe_name: str
    ingredients: list[str]

response = client.models.generate_content(
    model="gemini-2.5-flash-lite",
    contents="Give me a recipe for chocolate chip cookies.",
    config=types.GenerateContentConfig(
        response_mime_type="application/json",
        response_schema=Recipe,
    ),
)
print(response.text)

JavaScript

import { GoogleGenAI, Type } from '@google/genai';

const ai = new GoogleGenAI({});

const response = await ai.models.generateContent({
    model: 'gemini-2.5-flash-lite',
    contents: 'Give me a recipe for chocolate chip cookies.',
    config: {
        responseMimeType: 'application/json',
        responseSchema: {
            type: Type.OBJECT,
            properties: {
                recipe_name: { type: Type.STRING },
                ingredients: {
                    type: Type.ARRAY,
                    items: { type: Type.STRING },
                },
            },
            required: ['recipe_name', 'ingredients'],
        },
    },
});
console.log(response.text);

Java

import com.google.genai.Client;
import com.google.genai.types.GenerateContentConfig;
import com.google.genai.types.GenerateContentResponse;
import com.google.genai.types.Schema;
import com.google.genai.types.Type;
import java.util.Arrays;
import java.util.Map;

Client client = new Client();

Schema recipeSchema =
    Schema.builder()
        .type(Type.Known.OBJECT)
        .properties(
            Map.of(
                "recipe_name", Schema.builder().type(Type.Known.STRING).build(),
                "ingredients",
                    Schema.builder()
                        .type(Type.Known.ARRAY)
                        .items(Schema.builder().type(Type.Known.STRING).build())
                        .build()))
        .required(Arrays.asList("recipe_name", "ingredients"))
        .build();

GenerateContentResponse response =
    client.models.generateContent(
        "gemini-2.5-flash-lite",
        "Give me a recipe for chocolate chip cookies.",
        GenerateContentConfig.builder()
            .responseMimeType("application/json")
            .responseSchema(recipeSchema)
            .build());
System.out.println(response.text());

Go

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)
    }

    recipeSchema := &genai.Schema{
        Type: genai.TypeObject,
        Properties: map[string]*genai.Schema{
            "recipe_name": {Type: genai.TypeString},
            "ingredients": {
                Type:  genai.TypeArray,
                Items: &genai.Schema{Type: genai.TypeString},
            },
        },
        Required: []string{"recipe_name", "ingredients"},
    }

    response, err := client.Models.GenerateContent(
        ctx,
        "gemini-2.5-flash-lite",
        genai.Text("Give me a recipe for chocolate chip cookies."),
        &genai.GenerateContentConfig{
            ResponseMIMEType: "application/json",
            ResponseSchema:   recipeSchema,
        },
    )
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(response.Text())
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "contents": [{
        "parts": [{
            "text": "Give me a recipe for chocolate chip cookies."
        }]
    }],
    "generationConfig": {
        "responseMimeType": "application/json",
        "responseSchema": {
            "type": "OBJECT",
            "properties": {
                "recipe_name": { "type": "STRING" },
                "ingredients": {
                    "type": "ARRAY",
                    "items": { "type": "STRING" }
                }
            },
            "required": ["recipe_name", "ingredients"]
        }
    }
}'

# Response
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "{\n  \"recipe_name\": \"Chocolate Chip Cookies\",\n  \"ingredients\": [\n    \"1 cup butter\",\n    \"1 cup sugar\",\n    \"2 cups flour\",\n    \"1 cup chocolate chips\"\n  ]\n}"
          }
        ],
        "role": "model"
      }
    }
  ]
}

After (Interactions API)

Interactions API에서는 출력 형식 제어가 최상위 response_format 배열로 이동해요.

Python

from google import genai
from pydantic import BaseModel

client = genai.Client()

class Recipe(BaseModel):
    recipe_name: str
    ingredients: list[str]

interaction = client.interactions.create(
    model="gemini-3.8-flash",
    input="Give me a recipe for chocolate chip cookies.",
    response_format=[
        {
            "type": "text",
            "mime_type": "application/json",
            "schema": Recipe.model_json_schema(),
        }
    ],
)

print(interaction.output_text)

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const interaction = await client.interactions.create({
    model: 'gemini-3.8-flash',
    input: 'Give me a recipe for chocolate chip cookies.',
    response_format: [
        {
            type: 'text',
            mime_type: 'application/json',
            schema: {
                type: 'object',
                properties: {
                    recipe_name: { type: 'string' },
                    ingredients: {
                        type: 'array',
                        items: { type: 'string' }
                    }
                },
                required: ['recipe_name', 'ingredients']
            }
        }
    ]
});
console.log(interaction.output_text);

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.CreateModelInteraction;
import com.google.genai.gaos.models.interactions.CreateModelInteractionResponseFormat;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Model;
import com.google.genai.gaos.models.interactions.ResponseFormat;
import com.google.genai.gaos.models.interactions.TextResponseFormat;
import com.google.genai.gaos.models.interactions.TextResponseFormatMimeType;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

Client client = new Client();

Map<String, Object> properties = new HashMap<>();
properties.put("recipe_name", Map.of("type", "string"));
properties.put("ingredients", Map.of("type", "array", "items", Map.of("type", "string")));

Map<String, Object> schema = new HashMap<>();
schema.put("type", "object");
schema.put("properties", properties);
schema.put("required", Arrays.asList("recipe_name", "ingredients"));

CreateModelInteraction request =
    CreateModelInteraction.builder()
        .model(Model.of("gemini-3.8-flash"))
        .input(InteractionsInput.of("Give me a recipe for chocolate chip cookies."))
        .responseFormat(
            CreateModelInteractionResponseFormat.of(
                Arrays.asList(
                    ResponseFormat.of(
                        TextResponseFormat.builder()
                            .mimeType(TextResponseFormatMimeType.APPLICATION_JSON)
                            .schema(schema)
                            .build()))))
        .build();

Interaction interaction =
    client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();
System.out.println(interaction.outputText().orElse(""));

Go

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

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

    schema := map[string]any{
        "type": "object",
        "properties": map[string]any{
            "recipe_name": map[string]any{"type": "string"},
            "ingredients": map[string]any{
                "type":  "array",
                "items": map[string]any{"type": "string"},
            },
        },
        "required": []string{"recipe_name", "ingredients"},
    }

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.8-flash"),
            Input: interactions.NewInteractionsInput("Give me a recipe for chocolate chip cookies."),
            ResponseFormat: genai.Ptr(interactions.NewCreateModelInteractionResponseFormat(
                interactions.NewResponseFormat(interactions.TextResponseFormat{
                    MimeType: interactions.TextResponseFormatMimeType("application/json").ToPointer(),
                    Schema:   schema,
                }),
            )),
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
    if res.Interaction.OutputText != nil {
        fmt.Println(*res.Interaction.OutputText)
    }
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "model": "gemini-3.8-flash",
    "input": "Give me a recipe for chocolate chip cookies.",
    "response_format": [
        {
            "type": "text",
            "mime_type": "application/json",
            "schema": {
                "type": "OBJECT",
                "properties": {
                    "recipe_name": { "type": "STRING" },
                    "ingredients": {
                        "type": "ARRAY",
                        "items": { "type": "STRING" }
                    }
                },
                "required": ["recipe_name", "ingredients"]
            }
        }
    ]
}'

# Response
{
  "id": "int_structured",
  "steps": [
    {
      "type": "user_input",
      "status": "done",
      "content": [{ "type": "text", "text": "Give me a recipe for chocolate chip cookies." }]
    },
    {
      "type": "model_output",
      "status": "done",
      "content": [
        {
          "type": "text",
          "text": "{\n  \"recipe_name\": \"Chocolate Chip Cookies\",\n  \"ingredients\": [\n    \"1 cup butter\",\n    \"1 cup sugar\",\n    \"2 cups flour\",\n    \"1 cup chocolate chips\"\n  ]\n}"
        }
      ]
    }
  ]
}

멀티모달 생성

텍스트 너머의 모달리티(이미지나 오디오 같은)로 콘텐츠를 생성할 때, 주요 차이점은 응답이 생성된 미디어를 구조화하는 방식이에요.

Before (generateContent)

generateContent에서는 응답이 생성된 미디어를 candidate의 parts에 직접, 일반적으로 inlineData의 base64 데이터로 반환해요.

# Response structure concept
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "Here is your generated image:"
          },
          {
            "inlineData": {
              "mimeType": "image/jpeg",
              "data": "...base64..."
            }
          }
        ]
      }
    }
  ]
}

After (Interactions API)

Interactions API에서는 생성된 미디어가 타임라인의 model_output 단계 content 배열 안에서 개별 항목으로 나타나며, 상호작용의 연대순 흐름을 유지해요.

# Response structure concept
{
  "id": "int_123",
  "steps": [
    {
      "type": "model_output",
      "status": "done",
      "content": [
        {
          "type": "text",
          "text": "Here is your generated image:"
        },
        {
          "type": "image",
          "mime_type": "image/jpeg",
          "data": "...base64..." // Or a reference URL in future
        }
      ]
    }
  ]
}

이를 통해 입력과 텍스트 출력이 처리되는 방식과 응답 파싱이 일관되게 유지돼요. 모든 것이 타임라인의 한 단계예요.

서버 측 도구

Gemini는 Google Search 그라운딩 같은 내장 서버 측 도구를 지원해요. 주요 차이점은 응답이 도구 실행을 표현하는 방식이에요.

Before (generateContent)

generateContent에서 서버 측 도구는 대체로 불투명해요. 도구를 활성화하고 별도의 groundingMetadata 객체와 함께 최종 답변을 받아요. 핵심적으로 인용은 인라인이 아니에요. groundingSupports는 문자 인덱스를 사용해 텍스트 세그먼트를 groundingChunks의 웹 소스에 다시 매핑해요.

Python

from google import genai
from google.genai import types

client = genai.Client()

response = client.models.generate_content(
    model="gemini-2.5-flash-lite",
    contents="Who won Euro 2024?",
    config=types.GenerateContentConfig(
        tools=[{"google_search": {}}]
    ),
)

metadata = response.candidates[0].grounding_metadata
if metadata.search_entry_point:
    print(f"Search Entry Point: {metadata.search_entry_point.rendered_content}")

for support in metadata.grounding_supports:
    print(f"Citation: {support.segment.text}")

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const response = await client.models.generateContent({
    model: 'gemini-2.5-flash-lite',
    contents: 'Who won Euro 2024?',
    config: {
        tools: [{ google_search: {} }]
    }
});

const metadata = response.candidates[0].groundingMetadata;
if (metadata.searchEntryPoint) {
    console.log(`Search Entry Point: ${metadata.searchEntryPoint.renderedContent}`);
}
for (const support of metadata.groundingSupports) {
    console.log(`Citation: ${support.segment.text}`);
}

Java

import com.google.genai.Client;
import com.google.genai.types.Candidate;
import com.google.genai.types.GenerateContentConfig;
import com.google.genai.types.GenerateContentResponse;
import com.google.genai.types.GoogleSearch;
import com.google.genai.types.GroundingMetadata;
import com.google.genai.types.GroundingSupport;
import com.google.genai.types.Tool;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

Client client = new Client();

GenerateContentResponse response =
    client.models.generateContent(
        "gemini-2.5-flash-lite",
        "Who won Euro 2024?",
        GenerateContentConfig.builder()
            .tools(
                Arrays.asList(
                    Tool.builder().googleSearch(GoogleSearch.builder().build()).build()))
            .build());

List<Candidate> candidates = response.candidates().orElse(Collections.emptyList());
if (!candidates.isEmpty() && candidates.get(0).groundingMetadata().isPresent()) {
  GroundingMetadata metadata = candidates.get(0).groundingMetadata().get();
  if (metadata.searchEntryPoint().isPresent()) {
    System.out.println(
        "Search Entry Point: " + metadata.searchEntryPoint().get().renderedContent().orElse(""));
  }
  for (GroundingSupport support : metadata.groundingSupports().orElse(Collections.emptyList())) {
    Optional<String> segmentText = support.segment().flatMap(s -> s.text());
    segmentText.ifPresent(text -> System.out.println("Citation: " + text));
  }
}

Go

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)
    }

    response, err := client.Models.GenerateContent(
        ctx,
        "gemini-2.5-flash-lite",
        genai.Text("Who won Euro 2024?"),
        &genai.GenerateContentConfig{
            Tools: []*genai.Tool{
                {GoogleSearch: &genai.GoogleSearch{}},
            },
        },
    )
    if err != nil {
        log.Fatal(err)
    }

    if len(response.Candidates) > 0 && response.Candidates[0].GroundingMetadata != nil {
        metadata := response.Candidates[0].GroundingMetadata
        if metadata.SearchEntryPoint != nil {
            fmt.Println("Search Entry Point:", metadata.SearchEntryPoint.RenderedContent)
        }
        for _, support := range metadata.GroundingSupports {
            if support.Segment != nil {
                fmt.Println("Citation:", support.Segment.Text)
            }
        }
    }
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "contents": [{
        "parts": [{
            "text": "Who won Euro 2024?"
        }]
    }],
    "tools": [{
        "googleSearchRetrieval": {}
    }]
}'

# Response
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "Spain won Euro 2024, defeating England 2-1 in the final. This victory marks Spain's record fourth European Championship title."
          }
        ],
        "role": "model"
      },
      "groundingMetadata": {
        "webSearchQueries": [
          "UEFA Euro 2024 winner",
          "who won euro 2024"
        ],
        "searchEntryPoint": {
          "renderedContent": "<!-- HTML and CSS for the search widget -->"
        },
        "groundingChunks": [
          {"web": {"uri": "https://vertexaisearch.cloud.google.com.....", "title": "aljazeera.com"}},
          {"web": {"uri": "https://vertexaisearch.cloud.google.com.....", "title": "uefa.com"}}
        ],
        "groundingSupports": [
          {
            "segment": {"startIndex": 0, "endIndex": 85, "text": "Spain won Euro 2024, defeatin..."},
            "groundingChunkIndices": [0]
          },
          {
            "segment": {"startIndex": 86, "endIndex": 210, "text": "This victory marks Spain's..."},
            "groundingChunkIndices": [0, 1]
          }
        ]
      }
    }
  ]
}

After (Interactions API)

Interactions API에서 서버 측 도구는 전체 타임라인 투명성을 제공해요. API가 호출과 결과를 별도의 실행 steps(google_search_call과 google_search_result)로 기록해, 모델이 검색한 데이터가 정확히 무엇인지 드러내요.

또한 API는 인용을 인라인으로 반환해요. 별도의 메타데이터 객체에서 인덱스를 매핑하는 대신, model_output 단계 내의 텍스트 항목이 소스에 직접 연결하는 자체 annotations 배열을 포함해요.

Python

from google import genai

client = genai.Client()

interaction = client.interactions.create(
    model="gemini-3.8-flash",
    input="Who won Euro 2024?",
    tools=[{"type": "google_search"}],
)

for step in interaction.steps:
    if step.type == "google_search_result":
        print(f"Search Suggestions: {step.result[0].search_suggestions}")
    elif step.type == "model_output":
        print(f"Answer: {step.content[0].text}")
        if step.content[0].annotations:
            for anno in step.content[0].annotations:
                print(f"Citation: {anno.title} ({anno.uri})")

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const interaction = await client.interactions.create({
    model: 'gemini-3.8-flash',
    input: 'Who won Euro 2024?',
    tools: [{ type: 'google_search' }]
});

for (const step of interaction.steps) {
    if (step.type === 'google_search_result') {
        console.log(`Search Suggestions: ${step.result[0].search_suggestions}`);
    } else if (step.type === 'model_output') {
        console.log(`Answer: ${step.content[0].text}`);
        if (step.content[0].annotations) {
            for (const anno of step.content[0].annotations) {
                console.log(`Citation: ${anno.title} (${anno.uri})`);
            }
        }
    }
}

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.Annotation;
import com.google.genai.gaos.models.interactions.Content;
import com.google.genai.gaos.models.interactions.CreateModelInteraction;
import com.google.genai.gaos.models.interactions.GoogleSearch;
import com.google.genai.gaos.models.interactions.GoogleSearchResult;
import com.google.genai.gaos.models.interactions.GoogleSearchResultStep;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Model;
import com.google.genai.gaos.models.interactions.ModelOutputStep;
import com.google.genai.gaos.models.interactions.Step;
import com.google.genai.gaos.models.interactions.TextContent;
import com.google.genai.gaos.models.interactions.URLCitation;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
import java.util.Arrays;
import java.util.Collections;

Client client = new Client();

CreateModelInteraction request =
    CreateModelInteraction.builder()
        .model(Model.of("gemini-3.8-flash"))
        .input(InteractionsInput.of("Who won Euro 2024?"))
        .tools(Arrays.asList(GoogleSearch.builder().build()))
        .build();

Interaction interaction =
    client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();

for (Step step : interaction.steps().orElse(Collections.emptyList())) {
  if (step instanceof GoogleSearchResultStep) {
    GoogleSearchResultStep searchStep = (GoogleSearchResultStep) step;
    for (GoogleSearchResult res : searchStep.result().orElse(Collections.emptyList())) {
      System.out.println("Search Suggestions: " + res.searchSuggestions().orElse(""));
    }
  } else if (step instanceof ModelOutputStep) {
    ModelOutputStep modelOutput = (ModelOutputStep) step;
    for (Content contentBlock : modelOutput.content().orElse(Collections.emptyList())) {
      if (contentBlock instanceof TextContent) {
        TextContent textContent = (TextContent) contentBlock;
        System.out.println("Answer: " + textContent.text().orElse(""));
        for (Annotation anno : textContent.annotations().orElse(Collections.emptyList())) {
          if (anno instanceof URLCitation) {
            URLCitation cit = (URLCitation) anno;
            System.out.println(
                "Citation: " + cit.title().orElse("") + " (" + cit.url().orElse("") + ")");
          }
        }
      }
    }
  }
}

Go

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

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

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.8-flash"),
            Input: interactions.NewInteractionsInput("Who won Euro 2024?"),
            Tools: []interactions.Tool{
                interactions.NewTool(interactions.GoogleSearch{}),
            },
        }),
    })
    if err != nil {
        log.Fatal(err)
    }

    for _, step := range res.Interaction.Steps {
        if searchStep := step.GoogleSearchResultStep; searchStep != nil {
            for _, r := range searchStep.Result {
                if r.SearchSuggestions != nil {
                    fmt.Println("Search Suggestions:", *r.SearchSuggestions)
                }
            }
        } else if modelOutput := step.ModelOutputStep; modelOutput != nil {
            for _, contentBlock := range modelOutput.Content {
                if textContent := contentBlock.TextContent; textContent != nil {
                    fmt.Println("Answer:", textContent.Text)
                    for _, anno := range textContent.Annotations {
                        if cit := anno.URLCitation; cit != nil {
                            fmt.Printf("Citation: %s (%s)\n", cit.GetTitle(), cit.GetURL())
                        }
                    }
                }
            }
        }
    }
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "model": "gemini-3.8-flash",
    "input": "Who won Euro 2024?",
    "tools": [{"type": "google_search"}]
}'

# Response (showing grounding)
{
  "id": "int_grounded",
  "steps": [
    {
      "type": "user_input",
      "status": "done",
      "content": [{ "type": "text", "text": "Who won Euro 2024?" }]
    },
    {
      "type": "google_search_call",
      "status": "done",
      "content": [{ "type": "text", "text": "UEFA Euro 2024 winner" }]
    },
    {
      "type": "google_search_result",
      "status": "done",
      "content": [
        {
          "type": "text",
          "text": "Spain won Euro 2024..."
        }
      ]
    },
    {
      "type": "model_output",
      "status": "done",
      "content": [
        {
          "type": "text",
          "text": "Spain won Euro 2024, defeating England 2-1.",
          "annotations": [
            {
              "start_index": 0,
              "end_index": 42,
              "uri": "https://vertexaisearch...",
              "title": "aljazeera.com"
            }
          ]
        }
      ]
    }
  ]
}

함수 호출

함수 호출과 결과의 구조도 Steps 스키마에 맞게 변경됐어요.

Before (generateContent)

generateContent에서는 응답이 candidates 안에서 함수 호출을 반환해요.



JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

let response = await client.models.generateContent({
    model: 'gemini-2.5-flash-lite',
    contents: "What's the weather in Boston?",
    config: { tools: [weatherTool] }
});

const functionCall = response.candidates[0].content.parts[0].functionCall;
console.log(`Requested tool: ${functionCall.name}`);

const result = "52°F and rain";

response = await client.models.generateContent({
    model: 'gemini-2.5-flash-lite',
    contents: [
        { role: 'user', parts: [{ text: "What's the weather in Boston?" }] },
        response.candidates[0].content,
        {
            role: 'user',
            parts: [{
                functionResponse: {
                    name: functionCall.name,
                    response: { result: result }
                }
            }]
        }
    ],
    config: { tools: [weatherTool] }
});
console.log(response.text);

Java

import com.google.genai.Client;
import com.google.genai.types.Content;
import com.google.genai.types.FunctionCall;
import com.google.genai.types.FunctionDeclaration;
import com.google.genai.types.GenerateContentConfig;
import com.google.genai.types.GenerateContentResponse;
import com.google.genai.types.Part;
import com.google.genai.types.Schema;
import com.google.genai.types.Tool;
import com.google.genai.types.Type;
import java.util.Arrays;
import java.util.Map;

Client client = new Client();

FunctionDeclaration weatherFunc =
    FunctionDeclaration.builder()
        .name("get_weather")
        .description("Gets weather")
        .parameters(
            Schema.builder()
                .type(Type.Known.OBJECT)
                .properties(Map.of("location", Schema.builder().type(Type.Known.STRING).build()))
                .build())
        .build();

Tool weatherTool = Tool.builder().functionDeclarations(Arrays.asList(weatherFunc)).build();

GenerateContentResponse response =
    client.models.generateContent(
        "gemini-2.5-flash-lite",
        "What's the weather in Boston?",
        GenerateContentConfig.builder().tools(Arrays.asList(weatherTool)).build());

FunctionCall functionCall = response.functionCalls().get(0);
System.out.println("Requested tool: " + functionCall.name().orElse(""));

String result = "52°F and rain";

GenerateContentResponse finalResponse =
    client.models.generateContent(
        "gemini-2.5-flash-lite",
        Arrays.asList(
            Content.builder()
                .role("user")
                .parts(Arrays.asList(Part.fromText("What's the weather in Boston?")))
                .build(),
            response.candidates().get().get(0).content().get(),
            Content.builder()
                .role("user")
                .parts(
                    Arrays.asList(
                        Part.fromFunctionResponse(
                            functionCall.name().orElse(""), Map.of("result", result))))
                .build()),
        GenerateContentConfig.builder().tools(Arrays.asList(weatherTool)).build());
System.out.println(finalResponse.text());

Go

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)
    }

    weatherTool := &genai.Tool{
        FunctionDeclarations: []*genai.FunctionDeclaration{
            {
                Name:        "get_weather",
                Description: "Gets weather",
                Parameters: &genai.Schema{
                    Type: genai.TypeObject,
                    Properties: map[string]*genai.Schema{
                        "location": {Type: genai.TypeString},
                    },
                },
            },
        },
    }

    config := &genai.GenerateContentConfig{
        Tools: []*genai.Tool{weatherTool},
    }

    response, err := client.Models.GenerateContent(
        ctx,
        "gemini-2.5-flash-lite",
        genai.Text("What's the weather in Boston?"),
        config,
    )
    if err != nil {
        log.Fatal(err)
    }

    calls := response.FunctionCalls()
    if len(calls) > 0 {
        functionCall := calls[0]
        fmt.Println("Requested tool:", functionCall.Name)

        result := "52°F and rain"
        history := []*genai.Content{
            genai.NewContentFromText("What's the weather in Boston?", genai.RoleUser),
            response.Candidates[0].Content,
            genai.NewContentFromParts([]*genai.Part{
                genai.NewPartFromFunctionResponse(functionCall.Name, map[string]any{"result": result}),
            }, genai.RoleUser),
        }

        finalResponse, err := client.Models.GenerateContent(ctx, "gemini-2.5-flash-lite", history, config)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(finalResponse.Text())
    }
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "contents": [{
        "parts": [{
            "text": "What is the weather like in Boston, MA?"
        }]
    }],
    "tools": [{
        "functionDeclarations": [{
            "name": "get_weather",
            "description": "Get the current weather",
            "parameters": {
                "type": "OBJECT",
                "properties": {
                    "location": {"type": "STRING"}
                },
                "required": ["location"]
            }
        }]
    }]
}'

# Response
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "functionCall": {
              "name": "get_weather",
              "args": { "location": "Boston, MA" }
            }
          }
        ],
        "role": "model"
      },
      "finishReason": "STOP",
      "index": 0
    }
  ]
}

After (Interactions API)

도구 호출과 결과가 이제 타임라인의 별도 단계가 돼요.

Python

from google import genai

client = genai.Client()

weather_tool = {
    "type": "function",
    "name": "get_weather",
    "description": "Gets weather",
    "parameters": {
        "type": "object",
        "properties": {
            "location": {"type": "string"}
        },
    },
}

interaction = client.interactions.create(
    model="gemini-3.8-flash",
    input="What's the weather in Boston?",
    tools=[weather_tool],
)

for step in interaction.steps:
    if step.type == "function_call":
        print(f"Executing {step.name} for {step.arguments}")

        result = "52°F and rain"

        interaction = client.interactions.create(
            model="gemini-3.8-flash",
            previous_interaction_id=interaction.id,
            input=[
                {
                    "type": "function_result",
                    "call_id": step.id,
                    "name": step.name,
                    "result": [{"type": "text", "text": result}],
                }
            ],
        )
        print(interaction.output_text)

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const weatherTool = {
    type: "function",
    name: "get_weather",
    description: "Get weather for a location",
    parameters: {
        type: "object",
        properties: {
            location: { type: "string" }
        },
        required: ["location"]
    }
};

const interaction = await client.interactions.create({
    model: 'gemini-3.8-flash',
    input: "What's the weather in Boston?",
    tools: [weatherTool]
});

for (const step of interaction.steps) {
    if (step.type === 'function_call') {
        console.log(`Executing ${step.name} for ${JSON.stringify(step.arguments)}`);

        const result = "52°F and rain";

        const nextInteraction = await client.interactions.create({
            model: 'gemini-3.8-flash',
            previous_interaction_id: interaction.id,
            input: [
                {
                    type: 'function_result',
                    call_id: step.id,
                    name: step.name,
                    result: [{ type: 'text', text: result }]
                }
            ]
        });

        console.log(nextInteraction.output_text);
    }
}

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.CreateModelInteraction;
import com.google.genai.gaos.models.interactions.Function;
import com.google.genai.gaos.models.interactions.FunctionCallStep;
import com.google.genai.gaos.models.interactions.FunctionResultStep;
import com.google.genai.gaos.models.interactions.FunctionResultStepResultUnion;
import com.google.genai.gaos.models.interactions.Interaction;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Model;
import com.google.genai.gaos.models.interactions.Step;
import com.google.genai.gaos.models.interactions.TextContent;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

Client client = new Client();

Map<String, Object> properties = new HashMap<>();
properties.put("location", Map.of("type", "string"));

Map<String, Object> parameters = new HashMap<>();
parameters.put("type", "object");
parameters.put("properties", properties);
parameters.put("required", Arrays.asList("location"));

Function weatherTool =
    Function.builder()
        .name("get_weather")
        .description("Gets weather")
        .parameters(parameters)
        .build();

CreateModelInteraction request =
    CreateModelInteraction.builder()
        .model(Model.of("gemini-3.8-flash"))
        .input(InteractionsInput.of("What's the weather in Boston?"))
        .tools(Arrays.asList(weatherTool))
        .build();

Interaction interaction =
    client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();

for (Step step : interaction.steps().orElse(Collections.emptyList())) {
  if (step instanceof FunctionCallStep) {
    FunctionCallStep fcStep = (FunctionCallStep) step;
    System.out.println(
        "Executing "
            + fcStep.name().orElse("")
            + " for "
            + fcStep.arguments().orElse(Collections.emptyMap()));

    String result = "52°F and rain";

    FunctionResultStep funcResult =
        FunctionResultStep.builder()
            .callId(fcStep.id().orElse(""))
            .name(fcStep.name().orElse(""))
            .result(
                FunctionResultStepResultUnion.of(
                    Arrays.asList(TextContent.builder().text(result).build())))
            .build();

    CreateModelInteraction nextRequest =
        CreateModelInteraction.builder()
            .model(Model.of("gemini-3.8-flash"))
            .previousInteractionId(interaction.id().orElse(""))
            .input(InteractionsInput.ofStep(Arrays.asList(funcResult)))
            .build();

    Interaction nextInteraction =
        client.interactions.create(CreateInteractionRequestBody.of(nextRequest)).interaction().get();
    System.out.println(nextInteraction.outputText().orElse(""));
  }
}

Go

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

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

    weatherTool := interactions.NewTool(interactions.Function{
        Name:        genai.Ptr("get_weather"),
        Description: genai.Ptr("Gets weather"),
        Parameters: map[string]any{
            "type": "object",
            "properties": map[string]any{
                "location": map[string]any{"type": "string"},
            },
            "required": []string{"location"},
        },
    })

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model: interactions.Model("gemini-3.8-flash"),
            Input: interactions.NewInteractionsInput("What's the weather in Boston?"),
            Tools: []interactions.Tool{weatherTool},
        }),
    })
    if err != nil {
        log.Fatal(err)
    }

    for _, step := range res.Interaction.Steps {
        if fcStep := step.FunctionCallStep; fcStep != nil {
            fmt.Printf("Executing %s for %v\n", fcStep.Name, fcStep.Arguments)

            result := "52°F and rain"
            funcResult := interactions.NewStep(interactions.FunctionResultStep{
                CallID: fcStep.ID,
                Name:   genai.Ptr(fcStep.Name),
                Result: interactions.NewFunctionResultStepResultUnion([]interactions.FunctionResultSubcontent{
                    interactions.NewFunctionResultSubcontent(interactions.TextContent{Text: result}),
                }),
            })

            nextRes, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
                Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
                    Model:                 interactions.Model("gemini-3.8-flash"),
                    PreviousInteractionID: res.Interaction.ID,
                    Input:                 interactions.NewInteractionsInput([]interactions.Step{funcResult}),
                }),
            })
            if err != nil {
                log.Fatal(err)
            }
            if nextRes.Interaction.OutputText != nil {
                fmt.Println(*nextRes.Interaction.OutputText)
            }
        }
    }
}

REST

# Initial Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "model": "gemini-3.8-flash",
    "input": "What's the weather in Boston?",
    "tools": [{
        "type": "function",
        "name": "get_weather",
        "description": "Get weather for a location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": { "type": "string" }
            },
            "required": ["location"]
        }
    }]
}'

# Response (requires action)
{
  "id": "int_001",
  "status": "requires_action",
  "steps": [
    {
      "type": "user_input",
      "status": "done",
      "content": [
        { "type": "text", "text": "What's the weather in Boston?" }
      ]
    },
    {
      "type": "function_call",
      "status": "waiting",
      "id": "fc_1",
      "name": "get_weather",
      "arguments": { "location": "Boston, MA" }
    }
  ]
}

# Submit Tool Result Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta2/interactions" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "model": "gemini-3.8-flash",
    "previous_interaction_id": "int_001",
    "input": {
        "type": "function_result",
        "call_id": "fc_1",
        "name": "get_weather",
        "result": [
            { "type": "text", "text": "52°F with rain" }
        ]
    }
}'

# Final Response
{
  "id": "int_002",
  "status": "completed",
  "steps": [
    {
      "type": "function_result",
      "call_id": "fc_1",
      "name": "get_weather",
      "result": [
        { "type": "text", "text": "52°F with rain" }
      ]
    },
    {
      "type": "model_output",
      "status": "done",
      "content": [
        { "type": "text", "text": "It's 52°F with rain in Boston." }
      ]
    }
  ]
}

스트리밍

스트리밍의 핵심 차이는 Interactions API가 요청 본문에 "stream": true로 같은 엔드포인트를 사용한다는 점이에요. 반면 generateContent API는 전용 엔드포인트(:streamGenerateContent)를 호출해야 했어요.

또한 스트리밍 이벤트는 이제 전문화된 타입을 사용해 상호작용 수명 주기를 모니터링하고 타임라인을 따라 실행 단계를 추적해요.

Before (generateContentStream)

generateContent를 사용하면 응답 청크의 스트림을 소비해요.

Python

from google import genai

client = genai.Client()

response = client.models.generate_content_stream(
    model="gemini-2.5-flash-lite", contents="Tell me a story"
)
for chunk in response:
    print(chunk.text, end="")

JavaScript

const responseStream = await client.models.generateContentStream({
    model: 'gemini-2.5-flash-lite',
    contents: 'Tell me a story',
});
for await (const chunk of responseStream) {
    process.stdout.write(chunk.text);
}

Java

import com.google.genai.Client;
import com.google.genai.ResponseStream;
import com.google.genai.types.GenerateContentResponse;

Client client = new Client();

try (ResponseStream<GenerateContentResponse> responseStream =
    client.models.generateContentStream("gemini-2.5-flash-lite", "Tell me a story", null)) {
  for (GenerateContentResponse chunk : responseStream) {
    System.out.print(chunk.text());
  }
}

Go

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)
    }

    for chunk, err := range client.Models.GenerateContentStream(
        ctx,
        "gemini-2.5-flash-lite",
        genai.Text("Tell me a story"),
        nil,
    ) {
        if err != nil {
            log.Fatal(err)
        }
        fmt.Print(chunk.Text())
    }
}

REST

# Request
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:streamGenerateContent" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-d '{
    "contents": [{
        "parts": [{
            "text": "Tell me a story"
        }]
    }]
}'

# Response stream
event: content.start
data: {"event_type": "content.start", "index": 0, "content": {"type": "thought"}}
event: content.delta
data: {"event_type": "content.delta", "index": 0, "delta": {"type": "thought_summary", "text": "User wants an explanation."}}
event: content.stop
data: {"event_type": "content.stop", "index": 0}
event: content.start
data: {"event_type": "content.start", "index": 1, "content": {"type": "text"}}
event: content.delta
data: {"event_type": "content.delta", "index": 1, "delta": {"type": "text", "text": "Hello"}}
event: content.stop
data: {"event_type": "content.stop", "index": 1}

After (Interactions API)

Interactions API에서 스트리밍은 Server-Sent Events(SSE)와 전문화된 델타 타입을 사용해 실행 단계가 발생하는 대로 표현해요.

Python

from google import genai

client = genai.Client()

stream = client.interactions.create(
    model="gemini-3.8-flash",
    input="Tell me a story",
    stream=True,
)

for event in stream:
    if event.event_type == "step.delta" and event.delta:
        if getattr(event.delta, "type", None) == "text" and getattr(event.delta, "text", None):
            print(event.delta.text, end="", flush=True)
    elif event.event_type == "interaction.completed":
        print(f"\n\n--- Stream Finished ---")

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});

const stream = await client.interactions.create({
    model: 'gemini-3.8-flash',
    input: 'Tell me a story',
    stream: true,
});

for await (const event of stream) {
    if (event.event_type === 'step.delta' && event.delta) {
        if (event.delta.type === 'text' && event.delta.text) {
            process.stdout.write(event.delta.text);
        }
    } else if (event.event_type === 'interaction.completed') {
        console.log('\n\n--- Stream Finished ---');
    }
}

Java

import com.google.genai.Client;
import com.google.genai.gaos.models.interactions.CreateModelInteraction;
import com.google.genai.gaos.models.interactions.InteractionCompletedEvent;
import com.google.genai.gaos.models.interactions.InteractionSSEEvent;
import com.google.genai.gaos.models.interactions.InteractionSSEStreamEvent;
import com.google.genai.gaos.models.interactions.InteractionsInput;
import com.google.genai.gaos.models.interactions.Model;
import com.google.genai.gaos.models.interactions.StepDelta;
import com.google.genai.gaos.models.interactions.TextDelta;
import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
import com.google.genai.gaos.utils.EventStream;

Client client = new Client();

CreateModelInteraction request =
    CreateModelInteraction.builder()
        .model(Model.of("gemini-3.8-flash"))
        .input(InteractionsInput.of("Tell me a story"))
        .stream(true)
        .build();

try (EventStream<InteractionSSEStreamEvent> stream =
    client.interactions.create(CreateInteractionRequestBody.of(request)).events()) {
  for (InteractionSSEStreamEvent streamEvent : stream) {
    if (streamEvent.data().isPresent()) {
      InteractionSSEEvent event = streamEvent.data().get();
      if (event instanceof StepDelta) {
        StepDelta stepDelta = (StepDelta) event;
        if (stepDelta.delta().isPresent() && stepDelta.delta().get() instanceof TextDelta) {
          TextDelta textDelta = (TextDelta) stepDelta.delta().get();
          System.out.print(textDelta.text().orElse(""));
        }
      } else if (event instanceof InteractionCompletedEvent) {
        System.out.println("\n\n--- Stream Finished ---");
      }
    }
  }
}

Go

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

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

    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
        Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
            Model:  interactions.Model("gemini-3.8-flash"),
            Input:  interactions.NewInteractionsInput("Tell me a story"),
            Stream: genai.Ptr(true),
        }),
    })
    if err != nil {
        log.Fatal(err)
    }
    stream := res.InteractionSSEStreamEvent
    defer stream.Close()

    for stream.Next() {
        event := stream.Value()
        if stepDelta := event.GetDataStepDelta(); stepDelta != nil {
            if textDelta := stepDelta.GetDeltaText(); textDelta != nil {
                fmt.Print(textDelta.GetText())
            }
        } else if event.GetDataInteractionCompleted() != nil {
            fmt.Println("\n\n--- Stream Finished ---")
        }
    }
    if err := stream.Err(); err != nil {
        log.Fatal(err)
    }
}

REST

예시 SSE 스트림 출력


### Streaming tools and function calls

The way tools behave in the stream has shifted significantly from `generateContent` to provide more granular control and visibility.

#### Before (`generateContent`)

With `generateContent`, streaming function calls arrived complete in a single chunk. You could not see the arguments being generated in real-time, so the handler simply checked for a complete `functionCall` object.

### Python

### JavaScript

### Java

### Go

### REST

#### After (Interactions API)

The Interactions API streams function call arguments character-by-character as `arguments` events. The entire tool lifecycle — thought, call, result, and output — plays out as a series of distinct steps.

### Python

### JavaScript

### Java

### Go

### REST

더 알아보기 (Learn more)

Interactions API는 서버 측 히스토리 관리, 타입이 지정된 실행 단계, 인라인 인용, 함수 호출 단계, 서버 전송 이벤트(SSE) 스트리밍을 제공해 가장 간단하고 강력한 Gemini 개발 방식이에요. 관련 안내는 Interactions API 개요와 Google GenAI SDK로 마이그레이션 문서를 이어서 살펴보세요.