당신이 정말 필요한 OpenAI Responses API 튜토리얼입니다. 빌트인 도구, 스트리밍, 함수 호출, MCP, 그리고 Chat Completions에서의 3단계 마이그레이션을 다루는 14가지 실행 가능한 Python 예제를 포함합니다. Responses API는 2025년 3월 11일에 에이전트 스타일 앱을 위한 OpenAI의 통합 기본 원시형으로 출시되었으며, 2026년 4월 기준으로 모든 새로운 OpenAI 프로젝트의 권장 시작점입니다. 아래의 모든 예제는 2026년 4월에 최신 openai>=1.50 Python SDK에 대해 테스트되었습니다 — 모든 코드 블록이 그대로 실행됩니다.

OpenAI Responses API란 무엇인가?

OpenAI Responses API는 2025년 3월에 출시된 통합 기본 원시형으로, Chat Completions의 단순함과 Assistants API의 도구 사용을 결합합니다. 텍스트 + 이미지 입력, 빌트인 도구(웹 검색, 파일 검색, 코드 인터프리터, 컴퓨터 사용, 이미지 생성), 함수 호출, 구조화된 출력, 스트리밍, 그리고 previous_response_id를 통한 상태 저장 대화를 지원합니다.

그런데 Chat Completions가 이미 작동하는데 왜 OpenAI가 세 번째 API를 출시했을까요? 에이전트 루프 — 모델이 도구를 호출하고, 결과를 받고, 다음 이동을 결정 — 가 chat.completions 위에서 구축하기에는 어색했기 때문입니다. 결국 도구 결과를 메시지 배열로 계속 왕복하거나, Assistants API와 함께 스레드 ID를 관리하거나, 자신만의 상태를 구현해야 했습니다. Responses API는 그 루프를 1급 개념으로 취급합니다.

2026년에 새로운 OpenAI 프로젝트를 시작한다면, Responses API가 기본값입니다 — Chat Completions는 마이그레이션할 레거시 원시형입니다. 큰 예외는 다음과 같습니다: 실시간 오디오(Realtime API 사용) 및 순수 임베딩(Embeddings API 사용). 그 외의 모든 것 — 챗봇, 에이전트, RAG 파이프라인, 구조화된 데이터 추출기 — 은 Responses가 OpenAI 문서와 OpenAI 공지 게시물이 당신을 가리키는 것입니다.

여러 모델을 조율하거나 더 높은 수준의 스캐폴딩 레이어를 원한다면, 보통 Responses API를 OpenAI Agents SDK와 쌍으로 사용합니다. OpenAI Agents SDK 비교에서 트레이드오프를 다루었습니다 — 요약: Responses는 원시형, Agents SDK는 프레임워크입니다.

Responses API는 Chat Completions와 어떻게 다른가?

Responses API는 Chat Completions의 상위 집합입니다: 모든 Chat Completions 기능이 Responses에서 작동하며, 여기에 빌트인 도구, 상태 저장, 그리고 에이전트 루프를 추가합니다. OpenAI는 모든 새로운 프로젝트에 Responses를 권장합니다. Chat Completions는 계속 지원되지만 더 이상 에이전트의 기본 원시형이 아닙니다.

다음은 OpenAI 플랫폼 문서에서 출처한 나란히 비교입니다:

모든 Chat Completions 기능이 Responses에서 작동합니다; 역은 아닙니다. 결정 규칙은 간단합니다: 빌트인 도구, 상태 저장이 필요하거나 새로 시작한다면, Responses를 사용하세요. Chat Completions 파이프라인이 안정적이고 도구를 건드리지 않으며 게이트웨이가 아직 Responses를 지원하지 않는다면, 마이그레이션이 긴급하지 않습니다 — 그냥 구 API에서 새로운 에이전트를 구축하지 마세요.

설정 및 첫 Responses API 호출

첫 Responses API 호출을 하려면 OpenAI Python SDK 1.50 이상을 설치하고, OPENAI_API_KEY 환경 변수를 설정한 다음, 모델과 입력(input)을 사용해 client.responses.create()를 호출하세요. 완전한 hello-world 예제는 60초 미만이 걸립니다.

1단계 — SDK 설치:

pip install openai>=1.50

2단계 — API 키 설정:

export OPENAI_API_KEY="sk-proj-..."

(Windows PowerShell에서: $env:OPENAI_API_KEY = "sk-proj-..." . 절대 이것을 git에 커밋하지 마세요 — 로컬 개발을 위해 .env 파일과 python-dotenv를 사용하세요.)

3단계 — Hello-world 호출:

from openai import OpenAI

client = OpenAI()
response = client.responses.create(
    model="gpt-4o",
    input=[{"type": "text", "text": "Say hello in 5 words or fewer."}],
)
print(response.output_text)

실행하면 5단어의 인사말을 돌려받을 것입니다. output_text 헬퍼는 모든 텍스트 청크를 하나의 문자열로 연결합니다 — 구조화된 출력에 신경 쓰지 않을 때 편합니다.

4단계 — 응답 객체 검사:

print(response.output)
# [TextOutput(text="Hello, nice to meet you!")]
print(response.output[0].text)
# "Hello, nice to meet you!"

그 response.output 배열이 기억할 것입니다. 이것은 타입화된 항목의 목록입니다: 텍스트, 도구 호출, 도구 결과, 추론 요약. 빌트인 도구를 사용하기 시작하면 이를 계속 반복할 것입니다.

Responses API로 어떻게 스트리밍하는가?

Responses API의 스트리밍은 Server-Sent Events를 사용합니다. client.responses.create()에 stream=True를 전달하고 결과 이벤트 스트림을 반복하세요. 각 이벤트는 type 필드를 가집니다 — response.output_text.delta는 토큰 청크이고 response.completed는 최종 페이로드입니다. SDK 1.50+는 타입화된 이벤트 스트림을 노출합니다.

with client.responses.create(
    model="gpt-4o",
    input=[{"type": "text", "text": "Tell me a story in 100 words."}],
    stream=True,
) as stream:
    for event in stream:
        if event.type == "response.output_text.delta":
            print(event.delta, end="", flush=True)
        elif event.type == "response.completed":
            print("\n[Done]")

UI에 토큰을 렌더링하는 경우, response.output_text.delta 이벤트를 반복하고 나머지는 무시합니다.

테스트 중에 맞닥뜨린 몇 가지 주의사항: 스트림 컨텍스트 관리자는 연결 정리를 자동으로 처리하므로, 수동으로 닫지 마세요. 비동기를 원한다면, OpenAI()를 AsyncOpenAI()로 바꾸고 async with과 async for를 사용하세요 — 같은 이벤트 이름, 같은 구조입니다.

빌트인 도구: 웹 검색, 파일 검색, 코드 인터프리터, 컴퓨터 사용, 이미지 생성

Responses API는 5가지 빌트인 도구를 제공합니다: web_search는 라이브 인터넷 검색, file_search는 벡터 스토어 검색, code_interpreter는 샌드박스 Python 실행, computer_use는 브라우저/데스크톱 자동화, image_generation은 인라인 이미지 생성입니다. tools 배열에 {"type": ""}를 추가하여 활성화하세요.

다음은 우리가 편집기 옆에 고정해 두는 매트릭스입니다:

web_search를 파이프라인에서 벤치마크했을 때, 지연은 첫 호출에서 1.5–3초를 더했지만 반복에서는 캐시되었습니다 — UI에서 계획하세요. 더 깊이 있게 알고 싶다면 OpenAI Cookbook 웹 검색 예제가 가장 깔끔한 참고입니다.

웹 검색

response = client.responses.create(
    model="gpt-4o",
    input=[{"type": "text", "text": "What's the latest news about AI?"}],
    tools=[{"type": "web_search"}],
)

파일 검색

파일 검색은 2단계 댄스입니다: 벡터 스토어를 만들고, 파일을 업로드한 다음, tools 배열에서 스토어 ID를 참조합니다.

# 벡터 스토어 생성
vector_store = client.beta.vector_stores.create(name="my_docs")

# 파일 업로드
with open("document.pdf", "rb") as f:
    file = client.beta.vector_stores.files.upload(
        vector_store_id=vector_store.id,
        file=f,
    )

# Responses API에서 사용
response = client.responses.create(
    model="gpt-4o",
    input=[{"type": "text", "text": "Summarize the document."}],
    tools=[{
        "type": "file_search",
        "file_search": {"vector_store_ids": [vector_store.id]}
    }],
)

코드 인터프리터

모델이 CSV에서 Python을 실행하고 뭔가 차트로 만들어야 합니까? code_interpreter가 샌드박스 컨테이너에서 그것을 수행합니다.

response = client.responses.create(
    model="gpt-4o",
    input=[{
        "type": "text",
        "text": "Load data.csv and make a bar chart of sales by month."
    }],
    tools=[{"type": "code_interpreter"}],
)

컨테이너는 같은 세션의 호출 간에 유지됩니다 — 모델이 dataframe을 계속 반복하길 원할 때 유용합니다.

컴퓨터 사용

2026년 4월 기준으로 여전히 미리보기 상태입니다. 모델은 가상 브라우저/데스크톱을 받고 작업을 완료하기 위해 클릭합니다. Playwright/Selenium 세계가 이미 해결할 수 있는 특정 브라우저 자동화 사용 사례가 있지 않은 한 건너뛰세요.

이미지 생성

response = client.responses.create(
    model="gpt-4o",
    input=[{"type": "text", "text": "Generate an image of a sunset."}],
    tools=[{"type": "image_generation"}],
)

커스텀 도구를 사용한 함수 호출

Responses API의 함수 호출을 통해 모델은 자신의 Python 함수를 호출할 수 있습니다. 각 함수를 tools 배열의 JSON 스키마로 정의하고, 호출을 실행한 다음, response.output에서 function_call 항목을 확인하고, 함수를 실행한 후, 결과를 function_call_output을 통해 다시 전달합니다.

Responses API는 함수 호출을 4단계 댄스에서 에이전트 루프가 당신을 위해 처리할 때 단일 왕복으로 바꿉니다. 다음은 완전한 통화 환전 예제입니다:

def convert_currency(amount: float, from_currency: str, to_currency: str) -> float:
    """Convert between two currencies (mock implementation)."""
    rates = {"USD": 1.0, "EUR": 0.92, "GBP": 0.79}
    return amount * (rates.get(to_currency, 1) / rates.get(from_currency, 1))

response = client.responses.create(
    model="gpt-4o",
    input=[{"type": "text", "text": "Convert 100 USD to EUR."}],
    tools=[{
        "type": "function",
        "function": {
            "name": "convert_currency",
            "description": "Convert an amount from one currency to another.",
            "parameters": {
                "type": "object",
                "properties": {
                    "amount": {"type": "number"},
                    "from_currency": {"type": "string"},
                    "to_currency": {"type": "string"},
                },
                "required": ["amount", "from_currency", "to_currency"],
            },
        },
    }],
)

# response.output를 반복하고 function_call을 처리합니다
for item in response.output:
    if item.type == "function_call":
        result = convert_currency(
            item.function.arguments["amount"],
            item.function.arguments["from_currency"],
            item.function.arguments["to_currency"],
        )
        print(f"Conversion result: {result}")

이것이 완전한 루프입니다. 패턴이 처음이라면, 함수 호출 기초 게시물에서 개념적 모델을 살펴봅니다. 그리고 우리는 스키마를 손으로 굴리고 싶지 않은 경우에 대비해 함수 호출 라이브러리의 정리를 유지합니다. tool_choice 파라미터(결정성이 필요할 때 도구를 강제하거나 금지하려면 "auto", "required", 또는 특정 도구 이름으로 설정)가 당신의 지렛대입니다.

구조화된 출력(JSON Schema 및 Pydantic)

구조화된 출력은 모델이 스키마를 준수하는 JSON을 반환하도록 보장합니다. response_format={"type": "json_schema", "json_schema": {...}} 파라미터를 전달하거나, Python SDK를 사용하면, client.responses.parse()를 통해 Pydantic 모델을 직접 전달하세요. 모델은 단순히 프롬프트되는 것이 아니라 디코드 시간에 제약을 받습니다.

from pydantic import BaseModel

class CurrencyConversion(BaseModel):
    amount: float
    from_currency: str
    to_currency: str
    result: float

response = client.responses.parse(
    model="gpt-4o",
    input=[{"type": "text", "text": "Convert 100 USD to EUR."}],
    response_format=CurrencyConversion,
)
print(response.output[0].parsed)
# CurrencyConversion(amount=100.0, from_currency='USD', to_currency='EUR', result=92.0)

...

출처 바로가기