대화 상태

대화 상태 (Conversation state)

사용자와 여러 턴을 주고받는 대화를 만들다 보면, "이전에 뭐라고 했는지"를 모델이 기억하게 하는 게 중요해요. OpenAI는 이 **대화 상태(conversation state)**를 관리하는 몇 가지 방법을 제공하는데, 어떤 방식이든 "한 번의 텍스트 생성 요청은 독립적이고 무상태(stateless)"라는 사실에서 출발해요. 그래서 이 가이드는 수동으로 컨텍스트를 쌓는 방법부터 시작해, 응답을 자동으로 이어주는 API 기능까지 단계별로 살펴볼게요.

출처: 공식문서

참고: GPT-5.5가 중간 업데이트를 최종 답변으로 처리하는 문제를 겪고 있다면, 통합 코드가 어시스턴트 메시지의 phase 필드를 올바르게 보존하는지 확인해 보세요. 자세한 내용은 Phase 파라미터를 참고하세요.

수동으로 대화 상태 관리하기

각 텍스트 생성 요청은 독립적이고 무상태지만, 텍스트 생성 요청에 추가 메시지를 파라미터로 넘기면 다중 턴 대화를 구현할 수 있어요. 노크 노크(knock-knock) 농담을 예로 볼게요.

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-6-astra",
    input=[
        {"role": "user", "content": "knock knock."},
        {"role": "assistant", "content": "Who's there?"},
        {"role": "user", "content": "Orange."},
    ],
)

print(response.output_text)

이렇게 userassistant 메시지를 번갈아 넣으면, 한 번의 요청 안에 대화의 이전 상태를 담을 수 있어요. 생성된 응답 사이에 컨텍스트를 공유하려면, 모델의 이전 응답 출력을 input으로 포함하고 다음 요청의 input에 이어 붙이면 돼요.

무상태 추론 모델 요청에서는 응답의 output 배열의 모든 항목을 보존하세요. Responses API는 기본적으로 추론 항목을 암호화해서 반환해요. 전체 output을 재생(replay)하면 추론 항목과 어시스턴트 phase 값이 그대로 유지돼요. 지속 추론을 지원하는 모델은 reasoning.context: "all_turns"를 써서 이전 턴의 사용 가능한 추론을 다음 샘플에 반영할 수 있어요. 호출 간 추론 보존을 참고하세요.

다음 예시에서는 모델에게 농담을 하나 요청한 뒤, 이어서 다른 농담을 요청해요. 이렇게 이전 응답을 새 요청에 이어 붙이면 대화가 자연스러워지고 이전 상호작용의 맥락이 유지돼요.

Responses API로 수동 상태 관리하기(Python):

from openai import OpenAI

client = OpenAI()

history = [{"role": "user", "content": "tell me a joke"}]

response = client.responses.create(
    model="gpt-6-astra",
    input=history,
    store=False,
)

print(response.output_text)

# Add all response output items, including encrypted reasoning items, to the conversation
history += response.output

history.append({"role": "user", "content": "tell me another"})

second_response = client.responses.create(
    model="gpt-6-astra",
    input=history,
    store=False,
)

print(second_response.output_text)

OpenAI의 대화 상태 API

우리 API는 대화 상태를 자동으로 관리하기 더 쉽게 만들어, 매 턴마다 입력을 수동으로 넘길 필요가 없게 해줘요.

Conversations API 사용하기

Conversations APIResponses API와 함께 동작해, 대화 상태를 고유 식별자를 가진 장기 실행 객체로 유지해요. 대화 객체를 만들면 세션·기기·잡을 넘나들며 계속 사용할 수 있어요.

Conversations는 메시지, 도구 호출, 도구 출력, 기타 데이터 같은 항목(items)을 저장해요.

conversation = openai.conversations.create()

다중 턴 상호작용에서는 여러 응답 항목을 체인으로 잇는 대신, conversation을 후속 responses에 넘겨 상태를 유지하고 후속 응답 간 컨텍스트를 공유할 수 있어요.

Conversations + Responses로 상태 관리하기(Python):

response = openai.responses.create(
    model="gpt-6-astra",
    input=[{"role": "user", "content": "What are the 5 Ds of dodgeball?"}],
    conversation=conversation.id,
)

이전 응답에서 컨텍스트 전달

대화 상태를 관리하는 또 다른 방법은 previous_response_id 파라미터로 생성된 응답 간 컨텍스트를 공유하는 거예요. 이 파라미터를 쓰면 응답을 체인으로 잇고 스레드형 대화를 만들 수 있어요.

이전 응답 ID를 넘겨 턴을 넘어 응답 체인하기(Python):

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-6-astra",
    input="tell me a joke",
)
print(response.output_text)

second_response = client.responses.create(
    model="gpt-6-astra",
    previous_response_id=response.id,
    input=[{"role": "user", "content": "explain why this is funny."}],
)
print(second_response.output_text)

아래 예시에서는 모델에게 농담을 하라고 요청하고, 별도로 왜 재미있는지 설명해 달라고 요청해요. previous_response_id가 필요 컨텍스트를 이어주므로 모델이 좋은 응답을 낼 수 있어요.

WebSocket 모드에서의 previous_response_id

Responses API WebSocket 모드를 쓰면 이어짐(continuation)은 HTTP 모드와 같은 previous_response_id 의미를 따르지만, 지속 소켓 위에서 반복되는 response.create 이벤트로 동작해요.

연결-로컬 캐시가 최근 이전 응답을 메모리에 보관해 지연이 낮은 이어짐을 제공해요. stream_id를 쓰면 각 레인(lane)이 최신 응답을 유지할 수 있고, previous_response_id가 여전히 계보(lineage)를 제어해요. 그래서 새로운 레인이 다른 레인의 응답에서 갈라져(fork) 나올 수 있고, 그 응답은 계속 사용 가능해요. 캐시되지 않은 ID를 해석할 수 없다면 previous_response_idnull로 설정하고 전체 입력 컨텍스트를 넘겨 새 턴을 보내세요.

모델 응답의 데이터 보존

Response 객체는 기본적으로 30일 동안 저장돼요. 대시보드 logs 페이지에서 보거나 API로 retrieve할 수 있어요. Response를 만들 때 storefalse로 설정하면 이 동작을 끌 수 있어요.

Conversation 객체와 그 안의 항목은 30일 TTL의 적용을 받지 않아요. 대화에 연결된 응답은 항목이 30일 TTL 없이 유지돼요. OpenAI는 명시적 동의 없이 API로 보낸 데이터를 모델 훈련에 사용하지 않아요 — 자세히 알아보기.

previous_response_id를 써도 체인의 모든 이전 입력 토큰은 API에서 입력 토큰으로 과금돼요.

컨텍스트 윈도우 관리

컨텍스트 윈도우를 이해하면 스레드형 대화를 성공적으로 만들고 모델 상호작용 간 상태를 관리하는 데 도움이 돼요.

컨텍스트 윈도우는 단일 요청에서 사용할 수 있는 최대 토큰 수예요. 이 최대 토큰 수에는 입력·출력·추론 토큰이 모두 포함돼요. 모델의 컨텍스트 윈도우는 모델 정보에서 확인할 수 있어요.

텍스트 생성의 컨텍스트 관리

입력이 복잡해지거나 대화 턴이 늘어나면, 출력 토큰컨텍스트 윈도우 한도를 모두 고려해야 해요. 모델 입력·출력은 토큰 단위로 계산되며, 입력을 파싱해 내용·의도를 분석하고 논리적 출력을 조립하는 데 쓰여요. 모델은 텍스트 생성 요청의 수명 주기 동안 토큰 사용량 한도가 있어요.

  • 출력 토큰은 모델이 프롬프트에 대한 응답으로 생성하는 토큰이에요. 각 모델은 출력 토큰 한도가 달라요. 예를 들어 gpt-4o-2024-08-06은 최대 16,384개 출력 토큰을 생성할 수 있어요.
  • 컨텍스트 윈도우는 입력·출력 토큰(일부 모델은 추론 토큰 포함)에 사용할 수 있는 총 토큰을 말해요. 우리 모델의 컨텍스트 윈도우 한도를 비교해 보세요. 예를 들어 gpt-4o-2024-08-06은 총 128k 토큰의 컨텍스트 윈도우를 가져요.

큰 프롬프트를 만들면(보통 추가 컨텍스트·데이터·예시를 포함해서) 모델의 컨텍스트 윈도우를 초과할 위험이 있고, 결과가 잘려 나올 수 있어요. tiktoken 라이브러리로 만든 토크나이저 도구로 특정 문자열이 몇 토큰인지 확인할 수 있어요.

예를 들어 o1 모델처럼 추론을 켠 모델로 Responses API 요청을 보내면, 다음 토큰 수가 컨텍스트 윈도우 총량에 포함돼요.

  • 입력 토큰(Responses API의 input 배열에 포함한 입력)
  • 출력 토큰(프롬프트에 대한 응답으로 생성된 토큰)
  • 추론 토큰(모델이 응답을 계획하는 데 쓰는 토큰)

컨텍스트 윈도우 한도를 초과해 생성된 토큰은 API 응답에서 잘려 나갈 수 있어요. 토크나이저 도구로 메시지가 사용할 토큰 수를 대략 추정할 수 있어요.

컴팩션(Compaction)

상세한 컴팩션 가이드는 별도 문서 Compaction으로 옮겨졌어요.

다음 단계

더 구체적인 예시와 유스케이스는 OpenAI Cookbook을 참고하거나, API로 모델 기능을 확장하는 방법을 알아보세요.

더 알아보기 (Learn more)