엔지니어링 노트도구와 실행

도구 185개 정의는 9만 9천 토큰, 이름만 싣고 고른 도구만 펼친다

에이전트에게 도구를 주려면 도구마다 이름, 설명, 인자 스키마로 된 정의를 요청에 실어야 합니다. 고객사가 MCP 서버를 연결할수록 이 정의가 불어납니다. 실운영 환경 한 곳에 연결된 외부 MCP 도구 185개의 정의를 설명까지 모두 펼치면, Anthropic 토큰 계산 API 기준으로 99,232토큰입니다. 사용자가 「어제 매출 얼마야」라고만 물어도 그 정의가 모델 호출마다 함께 나갑니다.

그래서 OwlAI는 도구 상당수의 정의를 처음부터 펼치지 않고, 시스템 프롬프트에 도구 이름과 한 줄 요약만 담은 이름 목록을 싣습니다. 모델이 목록에서 필요한 이름을 골라 도구를 켜는 도구에 넘기면, 그 도구의 정의만 펼쳐집니다. 업계에서는 이런 방식을 지연 로딩(온디맨드 로딩)이라고 부릅니다. 이 글은 모델이 제대로 고를 수 있게 이름 목록에 무엇을 실었는지, 그리고 도구를 켜도 요청 앞부분이 바뀌지 않게 어떻게 선언하는지를 설명합니다.

다 싣거나, 검색으로 골라 주거나, 개수를 자르는 방식

가장 단순한 방식은 모든 정의를 처음부터 싣는 것입니다. 켜는 왕복이 없고, 모델은 언제든 어떤 도구든 부를 수 있습니다. 대가는 질문과 상관없는 정의까지 매번 보낸다는 것입니다. 정의 하나도 가볍지 않습니다. 파워포인트를 만드는 도구의 정의는 약 10KB, 3,500토큰쯤 됩니다. 오피스 도구는 인자마다 거절 조건과 쓰는 법을 적어 두기 때문에 짧게 줄일 수 없습니다.

둘째는 도구 검색입니다. Anthropic의 도구 검색 도구는 모델이 정규식이나 자연어로 도구 카탈로그를 검색하면 맞는 도구를 기본 5개까지 펼쳐 줍니다. 도구가 수천 개로 늘어도 쓸 수 있다는 장점이 있습니다. 하지만 모델은 검색어에 걸린 도구만 보게 됩니다. 설명에 모델이 떠올린 낱말이 없는 도구는 후보에 오르지 못합니다. OwlAI는 어떤 도구를 쓸지를 모델이 판단하게 둡니다. 그래서 검색 대신 모델이 전체 이름을 보고 고르는 쪽을 택했습니다.

셋째는 목록을 자주 쓰는 도구 N개로 자르는 방식입니다. 목록 길이는 묶이지만, 목록에서 빠진 도구는 부를 방법이 없습니다. 그런 도구가 필요해지면 모델은 비슷한 이름을 지어내 부릅니다.

DB 조회 도구는 처음부터 싣고, 파일·외부 도구는 이름만 알린다

처음부터 정의를 펼치는 도구는 정해져 있습니다.

  • DB 조회 도구. 이 제품의 주된 쓰임이라 켜는 왕복 없이 바로 부르게 둡니다.
  • 사용자에게 되묻기, 위젯, 일정, 도구 켜기 같은 시스템 도구.
  • 웹 검색처럼 정의가 작아 켜는 왕복이 오히려 비싼 도구 몇 개.

파일·오피스·샌드박스 도구와 외부 MCP 도구는 이름 목록으로만 알립니다. 실운영 환경 한 곳에서 도구가 가장 많았던 턴은 처음부터 펼친 도구가 83개, 이름 목록으로만 알린 도구가 212개였습니다.

사용자가 @서버이름으로 커넥터를 지목한 턴에는 그 서버의 도구를 처음부터 펼칩니다. 사용자가 이미 커넥터를 골랐으니 켜는 왕복을 한 번 더 할 이유가 없습니다. 이것은 질문의 낱말을 보고 도구를 미리 짐작하는 것과 다릅니다. 사람이 고른 것을 그대로 따를 뿐입니다. 서버 하나당 60개까지 펼치고, 넘치는 도구는 이름 목록에 남아 여전히 켤 수 있으며, 잘린 서버와 개수는 로그로 남깁니다. 지목해서 펼친 도구는 다음 턴에도 켜진 채로 남습니다.

이름 목록에는 이름을 빠짐없이 싣고, 항목 하나의 길이를 묶는다

사용자가 쓸 수 있는 도구 가운데 이름 목록 대상인 도구는 전부 이름이 오릅니다. 대신 항목 하나가 차지하는 길이를 묶습니다.

  • 요약은 300자에서 자릅니다.
  • 필수 인자 이름을 최대 여섯 개까지 덧붙입니다.
  • 외부 MCP 도구는 서버별로 묶고, 서버가 스스로를 소개한 설명은 묶음마다 한 번만 싣습니다.

요약 길이는 모델이 고르는 품질을 바로 바꿉니다. 상한이 100자라면 이름 목록 대상 55개 가운데 33개가 잘립니다. 잘려 나가는 뒷부분이 「먼저 가이드를 읽어라」, 「이 인자는 선택이다」 같은 쓰는 법입니다. 그 상태로 같은 오피스 문서 요청을 세 번 보냈을 때 모델은 도구를 짐작으로 불러 세 번 모두 산출물을 만들지 못했습니다. 같은 55개 기준으로 상한을 300자로 두면 이름 목록이 약 586토큰 늘어납니다. 파워포인트 도구 정의 하나보다 작고, 잘못 부른 도구를 고쳐 다시 부르는 왕복보다 쌉니다.

필수 인자 이름은 도구 사이의 순서를 알려 줍니다. 어떤 커넥터의 검색 도구는 사이트 ID를 필수로 받고, 그 ID는 다른 도구가 돌려줍니다. 이름 목록에 요약만 있으면 모델은 검색 도구만 켜고 사이트 ID를 지어내 넣습니다. 필수 인자 이름이 보이면 모델은 ID를 돌려주는 도구를 함께 켭니다. 이름 몇 개라 항목마다 몇 토큰이면 됩니다.

고르는 것은 모델이고, 켜는 도구는 이름이 정확히 맞을 때만 켠다

모델은 이름 목록에서 고른 이름을 도구를 켜는 도구에 넘깁니다. 한 번에 여러 이름을 넘길 수 있고, 일을 하는 도구와 그 도구의 인자를 만들어 주는 도구를 한 번에 켜라고 목록 머리에 적어 둡니다. 켠 도구는 같은 턴의 다음 단계부터 부를 수 있고, 대화가 끝날 때까지 켜진 채로 남습니다. 다음 턴에는 저장해 둔 켠 도구 목록에서 다시 켭니다.

켜는 도구는 이름이 글자 그대로 맞는 도구만 켭니다. 모르는 이름, 권한이 없는 이름, 이미 처음부터 펼친 이름은 실패 목록으로 돌려줍니다. 모델이 도구 이름 대신 커넥터 이름을 넘기는 일이 잦은데, 이때는 그 커넥터에 속한 도구 이름들을 결과에 실어 줍니다. 이 경우에도 대소문자만 무시한 글자 일치로만 찾습니다. 비슷한 이름을 짐작해 틀린 후보를 주면 모델은 그 이름을 그대로 켜기 때문입니다. 후보를 받은 뒤 무엇을 켤지는 다시 모델이 정합니다.

켜지 않은 도구는 실행되지 않습니다. 이름 목록에 있는 도구를 모델이 켜지 않고 바로 부르면, 실행부는 「먼저 켜라」고 답합니다. 이 답은 「없는 도구다」, 「커넥터가 연결되지 않았다」와 다른 답입니다. 셋을 같은 답으로 돌려주면 모델은 쓸 수 있는 도구를 없는 도구로 읽고 포기합니다.

이름 목록은 권한을 넓히지 않습니다. 도구를 켤 때 실행부는 그 사용자의 권한, 도구 정책, 클라이언트 능력 검사를 다시 돌리고, 통과한 도구만 켭니다. 다음 턴에 켠 도구를 다시 켤 때도 같은 검사를 거쳐, 그사이 권한이 사라진 도구는 빠집니다. 도구 정책이 어떤 도구를 이름 목록에서 아예 빼는지는 「커넥터 도구의 위험은 고객사가 안다, 도구 정책은 설치마다 둔다」에서, 도구를 켤 때 작업 가이드가 함께 실리는 방식은 「양식 복사·가이드 읽기는 도구가 하고, 모델에는 인자로 묻는다」에서 다룹니다.

도구를 켜도 요청 앞부분은 그대로 둔다

도구 정의와 시스템 프롬프트는 요청 앞부분에 있어서, 여기가 한 글자라도 바뀌면 그 뒤의 대화 맥락을 캐시에서 읽지 못하고 다시 계산합니다(「시스템 프롬프트 한 줄에 캐시 읽기가 0이 된다, 바뀌는 블록은 뒤로 보낸다」). 도구를 켤 때마다 앞부분이 바뀐다면 토큰을 아끼려던 장치가 오히려 비용을 늘립니다.

그래서 이름 목록은 대화 내내 같은 바이트로 둡니다. 이미 켠 도구도 목록에 남깁니다. 켤 때마다 항목을 하나씩 뺀다면 시스템 프롬프트가 매번 바뀌기 때문입니다. 켠 도구가 목록에 남아 모델이 같은 도구를 또 켜려 할 수 있어서, 목록 머리에 「이미 부를 수 있는 도구는 켜지 말고 바로 불러라」라고 적어 둡니다. 이름 목록이 바뀌는 것은 대화 밖의 일이 생길 때뿐입니다. 조직의 도구 목록에 도구가 들어오거나 빠질 때, 관리자가 권한을 바꿀 때입니다.

도구 정의를 선언하는 방법은 모델에 따라 둘로 나뉩니다.

숨김 선언을 받는 모델 그 밖의 모델
쓰는 모델 Claude Fable 5.1, Opus 5.5, Sonnet 5.5, Haiku 5.5 그 밖의 Anthropic 모델, OpenAI API, vLLM으로 서빙하는 모델
처음 선언 이름 목록의 도구까지 전량 선언하고, 켜지 않은 도구에 defer_loading 표시 처음부터 펼치는 도구만 선언
도구를 켤 때 도구 배열은 그대로, 대화 맥락 끝에 「이 도구를 켠다」는 시스템 메시지를 덧붙임 켠 도구의 정의를 도구 배열에 더함
바뀌는 자리 대화 맥락 맨 끝 도구 배열 안 그 도구의 자리부터 뒤

defer_loading 표시를 단 정의는 요청에는 실리지만 모델의 컨텍스트에는 접힌 채로 들어갑니다. 앞의 185개 도구를 전부 펼치면 99,232토큰이고, 하나만 펼치고 나머지를 접으면 1,352토큰입니다(2026년 9월 10일, Anthropic 토큰 계산 API). 켜는 일은 도구 배열이 아니라 대화 맥락 끝에서 일어나므로, 도구 배열은 대화의 첫 요청부터 마지막 요청까지 같습니다.

도구를 켤 때 두 선언 방식에서 요청의 어느 자리가 바뀌는가
그림 1. 도구를 켤 때 두 선언 방식에서 요청의 어느 자리가 바뀌는가

숨김 선언을 받지 않는 모델에는 처음부터 펼치는 도구와 켠 도구의 정의만 보냅니다. 도구를 켜면 그 정의가 도구 배열에 더해지고, 이 배열은 대화 안에서 줄어들지 않습니다. 보낼 때는 배열을 이름순으로 정렬해서, 어떤 순서로 켰든 같은 도구 집합이면 같은 바이트가 나갑니다(「에이전트 루프는 하나만 두고, 기능별 동작은 훅으로 끼운다」). 도구를 켠 라운드에서는 그 도구가 들어간 자리 뒤를 다시 계산하지만, 그다음 라운드부터는 다시 같은 앞부분입니다.

설계에서 고른 것

이름 목록은 연결한 도구 수에 비례해 자랍니다. 앞의 설치에서 외부 MCP 도구 요약에 300자 상한을 적용하자 이름 목록이 29,771자에서 36,814자로 늘었습니다. 도구가 수천 개라면 검색이 맞는 선택입니다. 고객사가 연결하는 도구가 수백 개 안팎인 동안에는 모델이 전체 이름을 보고 고르는 쪽을 택했고, 항목당 길이 상한이 그 증가를 도구 수에 비례하는 만큼으로 묶습니다.

300자를 넘는 요약은 잘립니다. 내장 도구는 회귀 테스트가 요약 길이를 지켜서, 누가 요약을 늘리면 테스트가 실패하고 상한을 함께 검토하게 됩니다. 외부 MCP 서버가 보낸 설명은 같은 상한으로 잘라 싣습니다.

켜는 데는 왕복 하나가 듭니다. 문서를 만드는 턴은 켜고, 부르는 두 라운드를 거칩니다. 모든 정의를 처음부터 싣는 비용과 맞바꾼 것이고, 사용자가 @서버로 지목하면 이 왕복 없이 바로 부릅니다.

접은 정의도 공짜는 아닙니다. 접힌 정의의 크기는 설명 길이에 따라 자랍니다. 185개 도구의 설명을 200자로 잘라 두면 하나만 펼쳤을 때 764토큰이고, 설명 전체를 두면 1,352토큰입니다. 그래도 설명은 전체를 둡니다. 도구를 켜면 선언해 둔 정의가 그대로 펼쳐지므로, 줄인 설명이 곧 켠 뒤 모델이 읽는 설명이 되기 때문입니다.

숨김 선언을 받지 않는 모델에서는 도구를 켠 라운드의 캐시 손실을 감수합니다. 숨김 선언과 켜는 메시지는 모델이 받아 줘야 쓸 수 있고, 받지 않는 모델에 보내면 요청이 거부됩니다. 숨기기만 되고 켜는 메시지가 거부되면 그 도구는 다시 꺼낼 방법이 없습니다. 그래서 두 가지를 모두 실제 호출로 확인한 모델에만 씁니다.

플레이북 편집 화면의 대화는 도구를 전부 처음부터 펼칩니다. 편집에 쓰는 도구가 적고, 편집 모드에서는 도구를 켜는 도구 자체를 주지 않기 때문입니다.

연결한 MCP 서버가 늘면서 도구 정의가 질문보다 길어진 에이전트라면, 이름을 빠짐없이 싣는 목록부터 만들어 볼 만합니다. 그다음 확인할 것은 두 가지입니다. 도구를 켜도 목록과 도구 배열의 바이트가 그대로인지, 그리고 요약이 잘리면서 쓰는 법이 사라지지 않았는지입니다.