[컨텍스트] 3. 도구 : 좋은 도구 정의 작성법

컨텍스트 엔지니어링 3편 - 좋은 도구 정의 작성법

작성자

카테고리:

* 이 게시물은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

(컨텍스트 엔지니어링 ③) 도구

에이전트를 만들어 보면 알게 된다. 성능의 절반은 모델이 아니라 도구 정의에서 갈린다. 모델은 도구 이름과 설명만 보고 “이걸 지금 써야 하나, 인자는 뭘 넣나”를 판단한다. 설명이 부실하면 아무리 좋은 모델도 헛발질한다.

좋은 도구 설명의 첫째 조건은 “언제 쓰는지”까지 적는 것이다. “날씨를 조회한다”에서 끝내지 말고 “사용자가 특정 도시의 현재 날씨나 예보를 물을 때 호출한다”까지 써준다. 무엇을 하는지만 있으면 모델이 타이밍을 못 잡는다.

{
  "name": "search_orders",
  "description": "주문 번호나 고객명으로 주문을 검색한다. 사용자가 주문 상태·배송·환불을 물을 때 호출한다. 일반 상품 문의에는 쓰지 않는다.",
  "input_schema": {
    "type": "object",
    "properties": {
      "query": {"type": "string", "description": "주문 번호 또는 고객명"}
    },
    "required": ["query"]
  }
}

둘째, 이름을 구체적으로 짓는다. search보다 search_orders가, get보다 get_order_status가 낫다. 이름만으로 용도가 드러나면 모델이 덜 헷갈린다.

셋째, 도구를 너무 많이 늘어놓지 않는다. 도구가 수십 개가 되면 모델이 매번 그 목록을 다 훑느라 컨텍스트를 낭비하고 선택도 흐려진다. 이럴 땐 필요할 때만 관련 도구를 찾아오는 “도구 검색” 방식을 쓰거나, 애초에 도구 수를 꼭 필요한 만큼으로 줄인다.

나쁜 정의가 만드는 오호출 — 전형적인 패턴 셋

  • 설명이 ‘무엇’뿐(“날씨를 조회한다”) → 모델이 날씨와 무관한 질문에도 호출하거나, 물어봐야 할 때 안 부른다.
  • 이름이 포괄적(search) → 주문 검색이 필요한 자리에서 문서 검색 도구를 집는다.
  • 스키마에 형식이 없음(date: string) → “어제”, “8월 3일”, “2026-08-03″이 뒤섞여 들어온다.

셋 다 모델 잘못처럼 보이지만 정의 잘못이다 — 모델은 정의에 적힌 만큼만 안다. 그래서 완결형 정의는 이렇게 생겼다. 아래 한 벌에 이 편의 원칙이 전부 들어 있다.

{
  "name": "search_orders",                      // 구체적 이름
  "description": "주문을 검색한다. 사용자가 주문 내역·배송 상태를
    물을 때 호출한다. 재고나 상품 정보에는 쓰지 않는다(search_products 사용).",
  "input_schema": {
    "type": "object",
    "properties": {
      "customer_id": { "type": "string", "description": "고객 ID, 예: 'C-1024'" },
      "status": { "type": "string", "enum": ["paid","shipped","delivered"],
                  "description": "이 상태의 주문만 필터(선택)" },
      "since": { "type": "string", "description": "이 날짜 이후만, YYYY-MM-DD" }
    },
    "required": ["customer_id"]
  }
}
// 에러 반환도 정의의 일부처럼: {"error":"고객을 찾을 수 없음. ID 형식은 C-숫자."}

‘언제 쓰는지’에 더해 ‘언제 쓰지 않는지’(재고엔 다른 도구)까지 적은 것, enum과 형식 예시, 그리고 에러 문구까지 — 이 네 가지가 갖춰진 정의는 모델 교체 없이도 오호출을 크게 줄인다.

입력 스키마도 설명이다

도구 이름과 설명만큼 중요한 게 입력 스키마다. 인자 이름과 타입만 던지지 말고, 각 인자가 무엇을 기대하는지 설명을 붙인다. 애매하면 모델이 엉뚱한 값을 채운다. 필수/선택, 형식(날짜는 YYYY-MM-DD), 허용값(enum)을 스키마에 박아두면 모델이 헷갈리지 않고 검증까지 된다.

{
  "name": "get_weather",
  "description": "사용자가 특정 도시의 현재 날씨나 예보를 물을 때 호출한다.",
  "input_schema": {
    "type": "object",
    "properties": {
      "city": { "type": "string", "description": "영문 도시명, 예: 'Seoul'" },
      "unit": { "type": "string", "enum": ["c", "f"], "description": "온도 단위" }
    },
    "required": ["city"]
  }
}

결과와 에러도 모델이 읽는다

도구가 돌려주는 것도 결국 컨텍스트다. 성공이면 모델이 쓰기 좋은 형태 — 핵심 필드만 간결하게 — 로 주고, 실패면 왜 실패했고 어떻게 고칠지를 담아 돌려준다. 그냥 error만 던지면 모델이 다음 수를 못 둔다. “도시를 못 찾음. 영문 도시명인지 확인하라” 같은 메시지가 모델을 스스로 바로잡게 만든다.

하나의 도구, 하나의 일

도구는 잘게, 명확하게 나눈다. “데이터를 관리한다” 같은 만능 도구보다 “글 목록 조회”·”글 생성”처럼 한 가지 일만 하는 도구가 모델이 고르기 쉽다. 다만 도구가 수십 개로 늘면 선택이 흐트러진다. 자주 함께 쓰는 건 묶고, 안 쓰는 건 빼서 적정 수를 유지하는 게 정확도에 좋다.

파괴적 도구엔 안전장치를

삭제·전송·결제처럼 되돌리기 어려운 도구는 설명에 위험을 분명히 적고, 확인 단계나 읽기 전용 기본값으로 감싼다. 모델이 곧장 실행하기보다 “정말 지울까요?”를 먼저 묻도록 설계하는 게 안전하다. 이 관점은 MCP 보안(5편)에서 다룬 최소 권한·확인 단계와 그대로 이어진다.

정리하면 좋은 도구 정의는 ‘언제 쓰는지를 적은 설명 + 명확한 입력 스키마 + 모델이 읽을 수 있는 결과·에러’다. 모델을 바꾸기 전에 도구 정의부터 다듬는 게 성능을 올리는 가장 싼 방법일 때가 많다. 이어서 긴 대화의 기억 관리를 본다.

마지막으로, 만능 bash 도구 하나로 다 처리할지 전용 도구로 쪼갤지의 문제. 폭넓게 뭐든 하려면 bash가 편하지만, 위험한 작업에 확인을 걸거나, 결과를 예쁘게 보여주거나, 안전한 작업을 병렬로 돌리려면 전용 도구가 유리하다. 넓게 시작하되, 통제가 필요한 작업부터 전용 도구로 승격시키는 게 정석이다.

자주 묻는 질문 (FAQ)

도구 정의는 왜 중요한가요?

모델은 도구의 이름·설명·입력 스키마만 보고 무엇을 부를지 정합니다. 모호하면 잘못 부르므로 명확한 이름과 설명이 정확도를 좌우합니다.

도구는 잘게 나누는 게 좋나요, 크게 묶는 게 좋나요?

하나의 도구가 한 가지 일을 명확히 하도록 나누는 편이 낫습니다. 다만 너무 많으면 선택이 흐트러지니 적정 수를 유지하세요.

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다