Tóm tắt
Có ba điểm chính mà bài blog này chỉ ra:
Thứ nhất, GraphQL không thắng REST một cách tuyệt đối khi người dùng API là AI agent. Nó thắng ở đúng những chỗ mà trước đây không ai quan tâm: chọn được chính xác field cần lấy nên tiết kiệm token, cho phép agent tự hỏi server xem có những gì, và kiểm soát quyền tới từng field. Đổi lại, REST vẫn hơn hẳn về khả năng cache, mức độ chuẩn hóa và độ đơn giản khi vận hành.
Thứ hai, thứ quyết định thành bại là kiến trúc bạn chọn, không phải bản thân GraphQL. Nếu để agent tự viết query, tỷ lệ query hợp lệ chỉ khoảng 31 đến 50 phần trăm theo các nghiên cứu độc lập. Nếu bạn viết sẵn các query và cho agent chọn trong số đó, tỷ lệ gần như tuyệt đối. Đến thời điểm này, cách chạy được trên production là cách thứ hai.
Thứ ba, bài toán khó nhất là context và phân quyền, không phải cú pháp. Một schema thật có thể ngốn hơn 75.000 token, vượt quá khả năng nhét vào prompt. Còn về bảo mật, các khảo sát 2026 cho thấy agent đang được cấp quyền rộng hơn cả nhân viên làm cùng công việc đó, trong khi rất ít tổ chức có chính sách quản lý.
Quy ước thuật ngữ
Bài viết giữ nguyên các thuật ngữ tiếng Anh sau.
| Thuật ngữ | Nghĩa trong bài |
|---|---|
| introspection | Cơ chế cho phép client hỏi server "schema của anh có những type và field nào" |
| over-fetching | Lấy về nhiều dữ liệu hơn mức cần dùng |
| persisted operation | Query viết sẵn, lưu ở server, client chỉ gọi bằng tên hoặc ID |
| tool | Một hàm mà agent được phép gọi, kèm mô tả để nó biết khi nào nên gọi |
| tool-selection accuracy | Tỷ lệ agent chọn đúng tool cần dùng |
| context window | Vùng nhớ làm việc của model, tính bằng token, có giới hạn cứng |
| MCP | Model Context Protocol, giao thức chuẩn để agent kết nối tới tool bên ngoài |
| demand control | Nhóm kỹ thuật giới hạn độ nặng của query trước khi cho chạy |
1. Vì sao lại có chuyện "GraphQL hợp với agent"
Agent là loại người dùng API thứ ba
Stephan Schmidt đưa ra một cách phân loại gọn gàng. Trước đây API chỉ có hai loại người dùng. Loại thứ nhất là service gọi service, nơi developer hardcode sẵn field nào cần lấy. Loại thứ hai là trình duyệt hoặc app di động, nơi developer đọc tài liệu rồi chấp nhận lấy dư một chút cũng không sao.
Agent thì khác cả hai. Mỗi token đều tính tiền. Đọc tài liệu cũng tính tiền. Và quan trọng nhất, mọi field thừa mà API trả về sẽ nằm lại trong context suốt phần còn lại của cuộc hội thoại, tiếp tục tốn tiền ở mọi lượt gọi sau đó.
Ví dụ cụ thể: agent chỉ cần email và teamId của một user. Với REST, GET /users/42 trả về 40 field. 38 field thừa đó không biến mất sau khi dùng xong. Chúng đẩy cuộc hội thoại tiến gần hơn tới ngưỡng đầy context, nơi chất lượng suy luận của model bắt đầu xuống.
Bốn điểm GraphQL thực sự có lợi thế
Chọn field. Agent lấy đúng cái cần, không dư.
Tự khám phá schema tại runtime. Agent hỏi server xem có type nào, field nào, field nào đã bị đánh dấu ngừng dùng. Schema và tài liệu là một, không phải hai thứ phải đồng bộ bằng tay.
Phân quyền tới từng field. Vì mỗi field có resolver riêng nên kiểm tra quyền cũng có thể chạy ở mức field. REST thường chỉ dừng ở mức resource, kiểu một scope calendar.read cho cả tài nguyên.
Lỗi có cấu trúc. GraphQL trả lỗi kèm đường dẫn tới field bị lỗi. Agent nhận được thông tin dạng "field user.team.name thất bại vì không có quyền đọc team 7" thì biết phải làm gì tiếp. Còn khi REST trả về một trang HTML kèm mã 400, agent phải tốn thêm một lượt gọi model chỉ để đoán xem chuyện gì đã xảy ra.
WunderGraph bổ sung một lập luận nữa về khả năng khám phá. Agent không thể quét qua danh sách phẳng gồm hàng nghìn endpoint để tìm cái mình cần. Trong khi đó một schema GraphQL là đồ thị có thể đi theo quan hệ, chẳng hạn từ User sang orders rồi sang shippingAddress. Lưu ý rằng WunderGraph bán sản phẩm GraphQL, nên đây là lập luận có lợi ích liên quan.
Những phản biện đáng cân nhắc
GraphQL dễ vỡ hơn với agent. Nordic APIs nhận xét rằng khi mở GraphQL cho agent, bạn không phơi ra một tập endpoint mà phơi ra cả một bề mặt rộng. Agent phải suy luận về cấu trúc, chi phí, hình dạng dữ liệu và tính hợp lệ ở mỗi lần gọi. Sai một chỗ là hỏng cả query.
Thiếu chuẩn hóa. Laurent Schaffner chỉ ra rằng mỗi schema GraphQL là một thứ riêng biệt. Không có chuẩn chung như OpenAPI để tooling tái sử dụng. Trên thực tế, Azure AI Foundry và Semantic Kernel cắm thẳng vào OpenAPI 3.0 mà không cần code trung gian. Khoảng 83 phần trăm API công khai dùng REST, nên đầu tư của cả ngành vào tooling cũng dồn về đó.
Bề mặt tấn công khó phòng thủ hơn. Một query lồng sâu chỉ dài 128 byte có thể ngốn 10 giây CPU của server.
GraphQL không phải là thứ giàu metadata nhất. Có ý kiến cho rằng OpenAPI còn giàu metadata mô tả hơn, và GraphQL từ lâu vẫn chưa có câu chuyện tốt về schema metadata.
Trong một số bối cảnh, REST rõ ràng hơn. Với tự động hóa trình duyệt, các thao tác diễn ra tuần tự và có trạng thái. Thế mạnh gom nhiều nguồn dữ liệu trong một request của GraphQL không phát huy được gì.
Kết luận cân bằng: GraphQL không thay thế REST. Nó có xu hướng nằm bên dưới, làm lớp truy cập dữ liệu cho các tool mà agent gọi, chứ không phải lớp mà agent tiếp xúc trực tiếp.
Nguồn phần này: Stephan Schmidt (Amazing CTO, 29/5/2026), WunderGraph (20/3/2026, nguồn vendor), Nordic APIs, Laurent Schaffner (Medium), Browserbeam.
2. Hai cách cho agent dùng GraphQL
Cách một: để agent tự viết query
Bạn đưa agent một tool duy nhất có thể chạy bất kỳ query nào, kèm theo schema trong prompt. Agent tự nghĩ ra query.
export const graphqlExecutor = tool({
description: "Chạy một query GraphQL bất kỳ trên endpoint",
parameters: z.object({
query: z.string().describe("Chuỗi query hoặc mutation GraphQL hợp lệ"),
variables: z.record(z.any()).optional(),
authorization: z.string()
}),
execute: async ({ query, variables, authorization }) => {
// gửi thẳng tới endpoint GraphQL
}
});
Cách này viết rất nhanh và linh hoạt tối đa. Nhưng khi Dima Doronin thử nghiệm với GPT-4.1 tại QCSKU, ba vấn đề lặp lại liên tục:
- Agent bịa ra field và quan hệ không tồn tại trong schema.
- Agent nhầm kiểu dữ liệu, chẳng hạn xử lý một object như thể nó là chuỗi.
- Agent viết query không tối ưu, thường đòi về cả cụm dữ liệu liên quan thay vì đúng phần cần.
Cách hai: viết sẵn query, mỗi query thành một tool
Bạn viết tay các operation đã tối ưu. Mỗi cái trở thành một tool riêng. Agent chỉ còn nhiệm vụ chọn đúng tool và điền tham số.
export const searchSku = tool({
description: "Tìm sản phẩm trong catalog theo SKU, tên, ASIN hoặc UPC",
parameters: z.object({
teamId: z.number().describe("ID của team, dùng để tách dữ liệu giữa các tenant"),
search: z.string().describe("Từ khóa tìm kiếm"),
}),
execute: async ({ teamId, search }) => graphql().skuSearch({ teamId, search })
});
So sánh trực tiếp
Bảng dưới lấy từ thử nghiệm của Doronin.
| Tiêu chí | Query viết sẵn | Agent tự viết |
|---|---|---|
| Thời gian chạy query | Dưới 50ms | 50 đến 200ms |
| Tỷ lệ query đúng schema | 100% | Khoảng 40% |
| Hiệu quả token | Cao | Thấp |
| Cần người can thiệp | Không | Thường xuyên |
| Công sức làm ban đầu | Trung bình | Thấp |
| Công sức bảo trì | Cao | Thấp |
Con số 40% được xác nhận độc lập
Đây là phần quan trọng nhất nếu bạn muốn bài viết có sức nặng, vì nó không đến từ vendor.
IBM Research, hội nghị EMNLP 2024. Nhóm Kesarwani và cộng sự xây một bộ dữ liệu gồm 10.940 mẫu huấn luyện trên 185 kho dữ liệu và 957 mẫu kiểm thử trên 14 kho. Kết luận của họ: mô hình tốt nhất chỉ đạt độ chính xác khoảng 50 phần trăm khi được cho một ví dụ mẫu trong prompt. Ở chế độ không có ví dụ nào, phần lớn mô hình đạt dưới 15 phần trăm. Nhóm này cũng phân loại các kiểu lỗi, và danh sách đó gần như trùng khớp với những gì Doronin gặp: bịa endpoint, bịa thêm điều kiện lọc, chọn nhầm endpoint, lấy thiếu field, lấy dư dữ liệu, và nhạy cảm quá mức với cách diễn đạt câu hỏi.
Nghiên cứu NL2GraphQL công bố tại ACM. Bộ dữ liệu 1.845 mẫu trên 20 schema thật. Tám mô hình LLM khác nhau chỉ đạt từ 31 đến 48 phần trăm. Nguyên nhân chính được nêu là cấu trúc query sai, hiểu nhầm schema và gán sai tham số.
Một lưu ý về mức độ đầy đủ của bằng chứng: ngoài bảng của Doronin, hiện gần như không có nghiên cứu độc lập nào so sánh trực tiếp hai kiến trúc này về token, chi phí và độ trễ. Riêng con số độ chính xác thì đã có hai nguồn học thuật độc lập xác nhận.
Nguồn: Dima Doronin (ITNEXT, 26/9/2025), Kesarwani et al. (EMNLP 2024 Industry Track), NL2GraphQL (ACM SDGM).
3. Bài toán context và token
Schema lớn không nhét vừa
Arize AI ghi nhận rằng schema GraphQL trong thực tế dễ vượt 75.000 token, khiến việc đưa cả schema vào prompt là bất khả thi. Họ cũng nói thêm rằng cắt schema thành từng mẩu rồi tìm kiếm ngữ nghĩa cũng không giải quyết được, vì model sẽ chỉ nhìn thấy các mảnh rời rạc và mất mối liên hệ giữa chúng.
Bản thân danh sách tool cũng ngốn context
Mỗi định nghĩa tool tốn khoảng 100 đến 500 token. Nối năm server, mỗi server 30 tool, là đã mất 30.000 đến 60.000 token trước khi agent đọc câu hỏi đầu tiên.
Chloe Kim, kỹ sư backend tại Quandri, đo trên hệ thống thật của họ. Bốn server gồm Linear, Notion, Slack và Postgres, tổng cộng 77 tool, chiếm khoảng 21.077 token. Con số đó bằng 10,5 phần trăm context 200 nghìn token của Claude, và 16,5 phần trăm nếu dùng GPT-4o với 128 nghìn.
Phép đo đáng chú ý nhất của họ là so sánh cùng một thao tác. Tra cứu một issue trên Linear qua MCP tốn khoảng 12.957 token, trong đó 12.807 là định nghĩa tool luôn được nạp sẵn, chỉ 150 token là lời gọi thật. Gọi thẳng GraphQL hoặc dùng CLI cho cùng việc đó tốn khoảng 200 token. Chênh lệch 65 lần.
Càng nhiều tool, agent càng chọn sai
Đây là hiện tượng không tuyến tính. Nó không xuống dần mà rơi hẳn.
- Một thử nghiệm trên DEV Community: độ chính xác chọn tool đạt khoảng 95 phần trăm với bộ tool gọn, tụt xuống 71 phần trăm khi nạp toàn bộ GitHub MCP. Chênh 24 điểm chỉ vì context bị phình.
- Speakeasy đo thấy ở mức 20 tool, mô hình lớn chọn đúng 19 trên 20. Ở mức 107 tool, cả mô hình lớn lẫn nhỏ đều thất bại hoàn toàn.
- Nghiên cứu RAG-MCP (Gan và Sun, arXiv 2505.03275, tháng 5/2025) cho thấy nếu dùng tìm kiếm ngữ nghĩa để lọc tool trước khi đưa vào prompt, độ chính xác tăng hơn ba lần, từ 13,62 lên 43,13 phần trăm. Đồng thời số token của prompt giảm khoảng một nửa, từ 2.133 xuống 1.084.
Các hướng xử lý
Cắt nhỏ schema. Chỉ phơi phần schema mà agent thực sự cần, thay vì toàn bộ.
Introspection có ngữ nghĩa. ChilliCream đề xuất thêm một field __search cho phép agent mô tả bằng ngôn ngữ tự nhiên thứ nó đang tìm, server trả về các phần schema khớp nhất kèm đường dẫn về root.
{
__search(query: "thời tiết ở Bedok và có taxi không", first: 10) {
coordinate
score
pathsToRoot
definition {
__typename
... on __Field { fieldName description }
... on __Type { name kind description }
}
}
}
Họ công bố kết quả đo như sau. Đây là số liệu tự công bố của vendor, nên cần thận trọng khi trích dẫn.
| Cách khám phá API | Token gửi cho model | Chi phí |
|---|---|---|
| OpenAPI | 665.564 | 0,3950 USD |
| Schema GraphQL đầy đủ | 133.441 | 0,1072 USD |
| GraphQL kèm introspection ngữ nghĩa | 59.067 | 0,0895 USD |
Quy trình ba bước: tìm rồi kiểm tra rồi mới chạy. Agent tìm các type liên quan, dựng query, kiểm tra query có hợp lệ với schema không, chỉ khi hợp lệ mới thực thi. Cách này giảm mạnh số lần chạy query hỏng.
Nguồn: Arize AI, Chloe Kim (Quandri Engineering), DEV Community, Speakeasy, Gan & Sun (arXiv 2505.03275), ChilliCream (nguồn vendor).
4. Thiết kế schema cho agent đọc
Mô tả field quyết định agent chọn đúng hay sai
Đây là chi tiết nhỏ nhưng ảnh hưởng lớn nhất. Apollo lấy một ví dụ rất rõ. Giả sử schema có alerts(state: String!). Nếu không mô tả gì, model không biết state nghĩa là bang của Mỹ, hay là trạng thái, hay là thể rắn lỏng khí. Nó sẽ đoán, và đoán sai.
Viết như thế này thì agent phải đoán:
query GetWeatherAlerts($state: String!) {
alerts(state: $state) { severity description instruction }
}
Viết như thế này thì agent biết chính xác phải điền gì:
# Lấy cảnh báo thời tiết của một bang tại Mỹ, truyền vào mã hai chữ cái.
# Trả về mức độ nghiêm trọng, mô tả và hướng dẫn ứng phó.
query GetWeatherAlerts($state: String!) {
alerts(state: $state) { severity description instruction }
}
Nếu ngữ cảnh đã cố định, cách tốt nhất là bỏ luôn tham số. Agent không thể điền sai thứ mà nó không được phép điền:
# Lấy cảnh báo thời tiết hiện tại của bang Colorado.
query GetColoradoWeatherAlerts {
alerts(state: "CO") { severity description instruction }
}
Alias cũng hữu ích khi schema có sẵn những tên field khó hiểu mà bạn không đổi được:
query GetData {
transaction: tx {
id
short: some_really_long_field_name
}
}
Đặt tên theo việc, không theo bảng dữ liệu
Schema nên phản ánh năng lực nghiệp vụ chứ không phản ánh cấu trúc database. Một field tên renewalRisk cho agent biết ngay nó dùng để làm gì. Nếu thay vào đó bạn bắt agent tự gọi getInvoices, rồi getUsage, rồi getTickets, rồi tự ghép lại để suy ra rủi ro gia hạn, thì bạn vừa tốn ba lượt gọi vừa mở đường cho agent suy luận sai.
Enum tốt hơn String
Dùng enum để ràng buộc giá trị hợp lệ. Agent không phải đoán nên điền CO hay Colorado, vì schema đã liệt kê sẵn các lựa chọn. Tương tự, khai báo rõ field nào bắt buộc field nào không giúp agent biết cái gì được phép bỏ trống.
Ngừng dùng field khi người đọc là máy
Agent đọc @deprecated qua introspection ngay tại lúc chạy, nên cơ chế này mạnh hơn hẳn so với việc đánh version trong URL kiểu REST. Nhưng agent không đọc changelog. Vì vậy phần reason phải chỉ rõ dùng field nào thay thế, viết cho máy hiểu chứ không phải viết cho người.
Có nên làm một schema riêng cho agent
Nên cân nhắc. Ý tưởng là dựng một lớp trung gian chỉ phục vụ agent, chứa các operation viết sẵn và chỉ phơi những field an toàn, tách khỏi schema phục vụ ứng dụng cho người dùng.
Cái giá phải trả là thêm một bề mặt cần bảo trì. Cái được là bạn kiểm soát được cả token lẫn phạm vi dữ liệu, và không vô tình để lộ field nhạy cảm chỉ vì nó tồn tại trong schema chính.
Nguồn: Apollo GraphQL (nguồn vendor), WunderGraph, Lab Digital.
5. Bảo mật và phân quyền
Đây là phần đáng lo nhất và cũng là phần đang bị bỏ ngỏ nhiều nhất.
Schema trở thành bề mặt phân quyền
Vì mỗi field có resolver riêng nên kiểm tra quyền chạy được ở mức field. Đó là điểm GraphQL biểu đạt tốt hơn REST. Nhưng mặt trái là mỗi field cũng là một chỗ có thể cấu hình sai.
Rủi ro khi agent tự do ghép query
Có ba kiểu rủi ro cụ thể.
Lấy dư dữ liệu nhạy cảm. Agent không cố ý, nó chỉ lấy hết những gì có thể lấy.
Đi vòng qua các quan hệ trong đồ thị. Đây là kiểu nguy hiểm nhất và là đặc thù của GraphQL. Agent được phép đọc User, nhưng từ User nó đi tiếp sang Team, rồi từ Team sang payroll. Không có bước nào bị chặn vì từng bước đều hợp lệ về mặt kỹ thuật.
Nhầm ranh giới đọc và ghi. Nếu cùng một tool cho phép cả query lẫn mutation, bạn mất khả năng kiểm soát riêng phần ghi.
Junji Zhi nêu một ví dụ đơn giản mà rất đắt: không có gì trong một bản thử nghiệm ngăn được việc gọi ListProducts trả về 10.000 sản phẩm và làm nổ context window của agent. Kết luận của ông là ranh giới phân quyền đúng phải là operation, không phải field.
Số liệu về tình trạng phân quyền cho AI năm 2026
Teleport khảo sát 205 CISO và kiến trúc sư bảo mật, công bố tháng 2/2026. Kết quả đáng chú ý nhất: nhóm tổ chức cấp quyền quá rộng cho hệ thống AI có tỷ lệ gặp sự cố là 76 phần trăm. Nhóm áp dụng nguyên tắc quyền tối thiểu chỉ 17 phần trăm. Chênh nhau khoảng 4,5 lần.
Cùng báo cáo đó: 67 phần trăm tổ chức vẫn dùng credential tĩnh cho hệ thống AI, và chỉ 3 phần trăm có cơ chế kiểm soát tự động chạy ở tốc độ máy. CEO Ev Kontsevoy tóm tắt bằng một câu đáng trích: vấn đề không nằm ở chỗ AI không an toàn, mà ở chỗ chúng ta đang cấp quyền gì cho nó.
Vài số liệu bổ trợ từ các nguồn khác:
- Palo Alto Networks ghi nhận trung bình cứ một danh tính người thì có 109 danh tính máy, và dự kiến số lượng agent tăng 85 phần trăm trong 12 tháng tới.
- Khảo sát của CSA và Strata trên 285 chuyên gia cho thấy chỉ 18 phần trăm lãnh đạo bảo mật tin rằng hệ thống quản lý danh tính hiện tại của họ xử lý được agent.
- Khảo sát CISO toàn cầu của Okta: chỉ khoảng 45 phần trăm tự tin nhận diện và kiểm soát được agent. 21 phần trăm đang dùng tài khoản dùng chung hoặc service account quyền rộng cho agent.
Có nên bật introspection trên production
Đây là mâu thuẫn trực tiếp giữa hai nhu cầu. Giới bảo mật khuyến nghị tắt introspection trên production nếu API không dành cho công chúng, vì nó chính là tấm bản đồ đầy đủ về bề mặt tấn công, ở dạng máy đọc được. Nhưng agent lại cần đúng thứ đó để làm việc.
Ba cách dung hòa:
- Bật introspection nhưng yêu cầu xác thực.
- Thay introspection bằng một cơ chế tìm kiếm schema có kiểm soát.
- Chỉ bật trên lớp schema riêng cho agent, đã lọc sạch field nhạy cảm.
Một lưu ý kỹ thuật: tắt introspection thôi là chưa đủ. Kẻ tấn công vẫn có thể dò ra schema qua tính năng gợi ý tên field khi gõ sai.
Query viết sẵn như một danh sách trắng
Persisted operation khóa API chỉ cho chạy những query đã định nghĩa trước. Lợi ích gộp lại khá lớn: an toàn hơn, cache được, ghi log kiểm toán được, và bề mặt tấn công thu hẹp về đúng những gì bạn cho phép.
Một cấu hình thực tế thường thấy: mutation bắt buộc phải là operation viết sẵn để kiểm soát chặt phần ghi, còn query thì linh hoạt hơn.
Prompt injection khi agent có quyền ghi
Với agent chỉ đọc, một câu lệnh chèn độc hại gây rò rỉ dữ liệu. Với agent có quyền mutation, nó trở thành chuỗi hành động phá hoại.
Meta đưa ra một nguyên tắc gọn gàng tên là Rule of Two, công bố tháng 10/2025. Một agent chỉ nên thỏa mãn tối đa hai trong ba điều kiện sau: xử lý dữ liệu đầu vào không đáng tin, truy cập dữ liệu nhạy cảm, và thay đổi trạng thái bên ngoài. Đủ cả ba là mở đường cho tấn công.
Về mặt triển khai, có ba nguyên tắc:
- Kiểm tra quyền ở tầng thực thi, không dựa vào việc dặn dò trong system prompt.
- Bắt buộc người xác nhận với các mutation nhạy cảm.
- Mặc định chặn mutation, chỉ mở ra từng cái một.
Nguồn: Teleport State of AI in Enterprise Infrastructure Security 2026 (khảo sát do Eleven Market Research thực hiện, 12/2025), Palo Alto Networks Identity Security Landscape 2026, CSA và Strata (3/2026), Okta Global CISO 2026, PortSwigger, OWASP, Meta Agents Rule of Two, Junji Zhi.
6. Hiệu năng và chi phí vận hành
Query do máy sinh gây N+1 nhiều hơn
Query do người viết thường đã cân nhắc. Query do agent sinh thì hay đòi cả cụm dữ liệu liên quan, và mỗi quan hệ lồng nhau là một loạt truy vấn database. DataLoader gần như bắt buộc, và càng quan trọng hơn khi agent thử nhiều biến thể query liên tiếp.
Giới hạn độ nặng của query
Directive @cost cho phép gán trọng số cho từng field, tham số hoặc type. Tổng chi phí của một query được tính trước khi chạy.
directive @cost(weight: Int!) on
ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR
type Query {
# chi phí = trọng số nhân với số lượng bản ghi yêu cầu
parents(limit: Int!): [Parent] @cost(weight: 3)
}
type Parent {
name: String
children(limit: Int): [Child] @cost(weight: 5)
}
Điểm mấu chốt là từ chối query đắt trước khi thực thi, không phải sau. Và nên giới hạn theo chi phí thay vì theo số lượng request. Lý do: một request GraphQL duy nhất có thể lồng, gộp và nhân bản đủ để làm cạn tài nguyên server, trong khi bộ đếm request vẫn báo con số một.
Vì sao GraphQL khó cache hơn REST
REST tận dụng được cache của HTTP và CDN gần như miễn phí. GraphQL thì gửi POST tới một endpoint duy nhất với nội dung query thay đổi liên tục, nên hạ tầng cache sẵn có không giúp được gì. Schmidt nói thẳng rằng caching gần như miễn phí với REST và là cả một dự án kỹ thuật với GraphQL.
Với agent gọi lặp đi lặp lại, thiếu cache khiến chi phí và độ trễ tích lũy rất nhanh. Persisted query gỡ được một phần, vì mỗi query có mã băm cố định nên cache được theo mã đó.
Agent xử lý phân trang rất kém
Đây là lỗi thực tế hay gặp mà ít ai nói tới. Khi gặp cấu trúc phân trang chuẩn Relay, agent thường rơi vào một trong ba trạng thái: quên phân trang và chỉ lấy trang đầu, lặp vô hạn vì không biết khi nào dừng, hoặc kéo hết mọi trang và làm đầy context.
{
users(first: 10, after: "opaqueCursor") {
edges { cursor node { id name } }
pageInfo { hasNextPage endCursor }
}
}
Khuyến nghị là đừng bắt agent tự điều khiển con trỏ. Hãy bọc phân trang vào trong operation viết sẵn, đặt sẵn giới hạn an toàn, hoặc trả về dữ liệu đã tổng hợp thay vì danh sách thô.
Một ví dụ về mức độ nghiêm trọng, tuy không liên quan tới agent nhưng đúng bản chất vấn đề: một query lồng thêm một tầng vô ý đã đẩy độ trễ P95 từ 240ms lên 1,2 giây, kéo theo 47 lượt gọi resolver và đẩy CPU của database lên 78 phần trăm.
7. Quan hệ với MCP
Apollo MCP Server biến operation thành tool
Ý tưởng đơn giản: mỗi file .graphql chứa một operation sẽ trở thành một tool mà agent gọi được, không cần viết code trung gian.
# operations/GetUser.graphql -> tool tên "GetUser"
query GetUser($id: ID!) {
user(id: $id) { id name email }
}
Cấu hình quan trọng nhất là chế độ mutation:
overrides:
mutation_mode: none # mặc định, chặn mọi mutation
# mutation_mode: explicit # cho phép nhưng yêu cầu xác nhận
# mutation_mode: all # chạy thẳng, chỉ dùng khi thật sự hiểu rủi ro
operations:
source: manifest
path: ./persisted-query-manifest.json
Server này cũng cung cấp bốn tool cho phép agent khám phá schema theo quy trình có kiểm soát: xem schema, tìm kiếm, kiểm tra query hợp lệ, rồi mới chạy.
Ảnh hưởng của bản MCP 2026-07-28
Bản đặc tả ra ngày 28/7/2026 bỏ hoàn toàn phần bắt tay khởi tạo và bỏ header định danh phiên. Mỗi request giờ tự mang đủ thông tin về phiên bản giao thức, danh tính client và capability.
Với một MCP server đứng trước GraphQL, thay đổi này khá dễ chịu. Bạn chạy được sau một load balancer thường, không cần định tuyến dính phiên, không cần kho lưu phiên dùng chung. Nói cách khác, nó vận hành như một HTTP service bình thường, khớp với bản chất vốn không có trạng thái của GraphQL.
Cần lưu ý rằng đây là thay đổi không tương thích ngược ở mức giao thức. Server cũ phải sửa code thật chứ không chỉ nâng phiên bản thư viện.
Ba cách kết nối agent với GraphQL
| Cách làm | Được gì | Mất gì |
|---|---|---|
| Tự viết MCP server bọc GraphQL | Kiểm soát hoàn toàn, tùy biến logic theo nhu cầu | Phải tự làm lại phần định nghĩa kiểu và định hình dữ liệu trả về |
| Dùng Apollo MCP Server hoặc tương đương | Không cần viết code, có sẵn persisted query và giới hạn chi phí | Phụ thuộc vendor, vẫn phải tự tinh chỉnh token |
| Cho agent gọi thẳng endpoint qua một HTTP tool | Đơn giản nhất, ít token nhất cho các trường hợp đã biết trước | Không có khám phá, không có quản trị, dễ sinh query sai |
Federation có giúp agent không
WunderGraph lập luận rằng gộp toàn bộ API của tổ chức vào một supergraph giúp agent chỉ cần viết một query, phần điều phối để router lo.
Phản biện cần nêu để giữ cân bằng: bề mặt càng rộng thì agent càng khó chọn đúng. Một supergraph khổng lồ mà không có cơ chế tìm kiếm ngữ nghĩa và không giới hạn phạm vi sẽ làm vấn đề phình context trầm trọng hơn chứ không nhẹ đi. Federation chỉ có ích khi đi kèm khả năng khám phá có kiểm soát.
Nguồn: Apollo GraphQL (vendor), đặc tả MCP 2026-07-28, WunderGraph (vendor).
8. Bằng chứng thực tế đến đâu
Cần nói thẳng một điều: gần như không có công ty tên tuổi nào công bố kết quả đo được về việc cho agent dùng GraphQL trên production. Phần lớn thứ được gọi là case study thực chất là nội dung tiếp thị.
Những gì có được:
Arize AI xây một MCP server chuyển câu hỏi thành GraphQL, cho agent đi theo quan hệ trong schema thay vì nạp toàn bộ. Họ không công bố độ chính xác hay độ trễ.
Quandri là nguồn có số liệu cứng và độc lập nhất, đã trích ở phần 3. Họ kết luận rằng thay MCP server bằng các script CLI được đóng gói lại giúp giải phóng khoảng 21 nghìn token context. Cần đọc kèm một chi tiết quan trọng: chính họ nói thêm rằng tính năng tìm kiếm và nạp tool theo nhu cầu của Claude Code đã giảm hơn 85 phần trăm context dành cho định nghĩa tool, nên vấn đề phình context phần lớn đã được xử lý ở các phiên bản mới. Tiêu đề bài viết của họ mang tính khiêu khích, nội dung thì cân bằng hơn nhiều.
Một buổi chia sẻ cộng đồng do Apollo tường thuật cho biết khi rà soát MCP server của GitHub, mỗi lượt gọi tool trả về khoảng 8.500 token vì nhiều field trùng và thừa. Sau khi chọn lọc field, con số còn khoảng 2.000. Đây là số liệu tự báo cáo.
Tổng kết về độ tin cậy: bằng chứng độc lập mạnh nhất hiện có là hai thứ. Một là trần độ chính xác 31 đến 50 phần trăm khi để agent tự viết query. Hai là phép đo chi phí token của Quandri. Mọi con số về hiệu quả tiết kiệm do vendor công bố nên được ghi rõ là tự báo cáo.
9. So sánh với các hướng khác
| Hướng | Mạnh ở đâu | Yếu ở đâu | Nên chọn khi |
|---|---|---|---|
| GraphQL với query viết sẵn | Chọn field tiết kiệm token, lấy dữ liệu lồng nhau trong một lần, phân quyền tới field | Vận hành phức tạp, khó cache, phải tinh chỉnh context | API nội bộ nhiều quan hệ, cần quản trị chặt |
| REST kèm OpenAPI | Chuẩn hóa, mọi model đều quen, cache miễn phí, tooling trưởng thành | Lấy dư dữ liệu, phải gọi nhiều lượt, hàng nghìn endpoint phẳng khó tìm | API đơn giản, thao tác tuần tự |
| gRPC | Có kiểu chặt chẽ, hiệu năng cao | Không tự mô tả tốt cho model, nhị phân khó debug | Giao tiếp giữa các service nội bộ |
| Sinh SQL trực tiếp | Linh hoạt tối đa | Độ chính xác thấp, SQL sai vẫn chạy và trả kết quả sai một cách âm thầm | Phân tích chỉ đọc, có lớp ngữ nghĩa và người kiểm tra |
| Cho agent viết code gọi API | Tiết kiệm token rất mạnh, dữ liệu trung gian không vào context | Cần sandbox an toàn, thêm hạ tầng | Nhiều tool, dữ liệu lớn, quy trình phức tạp |
Hướng đáng chú ý nhất: cho agent viết code
Thay vì gọi tool trực tiếp, agent viết một đoạn code gọi API trong môi trường cách ly, xử lý vòng lặp và lọc dữ liệu ngay tại chỗ, rồi chỉ trả kết quả cuối về context.
Anthropic công bố tháng 11/2025 rằng cách này giảm lượng token từ 150.000 xuống 2.000, tương đương tiết kiệm 98,7 phần trăm. Một triển khai độc lập trên GitHub MCP báo cáo con số tương tự trong môi trường thật: từ 70.000 xuống 800 token, giảm 98 phần trăm.
Điểm quan trọng cho bài viết: hai hướng này không loại trừ nhau. Agent hoàn toàn có thể viết code gọi GraphQL query trong sandbox, tận dụng được cả khả năng chọn field lẫn khả năng xử lý dữ liệu ngoài context.
Vì sao sinh SQL trực tiếp kém tin cậy hơn
Vấn đề lớn nhất không phải là SQL sai cú pháp, vì cái đó báo lỗi ngay. Vấn đề là SQL sai logic vẫn chạy thành công và trả về dữ liệu sai mà không ai biết. Một bài đánh giá cho thấy độ chính xác khi thực thi ở mức cơ sở chỉ 2 phần trăm trước khi tối ưu prompt. Các nghiên cứu trên môi trường thật đều xếp hiểu nhầm schema là nguyên nhân lỗi hàng đầu.
Schema GraphQL đóng vai lan can ở đây. Query sai schema thì bị chặn ngay, không kịp trả về dữ liệu sai.
Nguồn: Anthropic (4/11/2025), glama.ai, Ragas, các nghiên cứu về text-to-SQL trong production.
10. Khung quyết định và các lỗi nên tránh
Lộ trình theo tầng
Bắt đầu bằng query viết sẵn. Đừng mở một tool chạy query tùy ý lên production. Độ chính xác 31 đến 50 phần trăm là không chấp nhận được với nghiệp vụ quan trọng.
Tách riêng bề mặt dành cho agent. Lọc bỏ field nhạy cảm ngay từ đầu, đừng dựa vào việc agent tự biết đường tránh.
Dựng phân quyền trước khi bật. Kiểm tra quyền ở mức field, cấp token quyền tối thiểu cho agent, và không bật introspection công khai.
Đặt trần chi phí. Dùng @cost, giới hạn độ sâu, giới hạn theo chi phí thay vì theo số request, và luôn có timeout.
Mặc định khóa mutation. Mở từng cái, có xác nhận của người với các hành động phá hủy.
Đo liên tục. Ghi log mọi operation, theo dõi token mỗi lượt gọi và tỷ lệ chọn đúng tool. Nếu tỷ lệ dưới 90 phần trăm thì vấn đề gần như luôn nằm ở phần mô tả tool.
Chỉ mở khám phá động khi query viết sẵn không phủ đủ nhu cầu, và khi mở thì luôn kèm trần chi phí.
Các lỗi nên tránh
- Nhét toàn bộ schema vào system prompt.
- Biến mọi endpoint thành tool, dẫn tới hàng trăm tool và agent chọn sai gần như hoàn toàn.
- Cho agent vừa chạy query tùy ý vừa có quyền ghi không giới hạn.
- Bật introspection công khai trên graph có chứa field nhạy cảm.
- Bắt agent tự điều khiển phân trang mà không đặt giới hạn mặc định.
- Đặt tên kiểu
getDatahaystatusrồi bỏ trống phần mô tả. - Giới hạn theo số request thay vì theo chi phí query.
- Trích dẫn con số tiết kiệm token của vendor như thể đó là dữ liệu đã được kiểm chứng độc lập.
Nguồn tham khảo chính
Nghiên cứu học thuật
- Kesarwani et al., "GraphQL Query Generation: A Large Training and Benchmarking Dataset", EMNLP 2024 Industry Track, trang 1595 đến 1607.
- "Synthetic Data Generation for Schema-Aware Query Interfaces: Benchmarking NL2GraphQL Systems", ACM SDGM.
- Gan & Sun, "RAG-MCP: Mitigating Prompt Bloat in LLM Tool Selection via RAG", arXiv:2505.03275, tháng 5/2025.
Thực nghiệm và phân tích
- Dima Doronin, "Building AI Agents on GraphQL: A Comparative Study of Two Architectural Approaches", ITNEXT, 26/9/2025.
- Chloe Kim, "MCP is dead", Quandri Engineering.
- Stephan Schmidt, "GraphQL Comeback Because of AI?", Amazing CTO, 29/5/2026.
- Junji Zhi, "Turning a graphql-ruby Endpoint into an MCP Server the Right Way", 14/6/2026.
- Nordic APIs, "How to Prepare GraphQL APIs For AI Agents".
Khảo sát bảo mật
- Teleport, "2026 State of AI in Enterprise Infrastructure Security", 17/2/2026.
- Palo Alto Networks, "2026 Identity Security Landscape".
- CSA và Strata, khảo sát tháng 3/2026.
- Okta, "Global CISO Insights 2026".
Nguồn vendor
- Apollo GraphQL: loạt bài về Apollo MCP Server và thiết kế schema cho agent.
- WunderGraph, "GraphQL Is the API Layer AI Agents Actually Need", 20/3/2026.
- ChilliCream, "Semantic Introspection", 22/4/2026.
- Arize AI, "Building a Text-To-GraphQL Agent In a Weekend".
Tài liệu kỹ thuật
- Đặc tả MCP 2026-07-28.
- Anthropic, "Code execution with MCP: Building more efficient agents", 4/11/2025.
- PortSwigger Web Security Academy, phần GraphQL.
- Meta, "Agents Rule of Two", 10/2025.
