LLM이 도구를 잘 고르게 하는 description 쓰기

MCP·에이전트 만들기 2026-07-26 13:22:00 mcptoolsllm도구설명zod

글 목록 →

MCP 서버에 도구를 여러 개 붙여두면, 그중 무엇을 언제 부를지는 LLM이 스스로 고른다. 이때 LLM이 보는 것은 구현 코드가 아니다. 도구의 이름·설명(description)·입력 스키마, 이 세 가지 텍스트뿐이다. 즉 description은 사람용 주석이 아니라 모델이 읽는 실행 인터페이스다. 여기가 부실하면 도구가 아무리 잘 작동해도 엉뚱한 때에 불리거나 아예 안 불린다.

나쁜 설명과 좋은 설명

똑같은 도구라도 설명 한 줄이 선택률을 가른다.

// 약함: 무엇을 하는지만 있고, 언제 쓰는지가 없다
{
  name: "search_posts",
  description: "글을 검색한다",
}

// 나음: 용도·시점·경계를 담았다
{
  name: "search_posts",
  description:
    "블로그에 이미 발행된 글을 제목·태그로 검색한다. " +
    "새 글을 쓰기 전 중복 주제를 피하려 할 때 먼저 부른다. " +
    "글을 생성하지는 않는다 — 발행은 publish_post를 쓴다.",
}

두 번째가 담은 것은 세 가지다. 무엇을(발행된 글을 검색), 언제(새 글 쓰기 전 중복 확인), 그리고 경계(생성은 이 도구가 아님). 특히 경계 문장이 중요하다. 비슷한 도구가 여럿일수록 LLM은 “이건 저 도구 말고 이 도구”라는 구분선을 설명에서 찾는다. 이름만으로는 search_postspublish_post가 어떻게 나뉘는지 모델이 확신하기 어렵다.

파라미터에도 설명을 단다

입력 스키마의 각 필드도 모델이 채워 보내는 값이다. 필드 이름만으로 뜻이 모호하면 설명을 붙인다. zod를 쓴다면 .describe()로 단다.

import { z } from "zod";

const schema = z.object({
  query: z.string().describe("검색어. 제목·태그에서 부분 일치로 찾는다"),
  section: z
    .enum(["mcp", "security"])
    .describe("특정 섹션으로 한정할 때만. 생략하면 전체 검색"),
  limit: z.number().int().default(10).describe("최대 결과 개수 (기본 10)"),
});

기본값·단위·“생략하면 어떻게 되는지”를 적어두면 모델이 불필요한 인자를 지어내는 일이 준다. 반대로 section: "특정 섹션"처럼 뭉뚱그리면, 모델은 없는 섹션 이름을 만들어 넣기도 한다.

실무 체크리스트

  • 첫 문장에 무엇을 하는지 한 줄로 적는다.
  • 언제 쓰는지, 그리고 가능하면 언제 안 쓰는지를 적는다.
  • 비슷한 도구와의 경계를 문장으로 긋는다.
  • 부작용·전제(파일을 만든다, 되돌릴 수 없다 등)를 숨기지 않는다.
  • 모든 파라미터에 설명을 달고, 기본값·단위·허용 값을 밝힌다.

도구 설명은 결국 모델에게 건네는 사용 설명서다. 사람이 처음 보는 API 문서를 읽고 손을 뻗듯, 모델도 그 문장만 읽고 도구를 고른다. 구현 코드를 다듬는 만큼 설명문도 다듬을 값어치가 있다 — 여기서는 잘 쓴 한 문장이 곧 잘 동작하는 도구다.

댓글

이 글에 대한 의견은 아래 댓글로 남겨주세요 (GitHub 계정 필요). 로그인 없이 남기고 싶다면