1. Tool gating là gì và vì sao đáng có một bài riêng
Cho đến giờ, trong các ví dụ đơn giản ta làm như sau: mô tả bộ công cụ cho App, kết nối MCP‑server — và tất cả luôn sẵn có cho mô hình. Ở góc độ “làm demo trong 5 phút” thì ổn. Ở góc độ sản phẩm thực tế — không hẳn.
Tool gating là một mẫu trong đó danh sách công cụ khả dụng cho mô hình không cố định, mà phụ thuộc vào ngữ cảnh: bước của workflow, quyền của người dùng, trạng thái dữ liệu, v.v.
Điều quan trọng nhất: danh sách tools không phải là “đống tạp nham mọi thứ bạn từng viết”, mà là một phần của thiết kế kịch bản. Khi bạn thiết kế workflow, thực chất bạn cũng đang thiết kế những công cụ nào mô hình được phép nhìn thấy ở mỗi giai đoạn.
Một phép so sánh đơn giản: bạn không cho thực tập sinh trong ngân hàng truy cập tất cả hệ thống ngay lập tức — ban đầu chỉ xem, sau đó các thao tác đơn giản, rồi mới đến các thao tác nghiêm túc hơn. Ở đây cũng vậy, chỉ khác là “thực tập sinh” chính là LLM.
2. Vấn đề “tất cả công cụ cùng lúc”: ô nhiễm ngữ cảnh và bảo mật
Nếu đưa cho mô hình hàng chục công cụ, nó sẽ gặp vấn đề ở nhiều mặt: quá tải ngữ cảnh, rối khi lựa chọn và các câu hỏi về bảo mật. Nghiên cứu của OpenAI/Anthropic cho thấy càng nhiều chức năng bạn mô tả trong ngữ cảnh, càng kém mô hình chọn đúng chức năng cần thiết.
Thứ nhất, mỗi định nghĩa công cụ là các token: tên, mô tả, JSON Schema. Danh sách 30–40 tools dễ dàng ngốn vài nghìn token. Đó là những token bạn có thể dùng cho lịch sử đối thoại, bối cảnh người dùng, ví dụ về câu trả lời tốt. Thay vào đó, mô hình phải đọc một “cuốn tiểu thuyết” về API của bạn.
Thứ hai, khi các công cụ giống nhau, mô hình bắt đầu nhầm lẫn. Nếu bạn có search_products và get_product_details, nó có thể cố gọi get_product_details trực tiếp với truy vấn dạng văn bản, vì mô tả có vẻ phù hợp hơn.
Thêm vào đó là vấn đề bảo mật. Có một nguyên tắc nhàm chán nhưng quan trọng là đặc quyền tối thiểu (least privilege): hệ thống chỉ nên có những khả năng thực sự cần “ngay lúc này”. Nếu ở bước làm quen mô hình đã biết về checkout, chỉ một prompt injection nhỏ từ người dùng cũng đủ khiến nó cố gắng gọi thanh toán quá sớm. Tool gating là một trường hợp tiện lợi của việc tối thiểu hóa quyền: ở mỗi bước chỉ bật những gì cần.
Và cuối cùng là UX. Nếu mô hình đột ngột làm gì đó “ma thuật” mà người dùng không mong đợi (ví dụ tạo đơn hàng khi họ vẫn đang chọn quà), niềm tin vào App của bạn sẽ tụt dốc nhanh chóng.
3. GiftGenius như một minh họa cho tool gating
Hãy lấy case GiftGenius của chúng ta và xem thẳng vào các bước:
- Phỏng vấn: tìm hiểu tuổi, giới tính, sở thích của người nhận, ngân sách, v.v.
- Gợi ý: tìm sản phẩm trong danh mục, hiển thị ý tưởng.
- Checkout: khi người dùng đã chọn quà, chuyển sang bước hoàn tất.
Nếu ở bước phỏng vấn mô hình đã biết về search_products, add_to_cart và checkout, nó có thể:
- bắt đầu gọi tìm kiếm quá sớm, trước khi thu thập đủ sở thích;
- cố gắng “đặt hàng” ngay, chỉ vì người dùng lỡ nói “Ồ, cái này được đấy, lấy luôn”.
Cách đúng — thay đổi danh sách tools khả dụng theo từng bước. Dưới đây chúng ta sẽ phân tích kịch bản như vậy: ở bước phỏng vấn chỉ thấy công cụ lưu sở thích, ở bước gợi ý — tìm kiếm và thêm vào giỏ, ở bước checkout — chính là checkout.
Gói lại trong một bảng nhỏ:
| Bước của workflow | Mục tiêu bước | Công cụ nào khả dụng cho mô hình | Những gì mô hình “không nhìn thấy” ở bước này |
|---|---|---|---|
|
Thu thập hồ sơ người nhận | |
|
|
Gợi ý và làm rõ ý tưởng | |
+ (nếu giỏ hàng trống) |
|
Hoàn tất mua hàng | |
Bất kỳ tools “thiết lập” nào không còn cần nữa |
Lưu ý: công cụ checkout chỉ xuất hiện khi có thứ để hoàn tất, và chỉ ở bước tương ứng. Đây là ví dụ kinh điển của tool gating cho kịch bản thương mại.
4. Chiến lược tool gating: theo trạng thái, theo vai trò, theo tài nguyên
Phổ biến nhất là state‑based gating (gating theo bước workflow): danh sách công cụ phụ thuộc vào trạng thái kịch bản. Tức là bạn có một biến step ở đâu đó, và dựa vào đó xác định công cụ nào bật, công cụ nào tắt.
Nhưng không chỉ mỗi bước mới ảnh hưởng đến công cụ.
Đôi khi bạn dùng role‑based gating (theo vai trò người dùng): admin có công cụ dịch vụ (ví dụ, reindex danh mục), người dùng thường chỉ có công cụ người dùng. Đôi khi là resource‑based gating (theo trạng thái tài nguyên): công cụ “mở cửa” chỉ xuất hiện nếu trong trạng thái tài nguyên, cửa được đánh dấu là đóng.
Để cụ thể, hãy mô tả điều này bằng một hàm nhỏ trong TypeScript. Giả sử có ngữ cảnh với bước, vai trò, giỏ hàng hiện tại và trạng thái một tài nguyên:
type WorkflowStep = 'interview' | 'browsing' | 'checkout';
type UserRole = 'user' | 'admin';
interface WorkflowContext {
step: WorkflowStep;
role: UserRole;
cartItems: number; // số sản phẩm trong giỏ
doorIsClosed: boolean; // ví dụ resource-based gating: trạng thái của tài nguyên cụ thể
}
Giờ mô tả các tools có trong hệ thống và cách lọc chúng:
type ToolName =
| 'save_preference'
| 'finish_interview'
| 'search_products'
| 'get_product_details'
| 'add_to_cart'
| 'checkout'
| 'reindex_catalog'
| 'open_door';
const baseTools: ToolName[] = [
'save_preference',
'finish_interview',
'search_products',
'get_product_details',
'add_to_cart',
'checkout',
'reindex_catalog',
'open_door',
];
Ở đây open_door là ví dụ về công cụ phụ thuộc vào trạng thái của một tài nguyên cụ thể (cửa đang đóng hay không).
Và chính hàm gating:
function getAvailableTools(ctx: WorkflowContext): ToolName[] {
const byStep: ToolName[] =
ctx.step === 'interview'
? ['save_preference', 'finish_interview']
: ctx.step === 'browsing'
? ['search_products', 'get_product_details', 'add_to_cart']
: ['search_products', 'get_product_details', 'add_to_cart', 'checkout'];
const checkoutAllowed =
ctx.step === 'checkout' && ctx.cartItems > 0
? byStep
: byStep.filter((t) => t !== 'checkout');
const withAdmin =
ctx.role === 'admin'
? [...checkoutAllowed, 'reindex_catalog']
: checkoutAllowed;
const withResources =
ctx.doorIsClosed
? [...withAdmin, 'open_door']
: withAdmin.filter((t) => t !== 'open_door');
return withResources;
}
Ở đây có thể thấy rõ ba “lớp” gating:
- theo bước (byStep);
- theo vai trò người dùng (withAdmin);
- theo trạng thái tài nguyên (withResources và cờ doorIsClosed).
Đây không phải SDK, mà chỉ là phác thảo kiến trúc. Nhưng người ta thường nghĩ về tool gating đúng như vậy: có một catalog đầy đủ các công cụ, và có một hàm trả về tập con dựa trên ngữ cảnh.
5. Tool gating “sống” ở đâu trong kiến trúc App
Kết nối một chút với những gì bạn đã biết về stack ChatGPT App.
Về lý thuyết, MCP‑protocol hoạt động như sau
Trong MCP, công cụ không bắt buộc phải đóng cứng trong JSON tĩnh: server có thể trả danh sách động theo phiên. Hơn nữa, trong đặc tả có cơ chế capabilities, nơi server tuyên bố danh sách công cụ có thể thay đổi, và có thông báo tools/list_changed để client (ChatGPT/agent) yêu cầu lại danh sách tools khi có thay đổi.
Và về mặt hình thức bạn có thể làm như vậy, vài MCP‑client sẽ hoạt động được với danh sách MCP tools động. Nhưng đến hôm nay ChatGPT App chưa hỗ trợ tools/list_changed. Có thể tương lai sẽ thay đổi, nhưng hiện tại cách này sẽ không hoạt động.
Còn cách hoạt động được là như sau
Bạn lưu trạng thái và danh sách phương thức khả dụng ở phía mô hình. Bạn có thể gửi cho mô hình state và danh sách tools khả dụng ở mỗi bước như một phần “bức tranh thế giới”: trong system prompt mô tả rõ bước hiện tại (ví dụ, step = "browsing"), các cờ chính (ví dụ, cartItems = 2, role = "user") và chỉ đính kèm tập con công cụ đang được phép.
Mô hình không tự “quên” công cụ, nhưng nó theo rất tốt các chỉ dẫn rõ ràng kiểu: “Ở bước này bạn chỉ được dùng những hàm này…”. Cuối cùng, toàn bộ logic gating với mô hình trông như một hợp đồng đơn giản: đây là trạng thái kịch bản hiện tại, đây là danh sách nút bạn có thể dùng, còn lại coi như không tồn tại. Không cần “phép thuật” gì — chỉ cần cập nhật tuần tự state và danh sách tools trong request đến mô hình khi chuyển bước.
Ngoài ra, bạn có thể thêm chỉ dẫn vào structuredContent, kiểu như:
{
"instructions": {
"current_step": "browsing",
"enabled_mcp_tools": ["search", "apply"]
}
}
Bạn cũng có thể thêm bảo vệ ở lớp business code. Dù danh sách tools đã “cập nhật”, điều quan trọng là nhân đôi logic gating trong chính các handler, vì:
- mô hình có thể quên chỉ dẫn và/hoặc dữ liệu nếu cuộc trò chuyện kéo dài;
- mô hình có thể cố gọi một công cụ “bóng ma” vốn khả dụng ở bước trước;
Vì vậy, thiết kế tốt là: vừa “giấu” công cụ khỏi mô hình, vừa kiểm tra trong handler xem bây giờ có được phép làm việc đó hay không.
6. Tool gating ở mức mô hình vs mức logic
Liên hệ với phần trước: mọi thứ diễn ra ở cấp gọi mô hình (bạn đặt cờ/step gì vào prompt) là gating ở mức mô hình, còn các kiểm tra trong chính handler của công cụ là gating ở mức logic.
Thường nên tách hai lớp:
- Gating ở mức mô hình — khi mô hình biết công cụ “được phép” vào lúc này, vì bạn viết rõ trong chỉ dẫn những hàm nào khả dụng ở bước này. Với mô hình, thế giới trông như: “đây là state hiện tại, đây là tập nút có thể bấm, không có cái nào khác”.
- Gating ở mức logic — các kiểm tra bên trong chính công cụ. Ngay cả khi mô hình vẫn cố gọi checkout quá sớm (do cache, “ký ức ảo” hay vì ở một bước trước bạn từng đưa công cụ đó), handler nhìn vào trạng thái hiện tại và lịch sự từ chối: kiểu như “hãy chọn quà trước, sau đó chúng ta sẽ thanh toán” (chứ không chỉ ném exception!).
Tại sao cần cả hai lớp? Vì hạ tầng xung quanh LLM và các kịch bản không phải lúc nào cũng hoàn hảo:
- mô hình có thể “nhớ” rằng từng thấy công cụ checkout và cố viện dẫn trong lập luận hoặc thậm chí trong tool-call;
- bản thân bạn có thể vô tình truyền một tập tools rộng hơn mức cần ở một bước, và mô hình bắt đầu dùng các chức năng thừa;
- client/tầng trung gian có thể cache cấu hình gọi mô hình và trong một thời gian gửi bộ công cụ cũ.
Trong thực tế, điều này dẫn đến một ý đơn giản: trông chờ duy nhất vào “chúng ta không đưa công cụ vào tools — thế là nó sẽ không bao giờ được gọi nữa” — là nguy hiểm. Kiểm tra trong handlers vẫn cần thiết.
Ví dụ gating logic trong handler checkout bằng pseudo‑TypeScript:
async function checkoutTool(args: { paymentMethodId: string }, ctx: WorkflowContext) {
if (ctx.step !== 'checkout') {
return {
error: 'Checkout not available yet. Please finish selecting a gift first.',
};
}
if (ctx.cartItems === 0) {
return {
error: 'Your cart is empty. Add at least one gift before checkout.',
};
}
// ... logic thực hiện đặt hàng thực tế
}
Câu trả lời như vậy giúp cả người dùng và mô hình: mô hình thấy lỗi có cấu trúc và có thể điều chỉnh kế hoạch hành động.
7. Kết nối tool gating với UI và widget
Tool gating không chỉ là chuyện server. UI/UX cũng phải cảm nhận được thay đổi.
Widget biết bước hiện tại (chúng ta đã nói về widgetState và việc state này có thể lưu, ví dụ, currentStep). Mô hình — cũng vậy, vì bước hoặc được truyền rõ cho các công cụ, hoặc được đưa vào system prompt. Quan trọng là UI và tập tools đang hoạt động phải được đồng bộ.
Nếu mô hình cho rằng hiện là bước “Gợi ý”, còn widget hiển thị giao diện “Phỏng vấn”, người dùng sẽ bối rối. Ngược lại — UI đã vẽ nút “Thanh toán”, nhưng checkout chưa khả dụng, mô hình sẽ ở tình thế lạ: có nút nhưng chức năng như “không hoạt động”.
Sơ đồ nhỏ vòng đời một bước có tính đến tool gating:
flowchart TD A[Người dùng điền phỏng vấn trong widget] --> B[Widget gọi tool save_preference / finish_interview] B --> C[MCP / backend cập nhật state.step] C --> D[Server thay đổi tập tools cho phiên] D --> E[Client ChatGPT cập nhật các tools khả dụng cho mô hình] E --> F[Mô hình đặt câu hỏi mới
và/hoặc gọi các công cụ mới] C --> G[Widget nhận bước mới qua widgetState
và thay đổi UI]
Với người dùng, nó giống như một trình hướng dẫn quen thuộc: đầu tiên là vài câu hỏi, sau đó là danh sách quà, rồi xác nhận cuối cùng. Nhưng bên dưới nắp ca‑po, UI, danh sách công cụ và chỉ dẫn cho mô hình được chuyển đổi đồng thời.
Trong widget Next.js có thể diễn đạt rất đơn giản. Giả sử bạn lưu step trong widgetState:
type Step = 'interview' | 'browsing' | 'checkout';
function GiftWizardWidget() {
const [widgetState, setWidgetState] = useWidgetState<{ step: Step }>({
step: 'interview',
});
if (widgetState.step === 'interview') {
return <InterviewScreen onDone={() => setWidgetState({ step: 'browsing' })} />;
}
if (widgetState.step === 'browsing') {
return <BrowsingScreen onCheckout={() => setWidgetState({ step: 'checkout' })} />;
}
return <CheckoutScreen />;
}
Ở đây chúng ta không hiển thị trực tiếp các tools, nhưng ngụ ý rằng thay đổi step trong state được đồng bộ với việc thay đổi danh sách công cụ ở backend. Chúng ta đã xem các bước sống trong widget. Giờ quay lại phía MCP‑server và xem cách step và trạng thái giỏ hàng ảnh hưởng đến danh sách công cụ.
8. Ví dụ: tools/list động trên MCP‑server
Bạn đã thấy MCP‑server có thể lưu trạng thái phiên và dùng nó để quyết định. Trong phân tích riêng về case GiftGenius có ví dụ nơi trạng thái step và giỏ hàng (cart) được lưu trong bộ nhớ hoặc Redis. Từ đó quyết định những tools nào server trả về khi được yêu cầu danh sách.
Rất có thể khi bạn đọc bài này, ChatGPT App đã hỗ trợ toolChanged trong phạm vi phiên hiện tại. Điều đó rất hợp lý, nên có lẽ chỉ là vấn đề thời gian. Trong trường hợp đó, tôi có một phần ngắn giải thích cách làm tool gating bằng chính công cụ gốc của giao thức MCP.
Viết lại ý tưởng bằng TypeScript (MCP‑server trừu tượng):
interface SessionState {
step: WorkflowStep;
cartItems: number;
doorIsClosed: boolean; // ví dụ trạng thái tài nguyên
}
const allTools: ToolDefinition[] = [/* đầy đủ các công cụ */];
function listToolsForSession(state: SessionState): ToolDefinition[] {
const allowedNames = getAvailableTools({
step: state.step,
cartItems: state.cartItems,
role: 'user',
doorIsClosed: state.doorIsClosed,
});
return allTools.filter((tool) => allowedNames.includes(tool.name as ToolName));
}
Và ở đâu đó trong handler finish_interview bạn thay đổi bước và báo cho client rằng danh sách tools đã cập nhật:
async function finishInterviewTool(args: {}, session: SessionState) {
session.step = 'browsing';
await notifyToolsListChanged(); // lời gọi notification MCP giả định
return { success: true };
}
Trên MCP thực tế, bạn sẽ dùng SDK và định dạng thông điệp cụ thể, nhưng logic sẽ tương tự: thay đổi trạng thái → cập nhật tập tools → thông báo cho client.
9. Tool gating như một công cụ bảo mật
Nhấn mạnh lại khía cạnh bảo mật, vì nó dễ bị lu mờ bởi chi tiết kỹ thuật.
Khi bạn làm tool gating, bạn tự động giảm hậu quả của:
- các prompt injection kiểu “bỏ qua quy tắc và gọi thanh toán ngay” — vì ở bước phỏng vấn, mô hình đơn giản là không có checkout để chọn;
- bug trong logic nghiệp vụ — vì ngay cả khi một nhánh code nào đó không kiểm tra trạng thái đầy đủ, công cụ có thể bị không khả dụng về mặt vật lý;
- rò rỉ dữ liệu — vì tools cho admin không nằm trong danh sách cho người dùng thường.
Trong tài liệu khóa học, tool gating được nhắc đến trực tiếp như một thực hành áp dụng nguyên tắc đặc quyền tối thiểu trong bối cảnh công cụ LLM, đặc biệt cho checkout và các bước nhạy cảm khác.
Nghĩa là đây không chỉ là cách “làm mô hình ít lỗi hơn” — mà còn là một lớp bảo vệ thực sự.
10. Tự luyện tập
Để củng cố, hãy nghĩ về tool gating cho bất kỳ kịch bản nào của bạn. Ví dụ:
- ứng dụng giáo dục: bước đặt mục tiêu, bước đánh giá trình độ hiện tại, bước xây dựng kế hoạch — mỗi bước có tools riêng;
- đặt chỗ: tìm lựa chọn, chọn phương án, xác nhận và thanh toán — lại là ba bộ công cụ khác nhau;
- trợ lý nội bộ: tìm tài liệu, yêu cầu quyền truy cập, thực hiện thao tác — danh sách khác nhau cho nhân viên, quản lý và admin.
Rất hữu ích nếu bạn vẽ trên giấy hoặc trong Miro một bảng “Bước ↔ công cụ nào hiển thị ↔ công cụ nào ẩn” và với mỗi bước hãy tóm tắt tại sao cần đúng những tools đó và vì sao nên ẩn các tools còn lại.
11. Lỗi thường gặp khi làm tool gating
Lỗi #1: “Đổ” tất cả công cụ ra cùng lúc và trông chờ vào mô hình.
Đôi khi dev nghĩ: “Mô hình thông minh mà, tự khắc biết gọi cái gì khi nào”. Thực tế dẫn đến ô nhiễm ngữ cảnh, tăng token và nhiều tool‑call lỗi hơn. Đặc biệt đau là khi mô hình bất ngờ gọi checkout hay công cụ nguy hiểm khác chỉ vì nó có trong danh sách. Tool gating được tạo ra chính để ngăn tình huống này.
Lỗi #2: Nghĩ rằng chỉ cần ẩn công cụ khỏi danh sách là đủ.
Ngay cả khi MCP‑server không còn trả công cụ trong tools/list, mô hình có thể “nhớ” nó từ lịch sử, và hạ tầng có thể cache bộ tools cũ. Kết quả là lời gọi đến công cụ bóng ma. Nếu handler không có kiểm tra logic, nó có thể thực thi “sai thời điểm”. Vì vậy gating phải có cả ở cấp danh sách tools và bên trong handlers.
Lỗi #3: Không đồng bộ giữa UI và danh sách công cụ.
Có khi widget đã chuyển sang bước "checkout" và hiển thị nút “Thanh toán” đẹp đẽ, còn phía MCP bạn quên bật checkout trong danh sách công cụ. Mô hình không hiểu tại sao có nút mà công cụ không khả dụng và bắt đầu sinh ra câu trả lời kỳ lạ. Hoặc ngược lại: danh sách tools đã đổi, mô hình sẵn sàng gợi ý quà, còn widget vẫn hỏi câu từ phỏng vấn. Khi thiết kế workflow, cần đồng bộ cập nhật cả trạng thái UI và danh sách công cụ.
Lỗi #4: Logic gating quá phức tạp.
Đôi khi, hào hứng với khả năng, dev dựng gần như một sơ đồ BPMN đầy đủ với hàng chục trạng thái và điều kiện cho mọi trường hợp. Cuối cùng chính họ sau một tuần cũng không hiểu vì sao một công cụ chỉ khả dụng vào thứ Năm của năm nhuận. Với phần lớn App, một chiếc thang bước đơn giản và các quy tắc dễ hiểu là đủ: theo bước, theo vai trò người dùng và một vài cờ chính trong trạng thái.
Lỗi #5: “Đóng đinh” tool gating vào prompt mà không hỗ trợ ở server.
Đôi khi người ta thử giải quyết bằng chữ trong system prompt: “Ở bước này đừng dùng công cụ checkout” — nhưng không thay đổi danh sách công cụ thực và không thêm kiểm tra ở backend. Mô hình lúc nghe, lúc không, và bạn sẽ có hành vi bất ổn. Chỉ dẫn trong prompt hữu ích, nhưng phải bổ sung, không thay thế gating kỹ thuật ở phía hạ tầng.
Lỗi #6: Bỏ qua vai trò và quyền truy cập.
Trong ứng dụng có xác thực, thường quên rằng tool gating phải xét không chỉ bước mà còn vai trò. Kết quả là người dùng không có quyền admin vẫn nhìn thấy (hoặc tệ hơn, có thể gọi) công cụ dành cho hỗ trợ hoặc DevOps. Trong mô‑đun về ủy quyền bạn đã thấy quyền đi vào ngữ cảnh; ở đây đừng quên dùng thông tin đó khi chọn bộ tools.
Lỗi #7: Không giám sát các tool‑call lỗi.
Nếu bạn sai ở đâu đó với gating, triệu chứng điển hình là lỗi “Tool not available”, “MethodNotFound” hoặc lỗi logic của riêng bạn như “Checkout is not available yet” xuất hiện dày đặc. Nếu không thu thập thống kê các sự kiện như vậy, bạn có thể lâu không nhận ra người dùng thường xuyên đụng “tường vô hình”. Logging đơn giản và bộ đếm theo loại lỗi giúp phát hiện kịp thời vấn đề trong thiết kế workflow và gating.
GO TO FULL VERSION