2편의 도구는 인자를 안 받았다. 이번엔 받는다. 그리고 받은 걸 안 믿는다.
스키마
inputSchema: const {
'type': 'object',
'properties': {
'count': {'type': 'integer', 'description': 'How many to admit, 1 or more'},
},
'required': ['count'],
},
이걸 붙이면 부르는 쪽이 무엇을 보내야 하는지 안다. 모델이라면 여기 맞춰 인자를 만든다.
그런데 스키마는 보장이 아니다
'type': 'integer' 라고 적었다고 정수가 오는 게 아니다. 스키마는 부르는 쪽에 대한 안내이고, 그 쪽이 안 지킬 수도 있고 못 지킬 수도 있다.
그래서 핸들러가 다시 본다.
handler: (args) async {
final n = args['count'];
// The schema says integer; the caller may still send anything.
if (n is! int || n < 1) {
return CallToolResult(
content: [TextContent(text: 'desk.admit: count must be 1 or more')],
isError: true,
);
}
// And the desk cannot admit people who are not there.
if (n > waiting) {
return CallToolResult(
content: [TextContent(text: 'only $waiting waiting')],
isError: true,
);
}
waiting -= n;
return CallToolResult(
content: [TextContent(text: jsonEncode({'waiting': waiting}))],
);
},
두 거절은 성질이 다르다
검사가 둘인데 이유가 다르다.
n < 1 — 값 자체가 말이 안 된다. 0명을 들여보내라는 건 언제나 틀렸다. 입력의 문제다.
n > waiting — 값은 멀쩡한데 지금 상태에서 불가능하다. 3명 대기 중에 99명을 들여보낼 수 없다. 하지만 100명이 대기 중이면 99는 정상이다. 상태의 문제다.
이걸 구분해야 하는 이유는 메시지 때문이다.
count must be 1 or more ← 다시 보내도 똑같이 틀림
only 3 waiting ← 3 이하로 보내면 됨
isError: true 로 뭉뚱그리면 부르는 쪽이 재시도할지 포기할지 모른다.