Thiết kế phản hồi API gọn nhẹ cho tác nhân AI
Tác nhân yêu cầu hồ sơ khách hàng. API trả về thông tin khách hàng, 200 đơn hàng gần nhất, từng mặt hàng, dấu thời gian ở ba định dạng và _links cho mỗi mục. Bốn mươi nghìn token rơi vào cửa sổ ngữ cảnh, trong khi tác nhân chỉ cần địa chỉ email.
Lặp lại điều đó bốn lần trong một lượt chạy, tác nhân sẽ dùng gần hết ngân sách chỉ để đọc JSON không cần thiết. Sau đó nó quên hướng dẫn ban đầu, tóm tắt thay vì hoàn thành nhiệm vụ, và chi phí tăng trong khi chất lượng giảm.
Đây là vấn đề thiết kế API, không phải vấn đề prompt. Mọi trường trong phản hồi đều cạnh tranh với hướng dẫn, lịch sử hội thoại và kế hoạch của tác nhân trong một cửa sổ ngữ cảnh cố định. Việc cạn kiệt ngữ cảnh là một nguyên nhân khiến tác nhân AI thất bại trong môi trường sản xuất.
Apidog giúp bạn đo kích thước phản hồi thực tế của từng endpoint và mô phỏng phản hồi đã được cắt giảm trước khi API được triển khai.
Token đi đâu?
Các phản hồi dành cho trình duyệt và dashboard thường chứa dữ liệu không cần thiết cho tác nhân:
-
Envelope rườm rà:
data,meta,links,includedcó thể làm tăng gấp đôi tải trọng. Hypermedia hữu ích cho client theo liên kết, nhưng URL là token mà tác nhân hiếm khi cần. - Khóa lặp lại: Danh sách 200 mục, mỗi mục 15 trường, phải lặp lại khoảng 3.000 chuỗi khóa JSON.
- Mở rộng lồng nhau mặc định: Khách hàng → đơn hàng → mặt hàng là một cây dữ liệu tăng kích thước rất nhanh.
-
Định dạng dư thừa:
created_at,created_at_unixvàcreated_at_humantrả ba lần cùng một thông tin. -
Giá trị
nullhoặc rỗng: Phát mọi trường chưa được đặt là lãng phí hoàn toàn.
Chi phí token phụ thuộc vào kích thước văn bản đã tuần tự hóa, không phải số bản ghi. Hai trăm bản ghi phẳng với năm trường có thể rẻ hơn một đối tượng lồng nhau sâu.
Quy tắc 1: Trả về trường, không phải toàn bộ tài nguyên
Cho phép người gọi chọn đúng trường cần dùng:
GET /v1/customers/8812?fields=id,email,plan,status
{ "id": "8812", "email": "dana@example.com", "plan": "pro", "status": "active" }
Thay đổi này thường giảm khoảng 90% kích thước của một bản ghi đầy đủ. Hướng dẫn thiết kế API của Google mô tả mẫu field mask; GraphQL cũng giải quyết vấn đề này bằng cách bắt buộc chọn trường.
Khi triển khai:
- Xác thực danh sách trường bằng schema và từ chối tên không tồn tại. Đừng âm thầm bỏ qua một trường bị mô hình “ảo giác”.
- Dùng một tập trường mặc định nhỏ cho client không gửi
fields; đừng mặc định trả về mọi thứ. - Đặt
fieldslà bắt buộc trong schema công cụ:
{
"name": "getCustomer",
"description": "Lấy thông tin khách hàng bằng ID. Luôn truyền `fields` với chỉ những gì bạn cần. Các trường có sẵn: id, email, name, plan, status, created_at, billing_address, order_count.",
"input_schema": {
"type": "object",
"required": ["customerId", "fields"],
"properties": {
"customerId": { "type": "string" },
"fields": {
"type": "array",
"items": { "type": "string" },
"description": "Tên các trường cần trả về. Giữ danh sách này ở mức tối thiểu."
}
}
}
}
Mô tả công cụ là nơi mô hình học các quy tắc gọi API. Cả hướng dẫn gọi hàm của OpenAI và tài liệu sử dụng công cụ của Anthropic đều nhấn mạnh điều này. Một tham số tùy chọn dễ bị bỏ qua; tham số bắt buộc khiến mô hình phải chọn dữ liệu cần thiết.
Quy tắc 2: Luôn giới hạn danh sách
Endpoint danh sách không giới hạn là nguồn phổ biến gây tràn ngữ cảnh. Một yêu cầu “các đơn hàng gần đây” không nên trả về mọi đơn hàng từ năm 2019.
Đặt giới hạn cứng ở phía máy chủ. Nếu tác nhân gửi limit=5000, hãy trả tối đa 100 mục và cho biết phản hồi đã bị giới hạn.
Kết hợp các nguyên tắc trong bài viết về phân trang REST API và thiết kế phân trang cho hàng triệu bản ghi:
- Giới hạn mỗi trang khoảng 20–50 mục với bản ghi thông thường.
- Trả
totalđể tác nhân biết có bao nhiêu dữ liệu mà không phải phân trang chỉ để đếm. - Dùng cursor pagination thay vì offset, vì offset lệch khi dữ liệu thay đổi giữa các lần gọi.
- Trả tín hiệu rõ ràng như
"truncated": trueđể mô hình biết còn dữ liệu chưa xem.
Ngoài ra, hãy cung cấp cách tránh phân trang hoàn toàn: endpoint count, tìm kiếm có bộ lọc hẹp, hoặc endpoint tóm tắt. Phản hồi rẻ nhất là phản hồi không trả dữ liệu thừa.
Quy tắc 3: Cắt giảm ở lớp công cụ khi không sở hữu API
API bên thứ ba có thể không hỗ trợ chọn trường. Khi đó, cắt giảm phản hồi giữa HTTP client và mô hình:
KEEP = {
"getCustomer": ["id", "email", "plan", "status"],
"listOrders": ["id", "total", "status", "created_at"],
}
def project(tool_name, payload):
keep = KEEP.get(tool_name)
if keep is None:
return payload
if isinstance(payload, list):
return [{k: item.get(k) for k in keep if k in item} for item in payload]
return {k: payload.get(k) for k in keep if k in payload}
Để cách này vận hành tốt:
- Lưu toàn bộ phản hồi trong nhật ký chạy, nhưng chỉ gửi phần đã chiếu cho mô hình. Xem thêm cách truy vết các lệnh gọi công cụ của tác nhân.
- Báo rõ trường đã bỏ qua, ví dụ:
"_omitted": ["billing_address", "notes", "metadata"]. - Với dữ liệu dạng bảng, chuyển JSON sang CSV hoặc bảng Markdown để tên cột chỉ xuất hiện một lần.
id,total,status,created_at
ord_91,4900,paid,2026-08-21
ord_92,1200,refunded,2026-08-22
Quy tắc 4: Tóm tắt trên máy chủ cho trường hợp nặng
Nhiều câu hỏi không cần bản ghi chi tiết. Ví dụ: “Khách hàng này có khoản thanh toán thất bại nào trong tháng này không?” là câu hỏi boolean. Trả 40 đối tượng thanh toán để mô hình tự suy luận là tốn kém.
Với câu hỏi lặp lại, hãy thêm endpoint trả lời trực tiếp: tóm tắt trạng thái tài khoản, tổng hợp theo trạng thái hoặc một tập dữ liệu nhỏ. Đây là cải tiến giá trị nhất: thay vì cắt giảm phản hồi lớn, bạn tránh tạo nó.
Giữ các phản hồi tóm tắt ổn định về hình dạng và có version. Prompt của tác nhân phụ thuộc vào shape phản hồi; thay đổi âm thầm có thể làm nó hỏng. Tham khảo điều gì xảy ra khi API thay đổi bên dưới tác nhân và chiến lược tạo phiên bản API.
Đo lường trước và sau
Đừng tối ưu mù quáng. Đo ba chỉ số:
- Byte mỗi phản hồi, theo endpoint: Gửi yêu cầu thực tế đến mọi công cụ tác nhân có thể gọi. Endpoint vượt vài kilobyte là ứng viên cần tối ưu. Trong Apidog, bạn có thể chạy endpoint, xem trực tiếp kích thước phản hồi và lưu request để kiểm tra lại sau này.
- Token mỗi lần gọi công cụ: Byte chỉ là đại diện; token mới là hóa đơn. Đưa tải trọng qua tokenizer của nhà cung cấp, chẳng hạn tiktoken, rồi xếp hạng endpoint theo chi phí.
- Ngữ cảnh dùng cho mỗi lượt chạy: Theo dõi tổng token trong toàn bộ tác vụ. Nếu lượt chạy chạm giới hạn ngữ cảnh, cắt giảm sẽ giúp tác vụ hoàn thành thay vì chỉ rẻ hơn.
Sau đó, mô phỏng phản hồi mục tiêu bằng mock server trước khi đội API triển khai. Điều quan trọng là xác minh tác nhân vẫn thành công với ít dữ liệu hơn. Xem quy trình chạy tác nhân với API giả lập thay vì môi trường production.
Một phản hồi tốt trông như thế nào?
Phản hồi thân thiện với tác nhân nên nhỏ gọn, phẳng và trung thực về dữ liệu bị bỏ qua:
{
"customer": { "id": "8812", "email": "dana@example.com", "plan": "pro" },
"recent_orders": [
{ "id": "ord_91", "total_cents": 4900, "status": "paid" },
{ "id": "ord_92", "total_cents": 1200, "status": "refunded" }
],
"recent_orders_total": 47,
"truncated": true,
"_omitted": ["billing_address", "metadata", "order_line_items"]
}
Phản hồi này dưới 200 token, trả lời câu hỏi phổ biến, cho biết có 47 đơn hàng thay vì ngụ ý chỉ có hai, và giúp mô hình biết dữ liệu nào có thể yêu cầu tiếp theo.
Bắt đầu với endpoint đắt nhất: đo lường, thêm chọn trường, giới hạn danh sách, rồi chạy lại tác nhân. Chênh lệch thường đủ lớn để biện minh cho phần còn lại của công việc. Tải xuống Apidog để đo lường và mô phỏng trong cùng một dự án.
Ba tình huống thực tế
1. Phân loại hỗ trợ
Một tác nhân đọc ticket, lấy thông tin khách hàng và quyết định có cần leo thang không. Phiên bản ngây thơ lấy toàn bộ hồ sơ khách hàng cùng 50 ticket gần nhất, tiêu tốn 30.000 token trước khi đọc được khiếu nại hiện tại.
Phiên bản tốt hơn gọi endpoint tóm tắt trả về gói dịch vụ, trạng thái, số ticket đang mở và ngày liên hệ gần nhất. Khoảng 80 token, nhưng quyết định tốt hơn vì tín hiệu liên quan không bị chôn vùi.
2. Tác nhân vận hành nội bộ
Một tác nhân triển khai kiểm tra tình trạng của 40 dịch vụ. Đối tượng trạng thái đầy đủ có thể làm tràn cửa sổ ở dịch vụ thứ 12.
Hãy trả một dòng cho mỗi dịch vụ: tên, trạng thái và tỷ lệ lỗi. Toàn bộ 40 dịch vụ sẽ vừa trong vài trăm token, cho phép tác nhân suy luận trên cả hệ thống thay vì quên nửa đầu danh sách.
3. Nhập liệu và đối chiếu dữ liệu
Một tác nhân đối chiếu hóa đơn với thanh toán. Trả toàn bộ tài liệu hóa đơn khiến nó thất bại sau vài chục bản ghi.
Trả id, amount_cents, date và reference dưới dạng CSV cho phép nó xử lý vài trăm bản ghi trong một lần gọi, vì phép so sánh chỉ dùng bốn trường này.
Mẫu chung: tác nhân cần bề mặt ra quyết định, nhưng API thường trả về một tài liệu đầy đủ.
Bạn cần lịch sử chạy để thấy mô hình
Một lượt chạy chỉ cho biết có một phản hồi lớn. Xu hướng thực sự—endpoint nào vượt ngân sách và tần suất ra sao—chỉ xuất hiện sau nhiều lượt chạy.
Với dịch vụ tự triển khai, đó là telemetry của bạn. Với tác nhân lập trình thực hiện công việc được giao, nền tảng chạy tác nhân cần lưu dấu vết và kết quả. Sharkly lưu lịch sử thực thi theo Task, giúp so sánh giữa các lần chạy mà không cần tái tạo phiên terminal.
Thực thi ngân sách mà không có lịch sử chỉ cho bạn biết “có thứ gì đó quá lớn”, chứ không cho biết nên sửa endpoint nào trước.
Đặt ngân sách cho từng công cụ
Đừng chỉ giới hạn tổng ngữ cảnh của toàn bộ lượt chạy. Hãy đặt ngân sách cho mỗi công cụ, ví dụ 1.500 token.
Khi phản hồi vượt giới hạn:
- Cắt giảm theo phần trường đã chọn.
- Thêm dấu chỉ trường bị bỏ qua.
- Ghi nhận sự kiện vượt ngân sách.
Bạn sẽ có danh sách endpoint thường xuyên vượt ngưỡng, được xếp hạng theo tần suất gọi—đó chính là hàng đợi tối ưu hóa.
Ngân sách theo công cụ cũng bảo vệ khỏi các trường hợp đuôi dài: endpoint nhỏ trong môi trường thử nghiệm có thể trở nên khổng lồ với khách hàng có 4.000 đơn hàng. Giới hạn cứng biến tình huống đó thành phản hồi đã cắt giảm thay vì một tác vụ thất bại lúc 2 giờ sáng.
Câu hỏi thường gặp
Cắt bớt phản hồi có rủi ro nếu tác nhân cần dữ liệu bị thiếu?
Chỉ rủi ro khi bạn che giấu việc cắt bớt. Hãy trả tín hiệu rõ ràng và danh sách trường bị bỏ qua để mô hình có thể yêu cầu thêm. Cắt giảm âm thầm mới là nguyên nhân gây câu trả lời sai.
Có nên dùng GraphQL thay REST cho tác nhân?
GraphQL buộc chọn trường, nên giải quyết vấn đề này gọn gàng. Tuy nhiên, nó chuyển độ phức tạp sang việc tạo truy vấn, và mô hình thường viết truy vấn không hợp lệ. Thêm fields vào REST thường là thay đổi nhỏ hơn.
Phản hồi công cụ nên nhỏ đến mức nào?
Mục tiêu thực tế:
- Dưới 1.000 token cho một lần đọc bản ghi.
- Dưới 2.000 token cho một danh sách.
Vượt các mức này, hãy hỏi liệu tác nhân cần bản ghi hay chỉ cần câu trả lời.
Prompt caching có giải quyết vấn đề không?
Không hoàn toàn. Caching giảm chi phí của ngữ cảnh lặp lại, nhưng không giải phóng không gian cửa sổ ngữ cảnh. Phản hồi 40.000 token được cache vẫn chiếm 40.000 token.
Còn phản hồi nhị phân và tệp?
Không đưa trực tiếp vào ngữ cảnh. Lưu trữ tệp, cung cấp tham chiếu cùng mô tả ngắn, và cho tác nhân một công cụ riêng để trích xuất đúng phần cần thiết.
Nên cắt giảm trong API hay wrapper công cụ?
Cắt trong API khi bạn sở hữu nó, vì mọi client đều hưởng lợi và dữ liệu thừa không phải truyền qua mạng. Cắt trong wrapper khi đó là API bên thứ ba. Làm cả hai là tốt nhất.


Top comments (0)