프롬프트가 매번 주입된다?
이날은 에이전트 도구를 많이 만졌다.
그중에서 가장 인상적인 교훈은 “존재하지 않는 버그”를 고치려다
복잡하게 만든 토큰 예산 시스템이었다.
문제가 없는데 문제가 있다고 생각해서 과도하게 해결하려 했다.
결국 간단한 해결로 되돌렸다.
문제는 이렇게 시작했다.
기본 프롬프트가 매 요청마다 주입되고 있다고 생각했다.
그러면 토큰이 계속 낭비된다.
그래서 토큰 예산 시스템을 만들었다.
5만 토큰마다 한 번씩만 주입한다.
이전 주입 이후에 5만 토큰 이상 지나면 다시 주입한다.
압축이 일어나면 상태를 초기화한다.
세션마다 독립적으로 추적한다.
꽤 복잡한 시스템이었다.
상태 관리를 어디다
둘지 고민했다.
전역 변수는 안 됐다.
모듈 컨텍스트 격리 때문에 상태가 안 유지됐다.
파일 기반 상태도 시도했다.
임시 파일에 JSON으로 저장하는 거다.
근데 이게 전부 필요 없었다.
실제로 일어나고 있던 일
실제로는 프롬프트가 매번 주입되는 게 아니었다.
한 번 주입되고 컨텍스트에 남아 있을 뿐이었다.
에이전트는 첫 요청에 프롬프트를 받는다.
그 다음 요청에서는 프롬프트가 다시 주입되지 않는다.
근데 에이전트가 “아직 프롬프트가 보인다”고 한다.
이게 “다시 주입된 것”이 아니라, “컨텍스트에 남아 있는 것”이었다.
첫 번째 주입이 대화 컨텍스트에 들어간다.
이후 요청에서는 컨텍스트에 남아 있는 프롬프트를 계속 참조하는 거다.
“다시 주입”이 아니라 “컨텍스트 유지”다.
이걸 구분하지 못해서 복잡한 시스템을 만든 거다.
“매번 주입된다”고 잘못 생각해서 “매번 주입을 막아야 한다”고 과도하게 해결한 거다.
간단한 해결로 되돌리기
이해하고 나니 해결은 간단했다.
인메모리 변수 하나로 충분했다.
첫 주입 이후에는 주입하지 않는다.
압축이 일어나면 다시 주입한다.
끝이다.
복잡했던 시스템을 다
걷어내고 변수 하나로 바꿨다.
파일 기반 상태도, 전역 변수도, 복잡한 추적도 전부 필요 없었다.
과도하게 만든 이유
왜 이렇게 됐을까. 증상을 잘못 해석했다.
“에이전트가 프롬프트를 보인다”는 증상을 봤다.
“매번 주입되나 보다”라고 추론했다.
이 추론이 틀렸다.
근데 틀린 줄 모르고 추론에 맞춰서 시스템을 만들었다.
증상이 맞아도 해석이 틀릴 수 있다.
“보인다”는 사실은 맞다.
근데 “다시 주입되어서 보인다”는 해석이 틀렸다.
“컨텍스트에 남아 있어서 보인다”가 맞았다.
해석을 검증하지 않고 시스템을 만들면 이렇게 된다.
해석이 맞는지 확인하려면, 주입이 실제로 일어나는지 로그로 확인하면 된다.
그걸 안 하고 추론만으로 시스템을 만든 거다.
두 가지 훅의 차이
같은 날 프롬프트 주입과 관련해서 또 하나를 배웠다.
두 가지 훅이 다른 역할을 한다.
하나는 LLM이 받는 메시지를 변환하는 훅이다.
이건 LLM한테만 영향을 준다.
화면에는 안 보인다.
다른 하나는 메시지를 데이터베이스에 저장하는 훅이다.
이건 화면에도 보이고 LLM한테도 전달된다.
이 차이를 몰랐을 때, “화면에 보이게 하려면”이라는 목표가 안 됐다.
LLM 변환 훅을 썼으니까, LLM한테는 전달되는데 화면에는 안 보였다.
저장 훅을 쓰니까 화면에도 보이고 LLM한테도 전달됐다.
목적에 따라 다른 훅을 써야 한다.
분석기 패턴
그리고 외부 서비스 접근을 전문 에이전트로 분리하는 패턴도 정리했다.
주 에이전트가 직접 외부 서비스에 접근하지 않는다.
전문 에이전트한테 위임한다.
피그마 분석 에이전트, 지라 분석 에이전트가 각자 자기 서비스에만 접근한다.
분석 결과를 보고서로 반환한다.
주 에이전트는 보고서만 받는다.
이러면 주 에이전트의 컨텍스트가 깨끗하다.
API 응답 전체가 컨텍스트에 들어오지 않는다.
필요한 정보만 보고서로 들어온다.
마무리
이날의 교훈은 “문제를 잘못 진단하면 과도한 해결을 만든다”는 거다.
토큰 예산 시스템은 문제가 없는데 문제가 있다고 생각해서 만들었다.
“매번 주입된다”는 해석이 틀렸다.
“컨텍스트에 남아 있다”가 맞았다.
잘못된 해석에 맞춰서 복잡한 시스템을 만들었다.
이해하고 나니 변수 하나로 충분했다.
이걸 막으려면, 증상의 해석을 검증해야 한다.
“매번 주입되는가”를 로그로 확인하면 된다.
확인하지 않고 추론만으로 시스템을 만들면, 존재하지 않는 문제를 해결하게 된다.
존재하지 않는 문제를 해결하는 건 비용이다.
복잡도를 늘리고 버그를 만들고 유지보수 부담을 가중한다.
문제가 진짜인지 확인하는 시간이, 잘못된 해결을 만드는 시간보다 훨씬 적다.