CodeGym /Các khóa học /ChatGPT Apps /Công cụ của agent (Tools): schema, định tuyến, lỗi

Công cụ của agent (Tools): schema, định tuyến, lỗi

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

1. Công cụ của agent: thực chất là gì

Trong các mô-đun trước, bạn đã thấy công cụ từ phía Apps SDK — như “hàm backend” mà ChatGPT gọi qua App của bạn. Giờ đổi góc nhìn: xem công cụ qua mắt agent trong Agents SDK và tìm hiểu cách nó chọn gọi gì và xử lý lỗi ra sao.

Trong backend thông thường, bạn hay nghĩ theo “endpoint”, “phương thức controller”, “hàm service”. Trong thế giới agent, đơn vị hành động cơ bản là công cụ (tool). tools của agent và mcp-tools là khác nhau, dù có giao nhau.

Nói chính xác: công cụ trong ngữ cảnh ChatGPT Agents SDK là mô tả một hàm mà mô hình có thể yêu cầu thực thi. Mô hình không tự chạy code; nó tạo một yêu cầu có cấu trúc (thường là JSON), còn runtime (code của bạn, máy chủ MCP hoặc Agents SDK) sẽ thực hiện thao tác và trả về kết quả.

Trong hệ sinh thái ChatGPT Agents SDK, công cụ được mô tả bằng cấu hình: có name, descriptionparameters (JSON Schema của đối số). Agent nhìn thấy tập công cụ này, giữ chúng trong ngữ cảnh của mình và trong quá trình reasoning quyết định gọi tool nào và với đối số nào.

Agent (hoặc ChatGPT như host) nhận danh sách này, “ghi nhớ” vào ngữ cảnh của nó và trong quá trình suy luận (reasoning) quyết định: với yêu cầu người dùng, cần gọi công cụ nào và kèm đối số gì. Vì vậy trong các đặc tả mới lặp lại câu “tools are a contract” — công cụ là hợp đồng giữa mô hình và code của bạn, chứ không chỉ là “hàm trong Python/TS”.

Có thể ví von với API cổ điển. Route /api/gifts/search — thuần cú pháp: URL, phương thức, định dạng body. Còn tool search_gifts — là ngữ nghĩa: “tìm quà theo hồ sơ và ngân sách”. Mô tả công cụ là một dạng prompt có cấu trúc và dành cho LLM, chứ không phải cho con người.

2. Các loại công cụ: LLM‑agent có thể làm gì

Để không lạc giữa “các hàm làm được mọi thứ”, hữu ích khi nhìn công cụ như vài nhóm điển hình. Đây không phải kiểu hóa chính thức của SDK, mà là tư duy kiến trúc sẽ rất có ích.

Trong backend của chúng ta, LLM‑agents thường có ba nguồn công cụ.

  • Công cụ nghiệp vụ cục bộ. Những gì sống trong backend của bạn: làm việc với DB, logic domain (lọc, gợi ý, chấm điểm). Ví dụ, với GiftGenius có thể có công cụ lấy sản phẩm từ bảng PostgreSQL riêng hoặc tính điểm cá nhân hóa “món quà này phù hợp với người này đến mức nào”.
  • Công cụ MCP. Ở đây máy chủ MCP là nhà cung cấp công cụ (tools): nó đăng ký hàm, tài nguyên và prompt rồi cung cấp cho client (ChatGPT, LLM‑agent). Thông qua MCP, công cụ có thể gọi API bên ngoài, làm việc với file hoặc cung cấp template prompt.
  • Công cụ tích hợp. Những gì kết nối bạn với thế giới bên ngoài: ACP/commerce (tạo đơn hàng và checkout), gửi email, webhook, ghi vào CRM. Các công cụ (tools) này thường rủi ro hơn vì thay đổi trạng thái hệ thống bên ngoài, nên cần đặc biệt nghiêm ngặt về bảo mật và tính idempotent.

Còn có phân loại hữu ích khác — theo tính chất hành động. Trong nghiên cứu về LLM‑tools thường nêu: công cụ lấy dữ liệu (tìm kiếm, RAG, get_*), công cụ hành động có tác dụng phụ (create_order, send_email), thuần tính toán (calculate_loan) và hệ thống/điều khiển (handoff_to_human, finish_task).

Để cố định ý tưởng, hãy nhìn một bảng nhỏ.

Danh mục Ví dụ trong GiftGenius Tác dụng phụ Rủi ro
Data Retrieval
search_gifts, get_details
Không Thấp
Action / Mutating
create_order, buy_gift
Cao
Computation
estimate_delivery_cost
Không Trung bình
System / Control
finish_recommendation
Không Logic

Ở góc độ kiến trúc, điều quan trọng nhất: công cụ read‑only nên nhiều và rẻ, còn công cụ thay đổi trạng thái — hiếm, cực kỳ cẩn trọng, có log, idempotency và thường có xác nhận từ người dùng.

Tiếp theo chúng ta chủ yếu nói về công cụ lấy dữ liệu và công cụ Action, vì logic của GiftGenius xây trên đó.

3. JSON Schema như hợp đồng giữa mô hình và code của bạn

Giờ đi sâu vào cách mô tả công cụ. Trong ChatGPT Agents SDK (cũng như Apps SDK), định dạng tiêu chuẩn để mô tả tham số công cụ là JSON Schema: bạn mô tả kiểu object, các properties, kiểu trường, trường bắt buộc, ràng buộc, v.v.

Quan trọng: JSON Schema ở đây không chỉ để xác thực. Nó là một phần của prompt cho mô hình. Trong hướng dẫn chính thức của OpenAI về thiết kế công cụ (tools), người ta nói rõ chất lượng hoạt động của agent phụ thuộc mạnh vào việc các trường, tên và mô tả của chúng được mô tả đầy đủ, rõ ràng đến mức nào.

Xem ví dụ cho GiftGenius đã xuất hiện trong kế hoạch khóa học.

{
  "name": "search_gifts",
  "description": "Tìm quà dựa trên kiểu người nhận, sở thích và ngân sách.",
  "parameters": {
    "type": "object",
    "properties": {
      "recipient_type": {
        "type": "string",
        "description": "Ai là người nhận quà (ví dụ: 'nam', 'nữ', 'trẻ em')."
      },
      "interests": {
        "type": "array",
        "items": { "type": "string" },
        "description": "Sở thích chính (thể thao, sách, công nghệ, v.v.)."
      },
      "budget": {
        "type": "number",
        "description": "Ngân sách tối đa theo đơn vị tiền tệ của người dùng."
      }
    },
    "required": ["recipient_type", "budget"]
  }
}

Có vài điểm quan trọng.

  • Thứ nhất, namedescription. Với mô hình, đây là tín hiệu chính để khi nào dùng công cụ này. Tài liệu về định tuyến ngữ nghĩa nhấn mạnh mô tả công cụ thực chất là API cho mô hình: nếu bạn đặt tên func1 và ghi “làm gì đó hữu ích”, mô hình sẽ không hiểu khi nào gọi. Nếu đặt search_gifts và mô tả rõ ràng, việc lựa chọn sẽ hiển nhiên hơn.
  • Thứ hai, parameters. Tên trường và mô tả của chúng cực kỳ quan trọng. Với LLM, recipient_type dễ hiểu hơn nhiều so với type. Một mô tả tốt như “Ai là người nhận quà…” gợi ý rằng cần đưa kiểu người nhận vào đây, chứ không phải, ví dụ, kiểu gói quà.
  • Thứ ba, required. Đây không chỉ là xác thực phía bạn, mà còn là gợi ý cho mô hình: nó sẽ cố gắng điền các trường bắt buộc, còn trường không bắt buộc có thể bỏ qua nếu ngữ cảnh không rõ. Điều này giảm số tool‑call “rỗng” hoặc sai.

Hướng dẫn chính thức cho Apps SDK khuyên: thiết kế công cụ hẹp, một trách nhiệm, tên và mô tả rõ ràng, tránh công cụ “làm mọi thứ về quà” gom nhiều việc khác nhau.

4. Thiết kế công cụ cho GiftGenius: từ schema tới code

Lấy GiftGenius và thêm vào hai công cụ chủ chốt của LLM‑agent, hầu như cần trong mọi kịch bản:

  • suggest_gifts(profile, budget) — trả về danh sách ứng viên;
  • get_gift_details(gift_id) — mở chi tiết cho một món quà cụ thể.

suggest_giftsget_gift_details là ví dụ điển hình của công cụ nghiệp vụ cục bộ trong phân loại trước, chủ yếu thuộc loại Data Retrieval.

Schema cho suggest_gifts

Bắt đầu với JSON Schema thuần, rồi cho thấy nó có thể trông như thế nào trong code TypeScript của backend/runtime agent.

{
  "name": "suggest_gifts",
  "description": "Gợi ý danh sách quà dựa trên hồ sơ người nhận và ngân sách.",
  "parameters": {
    "type": "object",
    "properties": {
      "age": {
        "type": "integer",
        "minimum": 0,
        "maximum": 120,
        "description": "Tuổi của người nhận (năm)."
      },
      "relationship": {
        "type": "string",
        "enum": ["friend", "coworker", "partner", "family"],
        "description": "Mối quan hệ với người nhận: bạn bè, đồng nghiệp, người yêu/đối tác, gia đình."
      },
      "interests": {
        "type": "array",
        "items": { "type": "string" },
        "description": "Sở thích của người nhận (thể thao, sách, công nghệ, v.v.)."
      },
      "budget": {
        "type": "number",
        "minimum": 1,
        "description": "Ngân sách tối đa theo đơn vị tiền tệ của người dùng."
      }
    },
    "required": ["budget"]
  }
}

Ở đây ta dùng enum cho relationship để mô hình không bịa các chuỗi tùy ý như "đồng nghiệp tệ" và đẩy sâu vào code. Thiết kế schema cẩn thận giúp cả mô hình (thấy các giá trị hợp lệ) lẫn lập trình viên (ít bất ngờ khi runtime).

Giả sử ta có máy chủ MCP trên Node.js với McpServer nào đó. Đăng ký công cụ có thể như sau:

// ví dụ rút gọn về đăng ký công cụ trong máy chủ MCP
server.registerTool(
  {
    name: "suggest_gifts",
    description: "Gợi ý quà theo hồ sơ và ngân sách.",
    inputSchema: suggestGiftsSchema
  },
  async (input, ctx) => {
    const gifts = await findGiftsInDb(input, ctx.userLocale);
    return { items: gifts }; // JSON mà agent sẽ nhìn thấy sau đó
  }
);

Code được đơn giản hóa, nhưng logic rõ ràng: một nơi — mô tả hợp đồng (tên, mô tả, schema), nơi khác — phần triển khai.

Schema cho get_gift_details

Công cụ thứ hai, hầu như cần cho mọi vitrine:

{
  "name": "get_gift_details",
  "description": "Lấy đầy đủ thông tin về món quà theo định danh của nó.",
  "parameters": {
    "type": "object",
    "properties": {
      "gift_id": {
        "type": "string",
        "description": "UUID của quà trong cơ sở dữ liệu GiftGenius."
      }
    },
    "required": ["gift_id"]
  }
}

Và đăng ký tương tự:

server.registerTool(
  {
    name: "get_gift_details",
    description: "Trả về thông tin chi tiết về món quà.",
    inputSchema: getGiftDetailsSchema
  },
  async ({ gift_id }) => {
    const gift = await db.gifts.findById(gift_id);
    if (!gift) return { notFound: true };
    return { gift };
  }
);

Lưu ý: ở đây ta ngay lập tức cho thấy công cụ có thể trả về notFound: true. Đây là mầm mống của lỗi ngữ nghĩa (lỗi nghiệp vụ), sẽ bàn bên dưới. Agent có thể thấy “không tìm thấy quà” và quyết định: thử id khác hoặc đề xuất người dùng chọn sản phẩm khác.

5. Agent chọn gọi công cụ nào như thế nào

Giờ đến phần thú vị: định tuyến. Trong web‑app truyền thống, routing cứng: URL → controller cụ thể. Trong thế giới ChatGPT Apps và agent, việc chọn công cụ diễn ra theo ngữ nghĩa và xác suất.

Vòng lặp cấp cao có thể vẽ như sau:

flowchart TD
  U[User message] --> M["Model (agent)"]
  M -->|phân tích yêu cầu| C{Có cần tool không?}
  C -->|không| T[Phản hồi văn bản]
  C -->|có| S[Chọn công cụ]
  S --> K[Tạo đối số JSON]
  K --> R[Thực thi công cụ]
  R --> M2[Model thấy kết quả]
  M2 --> T2[Câu trả lời cuối cùng hoặc bước tiếp theo]

Ở mỗi bước agent nhìn thấy vài thứ:

  • Thứ nhất, system‑instructions (vai trò của agent, giới hạn);
  • Thứ hai, lịch sử hội thoại;
  • Và cuối cùng, danh sách công cụ (tools) với name, description, inputSchema.

Khi có tin nhắn người dùng, mô hình so sánh ngữ nghĩa yêu cầu với mô tả công cụ (so khớp ngữ nghĩa). Nếu yêu cầu “gợi ý quà cho bạn cùng làm dưới 50 đô la”, mô tả của suggest_gifts phù hợp hơn nhiều so với get_gift_details, và agent nhiều khả năng sẽ chọn nó.

Hướng dẫn chính thức nhấn mạnh hai điều ảnh hưởng mạnh đến chất lượng định tuyến.

  • Thứ nhất, tránh các công cụ trùng lặp về ngữ nghĩa: nếu có cả search_giftsfind_gifts mô tả gần như nhau, mô hình sẽ bối rối.
  • Thứ hai, bám nguyên tắc single responsibility cho công cụ: một tool — một nhiệm vụ rõ ràng, không phải “gợi ý quà, tạo đơn và gửi email”.

Bên trong các LLM‑agent khác nhau có cơ chế điều khiển chế độ chọn công cụ: ví dụ, “auto” (mô hình tự quyết định có cần công cụ không), “required” (bắt buộc gọi tool), “none” (tắt tools). Điều này giúp trong workflow phức tạp (đa bước), khi ở bước nào đó bạn muốn buộc gọi suggest_gifts thay vì để mô hình “tán gẫu”.

Ví dụ định tuyến ngữ nghĩa trong GiftGenius

Giả sử agent có ít nhất hai công cụ: suggest_giftsget_gift_details.

  1. Người dùng viết: “Gợi ý quà cho đồng nghiệp dưới 30 đô la, anh ấy thích board game”.
  2. Agent thấy yêu cầu có mục tiêu “gợi ý quà”, thông tin ngân sách và sở thích. Mô tả suggest_gifts khớp hoàn hảo — gọi công cụ này.
  3. Công cụ trả danh sách 5 món quà với id, tên và mô tả ngắn.
  4. Người dùng tiếp: “Nói chi tiết hơn về lựa chọn thứ ba”. Agent ánh xạ “lựa chọn thứ ba” với id từ kết quả trước, và theo ngữ nghĩa hợp với công cụ get_gift_details — nên gọi nó.

Lưu ý: không hề có đoạn code “nếu yêu cầu có từ ‘gợi ý’ thì gọi suggest_gifts”. Mô hình tự xử lý dựa trên mô tả và lịch sử hội thoại. Trách nhiệm của bạn là làm cho lựa chọn trở nên hiển nhiên với cả mô hình lẫn con người.

6. Lỗi của công cụ: không phải 500, mà là tín hiệu cho mô hình

Nhớ rằng trong get_gift_details ta đã cho thấy notFound: true? Đây chính là ví dụ về lỗi nghiệp vụ, mà agent phải nhìn và xử lý có ý thức, thay vì nhận 500 trần.

Giờ đến phần “đau” nhất. Trong REST‑API thường, có gì đó sập trong backend — trả 500 Internal Server Error, ghi stack trace vào log — và người dùng tự xoay xở. Với agent thì cách này hoạt động kém.

Các hướng dẫn thực tiễn và tài liệu về Agents SDK khuyên đối xử với lỗi công cụ như sự kiện có thể quan sát, không chỉ là lỗi sập. Đây thường được gọi là mẫu “Error as Observation”.

Nói gọn, bạn không nên “sập” vô cớ; bạn nên trả về cho mô hình một phản hồi có cấu trúc, giải thích điều gì đã sai để nó thích ứng: diễn đạt lại yêu cầu, hỏi người dùng, thử công cụ khác, v.v.

Thường chia lỗi thành ba nhóm.

  • Lỗi xác thực tham số. Mô hình có thể sinh tham số sai: thiếu trường bắt buộc, đưa chuỗi thay vì số, ra ngoài miền giá trị. Ở đây, schema và validation của bạn không chỉ để ném exception, mà còn để trả lời có ý nghĩa: ví dụ, trả về trường nào sai và vì sao.
  • Lỗi nghiệp vụ. Các tình huống dự kiến như “không tìm thấy sản phẩm”, “vùng không hỗ trợ”, “ngân sách quá thấp cho loại quà này”. Về phía API cũng là lỗi, nhưng nên trả trong phạm vi phản hồi bình thường — với mã và thông điệp rõ ràng, không phải crash.
  • Lỗi hệ thống. Timeout dịch vụ bên ngoài, sự cố mạng, DB trục trặc. Ở đây agent thường chỉ cần thông điệp chung chung, cẩn trọng như “dịch vụ tạm thời không khả dụng, hãy thử lại sau”. Không có stack trace, tên bảng hay chi tiết nội bộ không cần cho mô hình và có thể nguy hiểm về bảo mật.

Tài liệu chính thức về Agents SDK thậm chí gợi ý cơ chế failure_error_function, cho phép tạo thông điệp lỗi gọn gàng mà mô hình sẽ thấy, thay vì ném exception lên stack.

Cấu trúc lỗi “thân thiện”

Trong công cụ của agent (trong backend của bạn), bạn có thể quy ước mọi lỗi trả về dưới dạng đối tượng, ví dụ:

type ToolError = {
  code: string;      // 'VALIDATION_ERROR', 'OUT_OF_STOCK', ...
  message: string;   // dành cho mô hình
  retryable: boolean;
};

Kết quả của công cụ — là hợp kiểu:

type SuggestGiftsResult =
  | {
      ok: true;
      items: GiftSummary[];
    }
  | {
      ok: false;
      error: ToolError;
    };

Mô hình (hoặc runtime agent) thấy JSON này và quyết định: nếu retryable: true, có thể thử lại với thay đổi nhỏ; nếu là lỗi nghiệp vụ và không retry, tốt hơn quay lại người dùng và giải thích.

7. Ví dụ: xác thực, lỗi nghiệp vụ và lỗi hệ thống

Quay lại backend/công cụ của agent và xem cách hiện thực các ý tưởng trên trong code.

Lỗi xác thực

Giả sử công cụ suggest_gifts nhận vào ngân sách âm do mô hình sinh ra.

async function handleSuggestGifts(input: SuggestGiftsInput)
  : Promise<SuggestGiftsResult> {

  if (input.budget <= 0) {
    return {
      ok: false,
      error: {
        code: "VALIDATION_ERROR",
        message: "budget phải là một số dương.",
        retryable: false
      }
    };
  }

  const items = await findGiftsInDb(input);
  return { ok: true, items };
}

Ở đây ta chủ ý không ném exception, mà trả lỗi có cấu trúc. Agent có thể suy nghĩ lại yêu cầu: có thể nó nhầm đơn vị tiền tệ, và sẽ hỏi người dùng hoặc thừa nhận không thể gợi ý với ngân sách như vậy.

Lỗi nghiệp vụ

Giờ ví dụ với get_gift_details. Có thể không có món quà theo id đã cho.

async function handleGetGiftDetails(input: { gift_id: string }) {
  const gift = await db.gifts.findById(input.gift_id);

  if (!gift) {
    return {
      ok: false,
      error: {
        code: "GIFT_NOT_FOUND",
        message: "Không tìm thấy quà tặng với ID đã cho.",
        retryable: false
      }
    };
  }

  return { ok: true, gift };
}

Phản hồi của mô hình có thể là: “Có vẻ món quà đã chọn không còn nữa. Tôi có thể gợi ý vài lựa chọn tương tự không?”. Để làm vậy, agent không cần thấy lỗi SQL hay stack trace — chỉ cần codemessage dễ hiểu.

Lỗi hệ thống

Cuối cùng, ví dụ lỗi hệ thống. Giả sử công cụ của bạn gọi API giao hàng bên ngoài và đôi khi “ngã”.

async function handleEstimateDelivery(input: EstimateDeliveryInput) {
  try {
    const eta = await callDeliveryApi(input);
    return { ok: true, eta_days: eta };
  } catch (e) {
    return {
      ok: false,
      error: {
        code: "DELIVERY_SERVICE_UNAVAILABLE",
        message: "Dịch vụ giao hàng tạm thời không khả dụng.",
        retryable: true
      }
    };
  }
}

Agent có thể quyết định: “Có vẻ dịch vụ giao hàng hiện không khả dụng. Tôi vẫn sẽ hiển thị quà, nhưng thời gian giao thực tế có thể khác. Bạn có muốn tiếp tục không?”.

8. Bảo mật và tính idempotent của công cụ (lướt nhanh từ góc nhìn tools)

Chủ đề bảo mật và quyền hạn sẽ có riêng, nhưng công cụ của agent gắn chặt với chúng nên không thể bỏ qua.

Thứ nhất, cần tách công cụ đọc và công cụ ghi. Trong mô tả, schema và phân quyền, hãy chỉ rõ tool nào chỉ đọc dữ liệu và an toàn tuyệt đối, tool nào có thể trừ tiền, sửa đơn hàng, v.v. Tài liệu và diễn đàn về kịch bản agent nói thẳng về việc tách công cụ ReadOnly và Mutating (tools).

Thứ hai, với công cụ mutating, cần nghĩ về idempotency. Agent hoặc MCP‑client hoàn toàn có thể gọi lại (ví dụ do lỗi mạng), và bạn không muốn create_order tạo hai đơn thay vì một. Mẫu điển hình:

  • idempotency‑key được truyền như đối số của công cụ;
  • kiểm tra sự tồn tại của thao tác trước khi thực hiện;
  • chia bước thành “tạo nháp đơn hàng” và “xác nhận đơn”.

Tất cả gắn chặt với cách bạn thiết kế hợp đồng công cụ: nếu JSON Schema không có trường cho idempotency‑key, việc thêm tính idempotent sau này sẽ đau hơn nhiều.

9. Nhìn nhanh về Agents SDK: công cụ trông thế nào trong runtime

Phần này — tổng quan nhỏ cho ai sẽ làm với Agents SDK định hướng TypeScript. Dù phần chính của khóa nói về MCP, cũng hữu ích để hiểu Agents SDK nhìn công cụ như thế nào và tool điển hình trong runtime ra sao.

Trong tài liệu chính thức thường mô tả một “công cụ hàm”: bất kỳ hàm nào được mô tả qua đối tượng cấu hình (hoặc helper như tool(...)) và có type, có thể tự động biến thành công cụ, để SDK sinh JSON Schema và mô tả.

Về mặt khái niệm giống hệt những gì ta đã bàn: tên hàm, tham số và chú thích/description đóng vai trò tên, schema và mô tả của công cụ. Khác biệt là phần “cơ khí” do SDK và/hoặc thư viện schema (như Zod hay JSON Schema) lo cho bạn.

Ví dụ giả định (pseudo‑TypeScript, rút gọn):

type Gift = {
  id: string;
  title: string;
  // ...
};

const suggestGifts = tool({
  name: "suggest_gifts",
  description: "Gợi ý danh sách quà theo kiểu người nhận và ngân sách.",
  parameters: {
    type: "object",
    properties: {
      recipient_type: {
        type: "string",
        description: "Ai là người nhận quà (ví dụ: 'nam', 'nữ', 'trẻ em')."
      },
      budget: {
        type: "number",
        description: "Ngân sách tối đa theo đơn vị tiền tệ của người dùng."
      }
    },
    required: ["recipient_type", "budget"]
  }
}, async (args: { recipient_type: string; budget: number }): Promise<Gift[]> => {
  // Bên trong — logic domain của bạn
  return findGifts(args.recipient_type, args.budget);
});

SDK (hoặc helper tool) sẽ dựa trên đối tượng parameters để dựng JSON Schema và truyền cho agent, còn runtime lo validation và marshalling đối số đi/về. Về khái niệm chính là những gì bạn tự làm trong máy chủ MCP bằng TypeScript, chỉ khác là công cụ “gắn” thẳng vào runtime agent.

Điều quan trọng không phải học thuộc cú pháp helper tool, mà là nắm ý: gõ kiểu chất lượng + description/chú thích rõ ràng = công cụ chất lượng.

Gom lại: một công cụ agent tốt là hàm hẹp, mô tả rõ ràng với JSON Schema được cân nhắc, mô tả dễ hiểu cho mô hình và xử lý lỗi gọn gàng. Định tuyến ngữ nghĩa chỉ tốt khi công cụ không chồng lấn về nghĩa. Và các thao tác mutating phải an toàn và idempotent, nếu không agent ở production sẽ nhanh chóng thành nguồn bất ngờ.

10. Sai lầm thường gặp khi thiết kế công cụ của agent

Sai lầm №1: Công cụ quá rộng kiểu “do_everything”.
Đôi khi rất muốn nhét mọi thứ vào một công cụ manage_gifts, vừa tìm quà, vừa xem chi tiết, vừa tạo đơn, vừa gửi thư. Mô hình sẽ khổ: mô tả mơ hồ, định tuyến ngữ nghĩa xuống cấp, agent gọi công cụ này “cho chắc” ngay cả khi chỉ cần tìm kiếm đơn giản. Tốt hơn tách nhiệm vụ thành công cụ riêng với một trách nhiệm rõ ràng.

Sai lầm №2: Công cụ chồng lấn ngữ nghĩa.
Nếu có search_giftsfind_gifts đều “tìm quà theo sở thích”, mô hình sẽ chọn ngẫu nhiên giữa chúng. Hậu quả là hành vi bất ổn: cùng yêu cầu lúc thì vào tool này, lúc tool kia. Hãy đảm bảo mỗi tên và mô tả chiếm “ngách” riêng trong không gian ngữ nghĩa.

Sai lầm №3: Mô tả và trường schema kém hoặc thiếu.
Tên func1, mô tả “Does something” và tham số data: string — cách kinh điển để làm agent “ngốc”. Mô hình không biết đọc nguồn code của bạn. Nó dựa vào description, propertiesdescription của chúng trong schema. Nếu bạn không giải thích recipient_type là gì, mô hình sẽ đoán và sai.

Sai lầm №4: Chỉ nghĩ đến happy‑path, bỏ qua lỗi.
Nhiều triển khai công cụ giả định: “Luôn có tham số đúng và dịch vụ sẵn sàng.” Thực tế mô hình dễ sinh tham số sai, dịch vụ ngoài sập, DB đôi lúc “timeout”. Nếu không thiết kế định dạng lỗi và không trả thông điệp có ý nghĩa cho agent, nó không thể điều chỉnh hành vi và sẽ hoặc im lặng sập, hoặc bịa.

Sai lầm №5: Ném 500 thô và stack trace vào LLM.
Trong REST‑API ta quen log đầy đủ stack trace để debug nhanh. Trong bối cảnh agent, stack trace gửi cho mô hình vừa vô dụng (mô hình không biết SQLException là gì trong thư viện cụ thể của bạn) vừa có thể nguy hiểm (tiết lộ chi tiết hiện thực và có thể thông tin nhạy cảm). Hữu ích hơn là bắt exception, ghi chi tiết vào log, và gửi cho mô hình code cùng message gọn gàng.

Sai lầm №6: Thiếu idempotency ở công cụ mutating.
Công cụ create_order không có idempotency‑key — là lời mời gọi ra đơn kép, nhất là khi có lỗi mạng và retry tự động. Nếu agent của bạn chạy trong kịch bản thương mại, công cụ liên quan tiền bạc phải được thiết kế để gọi lặp không dẫn đến trừ tiền/đơn hàng nhân đôi.

Sai lầm №7: Nhét bí mật và chi tiết kỹ thuật vào schema hay mô tả.
Đôi khi lập trình viên quen tay viết trong description: “Bên trong gọi dịch vụ X tại https://internal-api.example.com”. Mô hình không cần thông tin này, người dùng càng không. Schema và mô tả là phần của prompt, sống trong ngữ cảnh của mô hình, và không nên chứa URL nội bộ, tên bảng riêng tư hay bí mật.

Sai lầm №8: Truyền mọi thứ vào công cụ, thay vì bộ trường được suy nghĩ kỹ.
Dễ bị cám dỗ “truyền toàn bộ prompt của người dùng như một chuỗi, rồi vào trong xử lý”. Làm vậy bạn mất lợi ích cấu trúc hóa qua JSON Schema: mô hình không hiểu phần nào của yêu cầu quan trọng cho logic, bạn mất validation và tính dự đoán. Tốt hơn trích các trường rõ ràng từ yêu cầu (budget, interests, user_location) và mô tả chúng như phần của hợp đồng công cụ.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION