
수십만 행을 돌려준 조회나 MCP 서버가 보낸 거대한 JSON처럼, 도구 결과 하나가 모델의 입력 상한을 한 번에 넘을 때가 있습니다. 이 요청을 그대로 보내면 프로바이더는 요청을 400으로 거절하거나, 입력을 잘라 내고 200을 돌려줍니다. 잘라 낸 경우가 더 위험합니다. 응답 어디에도 잘랐다는 표시가 없어서, 모델은 결과를 다 받았다고 믿고 보지 못한 행의 숫자까지 답에 씁니다. 대화가 길어질 때 앞부분을 요약하는 압축(「압축 임계값은 컨텍스트 윈도우가 아니라 입력 상한에서 잡는다」)도 여기서는 손쓰지 못합니다.
그래서 OwlAI는 도구 결과를 프로바이더에 보내기 전에 크기 상한을 겁니다. 상한을 넘은 결과는 결과의 모양에 맞춰 줄여서 싣고, 줄였다는 사실과 모델이 다음에 할 일을 결과에 함께 적습니다. 이 글은 상한을 어디에 거는지, 무엇부터 줄이는지, 모델에게 무엇을 알리는지를 다룹니다.
자동으로 잘라 내면 잘랐다는 표시 없이 200이 온다
OpenAI Responses API에는 truncation 파라미터가 있습니다. auto로 두면 입력이 컨텍스트 윈도우를 넘을 때 대화 앞부분의 항목을 버려 크기를 맞추고, disabled로 두면 요청을 400으로 거절합니다(공식 레퍼런스, 2026-11-03 확인). 입력 상한이 272,000토큰인 모델에 약 129만 토큰짜리 도구 결과를 실어 두 설정으로 같은 요청을 보냈습니다(2026년 8월 측정).
| 설정 | 응답 | 실제로 일어난 일 |
|---|---|---|
truncation: "auto" |
200 OK | 보낸 1,291,836토큰 가운데 응답의 input_tokens는 72,115였다. 94.4%를 버렸는데 응답에는 버렸다는 표시가 없고, 사용량 숫자만 버린 뒤의 값이었다 |
truncation: "disabled" |
400 context_length_exceeded |
서버 측 압축 파라미터(context_management)를 넣어도 빼도 같았다 |
auto는 요청이 실패하지 않으니 사용자가 언제나 답을 받는다는 장점이 있습니다. 대신 그 답이 무엇을 읽고 쓴 것인지 아무도 모릅니다. 400은 시스템이 알아채고 사용자에게 알릴 수 있지만, 잘린 입력으로 쓴 답은 정상 답변의 모양으로 화면에 나갑니다. 그래서 OwlAI는 OpenAI 요청에 truncation: "disabled"를 고정합니다. Anthropic은 넘치는 요청을 처음부터 400으로 거절하고, vLLM도 최대 길이를 넘었다는 오류로 거절합니다. 넘쳤을 때의 400은 컨텍스트 초과로 분류해 재시도 없이 사용자에게 알립니다.
압축은 이미 넘친 요청을 줄이지 못한다
context_management를 넣어도 결과가 같았던 이유는 압축의 순서에 있습니다. 압축하려면 모델이 원문을 한 번 읽어야 하는데, 원문이 상한을 넘으면 읽을 자리가 없습니다. 서버 측 압축이든 OwlAI가 직접 하는 압축이든, 압축은 다음 요청을 줄이는 장치입니다. 지금 보낼 요청을 상한 아래로 끌어내리는 일은 압축 앞에서 따로 해야 합니다.
그 일을 결과 크기 상한이 맡습니다. 도구 결과 하나의 상한은 기본 5만 토큰이고, 설치 설정 compaction.singleToolResultMaxTokens로 바꿀 수 있습니다. 한 라운드(모델을 한 번 부르고 그 응답의 도구 호출을 실행하기까지)에 병렬로 부른 도구들의 결과는 각각 상한 아래여도 합이 넘칠 수 있습니다. 그래서 그 라운드에 새로 붙는 결과의 합계에도 같은 상한을 걸고, 넘치면 큰 결과부터 줄입니다. 같은 토큰을 아끼면서 건드리는 결과 수가 가장 적은 순서입니다.
합계 상한을 결과 하나의 상한과 같은 값으로 둔 데는 이유가 있습니다. 압축 임계값과 입력 상한 사이의 여유 구간은 「입력 상한의 10%」와 「결과 하나의 상한」 가운데 큰 쪽으로 잡습니다(「압축 임계값은 컨텍스트 윈도우가 아니라 입력 상한에서 잡는다」). 이 구간이 어떤 모델에서든 5만 토큰보다 좁지 않으므로, 한 라운드에 붙는 양을 5만 토큰 아래로 묶으면 입력은 구간을 건너뛰지 않고 그 안에서 압축에 걸립니다.
결과 모양을 아는 리듀서가 먼저 줄인다
상한을 넘은 결과는 리듀서가 줄입니다. 리듀서는 도구마다 결과의 모양을 알고 덜어 낼 부분을 고르는 함수입니다. 먼저 미리보기 수준으로 줄이고, 그래도 상한을 넘으면 뼈대 수준으로 줄입니다.
| 결과 | 미리보기 | 뼈대 |
|---|---|---|
| SQL 실행 결과 | 앞 80행, 셀은 600자까지, 컬럼은 48개까지 | 컬럼 목록, 행 수, 잘림 여부만 두고 행은 모두 뺀다 |
| 문서·테이블 검색 결과 | 항목 24개까지, 본문은 600자까지 | 이름과 점수만 두고 본문은 뺀다 |
| 리듀서가 없는 도구(MCP 등) | 목록은 앞 5개, 문자열은 2,000자, 객체는 키 50개까지 | 목록은 앞 1개, 문자열은 300자, 객체는 키 15개까지 |
SQL 결과를 미리보기로 줄일 때는 결과에 이미 있던 잘림 여부를 바꾸지 않습니다. 80행에서 끊은 것은 표시를 위해 덜어 낸 것이고, DB가 상한에 걸려 행을 덜 돌려준 것(「RAG로는 건수와 합계를 셀 수 없다, 원천 DB에 SQL로 묻는다」)과는 다른 사실입니다. 그래서 「80행만 남겼고 몇 행을 덜었다」는 표시를 따로 붙입니다. 두 사실을 한 표시에 섞으면 로그로 오답을 거슬러 갈 때 모델이 실제로 본 값과 기록이 어긋납니다.
리듀서가 따로 없는 도구는 모양만 보고 줄이는 일반 리듀서가 받습니다. 고객사마다 붙이는 MCP 서버가 달라서 도구마다 리듀서를 미리 만들어 둘 수 없기 때문입니다. 일반 리듀서는 자른 자리마다 목록이면 전체 개수와 남긴 개수를, 문자열이면 원래 글자 수를 적습니다. 모양을 모른다고 결과를 통째로 비우면, 모델은 무엇을 조회했는지조차 모른 채 같은 조회를 다시 부릅니다.
줄였다고 적기 전에 정말 줄었는지 잰다
리듀서가 모르는 모양을 만나면 결과가 줄지 않을 수 있습니다. 사용자 PC 쪽 도구처럼 클라이언트와 주고받는 결과가 그런 예인데, 리듀서가 줄일 칸을 찾지 못한 채 비웠다는 표시만 덧붙여서 142,924자짜리 결과가 143,589자가 됐습니다. 이대로라면 기록에는 줄였다고 남고, 모델에게는 원본보다 큰 결과가 갑니다.
그래서 줄인 결과를 두 번 확인합니다. 먼저 글자 수가 원본보다 작은지 보고, 작으면 토큰 수가 상한 아래인지 봅니다. 둘 중 하나라도 아니면 200자 이하의 짧은 값(도구 이름, 성공 여부 같은 것)과 원래 크기만 남기고 본문을 통째로 들어냅니다. 본문이 어떤 모양이든 이 단계를 거치면 결과는 반드시 줄어듭니다. 문자열을 중간에서 잘라 넣는 방법은 쓰지 않습니다. JSON이 중간에 끊기면 모델은 결과를 읽지도 못하고 잘렸다는 사실도 알 수 없습니다.
토큰을 세는 비용도 아낍니다. 토큰 하나는 적어도 한 글자이므로, 결과의 글자 수가 상한보다 작으면 토큰 수도 상한보다 작습니다. 이 경우는 세지 않고 넘어갑니다. 대부분의 결과는 이 조건에 걸리므로, 프로바이더의 토큰 수 세기 API 왕복(실측 284~479ms)은 상한 근처의 큰 결과에만 붙습니다.
줄인 결과에는 줄인 사실과 다음 할 일을 붙인다
이 문제의 본질은 데이터가 조용히 사라진 것이었으므로, 줄이는 쪽이 다시 조용하면 고친 것이 없습니다. 줄인 결과에는 줄였다는 표시가 붙고, 그 안에 원래 토큰 수, 상한, 줄인 수준, 사유(결과 하나가 커서인지 라운드 합계가 커서인지), 모델에게 주는 안내가 들어갑니다.
「잘렸다」고만 알리면 모델은 받은 부분으로 답을 지어냅니다. 그래서 안내에는 다음 행동까지 적습니다. 공통으로 들어가는 내용은 세 가지입니다. 전체 결과를 본 것이 아니라는 사실, 받은 행 수를 총계로 말하지 말라는 것, 빠진 부분이 있다고 답에 밝히라는 것입니다. 여기에 상황에 따라 다음 행동 하나를 더합니다.
| 상황 | 다음 행동 |
|---|---|
| 원본이 파일로 남았고, 이번 호출에 파일을 읽는 도구가 있다 | 같은 도구를 다시 부르지 말고 파일을 나눠 읽는다. 사용자가 파일로 원하면 내용을 다시 쓰지 말고 그 파일을 첨부한다 |
| 원본이 파일로 남았지만, 이번 호출에는 읽는 도구가 없다 | 받은 것으로 답하고, 일부만 봤다고 밝힌다 |
| 원본이 파일로 남지 않았다 | 조건을 좁히거나 묶어서 다시 조회한다 |
갈래를 나눈 이유는 틀린 다음 행동을 주면 모델이 그 행동을 시도하다 실패하기 때문입니다. 파일 읽기 도구가 없는 플레이북 단계에서 「파일을 읽어라」라고 하면 모델은 없는 도구를 부릅니다. 원본이 이미 파일로 있는데 「다시 조회하라」고 하면 같은 쿼리를 한 번 더 돌립니다.
원본이 파일로 남는 것은 스필(spill) 덕분입니다. 1,000자를 넘는 도구 결과는 실행 직후 서버가 샌드박스 파일로 먼저 쓰므로(「조회 결과는 모델이 다시 받아 적지 않게 파일 경로로 넘긴다」), 5만 토큰 상한에 걸리는 결과는 줄어들기 전에 이미 원본이 파일에 있습니다. 안내에는 그 파일 경로와 서버가 실제로 쓴 행 수·바이트 수가 함께 실립니다. 모델이 「모두 몇 건」이라고 답할 때는 이 숫자를 씁니다.
상한은 대화 트리에 저장하기 전에 건다
OwlAI는 모델 호출 하나를 노드 하나로 저장하고, 다음 요청을 이 대화 트리에서 조립합니다(「고친 질문에 옛 답이 섞이지 않게 대화를 트리로 저장하기」). 결과 상한은 새로 붙는 도구 결과를 노드에 저장하기 전에 겁니다. 그래서 줄인 결과가 노드에 남고, 다음 라운드와 다음 턴도 줄인 결과로 요청을 조립합니다. 보내기 직전의 사본만 줄이면 이번 요청만 작아지고, 트리에는 원본이 남아 라운드마다 다시 상한을 넘습니다.
저장 전에 줄이면 프롬프트 캐싱에도 맞습니다. 결과 하나는 처음 붙을 때 한 번만 판정되고, 그 뒤로는 바이트가 바뀌지 않습니다. 다음 라운드의 요청 앞부분이 앞 라운드와 같으므로 캐시를 계속 읽습니다(「시스템 프롬프트 한 줄에 캐시 읽기가 0이 된다, 바뀌는 블록은 뒤로 보낸다」). 이 상한은 모든 모델 호출이 거치는 진입점에 있어서 채팅·플레이북·딥 리서치가 같은 규칙을 따릅니다. OwlAI가 직접 압축하는 vLLM 경로에서도 압축의 첫 단계가 같은 상한을 씁니다(「압축 임계값은 컨텍스트 윈도우가 아니라 입력 상한에서 잡는다」).
설계에서 고른 것
truncation: "disabled"의 대가는 넘치는 요청이 답 대신 오류로 끝난다는 점입니다. 언제나 답을 돌려주는 auto를 두고 오류를 고른 것은, 알아챌 수 있는 실패가 근거 없는 정상 답보다 다루기 쉬워서입니다. 그 오류가 사용자에게 가지 않게 하는 일은 결과 상한과 압축이 맡습니다.
토큰 수 세기가 실패하면 결과를 줄이지 않고 그대로 보냅니다. 셀 수 없다고 초과로 보고 줄이면, 세는 장치의 장애가 멀쩡한 결과의 손실로 번집니다. 정말 넘쳤다면 프로바이더가 400으로 알립니다. 이때 대화의 끝 노드는 실패한 노드 대신 마지막 성공 노드에 둡니다. 실패한 노드에는 초과의 원인인 거대한 결과가 들어 있을 수 있고, 그 노드를 끝으로 고정하면 다음 턴마다 같은 입력이 조립돼 대화가 다시는 진행되지 않습니다. 대가로 그 라운드의 도구 결과는 잃지만 대화는 이어집니다.
사유를 「결과 하나 초과」와 「라운드 합계 초과」로 나눠 로그에 남기는 것은 조치가 달라서입니다. 결과 하나가 크면 그 도구의 조회 범위를 좁혀야 하고, 합계가 크면 한 라운드의 병렬 호출을 줄여야 합니다. 일반 리듀서로 줄인 도구가 로그에 자주 보이면 그 도구에 전용 리듀서를 붙일 후보로 봅니다.
도구 결과가 수십만 행이나 수백만 글자까지 커질 수 있는 에이전트라면 두 가지를 먼저 확인할 만합니다. 프로바이더나 게이트웨이의 자동 잘라 내기가 켜져 있지 않은지, 그리고 결과를 줄이는 자리가 대화 기록에 저장하기 전인지입니다. 앞쪽이 켜져 있으면 모델은 보지 못한 데이터로 답을 쓰고, 뒤쪽이 저장 뒤라면 줄인 결과가 다음 라운드에 원본으로 되살아납니다.
어떤 문제를 해결하나요?
대화 엔진의 다른 글

엔지니어링 노트 · 대화 엔진
한 턴의 입력 토큰은 라운드 수의 제곱으로 늘어난다
모델 API는 호출 사이에 아무것도 기억하지 않습니다. 그래서 에이전트는 모델을 부를 때마다 시스템 프롬프트, 도구 정의, 그때까지의 대화 맥락을 전부 다시 보냅니다.

엔지니어링 노트 · 대화 엔진
OpenAI는 응답을 기본으로 저장한다, 대화 맥락은 직접 보낸다
OpenAI Responses API는 store를 따로 정하지 않은 응답을 프로바이더 서버에 저장하고, 공식 문서는 그 응답이 적어도 30일 남는다고 밝힙니다(2026-11-03 확인).

엔지니어링 노트 · 대화 엔진
고친 질문에 옛 답이 섞이지 않게 대화를 트리로 저장하기
사용자가 「지난달 거래처별 매출 상위 10곳」을 물었는데, 모델이 국내 거래처만 담긴 테이블을 조회해 답했다고 해 보겠습니다.
