Vì sao các tác nhân AI cần một đường đi khác qua ứng dụng của bạn?
Một phần ngày càng lớn của các yêu cầu tới ứng dụng của bạn đến từ các tác nhân AI: trợ lý lấy tài liệu cho người dùng, công cụ nghiên cứu đọc các trang sản phẩm, các quy trình tự động gọi API của bạn. Phần lớn trong số đó đang làm việc qua các giao diện được thiết kế cho con người. Chúng tải xuống toàn bộ trang HTML, phân tích bố cục, theo dõi điều hướng vốn dành cho chuột, và thử lại khi một trang nặng script không hiển thị theo cách chúng mong đợi.
Điều đó kém hiệu quả cho mọi bên. Tác nhân nhận nội dung nhiễu và mắc lỗi. Máy chủ của bạn tốn tài nguyên để dựng các trang mà không ai nhìn thấy. Và lưu lượng này khó suy luận, vì nó trông giống như một trình duyệt hoạt động bất thường. Nếu bạn هنوز chưa chắc bao nhiêu lưu lượng của mình là như vậy, hãy bắt đầu với cách biết lưu lượng trang web có đến từ tác nhân AI hay không.
Một cách tiếp cận tốt hơn là cung cấp cho tác nhân một lộ trình có cấu trúc: nội dung và hành động ở các định dạng dành cho chương trình, có thể được khám phá tại các vị trí chuẩn, với giới hạn thưởng cho các khách hàng tự nhận diện đúng cách. Hướng dẫn này cho thấy cách làm điều đó mà không cần xây dựng một ứng dụng thứ hai.
Bước 1: Quyết định tác nhân nên có thể làm gì
Hãy bắt đầu từ mục đích, không phải công nghệ. Xem các route nơi bạn đã thấy lưu lượng tác nhân và nhóm chúng lại:
- Đọc nội dung: tài liệu, thông tin sản phẩm, trang giá, bài trợ giúp, hồ sơ công khai.
- Truy vấn dữ liệu: tìm kiếm, tra cứu danh mục, tình trạng còn hàng, trạng thái.
- Thực hiện hành động: tạo, cập nhật hoặc xóa thứ gì đó thay mặt cho người dùng.
Đọc và truy vấn là những ứng viên tốt cho một lộ trình có cấu trúc, mở cho mọi client hoạt động đúng chuẩn. Hành động cần xác thực và ủy quyền bất kể client tự nhận diện như thế nào, và được đề cập trong hướng dẫn về bảo vệ các hành động nhạy cảm.
Bước 2: Cung cấp các định dạng thân thiện với máy
Với các route nội dung, hãy cung cấp cùng một thông tin nhưng không có phần khung giao diện của trang. Hai định dạng đáp ứng phần lớn nhu cầu:
- Markdown cho nội dung văn xuôi như tài liệu và bài viết, được phục vụ với media type
text/markdownđã đăng ký trong RFC 7763. Nó giữ nguyên tiêu đề, danh sách, liên kết và khối mã, và lại gọn nhẹ. - JSON cho các bản ghi có cấu trúc như sản phẩm, gói hoặc trạng thái.
Dùng content negotiation để URL giữ nguyên và client yêu cầu định dạng mà nó muốn:
import express from "express";
const app = express();
app.get("/docs/:slug", async (req, res) => {
const doc = await loadDoc(req.params.slug);
if (!doc) return res.status(404).end();
res.vary("Accept");
const preferred = req.accepts(["text/html", "text/markdown", "application/json"]);
if (preferred === "text/markdown") {
return res.type("text/markdown; charset=utf-8").send(doc.markdown);
}
if (preferred === "application/json") {
return res.json({ title: doc.title, updatedAt: doc.updatedAt, body: doc.markdown });
}
return res.render("doc", { doc });
});Gửi header Vary: Accept (RFC 9110 về Vary) để bộ nhớ đệm lưu trữ từng biểu diễn riêng biệt. Nhiều nhóm cũng công bố một URL thay thế rõ ràng, chẳng hạn thêm .md vào một đường dẫn tài liệu, vì một số client thấy điều đó dễ hơn thương lượng nội dung. Liên kết nó từ trang HTML bằng <link rel="alternate" type="text/markdown" href="..."> để có thể được khám phá. Các liên kết có kiểu như thế này tuân theo mô hình Web Linking.
Bước 3: Tác nhân khám phá lộ trình có cấu trúc của bạn như thế nào?
Tác nhân không thể dùng thứ mà chúng không tìm thấy. Hãy công bố các điểm vào ở những nơi mà client tự động vốn đã tìm đến:
/llms.txtở gốc site: một bản tóm tắt Markdown ngắn về những gì site của bạn cung cấp, kèm liên kết tới các trang hữu ích nhất và các phiên bản có cấu trúc của chúng, theo đề xuất llms.txt.- Một mô tả API như tài liệu OpenAPI, được liên kết từ tài liệu dành cho nhà phát triển của bạn và từ
/llms.txt. - Các liên kết thay thế trong các trang HTML trỏ tới phiên bản Markdown hoặc JSON.
- Một sitemap liệt kê các URL nội dung chuẩn của bạn, theo định dạng được xác định bởi Sitemaps protocol.
Một llms.txt tối thiểu:
# Example App
> Example App helps teams plan projects. This file lists the best entry points for automated clients.
## Docs
- [Getting started](https://example.com/docs/getting-started.md): setup in five steps
- [API reference](https://example.com/openapi.json): OpenAPI description of the public API
## Policies
- [Automated access](https://example.com/docs/automated-access.md): limits and how to sign requestsHãy giữ một trang giải thích các quy tắc dành cho client tự động: route nào mở, giới hạn nào áp dụng, và cách một client có thể tự nhận diện bằng các yêu cầu được ký để nhận được xử lý tốt hơn. Các quy tắc rõ ràng biến phỏng đoán thành hợp tác.
Bước 4: Định tuyến theo lớp tác nhân, không theo chuỗi user agent
Khi đã có phản hồi có cấu trúc, hãy quyết định ai mặc định nhận chúng. Dùng lớp tác nhân và cơ sở của nó thay vì phân tích chuỗi user agent theo kiểu tự phát. Các nhà vận hành AI có công bố user agent của họ, như OpenAI và Anthropic làm cho crawler và fetcher của họ, nhưng đối sánh chuỗi vẫn chỉ là một khai báo.
type ActorClass = "human" | "human_ai" | "agent" | "unknown";
type Basis = "verified" | "declared" | "inferred" | "unknown";
function preferStructured(actor: ActorClass, basis: Basis, accept: string): boolean {
if (accept.includes("text/markdown") || accept.includes("application/json")) return true;
return actor === "agent" && (basis === "verified" || basis === "declared");
}Hai nguyên tắc giúp điều này an toàn:
- Yêu cầu rõ ràng luôn được ưu tiên. Nếu client yêu cầu Markdown hoặc JSON, hãy phục vụ đúng định dạng đó, bất kể lớp nào.
- Giá trị mặc định không bao giờ lấy đi bất cứ thứ gì. Một khách truy cập Human hoặc Unknown luôn nhận được trang đầy đủ. Chọn một mặc định có cấu trúc cho tác nhân chỉ thay đổi định dạng, không thay đổi những gì họ được phép xem.
Bước 5: Tác nhân AI nên nhận giới hạn tốc độ nào?
Giới hạn tốc độ là nơi ngữ cảnh tác nhân mang lại giá trị rõ nhất. Thay vì một giới hạn cho mọi người, hãy cho mỗi tổ hợp giữa lớp và cơ sở một ngân sách hợp lý:
| Tác nhân và cơ sở | Route đọc | Ghi chú |
|---|---|---|
| Agent, Verified | Giới hạn cao hơn | Nhà vận hành có thể nhận diện và chịu trách nhiệm |
| Agent, Declared hoặc Inferred | Giới hạn tiêu chuẩn | Các tuyên bố không thể kiểm tra |
| Unknown | Giới hạn tiêu chuẩn | Không bao giờ trừng phạt theo mặc định |
| Human hoặc Human + AI | Giới hạn tiêu chuẩn | Việc sử dụng tương tác hiếm khi chạm tới giới hạn |
Hãy gắn giới hạn theo định danh ổn định nhất hiện có. Với các agent đã Verified, đó là nguồn gốc của nhà vận hành và định danh key. Với các trường hợp khác, hãy dùng credential hoặc một key ở cấp mạng.
function limitKey(actor: { actor: ActorClass; basis: Basis; operator?: string }, ip: string, apiKey?: string) {
if (actor.actor === "agent" && actor.basis === "verified" && actor.operator) return `op:${actor.operator}`;
if (apiKey) return `key:${apiKey}`;
return `ip:${ip}`;
}
function limitFor(actor: { actor: ActorClass; basis: Basis }): number {
if (actor.actor === "agent" && actor.basis === "verified") return 600;
return 120;
}Các con số ở đây chỉ là chỗ trống. Hãy đặt giá trị của bạn từ đường cơ sở mà bạn quan sát được. Khi một client vượt quá giới hạn, hãy trả về 429 Too Many Requests với header Retry-After và một liên kết đến trang truy cập tự động của bạn. Các tác nhân được xây tốt sẽ giảm tốc và đọc nó.
Bước 6: Giữ các hành động sau lớp ủy quyền
Một lộ trình có cấu trúc cho việc đọc không phải là lối đi vòng qua mô hình bảo mật của bạn. Bất kỳ hành động nào thay đổi dữ liệu vẫn cần:
- xác thực bằng một credential do ứng dụng của bạn cấp,
- ủy quyền cho hành động cụ thể đó, và
- bất kỳ kiểm tra bổ sung nào mà mức rủi ro của route yêu cầu.
Một chữ ký đã Verified cho bạn biết client nào đang gọi. Nó không cho bạn biết client đó có thể thay mặt một người dùng cụ thể hay không. Hãy tách riêng các câu hỏi đó. Bài viết của chúng tôi về điều mà các yêu cầu tác nhân AI đã ký chứng minh giải thích sự khác biệt này.
Bước 7: Đo tác động
Hãy chạy lộ trình có cấu trúc ở chế độ quan sát trước, rồi so sánh:
- tỷ lệ yêu cầu của tác nhân sử dụng các định dạng có cấu trúc,
- chi phí máy chủ và độ trễ cho lưu lượng tác nhân trước và sau,
- tỷ lệ lỗi và thử lại của các client tác nhân,
- có bao nhiêu client tự động chuyển từ Declared sang Verified sau khi bạn công bố hướng dẫn ký của mình.
Những con số này cho thấy lộ trình có cấu trúc có đang làm đúng vai trò của nó hay không và nên mở rộng ở đâu tiếp theo.
Bước 8: Phiên bản hóa và tài liệu hóa lộ trình có cấu trúc
Hãy coi lộ trình có cấu trúc như một giao diện công khai, vì các tác nhân sẽ xây dựng dựa trên nó. Một vài thói quen sẽ giúp nó đáng tin cậy:
- Giữ URL ổn định. Nếu một tác nhân đã học rằng
/docs/getting-started.mdtồn tại, nó nên tiếp tục hoạt động. Khi nội dung được chuyển đi, hãy trả về một301tới vị trí mới thay vì một404. - Bao gồm thời điểm cập nhật. Đặt trường
updatedAttrong các phản hồi JSON và một headerLast-Modifiedtrên Markdown, để tác nhân có thể cache hợp lý và chỉ làm mới khi có thay đổi. - Hỗ trợ conditional requests. Phản hồi với
If-None-MatchvàIf-Modified-Sincebằng304 Not Modified. Các tác nhân thăm dò nội dung của bạn sẽ tốn ít hơn nhiều khi chúng có thể xác thực lại thay vì tải toàn bộ một lần nữa. - Phiên bản hóa các thay đổi phá vỡ tương thích. Nếu cấu trúc JSON phải thay đổi, hãy thêm một đường dẫn phiên bản mới hoặc media type mới và giữ bản cũ trong một khoảng thời gian đã công bố.
- Công bố changelog cho trang truy cập tự động. Người vận hành các tác nhân hoạt động tốt sẽ đọc nó, và nó cho bạn một nơi để thông báo các route có cấu trúc mới.
app.get("/api/plans", async (req, res) => {
const plans = await loadPlans();
const etag = `"${plans.version}"`;
res.set("ETag", etag);
res.set("Cache-Control", "public, max-age=300");
if (req.get("if-none-match") === etag) return res.status(304).end();
return res.json({ updatedAt: plans.updatedAt, plans: plans.items });
});Những thực hành này cũng có lợi cho frontend của chính bạn và các tích hợp với đối tác. Một lộ trình có cấu trúc ổn định, có thể cache và có tài liệu nhanh chóng trở thành cách dễ nhất để bất kỳ chương trình nào làm việc với ứng dụng của bạn.
Các sai lầm phổ biến
- Phục vụ thông tin khác nhau cho tác nhân và con người. Phiên bản có cấu trúc nên chứa cùng thông tin như trang. Nội dung khác biệt làm người dùng bối rối khi họ hỏi trợ lý về sản phẩm của bạn.
- Che giấu lộ trình có cấu trúc. Nếu nó không được liên kết từ
/llms.txt, tài liệu API của bạn hoặc chính các trang, tác nhân sẽ tiếp tục quét HTML. - Thưởng cho các tuyên bố. Giới hạn cao hơn dành cho các client đã Verified. Một user agent bị sao chép không nên nhận ngân sách lớn hơn.
- Quên cache. Không có
Vary: Accept, một cache có thể phục vụ Markdown cho trình duyệt.
Proof of Actor phù hợp ở đâu
Proof of Actor cung cấp cho bạn lớp tác nhân và cơ sở mà bạn cần để định tuyến tác nhân đến nội dung có cấu trúc và đặt giới hạn công bằng. Nó phát hiện các tác nhân đã ký, đã khai báo và được suy luận, giúp bạn hiểu cách chúng sử dụng các route của bạn, và để quyết định định tuyến lại cho code của bạn.
Để thiết kế một lộ trình có cấu trúc cho ứng dụng của riêng bạn cùng đội ngũ của chúng tôi, đăng ký quyền truy cập sớm. Riêng với các kịch bản API và MCP, xem APIs và MCP Servers, và đọc ai đang gọi MCP server của bạn? để biết ngữ cảnh tác nhân áp dụng như thế nào cho các lời gọi công cụ Model Context Protocol.
Nguồn
- RFC 9110: HTTP Semantics: content negotiation (Section 12),
Vary(Section 12.5.5), conditional requests (Section 13) vàRetry-After(Section 10.2.3) - RFC 7763: The text/markdown Media Type: phục vụ các biểu diễn Markdown
- RFC 8288: Web Linking: các liên kết
alternatecó kiểu - RFC 6585: Additional HTTP Status Codes, Section 4:
429 Too Many Requests - llms.txt proposal: định dạng tệp
/llms.txt - OpenAPI Specification: mô tả API có thể đọc bằng máy
- Sitemaps protocol: định dạng tệp sitemap
- OpenAI: Overview of OpenAI Crawlers: các user agent và dải IP đã công bố của OpenAI
- Anthropic: crawler and fetcher documentation: ClaudeBot, Claude-User và Claude-SearchBot
- Model Context Protocol specification (2026-07-28): bản sửa đổi MCP hiện tại tính đến tháng 11 năm 2026