ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [OpenAI][Responses API] previous_response_id와 stateless replay를 언제 나눠야 하나
    기타개발지식/풀스택개발 2026. 6. 28. 09:16

    IT 리서치 노트

    [OpenAI][Responses API] previous_response_id와 stateless replay를 언제 나눠야 하나

    Responses API를 붙일 때 previous_response_id와 stateless replay 중 무엇을 먼저 고를지에서 설계가 갈린다. 2026년 6월 28일 기준 OpenAI 공식 문서를 다시 보면, 기본값은 previous_response_id 쪽이 더 단순하지만 Zero Data Retention, 수동 context trimming, 외부 저장 정책이 있으면 stateless replay가 필요해진다. 이 글은 둘을 기능 이름이 아니라 운영 조건 기준으로 나누는 법을 정리한다.

    1. 개요

    결론부터 말하면 특별한 보존 제약이 없다면 previous_response_id가 먼저다. OpenAI도 prior response context를 OpenAI가 관리하게 둘 때 이 값을 쓰라고 안내하고, reasoning guide에서도 보통 가장 단순한 경로라고 설명한다. 반대로 Zero Data Retention, 수동 replay, 자체 history trimming, 외부 감사 저장 규칙이 있으면 stateless replay가 더 맞다.

    다만 단순하다고 해서 previous_response_id가 모든 것을 대신하진 않는다. migrate guide는 top-level instructions가 자동 승계되지 않는다고 분명히 적고 있다. 즉 상태는 이어도, 안정적으로 반복해야 하는 정책 문장은 다시 넣어야 한다. 장기 세션에서 token 길이와 지연 시간이 실제로 커졌을 때 compaction을 어디에 붙일지까지 보고 싶다면 compaction item 운영 기준 글을 바로 이어서 보면 판단이 빨라진다.

    이미 store: false와 encrypted reasoning 글을 봤다면 이번 글은 그 전 단계인 경로 선택 기준이다. 또 background mode와 webhook 글은 비동기 실행 축이라면, 이번 글은 상태 전달 책임 축이다.

    2. 어디서 실제로 막히는가

    실무에서 먼저 꼬이는 지점은 세 가지다. 첫째, previous_response_id를 쓰면서 이전 시스템 규칙도 자동으로 이어질 것처럼 생각한다. 둘째, stateless replay를 택해 놓고 실제로는 assistant output_text만 다시 넣어서 reasoning item이나 함수 호출 결과 연결을 잃어버린다. 셋째, 장기 대화에서 context trimming이 필요해졌는데도 어떤 경로가 제어권을 더 주는지 구분하지 않고 출발한다.

    OpenAI 문서를 같이 읽으면 기준이 꽤 명확하다. previous_response_id는 다음 요청에 새 user message 중심으로 이어 가는 방식에 가깝고, stateless replay는 prior output items를 다시 넘기며 문맥을 직접 조립하는 방식에 가깝다. compaction 가이드는 이 둘의 다음 요청 구성이 다르다고 적고 있고, reasoning guide는 수동 replay에서 원래 item 구조를 보존하라고 말한다.

    이 차이를 놓치면 두 가지 실패가 반복된다. 하나는 previous_response_id를 쓰면서 instructions 재전달을 빼먹어 운영 규칙이 빠지는 경우다. 다른 하나는 stateless replay를 쓰면서 필요한 output item, phase, function 결과를 정확히 이어 주지 못해 응답 품질과 비용이 같이 흔들리는 경우다. 결국 두 경로는 비슷한 기능이 아니라 책임이 다른 경로라고 봐야 한다.

    • 증상: 멀티턴은 이어지는데 시스템 규칙이 조용히 빠진다.
    • 실패: previous_response_id가 instructions까지 모두 승계한다고 가정한다.
    • 막힘: stateless replay에서 필요한 output item을 일부만 다시 넣는다.
    • 누락: 보존 정책, 감사 저장, context trimming 요구를 설계 초기에 반영하지 않는다.
    증상 먼저 볼 곳 판단 기준
    규칙이 다음 턴에 빠진다 instructions 재전달 여부 top-level instructions를 다시 넣었는지 본다
    함수 호출 뒤 답이 흔들린다 replay item 목록 output item과 function 결과를 그대로 살렸는지 본다
    대화가 길어질수록 제어가 어렵다 history trimming 정책 OpenAI 관리와 수동 조립 중 어느 쪽이 맞는지 본다

    3. 실무에서 적용하는 순서

    실무에서는 다섯 단계로 나누면 결론이 빨라진다. 먼저 OpenAI가 prior response context를 관리해도 되는지 본다. 두 번째로 instructions를 매 턴 다시 넣어야 하는 운영 규칙이 있는지 본다. 세 번째로 Zero Data Retention이나 외부 저장 규칙 때문에 응답 상태를 직접 재구성해야 하는지 판단한다. 네 번째로 function calling과 reasoning item을 어느 수준까지 직접 다룰지 본다. 마지막으로 긴 대화에서 context trimming이나 compaction을 직접 제어해야 하는지 본다.

    1. 보존 정책 제약이 없고 구현 단순성이 우선이면 previous_response_id를 먼저 쓴다.
    2. 반복해야 하는 top-level instructions는 경로와 무관하게 매 요청에 다시 넣는다.
    3. ZDR, 자체 history 저장, 수동 trimming이 필요하면 stateless replay를 택한다.
    4. stateless replay에서는 필요한 output item과 함수 결과 연결을 그대로 보존한다.
    5. 긴 세션은 compaction까지 포함해 input array 관리 규칙을 따로 둔다.

    선택 기준을 짧게 말하면 이렇다. 팀이 원하는 것은 대개 둘 중 하나다. 하나는 구현 부담을 줄이고 빠르게 멀티턴을 안정화하는 것, 다른 하나는 대화 상태를 직접 저장하고 깎고 감사하는 것이다. 첫 번째라면 previous_response_id, 두 번째라면 stateless replay 쪽이 맞다. 억지로 둘을 섞으면 오히려 상태 책임이 모호해진다.

    점검 메모 예시
    state_mode=previous_response_id|stateless_replay
    repeat_instructions=true
    zdr_required=true|false
    manual_trimming=true|false
    function_results_replayed=true|false
    compaction_enabled=true|false

    특히 function calling이 들어간 세션은 stateless replay의 난이도가 더 높다. 이전 output item을 어떤 순서로 저장하고, function 결과를 어떤 call과 연결하고, reasoning 관련 item을 어디까지 같이 보내는지 직접 확인하고 비교해야 한다. 이 부담이 필요하지 않다면 previous_response_id부터 시작하는 편이 실제로 더 빠르다.

    4. 공식 문서와 예시 화면으로 확인하기

    첫 화면은 OpenAI의 Responses 전환 가이드다. 여기서는 상태를 OpenAI가 관리하게 둘지, 직접 replay할지를 문서가 어떻게 가르는지 먼저 확인한다.

    OpenAI는 prior response context를 OpenAI가 관리하게 둘 때 previous_response_id를 쓰라고 안내한다.
    OpenAI는 prior response context를 OpenAI가 관리하게 둘 때 previous_response_id를 쓰라고 안내한다.

    즉 기본 선택지는 previous_response_id 쪽이다. 별도 보존 정책이나 수동 trimming 요구가 없는데도 굳이 stateless replay부터 시작하면 구현 부담만 커질 수 있다.

    두 번째 자료는 latest model 가이드의 상태 관리 요약이다. 여기서는 multi-turn state handling의 기본 경로와 stateless 대안이 같은 문단 안에 같이 나온다.

    latest model 가이드는 multi-turn state handling에는 previous_response_id를, stateless 또는 ZDR에는 returned output items replay를 쓰라고 정리한다.
    latest model 가이드는 multi-turn state handling에는 previous_response_id를, stateless 또는 ZDR에는 returned output items replay를 쓰라고 정리한다.

    이 요약을 기준으로 보면 경로 선택은 훨씬 쉬워진다. 다만 previous_response_id를 택해도 안정적으로 반복할 instructions는 별도로 다시 넣어야 한다는 점은 migrate guide의 주의사항으로 같이 기억해야 한다.

    세 번째 화면은 conversation state 가이드다. previous_response_id로 chain responses를 만드는 기본 방식과, 수동 상태 관리 예시가 한 문서 안에서 나란히 나온다.

    conversation state 가이드는 previous_response_id 기반 연결과 stateless input array 방식을 모두 보여 준다.
    conversation state 가이드는 previous_response_id 기반 연결과 stateless input array 방식을 모두 보여 준다.

    이 문서를 같이 보면 비교 기준이 선명해진다. previous_response_id는 다음 턴에 새 user message만 보내는 쪽에 가깝고, stateless replay는 어떤 output item을 다시 넣을지 직접 책임지는 쪽에 가깝다.

    네 번째 자료는 reasoning guide의 권장 문장이다. reasoning 모델 기준으로도 previous_response_id가 보통 가장 단순한 경로라는 점을 문서가 직접 말한다.

    reasoning guide는 보통 previous_response_id가 가장 단순한 경로이며, 수동 replay에서는 원래 output item을 그대로 보존해야 한다고 설명한다.
    reasoning guide는 보통 previous_response_id가 가장 단순한 경로이며, 수동 replay에서는 원래 output item을 그대로 보존해야 한다고 설명한다.

    특히 function calling과 reasoning item이 섞이면 replay 품질이 곧 응답 품질로 이어진다. stateless를 택할 때는 단순히 메시지 문자열을 다시 붙이는 수준으로는 부족하다.

    다섯 번째 화면은 compaction 가이드다. 문서는 previous_response_id를 쓸 때와 stateless input-array chaining을 쓸 때 다음 턴에 무엇을 보내는지가 다르다고 적고 있다.

    compaction guide는 previous_response_id 경로와 stateless input-array chaining 경로의 다음 요청 구성이 다르다고 설명한다.
    compaction guide는 previous_response_id 경로와 stateless input-array chaining 경로의 다음 요청 구성이 다르다고 설명한다.

    이 차이는 장기 대화 운영에서 바로 체감된다. previous_response_id는 간단하지만 내부 상태를 OpenAI 쪽에 맡기는 구조이고, stateless replay는 더 많은 제어권을 주는 대신 입력 조립 책임이 커진다.

    코드로 보면 차이가 더 분명하다. previous_response_id는 새 메시지와 안정적으로 반복할 instructions를 넘기면 되고, stateless replay는 필요한 output item과 함수 결과를 직접 구성해야 한다.

    previous_response_id 방식과 stateless replay 방식의 요청 예시를 나란히 둔 코드다.
    previous_response_id 방식과 stateless replay 방식의 요청 예시를 나란히 둔 코드다.

    이미 encrypted reasoning replay 글을 읽었다면, 오른쪽 패턴이 왜 더 섬세한지 바로 연결된다.

    마지막 자료는 선택 기준표다. 이 표를 먼저 잡아 두면 구현 취향이 아니라 운영 조건으로 경로를 나눌 수 있다.

    보존 정책, context 제어권, 구현 복잡도로 previous_response_id와 stateless replay를 나눠 보는 표다.
    보존 정책, context 제어권, 구현 복잡도로 previous_response_id와 stateless replay를 나눠 보는 표다.

    또 reasoning summary와 usage 글을 같이 보면, replay를 직접 맡았을 때 무엇을 로그에 남겨야 하는지도 같이 정리된다.

    5. 주의사항과 리스크

    첫 번째 리스크는 previous_response_id가 instructions까지 다 자동으로 관리한다고 오해하는 것이다. 두 번째 리스크는 stateless replay를 택했는데 assistant text만 재사용하고 필요한 item 구조를 버리는 것이다. 세 번째 리스크는 compaction item을 사람이 읽는 설명 데이터처럼 다루는 것이다. compaction guide는 그것이 다음 창에서 필요한 상태를 carry forward하는 opaque item이라고 설명한다.

    또 이전 경로를 나중에 바꾸는 비용도 생각해야 한다. 서비스 초반에 previous_response_id로 단순하게 시작했다가도 보존 정책이나 감사 요구가 생기면 stateless replay로 옮겨야 할 수 있다. 반대로 처음부터 stateless로 출발했는데 실제로 수동 trimming이나 외부 저장이 거의 필요 없다면 유지비만 높아진다.

    • previous_response_id를 써도 반복해야 할 instructions는 다시 넣는다.
    • stateless replay는 message text가 아니라 output item 단위로 보존한다.
    • compaction item은 opaque 상태 조각으로 취급하고 사람이 읽는 로그로 오해하지 않는다.

    6. 결론

    Responses API에서 previous_response_id와 stateless replay는 기능 이름이 비슷해 보여도 상태 책임이 다르다. 구현 단순성과 빠른 안정화가 우선이면 previous_response_id가 먼저고, ZDR과 수동 trimming과 외부 저장 규칙이 중요하면 stateless replay가 맞다. 어떤 경로를 택하든 instructions 재전달과 item 보존 규칙은 따로 명확히 잡아 두는 편이 안전하다.

    • 기본값은 previous_response_id, 제어권 요구가 크면 stateless replay다.
    • top-level instructions는 자동 승계로 가정하지 않는다.
    • 긴 세션은 compaction과 replay 규칙을 같이 설계한다.

    7. 참고 링크

    1. https://developers.openai.com/api/docs/guides/migrate-to-responses
    2. https://developers.openai.com/api/docs/guides/conversation-state
    3. https://developers.openai.com/api/docs/guides/reasoning
    4. https://developers.openai.com/api/docs/guides/compaction
    5. https://developers.openai.com/api/docs/guides/latest-model
Designed by Tistory.