-
IT 리서치 노트
[OpenAI][Responses API] compact 결과에서 retained items를 어디까지 보존해야 하나
Responses API에서 compact 결과를 받은 뒤 어떤 item을 남기고 어떤 item을 버려도 되는지 헷갈리는 경우가 많다. 2026년 7월 1일 기준 OpenAI 공식 compaction 문서를 다시 보면 compacted window에는 compaction item만 오는 것이 아니라 retained items가 함께 올 수 있고, 이 returned window 자체가 canonical next context window다. 이 글은 compact 결과에서 retained items를 어디까지 보존해야 하는지, 그리고 언제 manual pruning을 멈춰야 하는지 정리한 것이다.
1. 개요
결론부터 말하면
/responses/compact가 돌려준 returned window는 우선 그대로 보존하는 편이 맞다. retained items는 compaction item 옆에 붙는 보조 조각이 아니라, 다음 turn 품질과 continuation에 필요한 canonical next context window의 일부다. 그래서 앱 로그를 따로 남기더라도 compacted window 자체를 다시 분해해서 next turn 입력을 만들기 시작하면 문제가 커진다.이미 compaction threshold 글이 언제 compact를 검토할지 다뤘다면, 이번 글은 compact 이후 보존 규칙이다. 또 previous_response_id와 stateless replay 글은 상태 전달 경로를 나누는 글이고, 오늘은 그 경로에서 compacted window를 어디까지 살려야 하는지의 문제다.
2. 어디서 실제로 막히는가
현장에서 자주 나오는 오해는 세 가지다. 첫째, compact 결과에는 opaque compaction item만 남고 나머지는 모두 버려도 된다고 본다. 둘째, retained items가 와도 디버깅이 불편하니 next turn 입력을 더 작게 만들려고 일부만 넘긴다. 셋째, 앱 감사 로그용 요약을 만들면서 compacted window 자체를 그 요약으로 대체한다.
하지만 compaction 가이드는 compacted window가 compaction item만이 아니라 retained items를 포함할 수 있다고 적고, output handling에서는 returned window를 prune하지 말라고 설명한다. WebSocket mode도 같은 규칙을 반복한다. 즉 compact 결과는 '참고용 제안'이 아니라 다음 요청에 바로 넣는 canonical window다.
이 차이를 놓치면 stateless replay와 ZDR 경로에서 특히 문제가 커진다. compact 결과를 임의로 줄이면 다음 turn에 필요한 returned output items가 빠질 수 있고, 그러면 reasoning continuity나 tool-call state가 예상보다 빨리 흔들린다. 반대로 사람 읽기 편한 로그를 따로 남기는 것은 괜찮지만, 그 로그가 next-turn window를 대신하면 안 된다.
- 증상: compact 뒤 next turn 품질이 들쭉날쭉해진다.
- 실패: compaction item만 남기고 retained items를 임의로 떼어 낸다.
- 막힘: 앱 감사 로그와 next-turn canonical window를 같은 것으로 취급한다.
- 누락: ZDR path에서도 returned output items를 다시 넘겨야 한다는 규칙을 빼먹는다.
상황 먼저 볼 곳 판단 기준 compact 결과가 길어 보인다 returned window 전체 먼저 as-is 사용 후 운영 로그를 분리한다 ZDR나 stateless replay를 쓴다 returned output items 전달 규칙 relevant items 누락이 없는지 본다 감사 로그를 남긴다 trace id, item count, compact 시점 canonical window 대체가 아니라 별도 기록인지 본다 3. 실무에서 적용하는 순서
실무에서는 다섯 단계로 가져가면 된다. 먼저 compact가 일어난 이유와 시점을 로그에 남긴다. 두 번째로 returned window를 그대로 다음 turn 입력 후보로 저장한다. 세 번째로 감사 로그가 필요하면 trace id, item 수, compact 시점만 별도 추출한다. 네 번째로 stateless 또는 ZDR 경로라면 relevant returned output items가 빠지지 않았는지 확인한다. 마지막으로 manual pruning이 꼭 필요하면 품질 검증이 끝난 뒤 별도 실험 경로에서만 한다.
- compact 시점과 이유를 먼저 로그에 남긴다.
- returned window를 as-is로 저장한다.
- 감사 로그는 trace id와 item 수만 별도로 뽑는다.
- ZDR/stateless path에서는 relevant returned output items 누락 여부를 확인한다.
- manual pruning은 품질 검증이 끝난 실험 경로에서만 한다.
핵심은 다음 요청용 window와 사람이 읽는 운영 메모를 분리하는 것이다. compact 결과를 받아 next turn에 그대로 넘기는 경로는 보수적으로 유지하고, 사람 읽기 편한 보고서는 그 옆에서 따로 만든다. 그래야 tool-call state, reasoning continuity, retained items 범위를 뒤섞지 않는다.
manual pruning이 정말 필요하다면 그때는 next-turn 품질, tool call 연속성, reasoning continuity가 깨지지 않는지 별도 테스트를 먼저 해야 한다. 운영 본선에서는 compacted window를 canonical source로 두는 편이 훨씬 안전하다.
4. 공식 문서와 예시 화면으로 확인하기
첫 화면은 compaction 가이드가 compacted window 안에 무엇이 들어올 수 있는지 설명하는 구간이다. 여기서는 compaction item만 오는 것이 아니라 이전 window에서 retained items가 함께 올 수 있다는 문장을 먼저 봐야 한다.
이 한 줄을 놓치면 compact 결과를 받은 뒤 최신 compaction item만 남기고 나머지를 임의로 덜어 내는 실수가 나온다. 이미 compaction threshold 글이 언제 compact를 검토할지 다뤘다면, 이번 글은 compact 결과를 받은 다음 window를 어떻게 보존해야 하는지에 더 가깝다.
두 번째 자료는 compact output 처리 규칙이다. 문서는 `/responses/compact` 결과를 prune하지 말고 canonical next context window로 그대로 넘기라고 설명한다.
즉 retained items 보존 범위는 애플리케이션이 새로 설계하는 것이 아니라, 우선 서버가 돌려준 compacted window 자체를 신뢰하는 것에서 시작한다. 팀이 수동 trimming을 꼭 하려면 왜 필요한지와 어떤 item을 절대 건드리지 않을지 먼저 문서화해야 한다.
세 번째 화면은 WebSocket mode에서 standalone compact를 받은 뒤 이어 붙이는 구간이다. WebSocket 연결에서도 compacted output을 as-is로 넘기라는 규칙이 반복된다는 점이 중요하다.
이 문장이 있기 때문에 retained items 보존 규칙은 HTTP stateless path와 WebSocket path가 크게 다르지 않다. 차이는 연결 방식이지 compact 결과를 자기 마음대로 다시 조각내도 된다는 뜻이 아니다.
네 번째 자료는 최신 모델 가이드의 stateless/ZDR 경로 설명이다. 여기서는 이전 turn에서 반환된 output items를 다시 넘겨야 한다는 기본 규칙을 다시 확인한다.
compact 결과를 보존하는 이유도 결국 여기로 이어진다. ZDR라고 해서 compact 결과를 최소화하고 싶어도, 다음 turn 품질에 필요한 returned output items를 통째로 잃으면 continuation이 흔들릴 수 있다. previous_response_id와 stateless replay 글과 함께 보면 이 보존 범위가 왜 필요한지 더 분명해진다.
이 표는 raw window, compacted window, 앱 로그용 요약을 같은 화면에서 분리하기 위한 것이다. retained items를 어디까지 보존할지 헷갈리는 팀은 보통 이 세 층위를 한 덩어리로 본다.
보존 범위를 명확히 나누면 compacted window는 다음 요청용, 앱 로그는 운영 감사용, 사람이 읽는 메모는 별도 설명용이라는 역할이 선명해진다. compact 결과를 감사 로그처럼 다시 정리하는 순간 canonical window를 깨뜨리기 쉽다.
마지막 자료는 compact 결과를 받은 뒤 남길 최소 체크리스트다. retained items를 보존할지 말지를 item 단위로 추측하지 말고, canonical window 전체를 기준으로 체크하는 편이 안전하다.
이 메모를 남겨 두면 compact 결과를 받은 뒤 팀원이 디버깅 편의 때문에 일부 item을 떼어 내는 실수를 막기 쉽다. 특히 conversation item TTL 글처럼 state surface를 여러 층으로 나눠 본 팀일수록 이 체크리스트가 잘 맞는다.
5. 주의사항과 리스크
첫 번째 리스크는 compact 결과를 사람이 읽는 요약처럼 다루는 것이다. 두 번째 리스크는 retained items가 많아 보인다는 이유만으로 일부를 next-turn 입력에서 지우는 것이다. 세 번째 리스크는 ZDR 경로라고 해서 compact 결과도 최소화해야 한다고 오해하는 것이다.
운영 전에는 최소한 returned window as-is 저장 여부, relevant output items 확인 여부, 감사 로그 분리 여부를 같이 남기는 편이 좋다. 이 세 가지가 없으면 compact 이후 품질 저하가 window pruning 때문인지 모델 변화 때문인지 분리하기 어렵다.
- compacted window는 우선 canonical next context window로 본다.
- retained items는 '남아도 되는 여분'이 아니라 continuation에 필요한 일부일 수 있다.
- 감사 로그와 next-turn window를 같은 구조로 쓰지 않는다.
6. 결론
OpenAI Responses API에서 compact 결과의 retained items 보존 범위는 item별 추측보다 returned window 전체를 그대로 넘기는 쪽이 기본값이어야 한다. compacted window는 canonical next context window이고, retained items도 그 일부다. 감사 로그가 필요해도 next-turn 입력을 다시 분해해 만들기보다 별도 로그를 두는 편이 안전하다.
여기서 한 단계 더 들어가서 compacted window 자체를 앱 캐시에 얼마나 오래 둘지 정해야 한다면, compacted window를 자체 캐시에 보관할 때 만료 기준을 어디까지 둘지 정리한 후속 글이 이어진다. retained items 보존 범위와 cache TTL은 같은 질문처럼 보여도 운영 기준은 다르다.
- compaction 이후 첫 기준은 as-is 보존이다.
- retained items는 compacted window와 함께 본다.
- manual pruning은 실험 경로에서만 검증 후 도입한다.
7. 참고 링크
'기타개발지식 > 풀스택개발' 카테고리의 다른 글