CodeGym /행동 /ChatGPT Apps /지시문에서 tools와 메타데이터 설계로: 디스커버리와 라우팅

지시문에서 tools와 메타데이터 설계로: 디스커버리와 라우팅

ChatGPT Apps
레벨 5 , 레슨 4
사용 가능

1. 왜 좋은 tools와 메타데이터 없이는 지시문만으로 부족한가

하나 짚고 넘어가야 할 불편한 진실: 모델은 여러분의 코드를 보지 못합니다. Next.js에 어떤 컨트롤러가 있고, TypeScript에 어떤 함수들이 있으며, 추천 서비스에 어떤 멋진 휴리스틱을 모아두었는지 모릅니다.

모델이 여러분의 App을 보는 창은 몇 가지 인터페이스뿐입니다:

  1. System‑prompt (역할 계약).
  2. 도구 설명: 이름, description, inputSchema, outputSchema, 애노테이션 등.
  3. 앱 자체의 메타데이터: 이름, 아이콘, 짧고 긴 설명, 카테고리, conversation starters 등.

요청을 처리할 때 모델은 대화 컨텍스트와 이 메타데이터를 보고 다음을 결정합니다:

  • 어떤 App을 제안할 필요가 있는가;
  • 그렇다면 이용 가능한 앱 중 어떤 것을;
  • 그리고 선택된 App에서 현재 요청에 맞는 정확한 도구가 무엇인지.

이전 모듈 5의 전편에서는 system‑prompt와 UX 지시문으로 모델에 “말로” 전할 수 있는 것들을 다뤘습니다. 이제 텍스트 외에 모델이 보는 것, 즉 tools와 메타데이터로 넘어갑니다.

그래서 모듈 5의 과제는 사실 이중적입니다. 먼저 system‑prompt에서 “이 App이 무엇을 하고 어떻게 행동해야 하는지”를 정리한 다음, tools와 메타데이터의 디자인으로 모델이 실제로 활용할 수 있는 형태—discovery와 라우팅을 포함—로 포장합니다.

스스로 이렇게 정리할 수 있습니다: system‑prompt는 헌법이고, tools와 메타데이터는 법과 그 주변의 관료 시스템—신청서 양식, 데이터베이스 스키마 등—입니다. 헌법만으로는 멀리 갈 수 없습니다.

2. 디컴포지션: “하나의 작업 — 하나의 tool”, 하지만 합리적으로

가장 아픈 지점부터 시작해 봅시다: 도구를 몇 개 만들고 어떻게 쪼갤 것인가.

직관적 원칙: 하나의 도구는 하나의 명확한 작업. 이렇게 하면 모델의 선택이 크게 쉬워집니다. 거대한 do_everything 함수 1개가 아니라, 좋은 이름을 가진 여러 단정한 액션이 있는 셈입니다.

GiftGenius에는 이런 기본 도구들이 있을 수 있습니다:

  • profile_to_segments — 수신자에 대한 자유 형식 설명(나이, 관심사, 관계, 컨텍스트)을 "tech", "fitness", "gamer" 같은 정규화된 세그먼트로 변환.
  • recommend_gifts — 세그먼트, 예산, 로캘, 사유에 따라 선물 id 목록을 추천.
  • get_gift — 선택한 선물의 id로 전체 카드(설명, 미디어, SKU/옵션)를 조회.
  • (선택) similar_gifts — 선택한 선물을 기준으로 3–5개의 유사한 대안을 제안.

이론적으로는 gift_tool 하나에 mode: "profile_to_segments" | "recommend" | "details" | "similar" 같은 파라미터를 둘 수도 있습니다. 하지만 그러면 여러분도 모델도 더 힘들어집니다: 설명이 장황해지고 inputSchema가 비대해지며, 모델이 도구를 고를 때 명확한 앵커가 줄어듭니다.

안티‑패턴: God Tool

다음과 같은 구조를 상상해 보세요:

server.registerTool(
  "gift_tool",
  {
    description: "선물 관련 다양한 작업.",
    inputSchema: { /* 50개의 필드와 플래그 */ },
  },
  async ({ input }) => { /* mode에 따른 거대한 switch */ }
);

모델의 머릿속에서는 “선물과 관련된 어떤 추상적인 도구가 하나 있고, 나머지는 대충 알아서 하자”처럼 보입니다. 이는 선택의 정확도를 떨어뜨리고, discovery를 방해하며, 여러분의 유지 보수도 복잡하게 만듭니다.

그렇다고 반대로—사소한 동작마다 50개의 초미니 도구를 만드는—극단으로 가는 것도 좋지 않습니다. 각 추가 도구는 컨텍스트를 차지하고, 모델의 주의를 소모하며, 라우팅 오류 가능성을 높입니다. 문서에서도 명시적으로 경고합니다: 너무 많은 작은 도구는 품질에 악영향이며, 특히 설명이 서로 겹칠 때 그렇습니다.

실무에서 쓰기 좋은 규칙:

  • 사용자가 시나리오에서 하나의 “단계”로 인식하는 것(예: 프로필 기반 첫 선물 추천)은 별도 tool 후보로 좋습니다.
  • 항상 그 단계 내부에서만 수행되고 독립적 의미가 없는 것(예: 스코어 계산, 카드 조회 로깅 등)은 도구 구현 내부에 남겨 두는 편이 낫습니다.

이 원칙으로 시나리오를 2–4개의 도구로 이미 나눴다고 해봅시다. 다음으로 중요한 질문은—모델이 추측 없이 사용할 수 있도록 이 도구들의 입력을 어떻게 설명할 것인가입니다. 여기서부터 시작합니다.

3. Use‑case를 Input Schema로 투영하기

이제 하나의 구체적인 use‑case를 잡고, 도구에 실제로 어떤 데이터가 필요한지 솔직하게 살펴봅니다.

시나리오: “마감 직전의 선물러: 25살 친구, 축구와 보드게임을 좋아함, 예산은 50달러 이내, 5–7개의 아이디어를 골라줘.”

Jobs‑to‑be‑Done 관점에서 GiftGenius 추천 코어의 과제는 선택지를 작은 목록으로 줄이고 “엉뚱한 걸 고를까 봐”의 불안을 낮추는 것입니다. 채팅 상에서 어시스턴트가 필요로 하는 것은:

  • 수신자에 대한 기본 정보(나이, 성별, 선물자와의 관계);
  • 관심사/취미;
  • 예산과 통화;
  • 사유(생일, 기념일, 새해 등);
  • 선택적으로—배송 필터링을 위한 국가/도시.

GiftGenius 아키텍처에서는 이를 두 단계로 나눕니다:

  1. profile_to_segments(input)은 “원시” 데이터(나이, 관심사, 텍스트 설명)를 받아 이후 작업하기 좋은 정규화된 세그먼트로 변환합니다.
  2. recommend_gifts(segments, budget, locale, occasion)은 세그먼트와 예산을 바탕으로 카탈로그에서 구체적인 선물 id를 추천합니다.

ChatGPT ↔ MCP 계약 관점에서는 두 번째 단계—즉 recommend_gifts의 스키마—를 잘 설명하는 것이 중요합니다. 대부분의 추천 시나리오에서 바로 이 도구가 사용되기 때문입니다.

또한 사용자가 처음부터 모든 정보를 제공해야 할 필요는 없습니다: 모델이 후속 질문으로 일부를 보완할 수 있습니다(“대략 예산은 얼마인가요?”). 따라서 프로필의 일부 필드는 선택적일 수 있습니다. 하지만 recommend_gifts에 도달했을 때는 정규화된 파라미터 집합을 갖춰야 합니다.

예시: recommend_gifts를 위한 TypeScript + JSON Schema

MCP 서버를 TypeScript로 구현하면 다음과 같을 수 있습니다:

// apps/mcp/server.ts
import { McpServer } from "@openai/mcp-server";

const server = new McpServer();

server.registerTool(
  "recommend_gifts",
  {
    title: "선물 추천",
    description:
      "수신자 세그먼트, 예산, 로캘, 사유에 따라 선물을 골라야 할 때 이 도구를 사용하세요.",
    inputSchema: {
      type: "object",
      properties: {
        segments: {
          type: "array",
          description:
            "수신자 세그먼트 목록, 예: ['tech', 'football_fan']. 보통 profile_to_segments의 결과를 사용합니다.",
          items: { type: "string" },
          minItems: 1
        },
        budget: {
          type: "object",
          description:
            "사용자 통화 기준의 선물 예산 범위(최소/최대).",
          properties: {
            min: {
              type: "number",
              minimum: 0,
              description: "사용자가 지출할 최소 금액."
            },
            max: {
              type: "number",
              minimum: 0,
              description: "사용자가 지출할 최대 금액."
            },
            currency: {
              type: "string",
              minLength: 3,
              maxLength: 3,
              description: "세 글자 통화 코드(예: USD, EUR, RUB)."
            }
          },
          required: ["min", "max", "currency"]
        },
        locale: {
          type: "string",
          description:
            "BCP‑47 형식의 사용자 로캘(예: 'ru-RU' 또는 'en-US')."
        },
        occasion: {
          type: "string",
          description:
            "선물 사유, 예: 'birthday', 'new_year', 'anniversary'."
        }
      },
      required: ["segments", "budget", "locale", "occasion"]
    }
  },
  async ({ input }) => {
    // 여기서는 일단 복잡하게 가지 말고, 더미를 반환합니다
    return {
      content: [
        {
          type: "text",
          text: `세그먼트 ${input.segments?.join(
            ", "
          )}에 맞춰 예산 ${input.budget?.min}–${input.budget?.max} ${input.budget?.currency} 범위에서 선물을 고르는 중...`
        }
      ],
      structuredContent: {}
    };
  }
);

몇 가지 포인트를 유념하세요.

첫째, enum 유사 제약과 이해하기 쉬운 설명을 적극적으로 사용합니다. 형식상 문자열일지라도 description이 모델에 기대 값을 암시하기에, 인수를 올바르게 채울 확률이 크게 올라갑니다. 모호한 "사유": "대충 생일 같은 거" 대신 깔끔한 occasion: "birthday"가 됩니다.

둘째, 필드 설명은 “팀원을 위한” 문서가 아니라, 모델에게 직접 건네는 힌트처럼 씁니다: 이 필드는 무엇이고, 어떤 전형적 값이 있으며, 예시는 있는지. Apps SDK 문서의 저자들도 매개변수마다 사람이 이해할 수 있는 descriptions와 예시를 권장합니다.

입력 스키마에 없어야 할 것들

자주(그러나 넣으면 안 되는) 기생 필드 예시:

  • 내부 식별자(tenantId, internalSegment)—서버에서 자체적으로 추가할 수 있습니다;
  • 모델이 알 수 없는 것들(예: deploymentRegion)—이는 여러분의 책임입니다;
  • 채팅 기록의 중복 필드(예: userPrompt): 모델은 원본 메시지를 이미 보므로 복붙을 강요하지 마세요.

Input Schema는 모델이 결정하고 채워야 할 것만 담아야지, 잡다한 모든 것을 넣는 보따리가 아닙니다.

4. Output Schema: 데이터만이 아니라 의미까지

Apps SDK에서는 도구 결과가 role: tool 메시지로 대화에 다시 들어옵니다. 그다음 모델이 이것을 어떻게 처리할지—답변을 어떻게 꾸밀지, 어떤 후속 질문을 할지, 위젯을 열지—결정합니다. 따라서 출력 스키마 설계는 입력만큼 중요합니다.

두 가지 접근이 있습니다.

“원시 데이터” 버전은 다음과 같습니다:

{
  "items": [
    { "id": "GIFT_1" },
    { "id": "GIFT_2" }
  ]
}

모델은 그냥 id 목록만 봅니다. 왜 이 후보들이 등장했는지, 후보가 몇 개인지, 어떤 것들이 더 좋은지 이해하지 못합니다. 뭔가 지어낼 수는 있지만 이상해질 확률이 큽니다.

의미 정보가 풍부한 버전:

{
  "items": [
    {
      "id": "GIFT_1",
      "score": 0.92,
      "reason": "'football_fan' 세그먼트와 강하게 매칭되고 예산에 잘 들어맞음."
    },
    {
      "id": "GIFT_2",
      "score": 0.81,
      "reason": "보드게임 애호가에게 적합하며, 예산 상단에 다소 가까움."
    }
  ],
  "meta": {
    "totalCandidates": 27,
    "returned": 5,
    "segmentsUsed": ["football_fan", "board_games"],
    "budget": { "min": 20, "max": 50, "currency": "USD" },
    "advice": "점수가 높은 항목과 설명이 명확한 것부터 시작하는 것이 좋습니다."
  }
}

이제 모델은 왜 이러한 선물인지 정직하게 설명하고, “총 27개 중 상위 5개를 보여줍니다. 이유는 다음과 같습니다” 같은 후속 대화를 구성할 수 있습니다.

예시: recommend_gifts의 Output Schema 설명

도구 설명에 결과 스키마를 추가합니다(형식적으로 생략 가능하더라도 명시하는 편이 좋습니다—모델과의 계약의 일부이므로):

const recommendGiftsOutputSchema = {
  type: "object",
  properties: {
    items: {
      type: "array",
      items: {
        type: "object",
        properties: {
          id: { type: "string", description: "카탈로그 내 선물의 ID." },
          score: {
            type: "number",
            description: "프로필 적합도 점수(0..1)."
          },
          reason: {
            type: "string",
            description:
              "해당 선물이 적합한 이유에 대한 짧은 설명(backend에서 생성될 수 있음)."
          }
        },
        required: ["id", "score"]
      },
      description: "추천된 선물 목록과 각 항목의 적합도 점수."
    },
    meta: {
      type: "object",
      properties: {
        totalCandidates: {
          type: "integer",
          description: "카탈로그에서 총 몇 개의 후보가 발견되었는지."
        },
        returned: {
          type: "integer",
          description: "이 호출이 반환한 선물 개수."
        },
        advice: {
          type: "string",
          description:
            "일반적인 조언: 예를 들어 어떤 유형의 선물부터 시작하면 좋을지."
        }
      }
    }
  },
  required: ["items"]
};

그리고 구현 내부에서 이 스키마를 활용합니다:

server.registerTool(
  "recommend_gifts",
  {
    title: "선물 추천",
    description:
      "세그먼트와 예산으로 3–7개의 선물을 골라야 할 때 사용하세요. 선물 id와 적합도 점수를 반환합니다; 상세 카드는 get_gift로 조회하세요.",
    inputSchema: /* 위와 동일 */,
    // outputSchema를 형식적으로 생략하는 경우도 있지만, 문서화에는 유용합니다:
    // outputSchema: recommendGiftsOutputSchema
  },
  async ({ input }) => {
    const recommendations = await recommendFromCatalog(input); // 우리의 비즈니스 로직

    return {
      content: [
        {
          type: "text",
          text: `적합한 아이디어 ${recommendations.items.length}개를 찾았습니다. 지금 상위권을 보여드릴게요.`
        }
      ],
      structuredContent: {
        items: recommendations.items,
        meta: {
          totalCandidates: recommendations.meta.totalCandidates,
          returned: recommendations.items.length,
          advice: recommendations.meta.advice
        }
      }
    };
  }
);

우리는 두 가지를 병행합니다: 사용자에게 최소한의 텍스트를 제공하고, 동시에 모델이 이후 대화와 follow‑up을 구성할 수 있도록 의미 있는 JSON을 제공합니다.

한편 get_gift는 id를 바탕으로 전체 카드를 가져오고(이름, 미디어, SKU 등), GiftGenius 위젯은 이를 선물 카드로 렌더링합니다.

5. 네이밍과 도구 설명은 discovery의 핵심

이제 가장 흥미로운 부분: 도구의 이름과 설명이 모델의 호출 여부에 어떻게 영향을 미치는가.

메타데이터에 관한 문서와 모범 사례는 다음을 권장합니다:

  • 행동 지향 이름 사용: profile_to_segments, recommend_gifts, get_gift, similar_gifts 등. tool1, search, do_stuff 같은 모호한 네이밍은 피합니다;
  • 설명을 “Use this when…” 스타일로 시작하여, 트리거 시나리오와 제약(“이럴 때는 사용하지 마세요…”)을 함께 명시합니다.

이는 여러분의 golden prompt set과 직접 연결됩니다. 설명의 문구가 실제 사용자 요청과 겹쳐야 합니다. 설명에 “수신자의 관심사와 예산을 기준으로 선물을 추천할 때 사용”이라고 적혀 있고, golden prompt에 “50달러 이하 게이머 친구 선물 추천”이 있다면, 모델은 요청과 도구를 훨씬 쉽게 매칭합니다.

좋은 도구 설명 예시

추가 도구 similar_gifts를 살펴봅시다. 특정 선물을 기준으로 유사 아이디어를 확장해 주는 도구입니다:

server.registerTool(
  "similar_gifts",
  {
    title: "유사한 선물",
    description:
      "사용자가 특정 선물을 선택한 뒤 유사한 대안을 몇 가지 더 보고 싶을 때 이 도구를 사용하세요. 처음부터 추천을 시작할 때는 사용하지 마세요 — 그 경우에는 recommend_gifts를 사용합니다.",
    inputSchema: {
      type: "object",
      properties: {
        giftId: {
          type: "string",
          description:
            "이전에 제시된 추천 목록의 선물 식별자. 이 선물을 기준으로 유사 항목을 찾습니다."
        },
        limit: {
          type: "integer",
          description:
            "반환할 유사 선물 개수(기본값 3–5).",
          minimum: 1,
          default: 5
        }
      },
      required: ["giftId"]
    }
  },
  async () => {
    /* ... */
  }
);

중요 포인트:

  • 언제 사용해야 하고 언제 사용하지 말아야 하는지를 명확히 말합니다.
  • 설명에 “유사한 대안”, “특정 선물을 선택” 같은 표현을 넣습니다—실제 사용자 요청에서 자주 등장할 단어들입니다.
  • recommend_gifts의 영역과 겹치지 않게 합니다—도구 선택 시 내부 경쟁을 줄입니다.

나쁜 설명 예시

description: "선물 작업."

이런 설명으로 모델이 이해할 수 있는 것은 거의 없습니다. 이런 도구는 GPT가 정말 “아무거나라도” 시도해 보려고 할 때만 간신히 호출될 수 있습니다.

6. 애노테이션과 hints: 행동의 “무게감”을 모델에 전달하기

도구는 이름과 스키마만이 아닙니다. ChatGPT에 해당 동작이 얼마나 위험/중요하며 사용자 확인이 필요한지도 알려줄 수 있는 애노테이션이 있습니다. Apps SDK 스펙에는 readOnlyHint, destructiveHint, openWorldHint 등 다양한 힌트가 있습니다.

  • readOnlyHint: true는 도구가 데이터를 읽기만 하고 상태를 변경하지 않음을 의미합니다. 그러면 어시스턴트는 불필요한 확인을 생략하고 더 자유롭게 호출할 수 있습니다.
  • destructiveHint: true는 도구가 무언가를 삭제하거나 되돌릴 수 없게 변경할 수 있음을 의미하므로, 사용자에게 명시적 “정말 진행하시겠습니까?”를 보여줘야 합니다.
  • openWorldHint: true는 이 동작이 외부 세계에 영향을 미침(소셜 포스팅, 계정 밖에 레코드 생성 등)을 나타내며, 마찬가지로 사용자에게 알리는 것이 중요합니다.

최소 레벨 — 확인 없이

public readonly tools가 있다면 readOnlyHint: true로 표시하는 것이 좋습니다. 예:

"annotations": {
  "readOnlyHint": true,
  "destructiveHint": false,
  "openWorldHint": false
}

이런 도구는 GPT가 별도의 대화형 확인 없이 호출할 수 있습니다.

한 번의 확인

서버에서 무언가를 변경하는 도구라면 readOnlyHint: false로 표시하는 것이 합리적입니다:

"annotations": {
  "readOnlyHint": false,
  "destructiveHint": false,
  "openWorldHint": false
}

모델은 이런 도구를 보면 보통 사용자에게 한 번의 확인을 요청할 것입니다(대개 ChatGPT UI의 모달 확인창).

위험한 동작

서버에서 무언가를 삭제하는 도구라면 destructiveHint: true로 표시하세요:

"annotations": {
  "readOnlyHint": false,
  "destructiveHint": true,
  "openWorldHint": false
}

이 경우 모델은 매우 조심스럽게 도구를 호출하며 두 번 확인합니다:

  • 먼저 텍스트로 사용자에게 확인을 요청하고,
  • 그 다음 플랫폼이 표준 확인 대화창을 띄웁니다.

이번 모듈의 범위에서 GiftGenius에 커머스 도구를 직접 만들지는 않지만, 나중을 대비해 create_gift_order를 스케치해 볼 수 있습니다:

server.registerTool(
  "create_gift_order",
  {
    title: "선물 주문 생성",
    description:
      "사용자가 선택한 선물을 구매하겠다는 명시적 동의를 받은 뒤에만 사용하세요. 시스템에 주문을 생성하고 상태를 반환합니다.",
    inputSchema: {
      type: "object",
      properties: {
        giftId: {
          type: "string",
          description: "사용자가 선택한 선물의 ID."
        },
        deliveryEmail: {
          type: "string",
          description: "디지털 선물을 보낼 이메일."
        }
      },
      required: ["giftId", "deliveryEmail"]
    },
    annotations: {
      destructiveHint: true,
      openWorldHint: true
    }
  },
  async () => {
    /* ... */
  }
);

애노테이션은 서버 측 권한 검사를 대체하지 않습니다. 다만 ChatGPT가 올바른 UX를 구성하도록 돕습니다: 확인을 요청하고, 경고를 표시하며, 이런 도구를 “몰래” 실행하지 않도록 합니다.

7. App 메타데이터와 두 레벨의 discovery

도구는 이야기의 절반입니다. 나머지 절반은—사용자가 애초에 어떻게 여러분의 App을 찾고 실행하는가입니다.

ChatGPT 생태계에는 두 가지 핵심 discovery 레벨이 있습니다.

첫째—in‑conversation discovery. 사용자가 채팅에 무언가를 입력하면(앱을 명시적으로 언급하지 않더라도) 모델은 다음을 봅니다:

  • 메시지 텍스트와 대화 이력;
  • 사용 가능한 앱과 그 도구들의 설명;
  • 브랜드 언급, 주제, 키워드.

이를 바탕으로 어떤 App을 제안할지, 제안한다면 어떤 앱을 어떤 시나리오로 쓸지 결정합니다. 여기서는 도구와 App 자체 설명이 특히 중요합니다. 설명에 “선물 추천”, “선물 아이디어”, “선물 예산” 같은 트리거가 있으면 모델이 여러분의 App을 선택할 확률이 크게 높아집니다.

둘째—글로벌 discovery: 카탈로그와 런처. 이 단계에서는 사람이 직접—이름, 아이콘, 짧은 설명, 태그를 보고—App을 고릅니다. 여러분의 앱이 무엇을 하고, 누구를 위한 것이며, 핵심 가치가 무엇인지 명확하게 설명하는 것이 중요합니다.

간단한 표로 정리하면:

레이어 모델/사용자가 보는 것 메타데이터에서 중요한 것
In‑conversation 대화 텍스트, 도구 및 App 설명 트리거 문구, 액션 지향 네이밍, 제약
카탈로그/런처 이름, 아이콘, short/long description, 태그 명확한 포지셔닝, 이해하기 쉬운 가치 제안

GiftGenius에 대해 예를 들면:

  • 이름: GiftGenius — 60초 안에 선물 추천.
  • 짧은 설명: 수신자 프로필을 수집해 5–7개의 선물 아이디어를 제안하고, ChatGPT 내에서 즉시 구매할 수 있습니다.
  • In‑conversation용 설명: 사용자가 선물 고르기를 요청하거나, 무엇을 선물해야 할지 모른다고 하거나, 예산/수신자 관심사/사유를 말할 때 이 앱을 사용하세요.

이 문구들은 system‑promptrecommend_gifts 도구 설명에서 이미 쓴 내용과 꼭 맞게 동기화하는 것이 바람직합니다. 그러면 모델은 모순된 텍스트의 모음이 아니라 일관된 전체 그림을 보게 됩니다.

8. ChatGPT의 “머릿속”에서 라우팅이 작동하는 방식

지금까지를 묶어서 전형적인 요청 흐름을 살펴봅시다—MCP 프로토콜의 자세한 내용은 다음 모듈에서 다룹니다.

사용자가 이렇게 쓴다고 합시다:

“축구와 보드게임을 좋아하는 형제에게 줄 선물 아이디어 좀 도와줘. 예산은 50달러까지.”

대략적인 알고리즘:

  1. 모델이 메시지와 이력을 분석합니다. “선물”, “형제”, “축구”, “보드게임”, “예산 50” 같은 단어를 파악합니다.
  2. 사용 가능한 App과 도구의 설명과 비교합니다. GiftGenius의 설명에는 “관심사와 예산 기반 선물 추천”이 명시되어 있으므로 관련성이 높습니다.
  3. 해당 세션에서 App이 아직 활성화되지 않았다면, 모델은 “GiftGenius 앱을 열어, 제공하신 매개변수로 선물을 고를 수 있습니다. 열까요?” 같은 안내 문구를 보냅니다—이것은 우리가 UX 지시문에 미리 적어둔 것입니다.
  4. 사용자가 동의하면, 모델은 App 내부에서 recommend_gifts 도구를 선택합니다. 현재 의도에 가장 잘 맞는 설명을 가진 도구이기 때문입니다. 여기서 이름, description, inputSchema 구조가 모두 신호로 작동합니다.
  5. 모델은 요청을 바탕으로 인수를 채웁니다: 먼저(필요하다면) profile_to_segments를 호출해 “형제, 축구와 보드게임을 좋아함”이라는 텍스트에서 ["football_fan", "board_games"] 세그먼트를 얻고, 이어서 recommend_giftssegments, budget: {min: 0, max: 50, currency: "USD"}, locale, occasion: "birthday"로 호출합니다.
  6. MCP 서버가 도구를 실행하고, itemsmeta를 담은 structured output을 반환합니다.
  7. 모델은 여러분이 outputSchema에 설명한 JSON을 읽고, 무엇을 찾았는지, 왜 이 선물들인지 설명하며, “카테고리로 더 좁혀볼까요?”, “이 선물과 유사한 것을 보여드릴까요?”, “이 선물을 구매로 진행할까요?” 같은 follow‑up을 제안합니다.

이 과정을 간단한 블록 다이어그램으로 표현하면:

flowchart TD
  A[User: 선물에 대한 요청] --> B[ChatGPT가 컨텍스트 분석]
  B --> C[App 및 tools 메타데이터와 비교]
  C -->|관련성 높음| D[GiftGenius 안내]
  D -->|사용자 동의| E["recommend_gifts 호출 (+ profile_to_segments)"]
  E --> F[GiftGenius MCP 서버]
  F --> G[items/meta가 포함된 JSON 결과]
  G --> H[모델이 응답과 follow‑up 구성]

도구와 use‑case를 잘 설명할수록 이 과정에서의 우연성은 줄고 라우팅은 더 안정적입니다.

인사이트: Tool Call SEO

Apps 생태계에서는 곧 사람의 카탈로그 주목도 경쟁뿐 아니라 모델의 주목도 경쟁도 벌어집니다. 같은 사용자 요청에 대해 ChatGPT가 열댓 개의 앱을 호출할 수도 있고, 선택은 발표 자료의 디자인이 아니라 모델 “머릿속 검색 결과”에서 이뤄집니다. 이 보이지 않는 층은 갈수록 SEO와 닮아가는데, 페이지 대신 여러분에겐 tools와 MCP 서버가 있습니다.

모델은 사실 후보를 랭킹합니다: 먼저 App 레벨, 그다음 개별 도구 레벨. 이름, descriptions, 스키마, 애노테이션을 보고 요청의 표현과 대조합니다. recommend_gifts 설명에 “수신자의 관심사와 예산에 따른 선물 추천”이 있고, 요청에 “50달러 예산의 게이머 친구 선물 추천”이 있다면, “선물 작업” 같은 모호한 설명의 search보다 상위에 노출될 확률이 큽니다.

여기서 Tool Call SEO라는 실용적 아이디어가 나옵니다: 이름, descriptions, enum 값, 메타데이터를 키워드와 스니펫처럼 다루세요. 개발자 계약을 서술할 뿐 아니라, 여러분의 golden prompt set에서 나오는 실제 트래픽에 최적화하는 것입니다. 지나치게 일반적인 문구, 여러 도구의 영역 겹침, 틈새가 불명확한 God 도구—이 모든 것이 모델 머릿속에서 여러분 App의 “CTR”을 떨어뜨립니다.

9. 작은 실습

다음 과정을 머릿속으로(또는 저장소에서) 시도해 보세요.

먼저 GiftGenius의 핵심 시나리오 중 하나를 고릅니다—예: “제한된 예산으로 직장 동료 선물 고르기”.

이를 위해 다음을 정리합니다:

  1. 이 시나리오에 필요한 별도 도구가 무엇인지: 순수 recommend_gifts로 충분한가, B2B 케이스 전용 도구가 더 필요한가, 아니면 recommend_gifts 이후 similar_gifts로 변주만 주면 충분한가?
  2. recommend_gifts의 입력 스키마에 정말 필요한 필드는 무엇인가. 모델이 추측하지 않도록, 어떤 필드는 사용자에게 별도(후속 질문으로)로 물어보는 편이 나은가.
  3. 모델이 선택 이유를 정직하게 설명하고 다음 단계(예: B2B 모드로 전환, 디지털 선물만 보기, 가격대 좁히기)를 제안할 수 있도록 outputSchema는 어떻게 보여야 하는가.

그다음 지난 강의의 golden prompt set을 다시 보고 점검합니다:

  • 각 기준 요청에 대해 명백한 도구가 있는가(recommend_gifts, get_gift, similar_gifts 등);
  • 하나의 요청에 두 도구가 똑같이 “적합해 보이는” 상황이 생기지 않았는가(overlapping tools);
  • 모델의 혼동을 줄이기 위해 설명을 강화하거나 도구 이름을 바꿔야 하는가.

이는 프롬프트, 스키마, 로직에 큰 변경을 가하기 전마다 반복하게 될 과정입니다—일종의 discovery 품질 미니 평가입니다.

위 내용을 체크리스트로 요약하면, 이번 단계에서 여러분이 해야 할 일은:

  • 시나리오를 2–4개의 의미 있는 도구로 정직하게 분해하고;
  • inputSchema/outputSchema를 예시와 enum으로 깔끔히 설명하며;
  • 이름, descriptions, 애노테이션을 정리하고;
  • system‑prompt와 App 메타데이터와의 정합성을 맞춥니다.

다음 모듈에서는 MCP를 통해 이것이 실제로 어떻게 동작하는지, 그리고 discovery/라우팅에서 이상한 행동을 어떻게 진단하는지 살펴보겠습니다.

10. 도구와 메타데이터 설계 시 흔한 실수

실수 №1: “system‑prompt에 다 써놨으니, 도구는 알아서 쓰겠지”.
App의 역할, 책임 범위, UX 동작을 멋지게 정리했더라도, tool1, search, do_stuff 같은 이름과 설명 없는 스키마로 도구를 내버려두면, 모델은 멋진 텍스트를 실제 호출과 연결하지 못합니다. ChatGPT에게 도구는 주 인터페이스입니다; 제대로 된 메타데이터 없이는 어떤 system‑prompt도 구원해주지 못합니다.

실수 №2: 온갖 일을 다 하는 God Tool.
mode 파라미터 하나로 “최적화”하고 싶은 마음은 이해하지만, 이는 괴물 같은 JSON 스키마, 뒤얽힌 설명, 라우팅 저하로 이어집니다. 모델은 어떤 모드를 써야 할지 추측하고, 여러분은 서버의 거대한 switch를 유지보수하게 됩니다. 특정 시나리오 단계별로 명확한 여러 도구가, “다 해줘” 하나보다 낫습니다.

실수 №3: “혹시 몰라서”로 가득 찬 입력 스키마.
개발자가 inputSchema에 언젠가 쓸지도 모를 모든 파라미터와 몇 가지 내부 필드까지 밀어 넣으려는 경우가 흔합니다. 결과적으로 모델은 알 수 없는 것(tenantId 등)을 추측하려고 하고, 여러분은 이상한 값에 놀라게 됩니다. Input Schema에는 모델이 대화에서 실제로 도출하거나 질문으로 확인할 수 있는 것만 넣어야 합니다. 내부 디테일은 서버에서 추가하세요.

실수 №4: 메타 정보가 전혀 없는 “벙어리” 출력 데이터.
도구에서 단순 배열만 반환하는 것은 유혹적입니다. 하지만 그러면 모델은 이 결과가 나왔는지 이해하지 못합니다. score, reason, searchCriteria, totalCandidates 같은 필드가 없으면, 정직한 설명과 follow‑up 구성이 어려워집니다. 검색 기준과 조언이 담긴 작은 meta 래퍼만 추가해도 답변 품질이 크게 개선됩니다.

실수 №5: 판에 박힌 설명—“선물 작업”, “강의 검색”, “데이터 처리”.
이런 설명은 트리거도 제약도 제공하지 못합니다. 모델은 언제 도구를 호출해야 하고, 어느 영역에 적용되는지 모릅니다. 좋은 설명은 “이럴 때 이 도구를 사용하세요…”로 시작하고, 구체적 시나리오와 “이럴 때는 사용하지 마세요…”를 포함합니다. 이상적으론 golden prompt set의 문구와 겹치게 쓰는 것입니다.

실수 №6: 애노테이션 무시 및 읽기 전용과 변경 동작 섞기.
데이터를 읽기만 하는 도구(readOnlyHint)와 실제 동작을 수행하는 도구(destructiveHint, openWorldHint)를 구분해 표시하지 않으면, 모델은 올바른 확인 UX를 구성할 수 없습니다. 결국 모든 단계에서 쓸데없는 “정말 진행하시겠습니까?”가 나오거나, 반대로 조용히 구매/변경이 이뤄지는 일이 생깁니다. 애노테이션은 동작의 중요도를 모델에 알리는 저렴하고 효과적인 수단입니다.

실수 №7: 카탈로그용 App 메타데이터와 in‑conversation 메타데이터가 따로 노는 경우.
카탈로그의 짧은 설명은 마케터가 쓴 “삶을 바꾸는 혁신적 AI 어시스턴트”이고, 도구 descriptions와 system‑prompt는 개발자가 쓴 “예산 기반 선물 추천”인 경우가 있습니다. 그러면 카탈로그에서는 앱이 무엇인지 불명확하고, 채팅에서는 “이게 무슨 서비스야?”라는 질문을 앱의 실제 능력과 매칭하지 못합니다. 메타데이터는 하나의 통합된 스펙처럼 작성하세요. 서로 다른 두 마케팅 텍스트로 쓰지 마세요.

1
설문조사/퀴즈
ChatGPT App의 동작, 레벨 5, 레슨 4
사용 불가능
ChatGPT App의 동작
모델을 위한 지침과 ChatGPT App의 동작
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION