
상주형 AI 에이전트의 LLM 호출 비용을 제어하는 설계 패턴
요약
상주형 AI 에이전트 운용 시 발생하는 과도한 LLM 호출 비용을 제어하기 위한 설계 패턴을 제안합니다. 해시 비교를 통한 변화 감지, 차분 데이터 전달, 폴링 빈도 계층화, 배치 처리 및 프롬프트 캐싱 최적화 전략을 다룹니다.
핵심 포인트
- LLM 호출 전 해시 비교 등 저렴한 로직으로 변화 여부 선판단
- 전체 스냅샷 대신 변화된 차분(Difference) 데이터만 LLM에 전달
- 업무 중요도에 따라 모니터링 폴링 빈도를 계층화하여 관리
- 동일 테넌트 내 이벤트를 배치 처리하여 프롬프트 오버헤드 감소
- 고정 프롬프트를 앞단에 배치하여 API 캐시 적중률 극대화
채팅이나 메일을 상시 모니터링하며 작동하는 AI 에이전트는, 사용자의 질문에 답하기만 하는 챗봇과 달리, 아무도 보고 있지 않은 동안에도 LLM을 계속 호출해 버린다는 특유의 비용 문제를 안고 있습니다. 모니터링 대상이 늘어날수록 폴링 (Polling) 할 때마다 LLM에 던지는 토큰량이 쌓이게 되어, 어느샌가 "테넌트(Tenant)당 월간 비용을 예측할 수 없는" 상태가 됩니다. 이 기사에서는 상주형 에이전트를 운용하면서 정립한 "LLM 호출 비용을 제어하는 설계 패턴"을 정리합니다.
대화형 챗봇은 호출 횟수가 사용자의 발언 횟수에 거의 비례하기 때문에 비용 추산이 쉽습니다. 반면, 상주형 에이전트는 인간이 아무것도 하지 않아도 정기적으로 계속 움직이기 때문에, 호출 횟수가 테넌트 수 × 모니터링 빈도로 기계적으로 증가합니다. 게다가 "만약을 위해 매번 LLM에게 요약하게 한다"는 구현을 하면, 변화가 없는 시간대에도 계속해서 비슷한 토큰을 소비하게 됩니다.
비용이 문제가 되는 것은 개발 중이 아니라, 테넌트 수가 늘어났을 때 처음입니다. 1테넌트라면 오차 범위일지 몰라도, 100테넌트 분량이 동일한 빈도로 폴링을 수행하면 무의미한 호출이 그대로 청구서에 반영됩니다.
가장 효과가 큰 것은, LLM을 호출할지 여부에 대한 판단 자체를 LLM에게 시키지 않는 것입니다.
async function checkAndSummarize(source: MonitoredSource) {
const snapshot = await fetchSnapshot(source);
const prevHash = await store.get(`hash:${source.id}`);
...
포인트는 두 가지가 있습니다.
- 변화 감지는 해시 비교나 차분 추출 등의 저렴한 처리로 끝내고, LLM은 "무엇이 변했는지 의미를 부여하는" 부분만 담당하게 한다.
- LLM에 전달하는 것은 스냅샷 전체가 아니라 차분(Difference)만으로 한다. 모니터링 대상이 비대해져도 전달하는 토큰량은 변화량에 비례하므로 크게 늘어나지 않는다.
모든 모니터링 대상을 동일한 빈도로 폴링할 필요는 없습니다. 반응 속도가 가치와 직결되는 것과 그렇지 않은 것을 구분합니다.
| 계층 | 예 | 폴링 빈도 |
|---|---|---|
| 리얼타임 | 상담 중인 채팅, 긴급 연락 | 수십 초~1분 |
| ... |
"전부 리얼타임으로 해두면 안심"이라는 생각은 비용 측면에서 가장 비싼 선택입니다. 그 정보가 1시간 늦게 도착해도 업무상 지장이 없는가를 기준으로 계층을 결정하고, 낮은 계층으로 내릴 수 있는 것은 적극적으로 내립니다.
여러 모니터링 대상 및 여러 테넌트의 요약을 개별적으로 LLM에 던지는 것이 아니라, 일정 시간 분량을 모아서 1회의 프롬프트(Prompt)로 처리하면 프롬프트의 고정 부분(시스템 프롬프트나 지시문)의 오버헤드를 분산할 수 있습니다.
// 나쁜 예: 10건의 탐지 이벤트에 대해 10번 LLM을 호출
for (const finding of findings) {
await llm.summarize(finding);
...
단, 배치(Batch)화는 동일 테넌트 내로 한정합니다. 테넌트를 넘나들며 프롬프트에 섞으면 타사의 정보가 동일한 컨텍스트(Context)에 올라가게 되어 마스킹 설계가 복잡해질 뿐만 아니라, 테넌트 분리 원칙에도 어긋나므로 피해야 합니다.
많은 LLM API는 프롬프트의 앞부분을 캐시(Cache)하여 저렴하게 재사용할 수 있습니다. 상주 에이전트는 시스템 프롬프트나 도구 정의가 호출할 때마다 바뀌지 않는 경우가 많으므로, 변하는 부분(차분 데이터)을 끝에 배치하고 고정 부분을 앞에 모으는 것만으로도 캐시 적중률이 올라가 실질적인 비용이 낮아집니다. 프롬프트 구성 순서를 바꾸는 것만으로 효과를 볼 수 있어 구현 비용 대비 효과가 큰 대책입니다.
설계로 다 막을 수 없는 무의미한 호출은 반드시 남기 마련이므로, 테넌트별 호출 횟수와 토큰량을 집계하여 평균에서 벗어난 테넌트를 감지하는 운용을 시스템에 포함시킵니다. 특정 테넌트만 이상하게 폴링이 많다면 모니터링 대상 설정 실수(불필요한 소스까지 등록되어 있는 등)가 원인인 경우가 많으며, 이는 비용 절감뿐만 아니라 설정 오류의 조기 발견으로도 이어집니다.
- LLM 호출 필요 여부 판정은 규칙 기반(Rule-based)으로 먼저 수행하고, LLM에는 "차분의 의미 부여"만 맡긴다.
- 모니터링 대상은 중요도에 따라 계층화하고, 실시간성이 불필요한 것은 빈도를 낮춘다.
- 동일 테넌트 내라면 호출을 배치화하고, 프롬프트의 고정 부분을 앞에 두어 캐시를 활용한다.
- 테넌트 단위로 비용을 가시화하여, 이상치(Outlier)를 통해 설정 실수를 조기에 발견한다.
상주형 에이전트는 '계속 움직이는 것' 자체가 가치이기 때문에, 비용 제어를 뒷전으로 미루면 비용 측면에서 스케일링(Scaling)할 수 없게 됩니다. 기능을 고도화하기 전에 이 5가지 패턴을 미리 체크해 두면 나중에 발생할 기술 부채를 줄일 수 있습니다.
필자는 Slack / Teams / Chatwork에 상주하는 기업용 AI 스태프 「HACH」를 개발하고 있습니다. 테넌트(Tenant) 수가 늘어나더라도 비용이 선형적으로만 증가하도록, 바로 이 설계 방식으로 운영하고 있습니다. 유사한 에이전트를 개발하고 계신 분들께 참고가 되기를 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기