CodeGym /Các khóa học /ChatGPT Apps /Từ hướng dẫn đến thiết kế tools và metadata: discovery và...

Từ hướng dẫn đến thiết kế tools và metadata: discovery và định tuyến

ChatGPT Apps
Mức độ , Bài học
Có sẵn

1. Vì sao chỉ có hướng dẫn là chưa đủ nếu thiếu tools và metadata tốt

Quan trọng là phải ghi nhớ một sự thật hơi khó chịu: mô hình không nhìn thấy code của bạn. Nó không biết trong Next.js có những controller nào, trong TypeScript có những hàm gì và bạn đã gom những heuristic “tuyệt vời” nào trong dịch vụ gợi ý.

Nó nhìn thấy App của bạn qua vài giao diện:

  1. System‑prompt (hợp đồng vai trò).
  2. Mô tả công cụ: tên, description, inputSchema, outputSchema, annotation, v.v.
  3. Metadata của chính ứng dụng: tên, biểu tượng, mô tả ngắn và dài, danh mục, conversation starters, v.v.

Khi xử lý một yêu cầu, mô hình nhìn vào ngữ cảnh hội thoại và các metadata này để quyết định:

  • có nên gợi ý App nào không;
  • nếu có — chính xác là App nào trong số các App hiện có;
  • và nếu App đã được chọn — tool nào của App đó phù hợp với yêu cầu hiện tại.

Ở phần trước của Module 5, chúng ta làm những gì có thể “kể” cho mô hình bằng lời — system‑prompt và hướng dẫn UX. Giờ chuyển sang những gì nó thấy ngoài văn bản: tools và metadata.

Vì vậy, nhiệm vụ của Module 5 thực ra là kép. Trước hết trong system‑prompt bạn nêu “App này phải làm gì và hành xử ra sao”, sau đó trong thiết kế tools và metadata, bạn đóng gói điều đó thành hình thức mà mô hình thực sự biết cách sử dụng — bao gồm cả cho discovery và định tuyến.

Có thể tự nhắc mình thế này: system‑prompt là hiến pháp, còn tools và metadata — là luật và phần “hành chính” xung quanh: biểu mẫu, schema cơ sở dữ liệu, v.v. Nếu chỉ dừng ở hiến pháp, ta sẽ không đi được xa.

2. Phân rã: “một nhiệm vụ — một tool”, nhưng phải hợp lý

Bắt đầu với câu hỏi đau đầu nhất: rốt cuộc cần bao nhiêu tool và cắt chúng ra sao.

Nguyên tắc trực giác: mỗi tool — một tác vụ rõ ràng. Điều này giúp mô hình chọn lựa dễ dàng: thay vì 1 hàm “quái vật” do_everything, sẽ có vài hành động gọn gàng với tên gọi tốt.

Với GiftGenius, chúng ta có thể có các tool cơ bản sau:

  • profile_to_segments — chuyển mô tả tự do về người nhận (tuổi, sở thích, mối quan hệ, ngữ cảnh) thành các segment chuẩn hóa như "tech", "fitness", "gamer".
  • recommend_gifts — chọn danh sách id quà tặng theo segment, ngân sách, locale và dịp.
  • get_gift — lấy đầy đủ thẻ quà đã chọn (mô tả, media, SKU/biến thể) theo id.
  • (tùy chọn) similar_gifts — dựa trên một món quà đã chọn, đề xuất thêm 3–5 phương án tương tự.

Về lý thuyết, có thể làm một gift_tool với tham số mode: "profile_to_segments" | "recommend" | "details" | "similar", nhưng như vậy bạn làm khó cả mình lẫn mô hình: mô tả trở thành một “tấm ga giường”, inputSchema phình to, và khi chọn tool thì mô hình có ít neo rõ ràng hơn.

Anti‑pattern: God Tool

Hãy tưởng tượng sơ đồ sau:

server.registerTool(
  "gift_tool",
  {
    description: "Các thao tác khác nhau với quà tặng.",
    inputSchema: { /* 50 trường và cờ */ },
  },
  async ({ input }) => { /* switch khổng lồ theo mode */ }
);

Trong đầu của mô hình, nó trông như “có một tool trừu tượng nào đó về quà, còn lại để sau tính”. Điều này làm giảm độ chính xác khi chọn, cản trở discovery và khiến bạn bảo trì khó hơn.

Nhưng rơi vào cực đoan khác — tạo 50 tool siêu nhỏ cho từng việc vặt — cũng không tốt. Mỗi tool bổ sung đi vào ngữ cảnh, làm tăng tải chú ý của mô hình và tăng rủi ro định tuyến sai. Tài liệu hướng dẫn cảnh báo rõ: quá nhiều tool nhỏ — chất lượng giảm, đặc biệt khi mô tả của chúng chồng chéo.

Quy tắc thực hành tiện áp dụng:

  • mọi thứ mà người dùng cảm nhận là một “bước” trong kịch bản (ví dụ, chọn lứa quà đầu tiên theo hồ sơ) — là ứng viên tốt cho một tool riêng;
  • những việc luôn thực hiện nghiêm ngặt bên trong bước đó và không có ý nghĩa độc lập (ví dụ, tính điểm hoặc log việc xem thẻ) thì nên để trong phần triển khai của tool.

Giả sử theo nguyên tắc này, bạn đã cắt kịch bản thành 2–4 tool. Câu hỏi quan trọng tiếp theo — mô tả đầu vào của các tool thế nào để mô hình sử dụng mà không phải đoán mò. Bắt đầu từ đây.

3. Chiếu use case vào Input Schema

Giờ lấy một use case cụ thể và xem trung thực, tool thực sự cần dữ liệu gì.

Kịch bản: “Người tặng đang gần deadline: gợi ý 5–7 ý tưởng cho người bạn 25 tuổi, thích bóng đá và trò chơi trên bàn, ngân sách tới 50 USD”.

Từ jobs‑to‑be‑done có thể thấy nhiệm vụ của lõi gợi ý GiftGenius — thu hẹp lựa chọn xuống một danh sách nhỏ và giảm lo lắng “nhỡ chọn nhầm”. Ở mức giao tiếp trong chat, trợ lý cần:

  • thông tin cơ bản về người nhận (tuổi, giới tính, quan hệ với người tặng);
  • sở thích/hobby;
  • ngân sách và tiền tệ;
  • dịp (sinh nhật, kỷ niệm, Năm mới, v.v.);
  • tùy chọn — quốc gia/thành phố để lọc theo giao hàng.

Trong kiến trúc GiftGenius, việc này chia thành hai bước:

  1. profile_to_segments(input) nhận dữ liệu “thô” (tuổi, sở thích, mô tả dạng tự do) và chuyển thành các segment chuẩn hóa, tiện xử lý tiếp theo.
  2. recommend_gifts(segments, budget, locale, occasion) dựa theo segment và ngân sách để chọn id quà từ catalog.

Về phía hợp đồng ChatGPT ↔ MCP, chúng ta cần mô tả chính bước thứ hai — schema của recommend_gifts, vì đây là tool sẽ được dùng trong đa số kịch bản gợi ý.

Đồng thời không cần đòi hỏi người dùng cung cấp hết mọi thứ ngay: mô hình có thể hỏi bù qua follow‑up (“ngân sách khoảng bao nhiêu?”). Nghĩa là một số trường trong profile có thể là tùy chọn; nhưng khi đến recommend_gifts, phải có bộ tham số đã chuẩn hóa.

Ví dụ: TypeScript + JSON Schema cho recommend_gifts

Trên MCP server bằng TypeScript, có thể trông như sau:

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

const server = new McpServer();

server.registerTool(
  "recommend_gifts",
  {
    title: "Gợi ý quà tặng",
    description:
      "Hãy dùng tool này khi cần gợi ý quà theo segment của người nhận, ngân sách, locale và dịp.",
    inputSchema: {
      type: "object",
      properties: {
        segments: {
          type: "array",
          description:
            "Danh sách segment của người nhận, ví dụ ['tech', 'football_fan']. Thường lấy từ profile_to_segments.",
          items: { type: "string" },
          minItems: 1
        },
        budget: {
          type: "object",
          description:
            "Khoảng ngân sách cho quà theo tiền tệ của người dùng (tối thiểu/tối đa).",
          properties: {
            min: {
              type: "number",
              minimum: 0,
              description: "Số tiền tối thiểu người dùng sẵn sàng chi."
            },
            max: {
              type: "number",
              minimum: 0,
              description: "Số tiền tối đa người dùng sẵn sàng chi."
            },
            currency: {
              type: "string",
              minLength: 3,
              maxLength: 3,
              description: "Mã tiền tệ 3 ký tự (ví dụ: USD, EUR, RUB)."
            }
          },
          required: ["min", "max", "currency"]
        },
        locale: {
          type: "string",
          description:
            "Locale của người dùng theo chuẩn BCP‑47 (ví dụ 'ru-RU' hoặc 'en-US')."
        },
        occasion: {
          type: "string",
          description:
            "Dịp tặng quà, ví dụ 'birthday', 'new_year', 'anniversary'."
        }
      },
      required: ["segments", "budget", "locale", "occasion"]
    }
  },
  async ({ input }) => {
    // Ở đây chưa làm thông minh, trả về dữ liệu giả
    return {
      content: [
        {
          type: "text",
          text: `Đang chọn quà theo các segment ${input.segments?.join(
            ", "
          )} trong ngân sách ${input.budget?.min}–${input.budget?.max} ${input.budget?.currency}...`
        }
      ],
      structuredContent: {}
    };
  }
);

Lưu ý vài điểm.

Thứ nhất, chúng ta tận dụng mạnh các ràng buộc kiểu enum và mô tả dễ hiểu. Dù về hình thức chỉ là chuỗi, description gợi ý cho mô hình những giá trị nào được kỳ vọng, và điều này làm tăng đáng kể khả năng điền đúng đối số. Thay vì một chuỗi mơ hồ "occasion": "kiểu như sinh nhật", ta có occasion: "birthday" gọn gàng.

Thứ hai, mô tả trường không viết “cho người trong đội”, mà đúng nghĩa như gợi ý cho mô hình: đây là trường gì, các giá trị điển hình, có ví dụ hay không. Tác giả tài liệu Apps SDK khuyến nghị thêm mô tả và ví dụ dễ hiểu cho từng tham số.

Những thứ không nên có trong input schema

Những trường “ký sinh” điển hình mà nhiều người hay nhét vào:

  • các định danh nội bộ (tenantId, internalSegment) — vốn có thể bổ sung ở server;
  • những thứ mô hình không thể biết (ví dụ deploymentRegion) — đó là trách nhiệm của bạn;
  • các trường chỉ lặp lại lịch sử chat (ví dụ userPrompt): mô hình đã thấy thông điệp gốc, đừng bắt nó copy-paste.

Input Schema — chính là những gì mô hình cần quyết định và điền, chứ không phải túi hỗn tạp chứa tất cả mọi thứ.

4. Output Schema: không chỉ dữ liệu, mà còn là ý nghĩa

Trong Apps SDK, kết quả của tool quay lại hội thoại như một thông điệp có role: tool. Sau đó mô hình quyết định làm gì với nó: định dạng câu trả lời, hỏi follow‑up nào, có cần mở widget không, v.v. Vì vậy, thiết kế schema đầu ra cũng quan trọng không kém đầu vào.

Có hai cách tiếp cận.

Phương án “dữ liệu thô” trông như sau:

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

Mô hình chỉ thấy danh sách id, không hiểu vì sao các phương án này xuất hiện, có bao nhiêu ứng viên và cái nào tốt nhất. Nó có thể bịa ra gì đó, nhưng xác suất kỳ lạ sẽ cao hơn.

Phương án giàu ngữ nghĩa:

{
  "items": [
    {
      "id": "GIFT_1",
      "score": 0.92,
      "reason": "Khớp mạnh với segment 'football_fan' và nằm trong ngân sách."
    },
    {
      "id": "GIFT_2",
      "score": 0.81,
      "reason": "Phù hợp với người yêu board game, hơi gần ngưỡng trên của ngân sách."
    }
  ],
  "meta": {
    "totalCandidates": 27,
    "returned": 5,
    "segmentsUsed": ["football_fan", "board_games"],
    "budget": { "min": 20, "max": 50, "currency": "USD" },
    "advice": "Nên bắt đầu với các phương án có score cao và giải thích rõ ràng."
  }
}

Giờ mô hình có thể giải thích vì sao chính những món quà này, và xây dựng follow‑up: “Tôi đã tìm thấy 27 phương án, hiển thị 5 cái tốt nhất, lý do là đây”.

Ví dụ: mô tả Output Schema cho recommend_gifts

Thêm vào mô tả tool schema kết quả (dù về kỹ thuật có thể không cần chỉ định, nhưng nên làm — đó là một phần hợp đồng với mô hình):

const recommendGiftsOutputSchema = {
  type: "object",
  properties: {
    items: {
      type: "array",
      items: {
        type: "object",
        properties: {
          id: { type: "string", description: "ID quà trong catalog." },
          score: {
            type: "number",
            description: "Điểm phù hợp với hồ sơ (0..1)."
          },
          reason: {
            type: "string",
            description:
              "Giải thích ngắn gọn vì sao món quà phù hợp (có thể được sinh ở backend)."
          }
        },
        required: ["id", "score"]
      },
      description: "Danh sách quà được khuyến nghị kèm điểm phù hợp."
    },
    meta: {
      type: "object",
      properties: {
        totalCandidates: {
          type: "integer",
          description: "Tổng số ứng viên tìm thấy trong catalog."
        },
        returned: {
          type: "integer",
          description: "Số quà được trả về trong lần gọi này."
        },
        advice: {
          type: "string",
          description:
            "Khuyến nghị chung: ví dụ nên bắt đầu từ loại quà nào."
        }
      }
    }
  },
  required: ["items"]
};

Và dùng schema này trong phần triển khai:

server.registerTool(
  "recommend_gifts",
  {
    title: "Gợi ý quà tặng",
    description:
      "Dùng khi cần chọn 3–7 món quà theo segment và ngân sách. Trả về id quà và điểm phù hợp; thẻ chi tiết lấy qua get_gift.",
    inputSchema: /* như ở trên */,
    // Không phải lúc nào cũng chỉ định outputSchema, nhưng có ích cho tài liệu:
    // outputSchema: recommendGiftsOutputSchema
  },
  async ({ input }) => {
    const recommendations = await recommendFromCatalog(input); // business logic của chúng ta

    return {
      content: [
        {
          type: "text",
          text: `Đã tìm thấy ${recommendations.items.length} ý tưởng phù hợp. Sẽ hiển thị những cái tốt nhất.`
        }
      ],
      structuredContent: {
        items: recommendations.items,
        meta: {
          totalCandidates: recommendations.meta.totalCandidates,
          returned: recommendations.items.length,
          advice: recommendations.meta.advice
        }
      }
    };
  }
);

Chúng ta làm hai việc: đưa cho mô hình một đoạn văn bản tối thiểu dành cho người dùng và đồng thời đặt kèm JSON ngữ nghĩa để nó xây tiếp hội thoại và follow‑up.

Trong khi đó, get_gift sẽ kéo thẻ chi tiết theo id (tên, media, SKU, v.v.), và widget GiftGenius render chúng như các thẻ quà.

5. Đặt tên và mô tả tool như nền tảng cho discovery

Giờ phần hấp dẫn: cách tên gọi và mô tả của tool ảnh hưởng đến việc mô hình có gọi chúng hay không.

Tài liệu và best practice về metadata khuyên:

  • dùng tên hướng hành động: profile_to_segments, recommend_gifts, get_gift, similar_gifts, thay vì tool1, search, do_stuff;
  • bắt đầu mô tả kiểu “Use this when… / Hãy dùng tool này khi…”, mô tả các kịch bản kích hoạt và giới hạn (“đừng dùng cho…”).

Điều này liên quan trực tiếp đến golden prompt set của bạn. Câu chữ trong mô tả nên giao cắt với các yêu cầu thực tế của người dùng. Nếu trong mô tả viết “Hãy dùng khi người dùng nhờ gợi ý quà theo ngân sách và sở thích người nhận”, còn trong golden prompt của bạn có “hãy gợi ý quà cho bạn gamer với 50 USD”, mô hình sẽ dễ dàng ghép yêu cầu với tool.

Ví dụ mô tả tool tốt

Xem thêm tool phụ của GiftGenius — similar_gifts, giúp mở rộng bộ sưu tập bằng các ý tưởng tương tự dựa trên một món quà cụ thể:

server.registerTool(
  "similar_gifts",
  {
    title: "Quà tặng tương tự",
    description:
      "Hãy dùng tool này khi người dùng đã chọn một món quà cụ thể và muốn xem thêm vài phương án tương tự. Đừng dùng cho lần gợi ý đầu tiên từ con số 0 — việc đó dành cho recommend_gifts.",
    inputSchema: {
      type: "object",
      properties: {
        giftId: {
          type: "string",
          description:
            "Định danh của món quà từ gợi ý trước, cần tìm các phương án tương tự cho nó."
        },
        limit: {
          type: "integer",
          description:
            "Số lượng quà tương tự trả về (mặc định 3–5).",
          minimum: 1,
          default: 5
        }
      },
      required: ["giftId"]
    }
  },
  async () => {
    /* ... */
  }
);

Những điểm quan trọng:

  • Chúng ta nói rõ khi nào nên dùng tool và khi nào không.
  • Trong mô tả có các từ “phương án tương tự”, “đã chọn một món quà cụ thể” — đúng những từ sẽ thường gặp trong yêu cầu thực.
  • Tránh chồng chéo với phạm vi của recommend_gifts — giảm cạnh tranh giữa các tool khi lựa chọn.

Ví dụ mô tả tệ

description: "Làm việc với quà tặng."

Từ mô tả này, mô hình gần như không hiểu gì. Tool như vậy chỉ hoạt động nếu GPT đang cố gọi “bừa” cái gì đó.

6. Annotations và hints: cách gợi ý cho mô hình mức độ nghiêm trọng của hành động

Tool không chỉ là tên và schema, mà còn là các annotation gợi ý cho ChatGPT mức độ nguy hiểm/quan trọng của hành động và có cần hỏi người dùng xác nhận hay không. Trong Apps SDK có các hint khác nhau cho việc này như readOnlyHint, destructiveHint, openWorldHint và các loại khác.

  • readOnlyHint: true cho biết tool chỉ đọc dữ liệu và không thay đổi trạng thái. Khi đó, trợ lý có thể bỏ bớt xác nhận thừa và gọi tự do hơn.
  • destructiveHint: true báo hiệu tool có thể xóa hoặc thay đổi không thể hoàn tác, vì vậy cần hiển thị “Bạn có chắc không?” cho người dùng.
  • openWorldHint: true chỉ ra hành động tác động đến thế giới bên ngoài (đăng mạng xã hội, tạo bản ghi ngoài tài khoản, v.v.), và cũng cần cảnh báo.

Mức tối thiểu — không cần xác nhận

Nếu bạn có public readonly tools, nên đánh dấu chúng readOnlyHint: true. Ví dụ:

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

Những tool như vậy có thể được gọi mà không cần quá nhiều xác nhận trong giao tiếp.

Một lần xác nhận

Nếu bạn có các tool thay đổi điều gì đó trên server, hợp lý là đánh dấu readOnlyHint: false:

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

Khi thấy tool như vậy, mô hình có khả năng sẽ yêu cầu người dùng xác nhận một lần (thường là một hộp thoại modals trong UI ChatGPT).

Hành động nguy hiểm

Nếu bạn có tool xóa gì đó trên server, hãy đánh dấu destructiveHint: true:

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

Mô hình sẽ gọi tool này rất thận trọng và xác nhận hai lần:

  • đầu tiên hỏi xác nhận người dùng bằng văn bản,
  • sau đó nền tảng hiển thị hộp thoại chuẩn.

Trong phạm vi module này với GiftGenius, chúng ta chưa viết các tool commerce, nhưng có thể phác thảo create_gift_order trong tương lai:

server.registerTool(
  "create_gift_order",
  {
    title: "Tạo đơn hàng quà tặng",
    description:
      "Chỉ dùng sau khi người dùng đã đồng ý rõ ràng mua món quà đã chọn. Tạo đơn hàng trong hệ thống và trả về trạng thái.",
    inputSchema: {
      type: "object",
      properties: {
        giftId: {
          type: "string",
          description: "ID món quà mà người dùng đã chọn."
        },
        deliveryEmail: {
          type: "string",
          description: "Email để gửi quà số."
        }
      },
      required: ["giftId", "deliveryEmail"]
    },
    annotations: {
      destructiveHint: true,
      openWorldHint: true
    }
  },
  async () => {
    /* ... */
  }
);

Các annotation không thay thế kiểm tra quyền hạn của bạn ở server; chúng chỉ giúp ChatGPT xây UX: hỏi xác nhận, hiển thị cảnh báo và không thực thi các tool như vậy một cách âm thầm.

7. Metadata của App và hai cấp độ discovery

Tools chỉ là một nửa câu chuyện. Nửa còn lại — làm sao người dùng tìm và khởi chạy App của bạn.

Trong hệ sinh thái ChatGPT có hai cấp độ discovery chính.

Thứ nhất — in‑conversation discovery. Khi người dùng viết gì đó trong chat (ngay cả khi không nhắc đến App), mô hình nhìn vào:

  • văn bản thông điệp và lịch sử hội thoại;
  • mô tả các ứng dụng khả dụng và tool của chúng;
  • nhắc đến thương hiệu, chủ đề và từ khóa.

Dựa vào đó, nó quyết định có nên gợi ý App nào không, nếu có — App nào và theo kịch bản nào. Ở đây, mô tả của tool và của App đặc biệt quan trọng. Nếu trong đó có các “trigger” như “gợi ý quà tặng”, “ý tưởng quà tặng”, “ngân sách quà tặng”, khả năng mô hình chọn App của bạn tăng mạnh.

Cấp độ thứ hai — discovery toàn cục: catalog và launcher. Ở đó là con người chọn: họ chọn App bằng mắt qua tên, biểu tượng, mô tả ngắn và thẻ. Tại đây bạn cần giải thích trung thực và rõ ràng ứng dụng làm gì, dành cho ai và giá trị chính là gì.

Có thể tóm lại trong một bảng nhỏ:

Lớp Mô hình/người dùng thấy gì Điều quan trọng trong metadata
In‑conversation Văn bản hội thoại, mô tả tools và App Cụm từ kích hoạt, tên hướng hành động, giới hạn
Catalog/launcher Tên, icon, short/long description, thẻ Định vị rõ ràng, value‑props dễ hiểu

Cho GiftGenius, chẳng hạn, có thể viết:

  • Tên: GiftGenius — gợi ý quà trong 60 giây.
  • Mô tả ngắn: Thu thập hồ sơ người nhận và đề xuất 5–7 ý tưởng quà với khả năng mua ngay trong ChatGPT.
  • Mô tả cho in‑conversation: Hãy dùng ứng dụng này khi người dùng nhờ chọn quà, chưa biết tặng gì, nêu ngân sách, sở thích của người nhận hoặc dịp.

Rất nên đồng bộ các câu chữ này với những gì bạn đã viết trong system‑prompt và mô tả của tool recommend_gifts. Khi đó, mô hình thấy một bức tranh nhất quán thay vì các đoạn văn mâu thuẫn.

8. Định tuyến hoạt động “trong đầu” ChatGPT như thế nào

Tập hợp mọi thứ lại và xem đường đi điển hình của một yêu cầu — chưa đi sâu vào giao thức MCP (sẽ ở các module sau).

Giả sử người dùng viết:

“Giúp tôi nghĩ quà cho anh trai, anh ấy mê bóng đá và board game, ngân sách tối đa 50 USD.”

Thuật toán đơn giản hóa:

  1. Mô hình phân tích thông điệp và lịch sử. Thấy các từ “quà”, “anh trai”, “bóng đá”, “board game”, “ngân sách 50”.
  2. So sánh với mô tả các App và tool khả dụng. Với GiftGenius, mô tả có “gợi ý quà theo sở thích và ngân sách”, nên xác suất App phù hợp cao.
  3. Nếu App chưa kích hoạt trong phiên này, mô hình tạo một câu “giới thiệu”: “Tôi có thể mở ứng dụng GiftGenius để giúp bạn chọn quà theo tham số của bạn. Mở nhé?” — câu này chúng ta đã viết sẵn trong hướng dẫn UX.
  4. Sau khi người dùng đồng ý, mô hình chọn trong App tool recommend_gifts, vì mô tả của nó phù hợp nhất với ý định hiện tại. Ở đây tên, description và cấu trúc inputSchema đều là tín hiệu đầu vào.
  5. Mô hình điền đối số của tool dựa trên yêu cầu: trước hết (nếu cần) gọi profile_to_segments để từ văn bản “anh trai, thích bóng đá và board game” thu được các segment ["football_fan", "board_games"], rồi gọi recommend_gifts với segments, budget: {min: 0, max: 50, currency: "USD"}, locale, occasion: "birthday".
  6. MCP‑server thực thi tool, tạo structured output với itemsmeta rồi trả về.
  7. Mô hình đọc JSON mà bạn đã mô tả trong outputSchema và dựng câu trả lời: giải thích đã tìm thấy gì, vì sao là những quà này, và đề xuất follow‑up (“bạn muốn thu hẹp theo danh mục?”, “hiển thị quà tương tự cho món này?” hoặc “mua món quà này?”).

Đây là sơ đồ khối đơn giản của tiến trình:

flowchart TD
  A[User: yêu cầu về quà tặng] --> B[ChatGPT phân tích ngữ cảnh]
  B --> C[So khớp với metadata của App và tools]
  C -->|relevant| D[Giới thiệu GiftGenius]
  D -->|người dùng đồng ý| E["Gọi recommend_gifts (+ profile_to_segments)"]
  E --> F[MCP server của GiftGenius]
  F --> G[Kết quả JSON với items/meta]
  G --> H[Mô hình tạo phản hồi và follow-up]

Bạn mô tả tool và use case càng tốt, yếu tố ngẫu nhiên càng ít và định tuyến càng ổn định.

Insight: Tool Call SEO

Trong hệ sinh thái Apps, sớm thôi bạn sẽ có không chỉ cạnh tranh giành sự chú ý của con người trong catalog, mà còn cạnh tranh giành sự chú ý của chính mô hình. Với cùng một yêu cầu, ChatGPT có thể gọi cả chục ứng dụng, và lựa chọn sẽ diễn ra không phải ở nơi ai có thiết kế trình bày đẹp hơn, mà ở “kết quả tìm kiếm” trong đầu mô hình. Lớp vô hình này ngày càng giống SEO, chỉ khác là thay vì trang web, bạn có tools và MCP server.

Về bản chất, mô hình xếp hạng các ứng viên: trước ở mức App, sau ở mức tool riêng lẻ. Nó nhìn tên, descriptions, schema, annotation và so khớp chúng với câu chữ của yêu cầu. Nếu trong mô tả recommend_gifts có “gợi ý quà theo ngân sách và sở thích người nhận”, còn yêu cầu là “hãy gợi ý quà cho bạn gamer với 50 USD”, tool này có nhiều cơ hội “lọt top” hơn so với search chung chung với mô tả “làm việc với quà”.

Từ đây nảy ra ý tưởng thực hành Tool Call SEO: coi tên, descriptions, giá trị enum và metadata như từ khóa và snippet. Bạn không chỉ mô tả hợp đồng cho developer — bạn tối ưu nó cho lưu lượng yêu cầu thực từ golden prompt set của mình. Câu chữ quá chung, vùng chồng chéo giữa các tool, God tool không có ngách rõ — tất cả đều làm giảm “CTR” của App trong đầu mô hình.

9. Bài tập thực hành nhỏ

Hãy thử (trên giấy hoặc trong repo của bạn) làm như sau.

Đầu tiên chọn một kịch bản chủ chốt của GiftGenius — ví dụ, “Chọn quà cho đồng nghiệp với ngân sách hạn chế”.

Hãy phác thảo cho nó:

  1. Tool riêng nào cần cho kịch bản này: chỉ recommend_gifts, hay bạn cần thêm tool chuyên biệt cho B2B, hoặc, chẳng hạn, sau recommend_gifts dùng similar_gifts để tạo biến thể?
  2. Những trường nào thực sự cần thiết trong input schema của recommend_gifts. Trường nào có thể hỏi riêng người dùng (qua follow‑up), thay vì bắt mô hình đoán.
  3. outputSchema nên trông thế nào để mô hình có thể giải thích lựa chọn và đề xuất bước tiếp theo (ví dụ, chuyển sang chế độ B2B, chỉ hiển thị quà số, thu hẹp theo khoảng giá).

Sau đó xem lại golden prompt set từ bài trước và kiểm tra:

  • với mỗi yêu cầu mẫu, có tool hiển nhiên tương ứng không (recommend_gifts, get_gift, similar_gifts, v.v.);
  • có xảy ra việc hai tool cùng “phù hợp” với cùng một yêu cầu (overlapping tools) không;
  • có cần tăng cường mô tả hoặc đổi tên tool nào đó để mô hình bớt nhầm lẫn không.

Đây chính là quá trình bạn sẽ lặp lại trước mỗi thay đổi lớn về prompt, schema hoặc logic — về bản chất là mini‑eval cho chất lượng discovery.

Nếu tóm lại mọi thứ ở trên thành checklist, lúc này bạn cần:

  • phân rã kịch bản thành 2–4 tool có ý nghĩa rõ ràng;
  • mô tả cẩn thận inputSchema/outputSchema với ví dụ và enum;
  • chỉnh lý tên gọi, descriptions và annotation;
  • đồng bộ với system‑prompt và metadata của App.

Ở các module tiếp theo, chúng ta sẽ xem mọi thứ hoạt động qua MCP và cách chẩn đoán hành vi lạ của discovery/định tuyến.

10. Các lỗi thường gặp khi thiết kế tools và metadata

Lỗi #1: “Chúng tôi đã mô tả hết trong system‑prompt, tools sẽ tự biết đường”.
Nếu bạn mô tả rất hay vai trò của App, ranh giới trách nhiệm và hành vi UX, nhưng lại để tool với tên tool1, search, do_stuff và schema không có mô tả, mô hình sẽ không thể liên kết văn bản đẹp đẽ của bạn với lời gọi thực tế. Với ChatGPT, tool là giao diện chính; không có metadata tử tế thì system‑prompt cũng không cứu nổi.

Lỗi #2: God Tool làm mọi thứ.
Mong muốn “tối ưu hóa” bằng một hàm với tham số mode là dễ hiểu, nhưng dẫn đến JSON schema “quái vật”, mô tả rối rắm và định tuyến kém. Mô hình bắt đầu đoán dùng chế độ nào, còn bạn phải duy trì một switch khổng lồ ở server. Tốt hơn là vài tool rõ ràng cho từng bước cụ thể, hơn là một tool “làm hết”.

Lỗi #3: Input schema bị nhồi đầy trường “để đó khi cần”.
Developer thường cố nhét vào inputSchema tất cả tham số có thể hữu ích, cộng thêm vài trường nội bộ. Kết quả, mô hình cố đoán thứ nó không thể biết (ví dụ tenantId), còn bạn ngạc nhiên trước các giá trị kỳ lạ. Input Schema chỉ nên chứa thứ mà mô hình thực sự có thể suy ra từ hội thoại hoặc hỏi thêm. Chi tiết nội bộ thì thêm ở server.

Lỗi #4: Dữ liệu output “câm” không có meta‑info.
Trả về từ tool chỉ mảng object nghe có vẻ hấp dẫn. Nhưng như vậy bạn tước đi khả năng vì sao những kết quả đó xuất hiện. Thiếu các trường như score, reason, searchCriteria, totalCandidates, mô hình khó xây lời giải thích trung thực và follow‑up. Thêm một lớp meta nhỏ với tiêu chí tìm kiếm và lời khuyên thường cải thiện chất lượng câu trả lời đáng kể.

Lỗi #5: Mô tả chung chung: “Làm việc với quà tặng”, “Tìm kiếm khóa học”, “Xử lý dữ liệu”.
Những mô tả như vậy tệ vì không cho mô hình trigger hay giới hạn nào. Nó không biết chính xác khi nào cần gọi tool và áp dụng ở đâu. Mô tả tốt bắt đầu bằng “Hãy dùng tool này khi…” và chứa kịch bản cụ thể, cùng các cấm đoán kiểu “Đừng dùng cho…”. Lý tưởng là các câu chữ này trùng với “yêu cầu vàng” trong golden prompt set của bạn.

Lỗi #6: Bỏ qua annotation và trộn lẫn hành động read‑only với hành động thay đổi dữ liệu.
Nếu bạn không đánh dấu các tool chỉ đọc dữ liệu (readOnlyHint) và các tool thực hiện hành động (destructiveHint, openWorldHint), mô hình không thể dựng UX xác nhận cho đúng. Kết quả là hoặc quá nhiều “Bạn có chắc không?” ở mọi bước, hoặc ngược lại, các mua bán/thay đổi diễn ra âm thầm mà không có sự đồng ý của người dùng. Annotation là cách rẻ và hiệu quả để gợi ý tầm quan trọng của thao tác cho mô hình.

Lỗi #7: Metadata App cho catalog và metadata cho in‑conversation “sống” ở hai thế giới khác nhau.
Có khi mô tả ngắn trong catalog do người marketing viết (“Trợ lý AI cách mạng thay đổi cuộc sống”), còn description của tools và system‑prompt do developer viết (“gợi ý quà theo ngân sách”). Kết quả là trong catalog không rõ App làm gì, còn mô hình trong chat không thể ghép các yêu cầu kiểu “đây là dịch vụ gì?” với khả năng thực của App. Hãy viết metadata như một đặc tả thống nhất, không phải hai văn bản marketing độc lập.

1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Hành vi của ChatGPT App
Chỉ dẫn cho mô hình và hành vi của ChatGPT App
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION