
AI 에이전트가 '잘못된 도구'를 호출하는 이유는 설명문 때문이다 — 10분 만에 고치는 도구 기술 계약 입문
요약
AI 에이전트가 잘못된 도구를 호출하는 원인이 도구 설명문의 부실함에 있음을 지적하며, 이를 해결하기 위한 '도구 기술 계약(Tool Description Contract)' 개념을 소개합니다. 설명문을 단순 문서가 아닌 모델과의 명시적 계약으로 작성하는 구체적인 방법론을 다룹니다.
핵심 포인트
- 도구 설명문은 사람이 아닌 모델의 판단을 위한 '계약'이어야 함
- 언제 호출할지/하지 않을지, 입력 의미, 부작용을 명시해야 함
- 설명문의 정보량이 에이전트의 도구 선택 신뢰성을 결정함
- 단순한 이름 재진술을 넘어 상세한 제약 조건을 포함해야 함
이 기사는 '보안 경계'가 아니라 '선택의 신뢰성'에 관한 이야기입니다
AI 에이전트에게 자작 도구(함수)를 부여하면, 대개 처음에 이런 사고가 발생합니다.
- 사용자는 "오늘의 일정을 알려줘"라고 물었을 뿐인데, 에이전트가
deleteEvent를 호출함 - "메모를 검색해줘"라고 부탁했더니, 검색하지 않고 갑자기
createMemo로 빈 메모를 생성함 - 인수에 넣어야 할 값을 착각하여, 전혀 상관없는 레코드를 업데이트함
이것은 "모델이 똑똑하지 않아서"가 아닙니다. 도구의 설명문(description)이 언제 호출할지, 언제 호출하지 않을지를 약속하고 있지 않기 때문입니다.
대상 독자는 function calling / MCP / 자작 에이전트에서 2개 이상의 도구를 설계하는 사람입니다. 반대로, 도구가 1개뿐인 사람이나 프롬프트 전체를 작성하는 방법만 알고 싶은 사람에게는 과잉 정보이므로, 조용히 창을 닫아주셔도 괜찮습니다.
결론: 도구의 설명문은 '문서'가 아니라 '계약'이다
먼저 용어를 하나만 정의하겠습니다. 이 기사의 뼈대입니다.
도구 기술 계약 (Tool Description Contract)
도구의 설명문에서 ①언제 호출할지 (when-to-use) ②언제 호출하지 않을지 (when-not-to-use) ③각 입력의 의미와 형식 ④부작용 (읽기 전용인지, 쓰기/파괴가 있는지)의 4가지를 LLM에 대해 명시적으로 약속하는 것.
포인트는 설명문을 "사람이 읽는 API 문서"가 아니라 "모델이 선택 판단에 사용하는 계약문"으로 다시 쓰는 것입니다. 모델은 도구 이름과 설명문, 그리고 인자 스키마(argument schema)만을 보고 호출 여부를 매번 판단합니다. 판단 재료가 빈약하면 판단이 흔들리는 것은 당연합니다.
1차 정보에서도 도구의 설명은 가능한 한 상세하게, 언제 사용하는지/사용하지 않는지까지 쓰는 것이 권장됩니다 (후술할 "출처" 참조).
10분 만에 가능한 첫 성공: 설명문 하나를 바꿔서 선택을 변경하기
이론보다 먼저, 직접 손을 움직여 "설명문으로 선택이 바뀌는" 경험을 해봅시다. 여기서는 LLM을 호출하지 않고도 재현할 수 있도록, 모델이 설명문을 어떻게 읽는지를 단순화된 선택 로직으로 실연합니다 (실제 LLM으로 교체하는 방법은 마지막에 제시합니다).
Step 1: 흔히 발생하는 "약한" 도구 정의
// tools.ts — 나쁜 예: 설명이 이름의 재진술에 불과함
export type Tool = {
name: string;
...
이 상태에서는 "어제 MTG 메모 있어?"라는 검색 의도를 가진 질문에도 모델은 create_note를 선택할 가능성이 있습니다. 설명문이 둘 다 "메모를 ~한다"라고만 되어 있어 구분이 불가능하기 때문입니다.
Step 2: 계약 형식으로 다시 쓰기
동일한 도구를 도구 기술 계약의 4가지 요소 (when-to-use / when-not-to-use / 입력의 의미 / 부작용)로 다시 작성합니다.
// tools.ts — 좋은 예: 설명문을 "계약"으로 만듦
export const contractTools: Tool[] = [
{
...
Step 3: "선택이 바뀐다"는 것을 최소 코드로 확인하기
설명문에 키워드가 들어감으로써 선택이 바뀌는 것을 간이 스코어러(scorer)로 재현합니다. 이는 실제 LLM의 대용이지만, "설명문의 정보량이 선택을 좌우한다"는 구조는 동일합니다.
// select.ts — 설명문의 일치도로 도구를 선택하는 최소 스코어러
import { Tool } from "./tools";
function scoreTool(tool: Tool, utterance: string): number {
...
// run.ts — 약한 정의와 계약 정의의 선택을 비교
import { weakTools, contractTools } from "./tools";
import { pickTool } from "./select";
...
$ npx tsx run.ts
weak : (판단 불능·확인 요청을 반환)
contract: search_notes
여기까지가 10분 만의 첫 성공입니다. "메모를 검색한다"를 "when-to-use / when-not-to-use / 부작용"이 포함된 내용으로 바꾼 것만으로, 모호했던 선택이 search_notes로 확정되었습니다. 여러분의 도구 1개로 먼저 똑같은 시도를 해보시기 바랍니다.
왜 설명문으로 선택이 바뀌는가 (원인의 분해)
에이전트가 도구를 하나 선택할 때, 모델이 보는 것은 기본적으로 다음 세 가지뿐입니다.
- 도구 이름 (Tool name)
- 도구 설명문 (description)
- 인자 스키마 (Argument schema) 및 그 설명
이 중 이름과 스키마는 "무엇을 할 수 있는지"만을 말할 수 있습니다. "지금 이 상황에서 호출해야 하는가"를 말할 수 있는 것은 설명문뿐입니다. 즉, 선택 실패의 대부분은 "호출할 수 있는가"가 아니라 "호출해야 하는가"에 대한 정보 부족으로 인해 발생합니다.
계약의 4가지 요소는 이 "호출해야 하는가"를 채우기 위해 설계되었습니다.
| 계약 요소 | 방지할 수 있는 전형적인 사고 |
|---|---|
| when-to-use | 호출해야 할 때 다른 도구를 선택함 / 아무것도 호출하지 않음 |
| ... |
구현: 계약을 "템플릿 함수"로 강제하기
설명문이 자유 기술 방식이면 작성자에 따라 누락이 발생합니다. **계약의 4가지 요소를 반드시 채우는 헬퍼 (Helper)**를 준비하여, 구조적으로 누락을 방지합니다.
// contract.ts — 설명문을 계약 포맷으로 정규화함
type Contract = {
summary: string; // 1줄 요약
...
이렇게 해두면, sideEffect를 필수 열거형 (Enum)으로 정의하고 있으므로, "부작용(side effect)을 쓰는 것을 잊은 도구"는 애초에 만들 수 없습니다. 리뷰 시에도 "계약의 4가지 요소가 채워져 있는가"만 확인하면 됩니다.
진짜 LLM (function calling)으로 교체하려면
간이 스코어러 (Scorer) 대신, contractTools의 description과 parameters를 사용 중인 SDK의 도구 정의 (예: tools 배열)에 그대로 전달하기만 하면 됩니다. 모델이 반환하는 tool_call의 name을 보면 선택이 바뀌었는지 실측할 수 있습니다. 설명문을 바꾸기 전후로, 동일한 발화 세트에 대한 선택 일치율을 비교하면 효과를 수치로 확인할 수 있습니다.
계약으로도 보장할 수 없다 — 반증과 한계
이 부분이 가장 중요한 장입니다. 도구 기술 계약은 "선택 확률을 높일" 뿐, 올바른 선택을 보장하지는 않습니다. 다음과 같은 경우에는 효과가 미미하거나 다른 대책이 필요합니다.
- 모델의 비결정성 (Non-determinism): 동일한 입력이라도 실행할 때마다 선택이 바뀔 수 있습니다. 계약을 두텁게 해도 0이 되지는 않습니다.
- 도구가 너무 많음: 20개, 30개로 늘어나면 설명문이 좋아도 오인하는 경우가 늘어납니다. 도구의 수를 줄이거나 그룹화하는 설계가 우선입니다.
- 설명문이 너무 김: 계약을 너무 많이 담아 장황해지면, 오히려 노이즈가 되어 정확도가 떨어질 수 있습니다. 4가지 요소를 간결하게 작성하는 것이 원칙입니다.
- 도구가 1개뿐임: 애초에 선택의 여지가 없으므로, 이 글의 효과는 거의 제로입니다.
즉, "계약을 썼으니까 안심"하는 것이 아니라, 쓴 후에 측정해야 합니다.
// eval.ts — 계약의 효과를 평가에서 지속적으로 측정하는 뼈대
type Case = { utterance: string; expected: string };
export function accuracy(
...
기대하는 도구 이름을 나열한 작은 케이스 집합을 만들고, 설명문을 바꿀 때마다 이 일치율을 돌립니다. "좋아진 것 같다"를 "일치율이 0.6에서 0.9가 되었다"로 바꾸는 것이, 계약 설계를 운영 자산으로 만드는 분기점입니다.
출처 (1차 정보와의 대조)
- Anthropic "Tool use (function calling)" 문서: 도구의
description은 상세히 작성할 것, 언제 사용할지/사용하지 않을지/반환값의 의미까지 포함할수록 성능이 올라간다고 명시. https://docs.anthropic.com/en/docs/build-with-claude/tool-use - OpenAI "Function calling" 가이드: 함수 이름, 설명, 파라미터 설명을 명확히 할 것, 모호함이 모델의 오선택으로 이어질 수 있음을 설명. https://platform.openai.com/docs/guides/function-calling
- Model Context Protocol 사양 (tools): 도구는
name,description,inputSchema를 가지며, 이것들이 모델에 제시되는 정보가 된다. https://modelcontextprotocol.io/docs/concepts/tools
(본문의 주장 중 "설명문의 상세화를 통해 선택 정확도가 올라간다"는 위 1차 정보에 근거함. "반드시 올바르게 선택한다"와 같은 보장 표현은 피하고, 확률 향상과 평가의 필요성으로 기술하였음.)
요약: 오늘 바로 실행할 1가지 액션
- 도구의 설명문은 "문서"가 아니라 **계약 (Contract)**입니다. 언제 사용하는지 (when-to-use) / 언제 사용하지 않는지 (when-not-to-use) / 입력값의 의미 / 부작용 (Side effect) 이 4가지 요소를 채우세요.
- 우선 자신의 도구 중 1개의 설명문에 when-to-use와 when-not-to-use를 추가하세요. 이것이 오늘의 1가지 액션입니다.
- 작성한 것으로 끝내지 말고, 작은 케이스 집합을 통해 선택 일치율을 측정하여 변화를 수치로 추적하세요.
관련하여, 도구에 사내 API를 전달할 때의 권한 설계는 MCP 서버 자체 제작 및 경계 설계(Boundary Design)에 관한 기사에 정리해 두었습니다. 함께 확인해 보세요.
AI 개발을 하며 시도해 보고 알게 된 설계 팁은 X(구 Twitter)에서도 매일 발신하고 있습니다. 괜찮으시다면 한 번 들러주세요.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기