Giảm độ trễ LLM là một trong những thách thức quan trọng nhất đối với các kỹ sư xây dựng ứng dụng AI có khả năng phản hồi tốt. Trong khi các mô hình ngôn ngữ lớn (LLM) tiếp tục tăng về năng lực, việc chúng tạo văn bản theo từng token có thể tạo ra những nút thắt gây khó chịu cho người dùng cuối, và thời gian chờ lâu dẫn trực tiếp đến mức độ tương tác thấp hơn và người dùng rời bỏ ứng dụng. Vì vậy, tối ưu hóa các pipeline suy luận về tốc độ là một yêu cầu cốt lõi đối với lập trình viên. Hướng dẫn này phác thảo cách cấu hình prompt caching, triển khai streaming phản hồi, cấu trúc định tuyến mạng edge và sử dụng các cấu hình serverless để cắt giảm độ trễ xử lý.
[!TIP] Mẹo về chỉ số hiệu suất: Khi đo độ trễ API, hãy tách Thời gian đến Token Đầu tiên (TTFT) ra khỏi tốc độ tạo tổng thể. TTFT thấp khiến ứng dụng có cảm giác tức thời đối với người dùng, ngay cả khi việc tạo toàn bộ đầu ra mất vài giây, vì văn bản bắt đầu hiển thị ngay lập tức.
Những điểm chính:
- Prompt Caching: Tái sử dụng các header tiền tố tĩnh để bỏ qua các trạng thái phân tích và giảm TTFT tới 80%.
- Streaming phản hồi: Đẩy token qua Server-Sent Events (SSE) để người dùng thấy văn bản được tạo tức thời.
- Edge Worker: Chạy việc ủy quyền và định tuyến yêu cầu tại các trung tâm edge khu vực gần người dùng.
- Định tuyến mô hình: Chuyển hướng các yêu cầu đơn giản của người dùng sang các mô hình nhẹ để tối ưu tốc độ.
Các thành phần của độ trễ API LLM
Để giảm thời gian phản hồi, trước tiên bạn phải hiểu những yếu tố nào chi phối độ trễ API tổng thể. Độ trễ tổng thể là tổng tích lũy của ba biến số riêng biệt.
Thứ nhất, thời gian truyền mạng đo lường mất bao lâu để một yêu cầu di chuyển từ client đến máy chủ của bạn, rồi tiếp tục đến API của nhà cung cấp mô hình. Điều này khiến khoảng cách truyền trở thành một nút thắt lớn.
Thứ hai, Thời gian đến Token Đầu tiên (TTFT) đại diện cho khoảng thời gian giữa lúc mô hình nhận yêu cầu và lúc tạo ra token đầu ra đầu tiên, đó là lý do vì sao prompt caching lại quan trọng đến vậy.
Cuối cùng, tốc độ tạo token đo lường tốc độ mà phần cứng xuất ra các token tiếp theo. Các ràng buộc phần cứng chi phối tốc độ tạo, nhưng lập trình viên vẫn giữ toàn quyền kiểm soát thời gian truyền và TTFT, nên định tuyến và caching thông minh có thể giảm mạnh độ trễ LLM.
Tăng tốc tích hợp LLM của bạnĐiều kiện tiên quyết
Trước khi bạn bắt đầu kết nối các tối ưu hóa dưới đây, hãy đảm bảo bạn đã chuẩn bị sẵn những điều sau. Không có điều nào là kỳ lạ, nhưng bỏ qua một trong số chúng thường gây ra những lỗi khó hiểu về sau.
- Một middleware hoặc runtime edge mà bạn kiểm soát. Các ví dụ sử dụng Cloudflare Workers, nhưng bất kỳ nền tảng serverless nào có thể proxy một yêu cầu đều hoạt động. Bạn cần một nơi để đứng giữa trình duyệt và nhà cung cấp mô hình.
- Thông tin xác thực API cho một nhà cung cấp hỗ trợ streaming và caching. OpenAI và Anthropic đều hỗ trợ. Lưu khóa dưới dạng secret (một Wrangler secret hoặc biến môi trường), không bao giờ để trong mã phía client.
- Node.js 18 trở lên nếu bạn muốn kiểm thử Workers cục bộ với
wrangler dev. Các API toàn cụcfetchvàReadableStreamđược dùng xuyên suốt đều có sẵn trong runtime đó và trong các trình duyệt hiện đại. - Một phép đo cơ sở. Ghi lại Thời gian đến Token Đầu tiên và tổng thời gian phản hồi hiện tại trước khi thay đổi bất cứ điều gì, để bạn có thể chứng minh mỗi tối ưu hóa thực sự có ích. Phần benchmarking dưới đây trình bày cách làm.
- Sự quen thuộc với Server-Sent Events (SSE). Các phản hồi streaming đến dưới dạng một chuỗi các dòng
data:, và bạn sẽ phân tích chúng trên client.
Triển khai prompt caching
Prompt caching là cách hiệu quả nhất để tối ưu TTFT cho các ứng dụng được xây dựng quanh những prompt hệ thống lớn. Khi một yêu cầu chứa một khối chỉ thị tĩnh dài (như prompt hệ thống của một agent hoặc một tài liệu tham chiếu RAG), nhà cung cấp mô hình phải phân tích và mã hóa các token đó trong mỗi lần thực thi. Cả Anthropic và OpenAI đều hỗ trợ prompt caching, tính năng này lưu các trạng thái token đã phân tích trong bộ nhớ. Các yêu cầu tiếp theo có chung tiền tố sẽ bỏ qua giai đoạn phân tích, giảm TTFT tới 80%.
Thời gian tồn tại của cache khác nhau giữa các nhà cung cấp. Anthropic duy trì cache trong khoảng năm phút không hoạt động, trong khi OpenAI dùng một mô hình suy giảm động. Do đó, việc lên lịch các ping fetch nền định kỳ có thể giữ các chỉ thị hệ thống quan trọng luôn hoạt động trong bộ nhớ máy chủ.
Cơ chế khác nhau đôi chút giữa hai nhà cung cấp, và việc cấu trúc yêu cầu cho đúng là điều quyết định cache có thực sự kích hoạt hay không. Với Anthropic, bạn đánh dấu một điểm ngắt cache một cách tường minh bằng cache_control. Mọi thứ trước điểm ngắt đều được lưu, nên nội dung ổn định, tĩnh phải đến trước và nội dung dễ thay đổi theo từng yêu cầu phải đến sau cùng:
1import Anthropic from "@anthropic-ai/sdk";
2
3const anthropic = new Anthropic();
4
5const response = await anthropic.messages.create({
6 model: "claude-opus-4-8",
7 max_tokens: 1024,
8 system: [
9 {
10 type: "text",
11 text: SYSTEM_INSTRUCTIONS // small, sent on every request
12 },
13 {
14 type: "text",
15 text: KNOWLEDGE_BASE, // large, static reference block
16 cache_control: { type: "ephemeral" }
17 }
18 ],
19 messages: [
20 { role: "user", content: userQuestion } // volatile — after the breakpoint
21 ]
22});
23
24// Confirm the cache is working
25console.log(response.usage.cache_read_input_tokens);
Lỗi phổ biến nhất là đặt một timestamp, ID yêu cầu, hoặc bất kỳ chuỗi nào thay đổi theo từng yêu cầu ở phía trước khối được cache. Vì caching là một phép khớp tiền tố, chỉ một byte bị thay đổi ở bất kỳ đâu trước điểm ngắt cũng làm vô hiệu mọi thứ sau nó, và cache âm thầm không bao giờ khớp. Hãy xác minh nó hoạt động bằng cách đọc usage.cache_read_input_tokens từ phản hồi: nếu giá trị đó vẫn ở mức 0 qua các yêu cầu giống hệt nhau, thì có thứ gì đó động đã lọt vào tiền tố. Cũng lưu ý rằng tiền tố được cache phải vượt qua một độ dài tối thiểu (khoảng 1.024 đến 4.096 token tùy mô hình) thì caching mới kích hoạt.
OpenAI có cách tiếp cận đơn giản hơn: caching là tự động cho các prompt trên khoảng 1.024 token, không có cờ cache_control nào để đặt. Tuy nhiên, kỷ luật tương tự vẫn được áp dụng. Hãy giữ khối chỉ thị tĩnh ngay ở đầu mảng messages và nối phần đầu vào thay đổi của người dùng ở cuối, để tiền tố có thể tái sử dụng vẫn giống hệt từng byte giữa các yêu cầu.
Để xem cấu trúc giá và tham số của prompt caching, hãy tham khảo Hướng dẫn Prompt Caching của Anthropic .
Edge compute và định tuyến serverless
Xử lý các yêu cầu LLM trên một máy chủ tập trung duy nhất tạo ra những bước nhảy mạng khổng lồ cho người dùng toàn cầu. Triển khai middleware API của bạn trên các mạng edge serverless (như Cloudflare Workers) rút ngắn những đường đi đó một cách đáng kể.
Edge worker nhận yêu cầu của client, ủy quyền phiên, và định tuyến nó đến trung tâm dữ liệu gần nhất của nhà cung cấp mô hình. Cấu trúc serverless này chuyển các token đến màn hình của người dùng ngay khoảnh khắc chúng được tính toán, nên giao diện có cảm giác cực kỳ nhạy. Middleware JavaScript dưới đây minh họa cách cấu hình các phản hồi streaming trực tiếp từ một runtime edge:
1export default {
2 async fetch(request, env) {
3 const payload = await request.json();
4
5 // Call the streaming LLM endpoint
6 const response = await fetch("https://api.openai.com/v1/chat/completions", {
7 method: "POST",
8 headers: {
9 "Authorization": `Bearer ${env.OPENAI_API_KEY}`,
10 "Content-Type": "application/json"
11 },
12 body: JSON.stringify({
13 model: "gpt-4o-mini",
14 messages: payload.messages,
15 stream: true
16 })
17 });
18
19 // Forward the stream directly to the client browser
20 return new Response(response.body, {
21 headers: { "Content-Type": "text/event-stream" }
22 });
23 }
24};
Cấu trúc serverless này chuyển các token đến màn hình của người dùng tức thời khi chúng được tính toán. Để tìm hiểu cách xây dựng các backend tối ưu hóa cho edge, hãy đọc hướng dẫn của chúng tôi về xây dựng một API serverless với Cloudflare Workers .
Phân tích luồng trên client
Chuyển tiếp luồng từ edge chỉ mới là một nửa công việc. Trình duyệt vẫn phải đọc các chunk đó khi chúng đến và render từng token, nếu không phản hồi sẽ tích tụ trong một buffer và xuất hiện tất cả cùng lúc, làm mất đi ý nghĩa. Đây là bước mà hầu hết các hướng dẫn bỏ qua, và là nơi hiệu suất cảm nhận thực sự được thắng hay thua.
Body của phản hồi là một ReadableStream gồm các byte thô. Các frame SSE đến dưới dạng các dòng data:, nhưng một chunk mạng đơn lẻ có thể chứa nhiều frame, hoặc chia một frame thành hai chunk, nên bạn phải đệm các dòng chưa hoàn chỉnh thay vì giả định rằng mỗi chunk là một thông điệp hoàn chỉnh:
1async function streamCompletion(messages, onToken) {
2 const response = await fetch("/api/chat", {
3 method: "POST",
4 headers: { "Content-Type": "application/json" },
5 body: JSON.stringify({ messages })
6 });
7
8 const reader = response.body.getReader();
9 const decoder = new TextDecoder();
10 let buffer = "";
11
12 while (true) {
13 const { value, done } = await reader.read();
14 if (done) break;
15
16 buffer += decoder.decode(value, { stream: true });
17 const lines = buffer.split("\n");
18 buffer = lines.pop(); // keep the trailing partial line
19
20 for (const line of lines) {
21 if (!line.startsWith("data: ")) continue;
22 const payload = line.slice(6).trim();
23 if (payload === "[DONE]") return;
24
25 try {
26 const json = JSON.parse(payload);
27 const token = json.choices?.[0]?.delta?.content;
28 if (token) onToken(token);
29 } catch {
30 // ignore keep-alive comments and malformed partial frames
31 }
32 }
33 }
34}
buffer.split("\n") theo sau bởi lines.pop() là chi tiết quan trọng: nó giữ lại bất kỳ dòng chưa hoàn chỉnh nào cho đến khi chunk tiếp theo hoàn thành nó. Bọc JSON.parse trong một try/catch giữ cho vòng lặp tiếp tục sống khi một comment keep-alive hoặc một frame nhận được một nửa xuất hiện. Callback onToken sau đó thêm mỗi mảnh vào DOM, nên văn bản xuất hiện ngay khoảnh khắc mô hình tạo ra nó.
Đo lường và benchmark độ trễ
Bạn không thể tối ưu điều bạn chưa đo lường. Trước và sau mỗi thay đổi, hãy ghi lại Thời gian đến Token Đầu tiên và tổng thời gian tạo để bạn có thể quy một cải thiện cho đúng nguyên nhân. Cách nhanh nhất để lấy mẫu TTFT là dùng curl, sử dụng time_starttransfer như một xấp xỉ gần cho byte đầu tiên đến client:
1curl -w "TTFT: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
2 -X POST https://your-worker.example.com/chat \
3 -H "Content-Type: application/json" \
4 -d '{"messages":[{"role":"user","content":"Hello"}]}' \
5 -o /dev/null -s
Đối với các con số ở cấp độ ứng dụng, hãy đo trực tiếp trong bộ phân tích của client. Đóng dấu thời gian khi yêu cầu rời đi và một lần nữa khi token đầu tiên đến:
1const start = performance.now();
2let firstTokenAt = null;
3
4await streamCompletion(messages, (token) => {
5 if (firstTokenAt === null) firstTokenAt = performance.now();
6 render(token);
7});
8
9console.log(`TTFT: ${Math.round(firstTokenAt - start)}ms`);
Chạy mỗi phép đo vài lần và lấy giá trị trung vị thay vì một mẫu đơn lẻ, vì biến động mạng và cold start có thể làm sai lệch các lần đọc một lần. Bảng dưới đây đưa ra các khoảng minh họa về nơi thời gian thường tiêu tốn trong một yêu cầu toàn cầu vận hành ổn định; hãy coi chúng như một hình mẫu để so sánh, không phải các con số cố định, vì các số liệu của riêng bạn sẽ phụ thuộc vào khu vực, mô hình và kích thước prompt.
| Giai đoạn | Đóng góp điển hình | Bạn kiểm soát được? |
|---|---|---|
| Truyền mạng (client đến edge) | 10–60 ms | Có — định tuyến edge rút ngắn nó |
| Xử lý middleware tại edge | 1–15 ms | Có — giữ worker gọn nhẹ |
| Thời gian đến Token Đầu tiên (prompt lạnh) | 400–1.200 ms | Một phần — caching cắt giảm mạnh |
| Thời gian đến Token Đầu tiên (tiền tố được cache) | 100–400 ms | Có — qua prompt caching |
| Tạo mỗi token | 10–50 ms/token | Không — do mô hình và phần cứng định đoạt |
Hai hàng đáng chú ý là các con số TTFT được cache so với lạnh. Khoảng cách đó là lợi ích đơn lẻ lớn nhất khả dụng cho hầu hết ứng dụng, đó là lý do prompt caching đứng đầu danh sách tối ưu hóa. Ngược lại, tốc độ tạo bị cố định bởi nhà cung cấp, nên định tuyến các yêu cầu đơn giản hơn sang một mô hình nhỏ hơn là đòn bẩy duy nhất ở đó.
Quy trình tối ưu hóa từng bước
Để tối ưu tốc độ ứng dụng phần mềm của bạn, hãy bắt đầu bằng cách tách các chỉ thị hệ thống tĩnh khỏi các đầu vào động của người dùng. Sự phân chia này cho phép bạn nhắm vào các điểm vào cache một cách gọn gàng.
Tiếp theo, kích hoạt các cờ prompt caching bên trong các payload API của bạn để đảm bảo nhà cung cấp mô hình lưu các token văn bản của bạn trong bộ nhớ.
Luôn cấu hình streaming phản hồi bằng các endpoint SSE tiêu chuẩn. Bằng cách viết các bộ phân tích frontend nhẹ để xử lý các chunk khi chúng đến, bạn cải thiện hiệu suất mà người dùng cảm nhận. Sau đó thiết lập các đường dự phòng cho mô hình: định tuyến các đầu vào cơ bản của khách hàng sang các mô hình nhỏ hơn, dành các mô hình suy luận lớn hơn cho các tác vụ nâng cao. Cuối cùng, hãy phân tích các bước nhảy mạng để xác nhận các worker serverless giảm độ trễ truyền. Để biết chi tiết về tối ưu hóa cơ sở dữ liệu, hãy xem hướng dẫn của chúng tôi về WordPress so với phát triển web tùy chỉnh .
Những cạm bẫy thường gặp và cách khắc phục
Một số ít lỗi lặp đi lặp lại khi các nhóm lần đầu triển khai streaming và caching. Nhận ra triệu chứng giúp tiết kiệm hàng giờ đoán mò.
| Triệu chứng | Nguyên nhân khả dĩ | Cách khắc phục |
|---|---|---|
cache_read_input_tokens vẫn ở mức 0 | Một timestamp, UUID, hoặc ID phiên nằm trước điểm ngắt cache, nên tiền tố thay đổi mỗi yêu cầu | Chuyển tất cả nội dung động ra sau khối tĩnh; tuần tự hóa bất kỳ JSON nào một cách xác định |
| Các token đến tất cả cùng lúc, không theo từng phần | Một proxy trung gian hoặc CDN đang đệm phản hồi | Gửi Cache-Control: no-transform và X-Accel-Buffering: no; đảm bảo kiểu nội dung text/event-stream được thiết lập |
| Luồng bị ngắt giữa chừng | Worker đã trả về trước khi body upstream kết thúc, hoặc đã chạm max_tokens | Trả về response.body trực tiếp thay vì chờ toàn bộ văn bản; tăng max_tokens |
| Token đầu tiên chậm dù đã caching | Tiền tố tĩnh dưới độ dài tối thiểu có thể cache của nhà cung cấp | Hợp nhất các chỉ thị để khối được cache vượt qua ngưỡng ~1.024 token |
| Bộ phân tích của client ném lỗi ở một số chunk | Một frame bị chia thành hai chunk mạng | Đệm các dòng chưa hoàn chỉnh như đã trình bày ở trên và bọc JSON.parse trong try/catch |
Một cái bẫy tinh vi hơn là việc đệm ở chính edge. Nếu bạn await response.text() bên trong worker trước khi trả về, bạn đã âm thầm biến một phản hồi streaming trở lại thành một phản hồi chặn. Luôn truyền thẳng body của luồng qua. Tương tự, hãy để ý đến các giới hạn CPU của worker: công việc nặng cho mỗi yêu cầu trong middleware cộng trực tiếp vào TTFT, nên hãy giữ logic ủy quyền và định tuyến tối thiểu và hoãn lại bất cứ thứ gì tốn kém.
Cân nhắc khi triển khai sản xuất
Để một bản demo streaming chạy trong trình duyệt thì đơn giản; chạy nó một cách đáng tin cậy dưới lưu lượng thực cần thêm vài lớp bảo vệ.
Đặt một timeout yêu cầu hợp lý cho lời gọi upstream để một kết nối nhà cung cấp bị treo không thể giữ một worker mở vô thời hạn, và kết hợp nó với một lần thử lại có dự phòng sang nhà cung cấp thứ hai hoặc một mô hình nhỏ hơn khi cái chính hết thời gian. Vì prompt caching tính một khoản phụ phí nhỏ cho các lần ghi cache và một khoản giảm giá lớn cho các lần đọc, nó chỉ có lợi khi một tiền tố được tái sử dụng; một ping nền vài phút một lần giữ một prompt hệ thống nóng thường trú mà không phải trả tiền để viết lại nó trong mỗi yêu cầu của người dùng.
Hãy đo lường liên tục thay vì chỉ lúc ra mắt. Ghi log TTFT và số token mỗi giây cho mỗi yêu cầu và cảnh báo khi giá trị trung vị trôi đi, vì một sự thoái lui từ phía nhà cung cấp hoặc một thay đổi về kích thước prompt sẽ xuất hiện ở đó đầu tiên. Cuối cùng, hãy tôn trọng các giới hạn tốc độ của nhà cung cấp: một đợt bùng nổ các luồng đồng thời có thể chạm phải chúng, nên hãy xếp hàng hoặc gạt bớt tải một cách nhã nhặn thay vì để các yêu cầu thất bại âm thầm. Những biện pháp này biến một prototype nhanh thành một ứng dụng luôn nhanh khi điều đó quan trọng.
Những điểm chính
- Nhắm vào Thời gian đến Token Đầu tiên (TTFT) và thời gian truyền để giảm độ trễ LLM.
- Tận dụng prompt caching trên các API mô hình để bỏ qua chi phí phân tích chỉ thị hệ thống.
- Dùng streaming phản hồi để chuyển các token theo thời gian thực, cải thiện tốc độ cảm nhận.
- Triển khai middleware API trên các runtime edge serverless để rút ngắn các đường mạng toàn cầu.
- Định tuyến các truy vấn đơn giản hơn của người dùng sang các mô hình nhẹ để tối ưu tốc độ thực thi.
- Thiết lập các công cụ giám sát hiệu suất để liên tục phân tích và giảm độ trễ trong điều kiện người dùng thực.
Câu hỏi thường gặp (FAQ)
Làm thế nào để tôi giảm độ trễ LLM trong sản xuất? Để giảm độ trễ LLM trong sản xuất, bạn nên triển khai prompt caching cho các chỉ thị tĩnh, bật streaming token, và triển khai các edge worker để tối ưu định tuyến yêu cầu. Bằng cách triển khai các runtime điều phối serverless gần hơn với các client toàn cầu, lập trình viên bỏ qua nhiều bước nhảy định tuyến mạng và chuyển token phản hồi đầu tiên theo thời gian thực.
Prompt caching là gì? Prompt caching là một tính năng API lưu các trạng thái văn bản đã phân tích trong bộ nhớ máy chủ, cho phép các yêu cầu tiếp theo dùng cùng tiền tố chạy nhanh hơn nhiều. Bằng cách bỏ qua chu kỳ phân tích hệ thống cho các tập dữ liệu chỉ thị lớn, tối ưu hóa này giảm Thời gian đến Token Đầu tiên (TTFT) tới tám mươi phần trăm.
Kích thước mô hình có ảnh hưởng đến độ trễ không? Có, các mô hình nhỏ hơn có tốc độ tạo token nhanh hơn nhiều, khiến chúng lý tưởng cho các tác vụ đơn giản nơi độ trễ là mối quan tâm hàng đầu. Định tuyến các yêu cầu phân loại hoặc trích xuất đơn giản sang các mô hình edge chuyên biệt đảm bảo thời gian phản hồi nhanh, đồng thời dành các mô hình dày đặc cho các tác vụ suy luận.
Streaming với Server-Sent Events (SSE) giúp giảm độ trễ cảm nhận như thế nào? Streaming SSE đẩy các token văn bản đầu ra từ máy chủ mô hình đến màn hình client theo thời gian thực khi chúng được biên dịch. Mặc dù điều này không giảm tổng thời gian thực thi, nó tối thiểu hóa Thời gian đến Token Đầu tiên (TTFT) và mang lại cho người dùng một giao diện ứng dụng nhạy và sống động.
Làm thế nào để tôi cache các phản hồi LLM động tại edge? Bạn có thể cache các phản hồi động tại edge bằng cách dùng các cơ sở dữ liệu KV hoặc các instance Redis với giới hạn TTL (Time to Live) ngắn. Cache các phản hồi động hiệu quả với các truy vấn lặp lại của người dùng hoặc các ý định dịch vụ khách hàng phổ biến, ngăn hoàn toàn các lời gọi mạng đến nhà cung cấp mô hình.
Bình luận