개발 가이드
AI tool schema는 계약서다 — 계약이 어긋나는 3가지 방식
작성: Stella 게시:
LLM에게 함수를 쓰게 하는 function calling(tool use)의 핵심은 tool 정의입니다. 이름과 설명, 그리고 인수 형태를 JSON Schema로 적어 API에 전달하면, 모델은 그 정의만 보고 인수를 만들어 보냅니다.
그래서 tool 정의는 문서라기보다 계약서에 가깝습니다. 모델은 schema를 읽고 인수를 만들고, 내 코드는 그 인수를 받아 실행합니다. 양쪽 모두 계약서만 믿고 움직이기 때문에, 계약서가 잘못되면 실패하는 방식도 계약서답게 세 가지로 갈립니다 — 그리고 셋 중 둘은 에러 메시지가 문제를 제대로 알려주지 않습니다.
검증이 가능한 부분은 실제로 실행해 확인했습니다. JSON Schema 동작은 표준 검증기인 ajv 8.20.0(Node.js v22.14.0)으로 재현했고, API 형식 규칙은 각 공식 문서 기준입니다.
참고: 형식은 셋, 내용물은 하나
먼저 용어 정리가 필요합니다. 같은 tool 정의라도 API마다 키 이름이 다릅니다.
| API | 인수 schema의 위치 |
|---|---|
| Anthropic (Claude) | input_schema |
| OpenAI | function.parameters |
| MCP | inputSchema |
키 이름만 다를 뿐 안쪽은 전부 JSON Schema이고, 최상위는 type: "object"여야 한다는 것도 같습니다. 아래에서 다루는 실수들은 세 형식 모두에 해당합니다.
어긋남 1. 계약서 자체가 거부된다 — 타입 오타
JSON Schema의 타입 이름은 일곱 개뿐입니다: string, number, integer, boolean, object, array, null. 프로그래밍 언어의 습관대로 str, int, float, bool을 쓰면 유효하지 않은 schema가 됩니다.
표준 검증기에 type: "str"이 들어간 schema를 컴파일해 봤습니다.
schema is invalid: data/properties/q/type
must be equal to one of the allowed values
검증기가 schema 자체를 거부합니다. API에 보내면 마찬가지로 요청 단계에서 거절되므로, 세 가지 어긋남 중에서는 그나마 나은 경우입니다 — 시끄럽게 실패하니까요. 다만 에러 메시지가 “허용된 값이 아니다”라고만 하고 무엇을 의도했는지는 짚어주지 않아서, str이 문제라는 걸 눈으로 찾아야 합니다.
어긋남 2. 계약은 유효한데, 이행이 불가능하다 — required 오타
이게 세 가지 중 가장 위험합니다. required 배열에 오타가 들어간 경우입니다.
{
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["citty"]
}
city를 정의해 놓고 citty를 필수로 지정했습니다. 놀라운 점은 이것이 JSON Schema 문법상 완전히 유효하다는 것입니다. 실제 검증기에 넣어 본 결과입니다.
schema compiles: true
validate({city: "Seoul"}) : false
→ "must have required property 'citty'"
validate({city: "Seoul", citty: "Seoul"}): true
schema는 문제없이 컴파일되고, 그 대신 올바른 입력이 영원히 거부됩니다. 모델이 schema를 아무리 충실히 따라 city를 보내도 검증은 항상 실패합니다. citty는 properties에 없으니 모델이 그 값을 만들어 보낼 이유도 없고요. 계약서에 “존재하지 않는 조항을 반드시 이행할 것”이라고 적어놓은 셈이라, 성립은 하지만 이행이 불가능한 계약이 됩니다.
문법상 유효하기 때문에 schema 검증 단계에서는 어떤 도구도 이걸 에러로 잡을 수 없습니다. 증상은 “도구 호출이 계속 검증에 실패한다”로만 나타나고, 원인은 코드가 아니라 계약서의 오타입니다. strict 검증을 켠 일부 API는 이런 불일치를 요청 단계에서 거절해 주지만, 그렇지 않은 환경에서는 오타 하나가 조용히 도구 전체를 무력화합니다.
어긋남 3. 계약이 애매해서, 모델이 추측한다
마지막 어긋남은 에러가 전혀 나지 않습니다. 계약서가 애매하면 모델은 빈칸을 추측으로 채웁니다.
- description이 없으면 모델은 이름만 보고 도구를 언제 쓸지, 인수에 뭘 넣을지 판단합니다.
search라는 이름만 있는 도구에 모델이 무엇을 검색어로 보낼지는 운에 가깝습니다. - property에 type이 없으면 모델이 인수 형태를 추측합니다. 숫자를 기대한 자리에
"30"(문자열)이 오는 식의 미묘한 불일치가 여기서 나옵니다. - enum을 안 쓰면 제한된 선택지(“celsius” 또는 “fahrenheit”)를 자유 텍스트로 받게 되어, 모델이 “섭씨”라고 보내는 것을 막을 수 없습니다.
이 계열은 모델의 확률적 동작이라 “이 입력이면 반드시 이 실패”로 재현해 보여드릴 수 없습니다. 대신 방향은 분명합니다. 계약서를 촘촘하게 쓸수록 추측의 여지가 줄어듭니다. description을 “언제 이 도구를 쓰는지”까지 쓰고, 모든 property에 type을 달고, 선택지가 정해진 값은 enum으로 못 박는 것이 그 방법입니다.
덧붙여 형식 규칙 하나: tool의 name은 영문·숫자·밑줄·하이픈으로 1~64자여야 합니다. 공백이나 한글이 들어간 이름(날씨 조회)은 API가 거절합니다.
보내기 전에 계약서부터 검토
어긋남 1(타입 오타)과 2(required 불일치), 그리고 name 규칙은 API를 호출하기 전에 기계적으로 잡을 수 있는 것들입니다. Stellar Brief의 AI Tool Schema Validator에 tool 정의를 붙여넣으면 형식(Anthropic·OpenAI·MCP)을 감지하고, str 같은 타입 오타에는 의도한 타입을 힌트로 제시하며, required와 properties의 불일치도 경고합니다. 입력한 정의는 브라우저에서만 분석되며 서버로 전송되지 않습니다.
어긋남 3(애매한 계약)은 기계가 절반만 잡아줍니다 — description 누락이나 type 없는 property는 검사되지만, description이 “좋은지”는 결국 사람이 판단할 일입니다. 계약서라는 비유로 돌아가면: 문법 검토는 도구에 맡기고, 조항의 내용은 작성자가 책임지는 것입니다.
이 글에 대하여
작성: Stella
Stellar Brief는 가능한 경우 공식 자료와 확인 가능한 출처를 우선 참고해 콘텐츠를 작성합니다.