courses
Google은 6주 동안 Flash 모델을 3번 릴리스했습니다: 7월 말 3.6, 8월 13일 3.7 Flash, 그리고 2026년 9월 2일 Gemini 3.8 Flash입니다. 3.7에서 오셨다면 업그레이드는 한 줄로 끝납니다. API 표면이 동일하기 때문입니다. 그보다 오래된 설정은 파라미터를 조정하지 않으면 여전히 깨집니다.
레거시 코드를 덧대기보다, 이 튜토리얼은 처음부터 깔끔한 셋업을 만듭니다. Interactions API로 Python 클라이언트를 초기화하고, 실제 토큰 수로 3가지 추론 레벨을 디버깅 과제에서 비교하며, PDF 인보이스에서 스키마에 맞는 JSON을 추출하고, 완전한 함수 호출 루프를 구현합니다. 마지막으로 3.6 Flash 이하에서 업그레이드하는 개발자를 위한 마이그레이션 체크리스트를 다룹니다.
진행하려면 Python 3.10+와 Google AI Studio API 키가 필요합니다. 이 가이드는 기능 발표가 아니라 코드 구현에 초점을 맞춥니다.
요약
-
Gemini 3.8 Flash(
gemini-3.8-flash)는google-genaiSDK에서 client.interactions.create()를 통해 Interactions API를 사용합니다. -
추론 깊이는 문자열 값(
thinking_level:low,medium,high)으로 설정합니다. -
레거시 샘플링 옵션(
temperature,top_p,top_k)은 더 이상 사용하지 않습니다 -
멀티턴 상태는 서버 측
previous_interaction_id로 관리합니다. -
도입 가격은 2026년 12월 31일까지 입력/출력 100만 토큰당 $0.75 / $3.75입니다.
-
3.7 Flash에서 오면 모델 문자열만 바꾸면 됩니다.
Gemini 3.8 Flash란?
Gemini 3.8 Flash는 Google의 범용 실무형 모델로, 2026년 9월 2일부터 gemini-3.8-flash 모델 ID로 일반 공개되었습니다. 3.7 Flash 출시 3주 후 도착했으며, 장기 코딩, 에이전트형 워크플로, 금융·법률 등 특화 도메인에서의 다단계 추론에 포지셔닝됩니다.
API 호출에 중요한 스펙은 3.7과 동일합니다:
- 100만 토큰 컨텍스트 윈도
- 최대 64k 출력 토큰
- 멀티모달 입력(텍스트, 이미지, 비디오, 오디오, PDF) + 텍스트 출력
- 2026년 12월 31일까지 입력 100만 토큰 $0.75, 출력 100만 토큰 $3.75(2027년 1월 1일부터 각각 $1.50, $7.50)
변한 것은 표면이 아니라 동작입니다. Google에 따르면 3.8은 복잡한 작업에서 더 열심히 일하며, 추가 추론 단계를 거치고 도구를 반복적으로 호출합니다. 이는 노력 수준이 높을수록 토큰 사용량이 늘 수 있음을 의미합니다. 깊이보다 효율이 중요한 워크로드에는 3.7 Flash가 계속 완전 지원됩니다.
벤치마크와 자세한 가격은 Gemini 3.8 Flash 가이드를 확인하시거나, 플랫폼 개요는 What is Google Gemini? 가이드를 참조하세요.
Gemini 3.8 Flash vs. 3.8 Flash Cyber
출시에는 2가지 변형이 포함되며, 이 중 1개만 모델 ID를 직접 입력할 수 있습니다.
- Gemini 3.8 Flash 는 범용 모델로, 오늘 기준 Google AI Studio와 Gemini API에서 사용할 수 있습니다.
- Gemini 3.8 Flash Cyber는 취약점 발견과 자동 패치를 위해 튜닝된 사이버보안 변형입니다.
Cyber 변형은 공개 API에서 사용할 수 없습니다. 액세스는 Google의 Fairwind Program을 통해 제공되며, 승인된 정부 기관, 중요 인프라 운영자, 소프트웨어 유지관리자로 제한됩니다.
이 튜토리얼을 따른다면 모델 ID는 gemini-3.8-flash입니다. 아래 내용 어디에도 Cyber 변형이 필요하거나 사용되지 않습니다.
Interactions API vs. generateContent
Gemini 3.8 Flash를 호출하려면 google-genai SDK의 client.interactions.create()를 사용하세요. Google은 2026년 6월 Interactions API를 GA로 공개했으며 모든 신규 작업에 권장합니다. generateContent도 동작하지만, 이제 레거시입니다. 서버 측 히스토리, 백그라운드 실행, 실행 단계 관찰 등 신규 기능은 먼저 Interactions에 추가됩니다.
실무에서 가장 큰 변화는 상태 관리입니다. 멀티턴 호출은 이제 서버 측 previous_interaction_id를 사용합니다. 마지막 상호작용 ID를 전달하면 서버가 상태 복원을 처리합니다. 더 이상 클라이언트에서 전체 대화 기록을 직접 덧붙이거나 재전송할 필요가 없습니다. 또한 모델 턴을 미리 채워 넣는 방식은 피하세요. 이는 레거시 generateContent 패턴이며 Gemini 3.x에서 깨집니다.
거의 모두가 한 번은 겪는 함정이 있으며, PDF 섹션에서도 다시 나옵니다: previous_interaction_id는 대화 기록만 복원합니다. tools, system_instruction, generation_config, response_format은 상호작용 범위이므로, 필요한 턴마다 다시 전달해야 합니다.
thinking_level이 샘플링 노브를 대체
이전 Gemini 모델에서는 temperature, top_p, top_k로 출력 무작위성을 제어했습니다. Gemini 3.x는 이러한 샘플링 노브를 제거하고 thinking_level로 대체했으며, 이제 이게 유일한 다이얼입니다.
허용 값은 3가지입니다:
-
low: 추론 토큰이 가장 적고, 가장 빠르고 저렴합니다. 추출, 분류, 직접 검수할 작업에 적합합니다. -
medium: 기본값이며, 코드와 에이전트 작업에 Google이 권장합니다. -
high: 가장 큰 추론 예산으로, 어려운 다단계 로직과 도구 의존 작업에 사용합니다.
minimal은 보내지 마세요. Gemini Flash 3.7부터 유효하지 않으며 400 검증 오류를 반환합니다.
3.7에서 이어지는 또 다른 규칙: frequency_penalty, presence_penalty, candidate_count는 이제 활성 API 에러를 던지므로 레거시 설정에서도 제거하세요.
Gemini 3.8 Flash API는 어떻게 설정하나요?
환경 설정은 약 2분이면 됩니다. Google AI Studio의 API 키와 업데이트된 google-genai Python 라이브러리가 필요합니다.
Google AI Studio에서 API 키 받기
Google AI Studio에 브라우저로 방문하여 Google 계정으로 로그인하세요. Create API Key를 클릭하고, Google Cloud 프로젝트를 선택하거나 생성한 뒤 비밀 키 문자열을 복사합니다.

터미널을 열고 export GEMINI_API_KEY=<your-key>로 환경 변수에 키를 저장하세요.
키를 절대 URL의 ?key= 쿼리 파라미터로 전달하지 마세요. 쿼리 문자열은 서버 로그, 브라우저 히스토리, 프록시 캐시에 남습니다. 코드를 작성하기 전에 플레이라운드에서 모델을 살펴보고 싶다면, Google AI Studio Tutorial이 Chat, Build, Stream 모드를 다룹니다. 이 글은 API에 집중합니다.
프로덕션 시스템에서는 인증 방식이 달라집니다. Vertex AI(현재 Gemini Enterprise Agent Platform의 일부)는 원시 API 키 대신 OAuth, IAM 역할, 리전별 엔드포인트를 제공합니다. 학습에는 AI Studio 키가 가장 빠른 경로이므로 이 튜토리얼은 전부 AI Studio 키를 사용하지만, 실제 사용자 데이터에 닿기 전에 Vertex 마이그레이션을 계획하세요.
google-genai 설치 및 클라이언트 생성
여전히 google-generativeai를 설치하라고 하는 튜토리얼이 많습니다. 그건 구버전 SDK이며 Interactions API가 없습니다. google-genai(버전 2.3.0 이상)를 설치하세요:
pip install -U google-genai
설치 후, Python이 라이브러리를 불러오고 오류 없이 클라이언트를 초기화하는지 확인하세요:
from google import genai # reads GEMINI_API_KEY from the environment
client = genai.Client()
print("Client initialized successfully.")
첫 Interactions API 호출 만들기
Interactions API로의 모든 요청은 전체 턴(입력, 모델의 사고, 도구 호출, 최종 출력)을 기록하는 Interaction 리소스를 생성합니다. SDK는 최종 텍스트를 output_text 편의 프로퍼티로 노출하므로, 수동으로 단계를 따라갈 일은 드뭅니다.
from google import genai
client = genai.Client()
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=(
"Write a pandas one-liner that adds a 7-day rolling average "
"revenue column per store_id to a DataFrame with columns "
"date, store_id, revenue. Reply with only the code, no explanation."
),
generation_config={"thinking_level": "medium"},
)
print(interaction.output_text)
usage = interaction.usage
print(
f"input={usage.total_input_tokens} | output={usage.total_output_tokens} | "
f"thinking={usage.total_thought_tokens} | total={usage.total_tokens}"
)
제 환경에서는 모델이 체이닝된 pandas 원라이너를 답했고, 사용량 라인은 다음과 같습니다:

이 숫자에는 3.7과의 첫 실제 차이가 숨어 있습니다. 더 긴 프롬프트와 출력 제한 없이 같은 작업을 다시 실행하자, 3.8은 출력 870 토큰에 대해 사고 1,436 토큰을 썼습니다. 제한을 두자 42 출력에 1,515 사고를 썼습니다. 추론 예산은 거의 변하지 않았습니다. 이는 같은 두 프롬프트에서 사고 토큰이 838→1,530으로 크게 흔들리던 3.7과 반대입니다.
즉, 3.8은 과제를 어떻게 표현했는지가 아니라 과제 자체에 따라 얼마나 깊게 생각할지 결정합니다. 이는 모델이 의도적으로 더 많이 추론하고 검증한다는 Google의 설명과 일치합니다. 사고 토큰은 출력과 같은 요율로 과금되므로, 제한된 호출에서는 청구된 토큰의 약 97%가 보이지 않는 추론이었습니다. 그래서 다음 섹션이 존재합니다.
응답 스트리밍하기
채팅 인터페이스처럼 사람이 지켜보는 경우, 전체 응답을 몇 초 기다리면 느리게 느껴집니다. client.interactions.create()에 stream=True를 전달하고 도착하는 청크를 즉시 출력하세요:
from google import genai
client = genai.Client()
stream = client.interactions.create(
model="gemini-3.8-flash",
input="Explain the difference between a JOIN and a correlated subquery in SQL.",
generation_config={"thinking_level": "low"},
stream=True,
)
for event in stream:
if event.event_type == "step.delta" and event.delta.type == "text":
print(event.delta.text, end="", flush=True)
print()
이 코드를 실행했을 때, 모델은 thinking_level: "low"에서도 길고 잘 구성된 답을 반환했습니다. 개념 비교, 요약 표, 각 고객의 최신 주문을 찾는 2가지 SQL 예시(파생 테이블 조인, SELECT 목록의 상관 서브쿼리)가 포함되었고, 처음 몇 단어는 거의 즉시 나타났습니다. 이것이 스트리밍의 요점입니다.
마지막 print()에는 이유가 있습니다. 이를 생략하면 마지막 청크가 줄 중간에서 끝나 zsh 프롬프트 앞에 %가 남습니다. 스트림이 모델 텍스트가 멈춘 지점에서 정확히 멈추기 때문입니다. 또한 델타는 텍스트만 전달하므로, 요청당 토큰 수를 로깅한다면 청크를 합산하지 말고 최종 완료 이벤트에서 읽으세요.
thinking_level은 비용과 품질에 어떻게 영향을 줄까요?
thinking_level은 Gemini 3.8 Flash가 답을 쓰기 전에 수행하는 추론의 양을 정합니다. 추론 토큰은 100만 개당 $3.75의 출력 요율로 과금되므로, 선택한 레벨이 비용과 대기 시간을 직접 좌우합니다. Google은 3.8이 이를 의도적으로 활용한다고 밝힙니다. 복잡한 작업에서 추가 추론 단계를 밟고, 높은 노력 수준에서 3.7보다 더 많은 토큰을 쓸 수 있습니다.
한 프롬프트를 low, medium, high로 실행
테스트는 동일한 프롬프트를 3가지 레벨에서 보낸 결제 재시도 함수의 경쟁 상태(race condition)입니다. 동시성 버그는 대충 읽으면 잡히지 않으므로, 레벨 차이가 있다면 여기서 드러나야 합니다. 이 글에서 하나만 실행한다면 이 코드 블록을 실행하세요. 수치가 어떤 장황한 설명보다 잘 말해줍니다.
import time
from google import genai
client = genai.Client()
BUGGY_CODE = '''
import threading
payment_attempts = {}
def retry_payment(order_id, charge_fn, max_retries=3):
"""Retry a failed payment up to max_retries times."""
if order_id not in payment_attempts:
payment_attempts[order_id] = 0
while payment_attempts[order_id] < max_retries:
success = charge_fn(order_id)
if success:
del payment_attempts[order_id]
return True
payment_attempts[order_id] += 1
return False
'''
PROMPT = (
"Two worker threads can call retry_payment() with the same order_id "
"at the same time. Identify the concurrency bug that can double-charge "
"a customer, and rewrite the function to fix it.\n\n" + BUGGY_CODE
)
for level in ["low", "medium", "high"]:
start = time.perf_counter()
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=PROMPT,
generation_config={"thinking_level": level},
)
elapsed = time.perf_counter() - start
usage = interaction.usage
print(f"\n=== thinking_level: {level} | {elapsed:.1f}s ===")
print(interaction.output_text)
print(
f"input={usage.total_input_tokens} | output={usage.total_output_tokens} | "
f"thinking={usage.total_thought_tokens}"
)
참고로, 취약점은 payment_attempts[order_id]에 대한 비원자적 확인-후-실행입니다. 동시성 하에서 2개의 스레드가 모두 while 조건을 통과하고, 둘 다 카운터를 증가시키기 전에 charge_fn()을 호출할 수 있습니다. 해결하려면 읽기-확인-청구-증가 흐름을 주문별 락으로 감싸거나, 게이트웨이에서 idemopotency 키를 사용해야 합니다.
결과 비교
제 실행 결과:
|
|
레이스 잡았나? |
수정이 올바른가? |
수정 설계 |
지연 시간 |
사고 토큰 |
출력 토큰 |
비용 |
|
|
예 |
예 |
주문별 락 + 완료 집합 |
7.8 s |
0 |
791 |
$0.0031 |
|
|
예 |
예 |
주문별 락 + 주문별 상태 dict |
16.6 s |
3,158 |
627 |
$0.0143 |
|
|
예 |
예 |
주문별 레코드(락, 시도 수, 완료) + 실패 경로 문서화 |
25.5 s |
4,512 |
896 |
$0.0204 |
세 레벨 모두 이중 청구 문제를 찾았고, 모두 주문별 락을 도입해 무관한 주문은 병렬로 처리했습니다. 이를 3.7과 비교하면 2번째가 핵심입니다. 거기서 low는 네트워크 호출 동안 유지되는 전역 락 하나로 모든 것을 감쌌고, 주문별 락은 medium에서야 나타났습니다. 3.8에서는 low가 사고 토큰 0, 7.8초, 1센트의 3분의 1도 안 되는 비용으로 더 나은 설계를 작성합니다.
그렇다면 레벨이 이제 주는 가치는 무엇일까요? 감사(audit) 깊이입니다. 이 코드는 4가지 뚜렷한 실패 모드(이중 청구, 동시 삭제에서의 KeyError, 성공 경로에서 상태 삭제 후 재청구, 비원자적 카운터 증가)를 갖고 있으며, high만 4가지를 모두 지적했습니다. low는 재청구 케이스를 놓쳤고, medium은 카운터를 놓쳤습니다.
high만 자신의 수정안의 실패 경로 의미도 명시했습니다. 재시도가 소진되면 이후 호출자는 다시 청구하는 대신 False를 받습니다.
사고 토큰 열은 Google의 "3.8은 더 열심히 일한다"는 주장이 터미널에서 드러난 것입니다. 같은 프롬프트에서 3.7 대비 medium은 2,343 → 3,158로, high는 2,217 → 4,512로 대략 2배가 되었고, 추가 토큰은 다른 결론이 아니라 더 완전한 분석을 가져왔습니다. 지연 시간은 이 실행에서 함께 증가했지만(7.8 s, 16.6 s, 25.5 s), 이 모델들의 단일 실행 시간은 출렁이므로, 초(second)보다 토큰 수를 비교하세요.
기본값 선택과 상향 기준
추론 레벨에 대한 제 경험칙은 다음과 같습니다.
-
3.8에서
low는 Google의 기본 medium 제안보다 더 큰 역할을 얻었습니다. 사고 토큰 0으로 올바르고 잘 설계된 수정을 제공했으므로, 중요해지기 전에 사람이 읽는 모든 것(분류, 초안, 요약, 리뷰할 코드)에는 여기서 시작하세요. -
medium은 출력이 검수 없이 바로 사용되는 곳에 유지하세요. 추가 추론이 실패 모드 분석을 더 완전하게 만들어주며, 읽지 않는 파이프라인에서는 바로 열거하지 않은 실패 모드가 터지기 쉽습니다. -
high는 실패 경로 자체가 제품인 출력에 예약하세요. 결제 흐름, 마이그레이션, 한 줄 한 줄 감사를 받는 작업 등이 그 예입니다. 제 실행에서 유일하게 4가지 버그를 모두 잡고 재시도 소진 후 동작을 문서화했습니다.
high가 low 대비 비용이 6.6배이므로, 2026년 12월 31일 이전 100만 출력 토큰당 $3.75와 이후 $7.50에서는 체감이 크게 다릅니다. 전역 상향보다 요청별로 상향하세요.
알아둘 만한 탈출구 하나: Google은 3.7 Flash가 효율 우선 워크로드에 계속 완전 지원된다고 명시합니다. 3.8의 추가 검증이 과제 요구보다 비용이 크다면, 해당 워크로드에 gemini-3.7-flash를 유지하는 것은 편법이 아니라 지원되는 선택입니다.
PDF에서 구조화 데이터를 어떻게 추출하나요?
Gemini 3.8 Flash는 입력으로 PDF를 직접 읽습니다. 인보이스나 리포트를 보내고 질문할 수 있습니다. 저는 인보이스 번호, 날짜, 4개 라인 아이템, 합계가 있는 1페이지 벤더 인보이스를 사용했습니다.
프롬프트에 PDF 첨부
Files API를 사용해 로컬 인보이스 PDF를 업로드해 봅시다. Files API는 Google 인프라에서 파일 저장과 캐싱을 처리합니다:
from google import genai
client = genai.Client()
print("Uploading invoice...")
doc = client.files.upload(file="invoice_aug_2026.pdf")
print(f"File uploaded: {doc.uri}\n")
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=[
{
"type": "text",
"text": "Extract the invoice number, total amount due, and due date.",
},
{"type": "document", "uri": doc.uri, "mime_type": doc.mime_type},
],
)
print(interaction.output_text)
제가 받은 출력:

3개 값 모두 정확했습니다. 업로드는 한 번만 수행되며, 이후 요청에서도 파일을 사용할 수 있습니다. 같은 문서에 대해 질문을 2개 이상 하게 되면 중요합니다. 답변은 마크다운 불릿으로 오는데, 읽기에는 괜찮지만 파이프라인에 넣기에는 적합하지 않습니다.
응답 스키마로 JSON 강제
산문이 아닌 JSON을 받으려면 response_format에 스키마를 전달합니다. Interactions API에서는 최상위 파라미터입니다. 오래된 튜토리얼에서 보이는 generationConfig 내부의 responseMimeType 설정은 레거시 generateContent 엔드포인트에 해당합니다.
import json
from google import genai
from pydantic import BaseModel
client = genai.Client()
class Invoice(BaseModel):
invoice_number: str
total_due_usd: float
due_date: str # ISO 8601
doc = client.files.upload(file="invoice_aug_2026.pdf")
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=[
{
"type": "text",
"text": "Extract the invoice number, total amount due in USD, and due date.",
},
{"type": "document", "uri": doc.uri, "mime_type": doc.mime_type},
],
response_format={
"type": "text",
"mime_type": "application/json",
"schema": Invoice.model_json_schema(),
},
)
invoice = json.loads(interaction.output_text)
print(invoice)
제가 받은 출력은 다음과 같습니다:

Pydantic 클래스가 필수 필드와 데이터 타입을 정의하고, model_json_schema()가 Gemini API에 필요한 JSON 스키마를 생성합니다. 처리 후 json.loads()가 모델 출력을 표준 Python 딕셔너리로 변환합니다. 여기서부터는 구조화 데이터를 DataFrame 행으로 바꾸거나, 데이터베이스에 커밋하거나, Google Sheet에 추가할 수 있습니다.
previous_interaction_id로 후속 질문하기
같은 문서에 대한 두 번째 질문에는 첫 상호작용의 id를 previous_interaction_id로 전달하세요. 서버는 이미 PDF와 첫 교환을 보유하고 있으므로, 둘 다 다시 보낼 필요가 없습니다:
follow_up = client.interactions.create(
model="gemini-3.8-flash",
previous_interaction_id=interaction.id,
input="List each line item on the invoice with its amount.",
)
print(follow_up.output_text)

반복된 compute 라인을 포함해 4개 항목을 순서대로 모두 반환했습니다. 반복에 대해 언급하지 않았는데, 질문 의도에는 그게 올바른 동작입니다. 이상을 표시하길 원하면 그렇게 요청하세요.
참고로 3.7도 여기서는 동일하게 동작했습니다. 즉, 3.8의 추가 성실함은 모델의 자체 추론에 적용되며, 요청하지 않은 감사를 자발적으로 하지는 않습니다.
이 호출에 대해 알아둘 두 가지:
-
response_format은 상호작용 범위이므로 넘겨받지 않습니다. 따라서 이 턴은 산문을 반환했습니다. -
상호작용은 기본적으로 저장됩니다(
store=True). 유료 티어는 55일, 무료 티어는 1일입니다.store=False는 호출을 상태 없음으로 만들지만, 그 경우previous_interaction_id를 이어 붙일 수 없습니다.
Gemini 3.8 Flash에 함수 호출을 어떻게 추가하나요?
Gemini 3.8 Flash에서의 함수 호출은 하나의 루프입니다. 모델이 도구 호출을 요청하고, 코드가 이를 실행해 결과를 돌려주면, 모델이 최종 답을 작성합니다. 이 섹션은 그 루프를 수동으로 구성합니다.
Google이 호스팅하는 멀티툴 에이전트로 루프를 대신 실행해 주길 원한다면, 다음 튜토리얼 Gemini API의 "Managed Agents"를 읽어보세요. 장기적으로 에이전트를 목표로 한다면, Building AI Agents with Google ADK 코스가 같은 프리미티브로 고객 지원 어시스턴트를 완성합니다.
도구 정의와 상호작용 루프 실행
도구는 lookup_exchange_rate(currency, date)이며, 간단한 인메모리 dict로 뒷받침되어 외부 API 없이 예제를 실행할 수 있습니다. 선언은 JSON 스키마입니다. 모델은 함수를 실행하지 않습니다. function_call 단계를 반환하며, 여러분의 코드가 다음을 수행하도록 요청합니다:
import json
from google import genai
client = genai.Client()
# Local "data source" standing in for a real FX API
RATES = {
("USD", "2026-08-03"): 87.42,
("USD", "2026-08-10"): 87.15,
("EUR", "2026-08-03"): 95.08,
}
def lookup_exchange_rate(currency: str, date: str) -> dict:
rate = RATES.get((currency.upper(), date))
if rate is None:
return {"error": f"No rate for {currency} on {date}"}
return {"currency": currency.upper(), "date": date, "inr_rate": rate}
rate_tool = {
"type": "function",
"name": "lookup_exchange_rate",
"description": "Look up the INR exchange rate for a currency on a date (YYYY-MM-DD).",
"parameters": {
"type": "object",
"properties": {
"currency": {"type": "string", "description": "ISO code, e.g. USD"},
"date": {"type": "string", "description": "YYYY-MM-DD"},
},
"required": ["currency", "date"],
},
}
# Turn 1: the model decides to call the tool
interaction = client.interactions.create(
model="gemini-3.8-flash",
input="What was the USD to INR exchange rate on 2026-08-03?",
tools=[rate_tool],
)
fc_step = next(s for s in interaction.steps if s.type == "function_call")
print(f"Model requested: {fc_step.name}({fc_step.arguments})")
# Your code executes the function locally
result = lookup_exchange_rate(**fc_step.arguments)
# Turn 2: send the result back; tools must be re-specified (interaction-scoped)
final = client.interactions.create(
model="gemini-3.8-flash",
previous_interaction_id=interaction.id,
input=[
{
"type": "function_result",
"name": fc_step.name,
"call_id": fc_step.id,
"result": [{"type": "text", "text": json.dumps(result)}],
}
],
tools=[rate_tool],
)
print(final.output_text)
출력:

여기서 일어난 3가지:
-
턴 1은 이름, 구조화된 인자,
id가 있는function_call단계를 반환했습니다. -
여러분의 Python이 조회를 실행했습니다.
-
턴 2는 해당 호출을 참조하는
function_result블록을 보냈습니다.
턴 2에서 tools 파라미터를 다시 전달한 이유는 PDF 섹션에서 response_format을 재전달해야 했던 것과 같습니다. previous_interaction_id는 히스토리만 가져오며, 설정은 아닙니다.
Gemini 3.x에서의 함수 호출 실수
도구 루프가 깨질 때는 거의 항상 두 가지 중 하나입니다.
첫째, 모든 결과는 자신의 호출과 매핑되어야 합니다. Interactions API에서는 function_result 블록의 call_id와 name입니다. 레거시 generateContent API에서는 FunctionResponse가 직전 FunctionCall의 id와 name을 일치시켜야 합니다. Gemini 3.x에서는 둘 다 선택 사항이 아닙니다.
둘째, Malformed_Function_Call 오류는 보통 모델이 도구 호출 전에 설명을 내보낼 때 발생합니다. Google의 3.8 개발자 가이드는 선행 텍스트를 정리하고, 인라인 지시사항은 \n\n으로 포맷하며, 작업 메모는 원시 텍스트가 아니라 전용 함수 호출로 감싸라고 권장합니다. 시스템 지시를 조이세요. 맹목적 재시도는 피하세요.
Gemini 3.8 Flash로 전환하면 무엇이 깨지나요?
출발점에 따라 다릅니다.
-
Gemini 3.7 Flash에서: 아무것도 없습니다. 모델 문자열을
gemini-3.8-flash로 바꾸면, API 표면이 동일하기 때문에 이 글의 모든 스니펫이 수정 없이 실행됩니다. -
Gemini 3.6 Flash 이하에서라면, 모델 설정에 동일한 15분 점검이 필요합니다.
마이그레이션 체크리스트(3.6 Flash 이하에서)
순서대로 진행하세요. 1~3번은 즉시 400을 유발하며, 4~5번은 조용한 품질 문제를 유발합니다.
-
모델 ID를
gemini-3.8-flash로 변경합니다. -
사용 중단된 샘플링 파라미터 삭제:
temperature,top_p,top_k는 Gemini 3.x에서 무시되거나 거부되며,frequency_penalty,presence_penalty,candidate_count는 활성 API 에러를 던집니다. 레거시 설정에서 모두 제거하세요. -
thinking_budget를thinking_level로 교체:low,medium,high만 사용합니다. 이전 minimal 값은 검증 오류를 반환합니다.thinking_budget과thinking_level을 한 요청에 함께 보내면 400이 반환됩니다. -
사전 채운 모델 턴 제거: 구성한 대화에서 이를 제거하고, 마지막 사용자 턴이 비어 있지 않은 텍스트를 갖도록 하세요. 히스토리 페이로드는 모델 턴으로 끝날 수 없습니다.
-
멀티턴 흐름 표준화: 클라이언트 측 히스토리 재생 대신
previous_interaction_id에 의존하세요. 필요한 턴마다 도구,system_instruction,generation_config를 다시 지정해야 합니다.
Google은 Gemini API 모델 문서에 권위 있는 버전을 게시하며, 코딩 에이전트가 스킬을 지원한다면 자동 경로도 포함합니다. 그래도 직접 한 번 읽어 보세요. 자동 마이그레이션은 temperature=0.2가 처음에 왜 있었는지는 알려주지 않습니다.
프로덕션에서 마주칠 오류
핸들러를 연결할 가치가 있는 4가지 상태 코드를 정리했습니다. 이 API에서 각 코드가 실제로 의미하는 바는 다음과 같습니다.
|
상태 |
주된 원인 |
대응 |
|
|
남아 있는 레거시 필드: |
요청을 수정하세요. 재시도는 무의미합니다 |
|
|
잘못되었거나 누락되었거나 제한된 |
키를 다시 내보내고, 이 API에 설정·허용되었는지, git에 커밋되지 않았는지 확인 |
|
|
해당 티어의 속도 제한(주로 배치 추출 작업 중) |
지터 포함 지수 백오프로 재시도; 부하 분산 고려 |
|
|
Google 측 일시 과부하 |
동일한 지터 백오프; 수분 이상 지속 시에만 알림 |
여기서 두 가지 더:
-
thinking_level: "high"와 긴 도구 루프를 결합할 때는 클라이언트 타임아웃을 명시적으로 설정하세요. 멈춘 요청은 실패한 요청보다 더 나쁘며, 3.8의 추가 성실함은 긴 추론 실행의 가능성을 낮추기보다 높입니다. -
모든 요청에
interaction.id를 로그하세요. 나중에 저장된 상호작용을 조회, 디버깅, 삭제하는 핸들이 됩니다.
마무리
이 글의 모든 내용은 3가지 변화로 귀결됩니다. Interactions API가 호출 규약을 바꿨고, thinking_level이 여러분이 조정하던 모든 샘플링 노브를 대체했으며, 서버 측 상태(previous_interaction_id) 덕분에 PDF 후속 질문과 도구 루프가 히스토리 재생이 아닌 한 줄짜리 턴이 되었습니다. Gemini 3.8 Flash는 이 표면을 바꾸지 않았습니다. 모델이 그 안에서 얼마나 열심히 일하는지만 바뀌었습니다. 그래서 이 글의 수치를 3.7에서 가져오지 않고 3.8에서 새로 측정했습니다.
제 레벨 권장을 곧이곧대로 따르기 전에, 비교 스크립트를 여러분 백로그의 과제에 돌려보세요. 결제 재시도 레이스에서 이긴 레벨이 여러분의 SQL 생성 워크로드에서는 질 수 있습니다.
단일 API 호출을 넘어 프로덕션 AI 시스템이 필요해질 때, 우리의 Associate AI Engineer for Developers 트랙은 전체 경로를 다루며, Associate AI Engineer for Data Scientists 트랙은 데이터 관점에서 동일한 내용을 다룹니다.
FAQs
Gemini 3.8 Flash에는 어떤 Python 패키지를 설치하나요?
pip으로 google-genai를 설치하세요(pip install -U google-genai). 이전 google-generativeai 라이브러리는 레거시이며 Gemini 3.x 설정 인자를 전달하면 실패합니다.
Gemini 3.8 Flash는 temperature, top_p, top_k를 지원하나요?
아니요. 샘플링 파라미터는 Gemini 3.x에서 사용 중단되었습니다. 3.8은 추가로 frequency_penalty, presence_penalty, candidate_count에 대해 활성 API 에러를 던집니다. 대신 thinking_level로 출력 동작을 제어하세요.
Gemini 3.8 Flash가 허용하는 thinking_level 값은 무엇인가요?
허용 값은 low, medium(기본값), high입니다. minimal 값은 유효하지 않으며 API 검증 오류를 반환합니다.
Google은 Gemini 3.8 Flash의 추론 토큰을 어떻게 과금하나요?
Google은 도입가 기간(2026년 12월 31일 종료) 동안 사고 토큰을 100만 토큰당 $3.75의 표준 출력 요율로 과금합니다. 또한 3.8은 노력 수준이 높을수록 더 많은 추론 토큰을 사용할 수 있다고 명시하므로, 추가 검증 사이클에 대한 비용을 지불하게 됩니다.
Gemini 3.8 Flash Cyber란 무엇이며, 사용할 수 있나요?
취약점 발견과 자동 패치를 위해 튜닝된 사이버보안 변형입니다. 공개 API에는 없으며, Google의 Fairwind Program을 통해 승인된 디펜더만 접근할 수 있습니다. 일반 개발자는 gemini-3.8-flash를 사용합니다.