에이전트·RAG

AGENT / 42번째 글

넣기 전에 줄이는 방법들

컨텍스트 압축을 손실 크기 순으로 네 단계로 나누고, 도구 결과의 필드 선별과 형식 교체처럼 손실 없이 절반을 줄이는 방법부터 축약·요약까지의 순서와 검증법을 정리합니다.

PALDYN Team11 MIN READ

지난 글에서 캐싱으로 고정분의 값을 깎는 얘기를 했다. 그런데 캐시는 비용과 지연을 줄여 줄 뿐, 모델이 읽어야 할 양 자체는 그대로다. 그리고 창을 실제로 넘치게 만드는 것은 대개 캐시가 듣지 않는 쪽 — 도구가 돌려준 결과와 검색해 온 문서다. 조회 API 응답을 그대로 대화에 얹는 코드가 두어 군데만 있어도 열 턴 만에 이력의 절반이 JSON 중괄호로 찬다.

여기서 많은 팀이 곧바로 "오래된 부분을 모델에게 요약시키자"로 간다. 그런데 요약은 압축 방법 중 가장 비싸고 가장 되돌리기 어려운 쪽이다. 그 앞에 훨씬 싼 단계가 둘 있고, 그 둘만으로 목표에 닿는 경우가 생각보다 많다.

손실 크기로 줄을 세운다

압축 기법을 "얼마나 줄어드는가"로 고르면 언제나 요약이 이긴다. 대신 "무엇을 잃는가"로 줄을 세우면 순서가 뒤집힌다.

압축 단계와 손실 크기

앞의 둘은 정보를 버리지 않는다. 형식만 바꾸거나 애초에 안 쓰던 값을 빼는 것이라 모델이 볼 수 있는 사실의 집합이 그대로다. 셋째는 정보를 화면에서 내리지만 도구를 다시 부르면 복구된다. 넷째만이 원문을 영영 잃는다. 그러니 순서는 정해져 있다 — 위에서부터 쓰고, 목표에 닿으면 멈춘다.

① 쓰지 않는 필드를 자른다

가장 흔하고 가장 큰 낭비가 여기 있다. 사내 API든 외부 서비스든 응답에는 그 화면을 그리려고 만든 필드가 잔뜩 들어 있는데, 모델은 그중 서너 개만 본다. 나머지는 토큰을 먹으면서 모델이 근거를 잘못 고를 확률만 올린다.

KEEP = ("id", "title", "status", "owner", "updated_at")

def shape_issues(raw):
    return [
        {k: item[k] for k in KEEP if k in item}
        for item in raw["items"]
    ]

허용 목록으로 잡는 것이 요령이다. 금지 목록으로 짜면 API가 필드를 하나 늘릴 때마다 조용히 새로 들어온다. 그리고 중첩된 객체는 대개 통째로 뺄 수 있다 — author 객체 전체 대신 author.name 한 줄이면 충분한 경우가 압도적으로 많다.

여기서 자주 나오는 반론이 있다. "혹시 모델이 그 값을 필요로 하면?" 그럴 때 필요한 것은 필드를 다 넣어 두는 것이 아니라 상세 조회 도구를 하나 더 두는 것이다. 목록은 얇게, 상세는 필요할 때만 — 사람이 쓰는 화면과 같은 설계다.

② 형식을 바꾼다

같은 사실이라도 어떤 모양으로 적느냐에 따라 토큰 수가 두 배 넘게 벌어진다. 특히 같은 키가 행마다 반복되는 JSON 배열이 그렇다.

표현 30행을 담을 때 성질
JSON 배열 (들여쓰기 포함) 가장 길다 키가 행마다 반복, 공백이 토큰을 먹는다
JSON 배열 (공백 제거) 25~30% 절감 여전히 키가 반복된다
마크다운 표 45~55% 절감 키가 헤더에 한 번, 모델이 읽기도 쉽다
탭 구분 텍스트 55~65% 절감 가장 짧지만 열 의미를 따로 알려 줘야 한다

실제 절감폭은 값의 길이에 따라 달라진다. 값이 길고 필드가 적으면 차이가 작고, 값이 짧고 필드가 많은 목록형 데이터일수록 차이가 커진다. 표로 바꿔서 이득이 큰 쪽은 후자다.

def as_table(rows, cols):
    head = " | ".join(cols)
    sep = " | ".join("---" for _ in cols)
    body = "\n".join(" | ".join(str(r.get(c, "")) for c in cols) for r in rows)
    return f"{head}\n{sep}\n{body}"

주의할 자리가 하나 있다. 값 안에 줄바꿈이나 구분자가 들어가면 표가 깨진다. 자유 서술 필드가 섞여 있다면 그 열만 따로 빼거나 길이를 잘라 둔다. 그리고 모델이 그 결과를 그대로 인용해 사용자에게 보여 주는 경로라면 형식을 함부로 바꾸지 않는 편이 낫다. 모델은 대개 받은 모양을 흉내 내서 출력한다.

③ 오래된 것만 축약한다

앞의 둘을 다 하고도 이력이 자란다면, 이제 무엇을 화면에서 내릴지를 정할 차례다. 도구 결과는 여기에 잘 맞는다. 최근 두세 개는 전문을 두고, 그보다 오래된 것은 한 줄로 줄인다.

def shrink_old_results(messages, keep_full=3):
    seen = 0
    for m in reversed(messages):
        if m["role"] != "tool":
            continue
        seen += 1
        if seen > keep_full and not m.get("shrunk"):
            m["content"] = summarize_shape(m["content"])   # "search_issues → 12건, 상위 3건: A, B, C"
            m["shrunk"] = True
    return messages

축약이 요약보다 안전한 이유는 복구 경로가 남아 있기 때문이다. 줄인 자리에 "이 조회는 12건을 돌려줬다"라고 남겨 두면, 모델이 그 내용이 다시 필요할 때 같은 도구를 부르면 된다. 그래서 축약문은 사라진 내용을 감추기보다 무엇이 있었는지 알려 주는 쪽으로 써야 한다. "결과 생략"이라고만 적으면 모델은 그 조회가 실패했다고 오해하기 쉽다.

무엇이 사라졌는지를 잰다

압축을 붙이면 토큰 그래프가 예쁘게 내려간다. 그 그래프만 보고 있으면 어느 순간 답이 나빠진 것을 놓친다. 그래서 압축률과 품질을 같이 봐야 한다.

ρ=1−T압축 후T원본\rho = 1 - \frac{T_{\text{압축 후}}}{T_{\text{원본}}}

ρ\rho 하나로는 아무 판단도 못 한다. 필요한 것은 평가셋에서 압축을 켠 쪽과 끈 쪽의 정답률 차이다. 그리고 이 검증은 기법마다 따로 해야 한다 — ①②는 거의 항상 차이가 없고, ③부터는 작업 성격에 따라 갈린다. 여러 도구 결과를 종합해야 답이 나오는 과제라면 축약이 바로 정확도를 깎는다.

측정할 때 흔한 실수가 평균만 보는 것이다. 압축의 피해는 전체 평균을 조금 내리는 방식이 아니라 특정 유형의 질문만 통째로 틀리는 방식으로 나타난다. 그래서 질문 유형별로 나눠 봐야 보인다.

압축하면 안 되는 것

무엇을 줄일지보다 무엇을 손대지 않을지를 먼저 정해 두면 사고가 준다. 아래는 어떤 단계에서도 원문 그대로 남기는 편이 안전한 항목이다.

항목 이유
사용자가 명시한 제약과 선호 요약에서 가장 먼저 뭉개지는데 어기면 바로 티가 난다
식별자·코드·경로 한 글자만 달라져도 쓸모가 없어진다
숫자와 단위 반올림이 섞이면 계산 결과가 조용히 틀어진다
확정된 결정 사항 되물으면 사용자가 같은 말을 반복하게 된다
실패한 시도의 기록 지우면 에이전트가 같은 시도를 다시 한다

마지막 줄이 특히 자주 빠진다. 오래된 실패 로그는 길고 쓸모없어 보여서 압축 대상 1순위가 되는데, 이것이 사라지면 에이전트가 아까 안 되던 방법을 그대로 다시 시도하는 고리에 빠진다. 실패는 내용을 다 지우더라도 무엇을 시도했고 왜 안 됐는지 한 줄은 남긴다.

순서를 지키면 대개 여기서 끝난다

정리하면 이렇게 된다. 도구 응답에서 안 쓰는 필드를 쳐낸다. 목록형 결과를 표로 바꾼다. 그러고도 남으면 오래된 도구 결과를 축약한다. 이 셋을 하고 나서도 창이 모자란다면 그때가 요약을 붙일 자리이고, 요약은 그 자체로 다뤄야 할 만큼 결정할 것이 많다. 다음 글에서 이어서 본다.


읽어주셔서 감사합니다. 😊

LATEST

에이전트·RAG의 최신 글

에이전트·RAG2026.08.23

에이전트는 어디서 어긋나기 시작하는가

에이전트가 이상한 답을 낼 때 증상은 마지막에 보이지만 어긋난 자리는 그보다 앞입니다. 한 걸음이 무너지는 여섯 자리를 나누고, 증상에서 원인을 거슬러 찾는 방법과 자리별 처방을 정리합니다.

11 MIN
에이전트·RAG2026.08.23

에이전트가 무엇을 했는지 나중에 알 수 있게 만들기

에이전트는 같은 입력에도 다른 경로로 갑니다. 그래서 로그 몇 줄로는 왜 그렇게 됐는지 복원이 안 됩니다. 트레이스를 어떻게 나누고 구간마다 무엇을 붙이며 어떤 지표를 볼지 정리합니다.

11 MIN
에이전트·RAG2026.08.23

에이전트 비용은 걸음 수보다 빨리 늘어난다

걸음이 두 배면 비용은 두 배가 아니라 서너 배입니다. 왜 그렇게 되는지, 어디서 새는지, 상한을 몇 겹으로 어떻게 거는지와 실제로 효과가 큰 순서대로의 대응을 정리합니다.

13 MIN