vooy는 택시를 부르고, 식당을 찾고, 캘린더를 잡고, 메일을 보내는 에이전트입니다. 맡길 수 있는 일이 늘어날수록 좋은 제품이니까, 우리는 계속 도구를 붙였습니다. 그런데 도구가 200개에 가까워졌을 때, 정반대 일이 벌어졌습니다.
에이전트가 할 수 있는 일은 늘었는데, 답은 느려졌고 도구를 고르는 실수도 잦아졌습니다. 어느새 기능을 추가할수록 제품이 나빠지는 이상한 구간에 들어와 있었습니다.
도구를 늘릴수록 답이 나빠졌습니다
에이전트가 도구를 쓰려면 모델이 이름과 설명, 입력 스키마를 먼저 읽어야 합니다. 도구가 수십 개일 때는 전부 컨텍스트에 넣어도 괜찮았습니다. 단순했고, 어떤 도구를 모델이 볼지 예측하기도 쉬웠습니다.
도구가 200개 가까이 되자 얘기가 달라졌습니다. 사용자가 "근처 맛집 찾아줘"라는 한 문장을 보내도, 모델은 그 전에 전체 도구 카탈로그부터 읽었습니다. 도구 정의만으로 턴당 약 15만 입력 토큰이 나갔고, 비슷한 이름과 스키마 사이에서 선택 정확도는 0.83까지 떨어졌습니다. 대화가 길어지면 히스토리까지 더해져 요청 자체가 실패하기도 했습니다.
사용자가 겪는 증상은 "왜 이렇게 느리지?"와 "왜 저 도구를 골랐지?" 두 가지였지만, 내부에서 본 원인은 하나였습니다. 도구 카탈로그를 매번 통째로 보여주는 설계가 스케일을 이기지 못한 것입니다.
vooy만 겪는 문제는 아닙니다. Anthropic도 같은 문제를 공개적으로 다루고 Claude API에 Tool Search Tool을 추가했습니다. 모든 도구를 미리 넣는 대신, 필요할 때 찾아 쓰는 방식입니다.
우리는 그 방향에 동의했습니다. 다만 vooy는 여러 모델을 라우팅하고 한국어와 일본어 요청을 함께 처리합니다. 특정 모델의 네이티브 기능에 전체 런타임을 묶는 대신, 같은 문제를 우리 제품에 맞게 다시 풀기로 했습니다.
Before
전체 도구 카탈로그를 매번 실어 보냄
도구 정의 ~150k 토큰
약 200개 이름 · 설명 · 스키마
대화 히스토리
사용자 발화
After
필요한 소수만 찾아 보여줌
고정 표면 + 검색된 도구 ~19k
대화 히스토리
검색된 상세 스키마
사용자 발화
"필요한 도구만 넣으면 되지 않나?"
우리도 처음에는 그렇게 했습니다. 사용자 요청을 보고 관련 도구만 고른 뒤, 그 턴의 tools 배열에 넣었습니다. 컨텍스트는 바로 줄었습니다.
그러자 다른 곳에서 문제가 터졌습니다. LLM API의 프롬프트 캐시는 앞부분이 이전 턴과 같을 때 가장 잘 동작합니다. 그런데 tools 배열은 보통 프롬프트 앞쪽에 있어서, 턴마다 이 목록을 바꾸자 뒤에 붙은 대화 히스토리의 캐시까지 함께 무너졌습니다.
짧은 요청에서는 성공처럼 보였습니다. 하지만 긴 세션에서는 캐시 히트가 떨어져 순비용이 오히려 커졌습니다. 캘린더에서 맛집으로, 다시 메일로 넘어가는 vooy의 전형적인 대화일수록 손해가 컸습니다.
이 실패에서 얻은 결론이 이후 설계의 기준이 됐습니다.
동적 도구 로딩과 프롬프트 캐시는 서로 싸우기 쉽다. 둘을 같이 살리려면 무엇을 바꿀지뿐 아니라, 어디를 바꿀지도 설계해야 한다.
찾는 일과 건네는 일을 나눴습니다
필요한 건 두 가지였습니다. 지금 쓸 도구를 찾아내는 검색기, 그리고 찾은 도구를 캐시를 깨지 않고 모델에게 건네는 전달 계층입니다.
사용자 발화
“인천공항으로 택시 불러줘”
도구 검색
어휘 매칭 + 의미 매칭을 합쳐 지금 필요한 도구를 고릅니다.
어휘의미상위 후보전달 계층
고정 표면은 유지하고, 상세 스키마만 붙입니다.
고정 표면스키마 추가모델
소수 도구만 보고 선택 · 실행
매 턴 고정
- 항상 쓰는 핵심 도구
- 검색 / 실행 메타 도구
필요할 때만 추가
- 관련 도구 상세 스키마
- 같은 세션에서 재사용
도구 이름이 아니라 사용자의 말을 검색합니다
도구 검색을 정보 검색 문제로 풀었습니다. 사용자 발화가 쿼리이고, 도구 이름과 설명이 문서입니다. BM25로 "카카오맵" 같은 정확한 단서를 잡고, 다국어 임베딩으로 "근처에서 저녁 먹을 곳" 같은 의미를 찾습니다. 두 랭킹을 RRF로 합쳐 최종 후보를 만듭니다.
검색 모델보다 더 큰 차이를 만든 건 코퍼스였습니다. 도구 설명에는 대개 "무엇을 하는 도구인지"만 적혀 있습니다. 하지만 사용자는 그렇게 말하지 않습니다. "오늘 비 와?", "이 근처 주차장 찾아줘"처럼 자기가 하려는 일을 말합니다.
그래서 각 도구에 실제 사용자가 할 법한 말을 한국어, 영어, 일본어로 보강했습니다. 이 작업만으로 정답 도구가 상위 5개 후보에 들어오는 비율(R@5)이 0.33에서 0.964로 뛰었습니다.
도구 목록은 고정하고 스키마만 나중에 붙입니다
검색 결과를 그대로 tools 배열에 넣으면 캐시 문제가 되돌아옵니다. 그래서 모델이 매 턴 보는 고정 도구 목록과, 검색했을 때만 붙는 상세 스키마를 분리했습니다.
고정 목록에는 항상 쓰는 핵심 도구 몇 개와, 나머지 도구를 찾고 실행해 주는 메타 도구만 둡니다. 이 목록은 세션 안에서 바뀌지 않습니다. 모델이 검색으로 도구를 찾으면, 그 상세 스키마를 프롬프트 앞부분이 아니라 뒤에 쌓이는 대화 히스토리에 붙입니다. 앞부분이 그대로라 캐시는 유지되고, 모델은 방금 찾은 도구를 바로 읽고 실행할 수 있습니다.
한 번 찾은 도구는 같은 세션에서 다시 쓸 수 있게 남겨두고, 컨텍스트를 접을 때 함께 정리합니다. 사용자 눈에는 그저 에이전트가 필요한 기능을 잘 고른 것처럼 보이지만, 내부에서는 검색과 스키마 전달이 프롬프트의 서로 다른 자리에서 일어나고 있습니다.
15만 토큰이 1.9만 토큰이 됐습니다
Sonnet으로 돌린 합성 벤치마크에서 결과는 분명했습니다.
전체 로드 vs 검색 기반
입력 토큰 / 턴
도구 선택 정확도
토큰
−87%
컨텍스트 헤드룸
정확도
+0.15
0.83 → 0.98
장기 세션 비용
~−40%
캐시 유지 상태
도구 선택 정확도는 0.83에서 0.98로 올랐습니다. 턴당 입력 토큰은 약 15만에서 1.9만으로 87% 줄었고, 프롬프트 캐시를 유지한 장기 세션의 순비용은 약 40% 낮아졌습니다.
같은 평가셋으로 Anthropic의 네이티브 tool search와도 비교했습니다. 전체 정확도에서도, 일본어 요청만 떼어 본 평가에서도 우리 시스템이 앞섰습니다. 다국어 사용자가 실제로 말하는 방식대로 검색 코퍼스를 만든 것이 차이를 만들었다고 보고 있습니다.
다만 프로덕션 리플레이에서는 모델마다 정확도 개선 폭이 달랐습니다. 도구 호출이 강한 모델은 큰 이득을 봤지만, 일부 모델은 "더 싸고 빨라졌지만 정확도는 비슷한" 수준이었습니다. 그래서 우리는 이 시스템을 무조건 더 똑똑하게 만드는 마법이 아니라, 정확도를 지키면서 컨텍스트 여유를 되찾는 설계로 봅니다.
검색 성공이 실행 성공은 아니었습니다
프로덕션 초기, 택시 요청을 받은 에이전트가 같은 검색만 반복하다 포기한 사건이 두 번 있었습니다. 첫 번째 사건의 원인은 설정이 끝나지 않은 택시 도구를 검색 대상에서 아예 숨겨둔 것이었습니다. 모델은 그런 도구가 있는지도 모른 채 같은 검색을 17번 반복했습니다.
두 번째 사건에서는 검색으로 찾은 도구를 다음 모델 스텝이 아니라 다음 사용자 턴에야 호출할 수 있었습니다. 이번에는 맞는 도구를 찾고도 쓸 수가 없어서 검색만 21번 반복했습니다.
원인은 달랐지만 사용자에게 보인 결과는 같았습니다. 아무 일도 일어나지 않았습니다.
두 사건 뒤로 검색 품질과 실행 가능성을 한 묶음으로 보기 시작했습니다.
- 설정이 끝나지 않은 도구도 찾을 수 있어야 합니다
- 찾은 도구는 같은 턴의 다음 모델 스텝에서 바로 호출할 수 있어야 합니다
- 입력 스키마는 모델이 추측하지 않아도 될 만큼 명확해야 합니다
마지막 조건은 예상보다 중요했습니다. 모든 도구를 미리 넣던 시절에는 런타임 보정에 가려지던 스키마 문제가, 동적 검색에서는 곧바로 실패로 드러났습니다. 도구 작성 규칙과 자동 검사를 함께 강화한 이유입니다. 검색 시스템이 도구 카탈로그의 품질까지 비추는 센서가 된 셈입니다.
도구를 하나 더 붙여도 무거워지지 않게
vooy가 더 많은 일상을 맡으려면 앱과 커넥터도 계속 늘어야 합니다. 예전 구조에서는 새 도구가 하나 늘 때마다 모든 대화가 조금씩 무거워졌습니다. 기능을 늘린 대가를, 그 기능을 쓰지도 않는 사용자까지 매 턴 치러야 했습니다.
검색 기반 구조에서는 카탈로그가 100개에서 1,000개로 늘어도, 모델이 매번 읽는 도구는 지금 필요한 몇 개뿐입니다. 새 기능을 추가하는 일이 "모델이 헷갈릴 선택지를 하나 더 주는 일"에서 "필요할 때 찾을 수 있는 항목을 하나 더 만드는 일"로 바뀝니다.
깊은 멀티턴 대화, 도메인 전환, 세션 단위 최적화는 아직 남은 숙제입니다. 그래도 한 가지는 분명해졌습니다. 도구가 많은 에이전트와 똑똑한 에이전트는 더 이상 반대말이 아닙니다.
우리는 모델에게 도구 200개를 한꺼번에 읽히는 대신, 필요한 순간에 찾아 건네는 쪽을 택했습니다. vooy가 할 수 있는 일이 늘어날수록, 모델이 매 턴 알아야 하는 것은 오히려 줄어듭니다.
vooy
도구 200개도 가볍게 다루는 에이전트를 써보세요
택시를 부르고, 맛집을 찾고, 캘린더와 메일을 넘나드는 vooy를 가장 먼저 만나보세요.