8 phút

Quản lý trạng thái giữa Frontend và Backend trong ứng dụng AI

Tìm hiểu cách trạng thái UI, phiên và dữ liệu di chuyển giữa frontend và backend trong ứng dụng AI, cùng các mẫu thực tiễn để đồng bộ, lưu trữ, cache và đảm bảo bảo mật.

Quản lý trạng thái giữa Frontend và Backend trong ứng dụng AI

Ý nghĩa của “trạng thái” trong một ứng dụng AI

“Trạng thái” là mọi thứ ứng dụng cần nhớ để hành xử đúng từ lúc này sang lúc khác.

Nếu người dùng nhấn Send trong giao diện chat, ứng dụng không nên quên những gì họ đã gõ, trợ lý đã trả lời ra sao, liệu một yêu cầu còn đang chạy hay không, hoặc những cài đặt nào (tone, model, công cụ) đang bật. Tất cả những điều đó đều là trạng thái.

Trạng thái, nói ngắn gọn

Một cách hữu ích để nghĩ về trạng thái là: sự thật hiện tại của ứng dụng—các giá trị ảnh hưởng đến những gì người dùng thấy và việc hệ thống sẽ làm tiếp theo. Điều đó bao gồm những thứ rõ ràng như ô nhập liệu, nhưng cũng có những “sự thật vô hình” như:

  • Cuộc hội thoại mà người dùng đang tham gia
  • Liệu phản hồi cuối cùng đang streaming hay đã hoàn tất
  • Danh sách tin nhắn và thứ tự của chúng
  • Các cuộc gọi công cụ và kết quả công cụ (kết quả tìm kiếm, tra cứu cơ sở dữ liệu, trích xuất file)
  • Lỗi, thử lại và chiến lược lùi khi bị giới hạn tần suất

Tại sao ứng dụng AI phức tạp hơn

Ứng dụng truyền thống thường đọc dữ liệu, hiển thị và lưu cập nhật. Ứng dụng AI thêm những bước trung gian và đầu ra tạm thời:

  • Một hành động của người dùng có thể kích hoạt nhiều thao tác backend (gọi LLM, gọi công cụ, gọi LLM khác).
  • Phản hồi có thể đến dần từng phần (token streaming), nên UI phải quản lý trạng thái từng phần.
  • Ngữ cảnh quan trọng: hệ thống cần giữ bộ nhớ cuộc hội thoại, kết quả công cụ và cài đặt mô hình nhất quán giữa các yêu cầu.

Chính sự chuyển động thêm này khiến quản lý trạng thái thường là phần ẩn phức tạp trong các ứng dụng AI.

Những gì hướng dẫn này sẽ đề cập

Trong các phần sau, chúng ta sẽ tách trạng thái thành các loại thực tiễn (trạng thái UI, trạng thái phiên, dữ liệu bền, và trạng thái mô hình/runtime), và cho biết mỗi loại nên nằm ở đâu (frontend hay backend). Chúng ta cũng sẽ bàn về cách đồng bộ, caching, công việc chạy lâu, cập nhật streaming và bảo mật—vì trạng thái chỉ hữu ích khi nó đúng và được bảo vệ.

Ví dụ ngắn

Hãy tưởng tượng một app chat nơi người dùng hỏi: “Tóm tắt hóa đơn tháng trước và đánh dấu những điểm bất thường.” Backend có thể (1) lấy hóa đơn, (2) chạy công cụ phân tích, (3) stream tóm tắt về UI, và (4) lưu báo cáo cuối cùng.

Để quá trình mượt mà, ứng dụng phải theo dõi tin nhắn, kết quả công cụ, tiến độ và đầu ra đã lưu—không được lẫn lộn các cuộc hội thoại hay rò dữ liệu giữa người dùng.

Bốn lớp trạng thái: UI, phiên, dữ liệu và mô hình

Khi mọi người nói “trạng thái” trong ứng dụng AI, họ thường trộn nhiều thứ khác nhau. Chia trạng thái thành bốn lớp—UI, phiên, dữ liệu và mô hình/runtime—giúp dễ quyết định nơi một thứ nên nằm, ai có thể thay đổi nó, và cách nó nên được lưu.

1) Trạng thái UI (người dùng đang làm gì ngay bây giờ)

Trạng thái UI là trạng thái trực tiếp, theo từng khoảnh khắc trong trình duyệt hoặc app: ô nhập văn bản, công tắc, mục được chọn, tab mở, và nút bị vô hiệu hoá.

Ứng dụng AI thêm vài chi tiết UI:

  • Hiển thị đang tải và trạng thái “đang suy nghĩ”
  • Token streaming (văn bản xuất hiện dần khi được sinh)
  • Tin nhắn nháp cục bộ (trước khi gửi)

Trạng thái UI nên dễ đặt lại và an toàn khi mất. Nếu người dùng refresh trang thì có thể mất—thường thì điều đó chấp nhận được.

2) Trạng thái phiên / cuộc hội thoại (ngữ cảnh chia sẻ cho luồng người dùng)

Trạng thái phiên liên kết người dùng với một tương tác đang diễn ra: danh tính người dùng, một conversation ID, và một cách nhìn nhất quán về lịch sử tin nhắn.

Trong ứng dụng AI, nó thường bao gồm:

  • Lịch sử tin nhắn (hoặc tham chiếu đến nó)
  • Dấu vết công cụ (công cụ/func đã gọi và kết quả)
  • “Tập làm việc” như project/tài liệu hiện tại, model được chọn, hoặc workspace

Lớp này thường trải giữa frontend và backend: frontend giữ định danh nhẹ, trong khi backend là nguồn chính cho tính liên tục và kiểm soát truy cập.

3) Trạng thái dữ liệu (bản ghi bền trong lưu trữ)

Trạng thái dữ liệu là thứ bạn lưu có chủ ý trong cơ sở dữ liệu: dự án, tài liệu, embedding, tùy chọn, nhật ký kiểm toán, sự kiện thanh toán, và bản ghi hội thoại đã lưu.

Khác với UI và trạng thái phiên, trạng thái dữ liệu nên:

  • Bền (sống sót sau khởi động lại)
  • Có thể truy vấn (có thể tìm/lọc)
  • Có thể kiểm toán (hiểu được những gì đã xảy ra sau này)

4) Trạng thái mô hình / runtime (AI đang được cấu hình như thế nào ngay lúc này)

Trạng thái mô hình/runtime là thiết lập vận hành dùng để tạo ra câu trả lời: system prompts, công cụ bật/tắt, temperature/max tokens, cài đặt an toàn, giới hạn tốc độ, và bộ nhớ đệm tạm thời.

Một phần là cấu hình (mặc định ổn định); một số là ngắn hạn (cache ngắn hạn hoặc ngân sách token cho mỗi yêu cầu). Phần lớn nên đặt ở backend để kiểm soát nhất quán và không lộ ra ngoài.

Tại sao tách biệt giảm lỗi

Khi các lớp này bị mờ, bạn gặp các lỗi kinh điển: UI hiển thị văn bản chưa được lưu, backend dùng prompt khác với frontend mong đợi, hoặc bộ nhớ cuộc hội thoại “rò” giữa người dùng. Ranh giới rõ ràng tạo nguồn tin cậy rõ ràng—và làm hiển nhiên phải gì cần tồn tại, gì có thể tái tính và gì cần được bảo vệ.

Những gì nên nằm ở frontend vs backend (và vì sao)

Cách tin cậy để giảm lỗi trong ứng dụng AI là quyết định, với mỗi phần trạng thái, nơi nó nên sống: trình duyệt (frontend), server (backend), hay cả hai. Lựa chọn này ảnh hưởng đến độ tin cậy, bảo mật và cảm giác “bất ngờ” khi người dùng refresh, mở tab mới hoặc mất kết nối.

Trạng thái frontend: nhanh, tạm thời và do người dùng điều khiển

Trạng thái frontend phù hợp cho những thứ thay đổi nhanh và không cần tồn tại qua refresh. Giữ nó cục bộ giúp UI phản hồi nhanh và tránh các API call không cần thiết.

Ví dụ chỉ frontend:

  • Tin nhắn nháp người dùng đang gõ
  • Bộ lọc cục bộ và thứ tự sắp xếp trong bảng
  • Modal mở/đóng, tab chọn, trạng thái hover

Nếu bạn mất trạng thái này khi refresh, thường là chấp nhận được (và mong đợi).

Trạng thái backend: có thẩm quyền, nhạy cảm và chia sẻ

Trạng thái backend nên chứa bất cứ thứ gì cần được tin cậy, kiểm toán hoặc thực thi nhất quán. Điều này bao gồm trạng thái mà các thiết bị/tab khác cần thấy, hoặc phải đúng ngay cả khi client bị thay đổi.

Ví dụ chỉ backend:

  • Quyền và vai trò (người dùng được phép làm gì)
  • Trạng thái thanh toán/đăng ký và giới hạn sử dụng
  • Công việc chạy lâu (index tài liệu, xuất lớn, fine-tune) và trạng thái của chúng

Một tư duy hữu ích: nếu trạng thái sai có thể tốn tiền, làm lộ dữ liệu, hoặc phá vỡ kiểm soát truy cập, nó thuộc về backend.

Trạng thái chia sẻ: cần phối hợp, nhưng có một nguồn tin cậy

Một số trạng thái tự nhiên là chia sẻ:

  • Tiêu đề cuộc hội thoại
  • Nguồn tri thức được chọn cho cuộc chat
  • Các trường hồ sơ người dùng dùng trên nhiều thiết bị

Ngay cả khi chia sẻ, hãy chọn một “nguồn sự thật”. Thông thường backend là có thẩm quyền và frontend cache một bản sao để nhanh.

Quy tắc ngón tay cái (và một anti-pattern phổ biến)

Giữ trạng thái gần nơi cần nhất, nhưng lưu những gì phải tồn tại qua refresh, thay đổi thiết bị hoặc gián đoạn.

Tránh anti-pattern lưu trạng thái nhạy cảm hoặc có thẩm quyền chỉ trong trình duyệt (ví dụ, coi cờ isAdmin ở client, tier gói hoặc trạng thái hoàn thành công việc là sự thật). UI có thể hiển thị những giá trị này, nhưng backend phải xác minh.

Vòng đời yêu cầu AI điển hình: từ click đến hoàn thành

Một tính năng AI cảm thấy như “một hành động”, nhưng thực tế là chuỗi chuyển trạng thái chia sẻ giữa trình duyệt và server. Hiểu vòng đời giúp tránh UI không khớp, thiếu ngữ cảnh và phí lặp.

1) Hành động người dùng → frontend chuẩn bị ý định

Người dùng nhấn Send. UI ngay lập tức cập nhật trạng thái cục bộ: có thể thêm bóng tin nhắn “pending”, vô hiệu hoá nút gửi, và chụp đầu vào hiện tại (văn bản, tệp đính kèm, công cụ được chọn).

Ở điểm này frontend nên tạo hoặc đính kèm các định danh tương quan:

  • conversation_id: cuộc thread này thuộc về đâu
  • message_id: ID do client tạo cho tin nhắn mới
  • request_id: duy nhất cho mỗi lần thử (hữu ích cho retry)

Những ID này cho phép hai bên nói về cùng một sự kiện ngay cả khi phản hồi đến muộn hoặc trùng lặp.

2) Gọi API → server xác thực và lưu

Frontend gửi request API kèm tin nhắn người dùng và các ID. Server xác thực quyền, giới hạn tần suất và dạng payload, rồi lưu tin nhắn người dùng (hoặc ít nhất là một bản ghi log bất biến) theo conversation_idmessage_id.

Bước lưu này ngăn “lịch sử ảo” khi người dùng refresh giữa chừng.

3) Server tái dựng ngữ cảnh

Để gọi mô hình, server xây dựng lại ngữ cảnh từ nguồn tin cậy:

  • Lấy các tin nhắn gần nhất cho conversation_id
  • Kéo các bản ghi liên quan (tài liệu, tùy chọn, kết quả công cụ)
  • Áp dụng chính sách cuộc hội thoại (system prompts, quy tắc bộ nhớ, cắt bớt)

Ý chính: đừng tin client cung cấp toàn bộ lịch sử. Client có thể lỗi thời.

4) Thực thi mô hình/công cụ → trạng thái trung gian

Server có thể gọi công cụ (tìm kiếm, tra cứu DB) trước hoặc trong khi sinh mô hình. Mỗi cuộc gọi công cụ tạo ra trạng thái trung gian nên được theo dõi theo request_id để có thể kiểm toán và retry an toàn.

5) Phản hồi (streaming hoặc không) → hoàn tất trên UI

Với streaming, server gửi token/ sự kiện từng phần. UI dần cập nhật tin nhắn trợ lý đang chờ, nhưng vẫn xem đó là “đang tiến hành” cho đến khi sự kiện cuối cùng báo hoàn tất.

6) Các điểm lỗi cần lường trước

Retry, gửi hai lần và phản hồi không theo thứ tự đều xảy ra. Dùng request_id để dedupe trên server, và message_id để hòa giải trên UI (bỏ qua các đoạn muộn không khớp yêu cầu đang hoạt động). Luôn hiển thị trạng thái “failed” rõ ràng với nút retry an toàn không tạo tin nhắn trùng lặp.

Phiên và bộ nhớ cuộc hội thoại: giữ ngữ cảnh mà không rối

Earn credits as you build
Share what you ship with Koder.ai or refer teammates to get extra credits.

Một phiên là “chuỗi” liên kết hành động người dùng: workspace họ đang ở, lần tìm kiếm cuối, nháp họ đang sửa và cuộc hội thoại một phản hồi AI nên tiếp tục. Trạng thái phiên tốt khiến app cảm thấy liên tục trên các trang—và lý tưởng là trên các thiết bị—mà không biến backend thành nơi chứa mọi thứ người dùng từng nói.

Mục tiêu trạng thái phiên

Hướng tới: (1) tính liên tục (người dùng rời đi rồi quay lại), (2) tính đúng đắn (AI dùng đúng ngữ cảnh cho đúng cuộc hội thoại), và (3) tính cách ly (một phiên không thể rò sang phiên khác). Nếu hỗ trợ nhiều thiết bị, coi session vừa theo user vừa theo device: “cùng tài khoản” không luôn là “cùng phiên mở”.

Cookies vs token vs server sessions

Bạn thường chọn một trong các cách sau để định danh phiên:

  • Cookies: đơn giản cho web vì trình duyệt gửi tự động. Tốt cho session truyền thống, nhưng phải đặt cờ bảo mật (HttpOnly, Secure, SameSite) và xử lý CSRF.
  • Tokens (ví dụ JWT): phù hợp cho API và mobile vì client đính kèm rõ ràng. Khả năng mở rộng tốt, nhưng thu hồi và xoay vòng cần thiết kế thêm (và không nên nhồi trạng thái nhạy cảm vào token).
  • Server sessions: server lưu dữ liệu phiên (thường trong Redis), client chỉ giữ một session ID mờ. Dễ thu hồi và cập nhật, nhưng bạn phải chạy và scale store phiên.

Chiến lược bộ nhớ cuộc hội thoại

“Bộ nhớ” chỉ là trạng thái bạn chọn gửi lại vào mô hình.

  • Toàn bộ lịch sử: chính xác nhất, nhưng tốn và có thể lộ nội dung nhạy cảm cũ.
  • Tóm tắt lịch sử: giữ một bản tóm tắt chạy kèm vài lượt gần nhất; rẻ hơn và thường “đủ tốt”.
  • Ngữ cảnh theo cửa sổ: chỉ N lượt gần nhất; đơn giản nhưng có thể mất quyết định quan trọng trước đó.

Mẫu thực tế là tóm tắt + cửa sổ: dự đoán được và giúp tránh hành vi mô hình bất ngờ.

Gọi công cụ: có thể lặp lại và kiểm toán được

Nếu AI dùng công cụ (tìm kiếm, truy vấn DB, đọc file), lưu mỗi cuộc gọi công cụ với: đầu vào, dấu thời gian, phiên bản công cụ, và output trả về (hoặc tham chiếu đến nó). Điều này cho phép giải thích “tại sao AI nói vậy”, chạy lại để debug và phát hiện khi kết quả thay đổi vì dataset hoặc công cụ đổi.

Hàng rào bảo mật về quyền riêng tư

Không lưu bộ nhớ dài hạn mặc định. Chỉ giữ những gì cần cho tính liên tục (conversation ID, tóm tắt, log công cụ), đặt giới hạn lưu giữ, và tránh lưu nguyên văn văn bản người dùng trừ khi có lý do rõ ràng và được đồng ý.

Đồng bộ trạng thái an toàn: nguồn sự thật và xử lý xung đột

Trạng thái nguy hiểm khi cùng một “thứ” có thể chỉnh sửa ở nhiều nơi—UI, tab khác, hoặc job nền. Giải pháp không phải là code tinh xảo mà là xác định quyền sở hữu rõ ràng.

Định nghĩa nguồn sự thật

Quyết định hệ thống nào có thẩm quyền cho mỗi phần trạng thái. Trong hầu hết ứng dụng AI, backend nên sở hữu bản ghi chuẩn cho những gì phải đúng: cài đặt cuộc hội thoại, quyền công cụ, lịch sử tin nhắn, giới hạn thanh toán và trạng thái công việc. Frontend cache và suy ra trạng thái để nhanh, nhưng nên giả sử backend đúng khi có xung đột.

Một quy tắc thực tế: nếu bạn sẽ bực khi mất nó khi refresh, nó có lẽ thuộc backend.

Cập nhật UI lạc quan (optimistic) — dùng cẩn thận

Cập nhật lạc quan làm app có cảm giác tức thì: bật một cài đặt, cập nhật UI ngay, rồi xác nhận với server. Hiệu quả cho hành động ít rủi ro và có thể đảo (ví dụ gắn dấu sao một cuộc hội thoại).

Nó gây nhầm khi server có thể từ chối hoặc biến đổi thay đổi (kiểm tra quyền, giới hạn, validate hoặc mặc định phía server). Trong những trường hợp đó, hiển thị trạng thái “saving...” và chỉ cập nhật UI sau khi có xác nhận.

Xử lý xung đột (hai tab, một cuộc hội thoại)

Xung đột xảy ra khi hai client cập nhật cùng một bản ghi dựa trên phiên bản khác nhau. Ví dụ: Tab A và Tab B cùng thay đổi temperature của model.

Dùng versioning nhẹ để backend phát hiện ghi chồng:

  • updated_at timestamps (đơn giản, dễ debug)
  • ETags / header If-Match (thuần HTTP)
  • Số sửa tăng dần (revision) (phát hiện xung đột rõ ràng)

Nếu version không khớp, trả về lỗi conflict (thường HTTP 409) và trả về đối tượng server mới nhất.

Thiết kế API giảm mismatch

Sau mỗi write, API nên trả về đối tượng đã lưu như trong DB (bao gồm mặc định phía server, trường chuẩn hoá, và version mới). Điều này cho phép frontend thay thế cache ngay—một cập nhật nguồn sự thật thay vì đoán thay đổi.

Caching và hiệu năng: tăng tốc mà không làm UI lỗi thời

Caching là cách nhanh nhất để làm app AI cảm giác tức thì, nhưng cũng tạo bản sao thứ hai của trạng thái. Nếu cache sai chỗ hoặc cache thứ sai, bạn sẽ giao một UI nhanh nhưng khó hiểu.

Những gì nên cache ở client

Cache phía client nên tập trung trải nghiệm, không phải quyền.

Ứng viên tốt: preview cuộc hội thoại gần đây (tiêu đề, đoạn trích cuối), tùy chọn UI (theme, model chọn, trạng thái sidebar), và trạng thái UI lạc quan (tin nhắn đang “sending”).

Giữ cache client nhỏ và có thể bỏ: nếu bị xóa, app vẫn hoạt động bằng cách fetch lại từ server.

Những gì nên cache ở server

Cache server nên tập trung công việc đắt hoặc lặp lại:

  • Kết quả công cụ có thể tái sử dụng (ví dụ forecast thời tiết cho cùng thành phố trong 5 phút)
  • Lookup embedding và kết quả tìm kiếm vector cho truy vấn lặp (thường TTL ngắn)
  • Trạng thái giới hạn tần suất và bộ đếm throttling

Bạn cũng có thể cache trạng thái dẫn xuất như đếm token, quyết định kiểm duyệt, hay kết quả parse tài liệu—bất cứ thứ gì xác định và tốn kém.

Cơ bản về vô hiệu hóa cache (không cầu kỳ)

Ba quy tắc thực tế:

  1. Dùng khóa cache rõ ràng mã hoá đầu vào (user_id, model, tham số công cụ, phiên bản tài liệu).
  2. Đặt TTL theo tốc độ thay đổi dữ liệu. TTL ngắn thường tốt hơn logic phức tạp.
  3. Bỏ qua cache khi tính đúng đắn quan trọng hơn tốc độ: sau khi người dùng cập nhật tài liệu, thay đổi quyền, hoặc yêu cầu refresh.

Nếu bạn không giải thích được khi một cache trở nên sai, đừng cache nó.

Đừng cache bí mật hoặc dữ liệu cá nhân trong cache chia sẻ

Tránh đặt API key, token xác thực, prompt thô chứa dữ liệu nhạy cảm, hoặc nội dung người dùng vào lớp cache chia sẻ như CDN. Nếu phải cache dữ liệu người dùng, cô lập theo user và mã hoá khi lưu—hoặc giữ trong DB chính.

Đo lường tác động: tốc độ vs UI lỗi thời

Caching nên được chứng minh, không giả định. Theo dõi p95 latency trước/sau, tỉ lệ hit cache và lỗi nhìn thấy bởi người dùng như “tin nhắn cập nhật sau khi hiển thị.” Một phản hồi nhanh nhưng sau đó mâu thuẫn với UI thường tệ hơn một phản hồi hơi chậm mà nhất quán.

Lưu trữ và công việc chạy lâu: jobs, queue và trạng thái

Ship with rollback safety
Iterate on state changes confidently with snapshots and rollback in Koder.ai.

Một số tính năng AI xong trong một giây. Những tác vụ khác mất phút: upload & parse PDF, embedding & index knowledge base, hoặc chạy workflow công cụ nhiều bước. Với những việc này, “trạng thái” không chỉ là trên màn hình—mà là thứ tồn tại qua refresh, retry và thời gian.

Những gì nên lưu và vì sao

Chỉ lưu những gì đem lại giá trị sản phẩm thực sự.

Lịch sử cuộc hội thoại: tin nhắn, timestamp, danh tính người dùng và (thường) mô hình/công cụ đã dùng. Điều này cho phép “resume later”, theo dõi và hỗ trợ.

Cài đặt người dùng và workspace: model mặc định, temperature, feature toggle, system prompts và tuỳ chọn UI theo người dùng.

File và artifact (upload, văn bản trích xuất, báo cáo sinh ra) thường để object storage với bản ghi DB trỏ tới. DB giữ metadata (owner, size, content type, trạng thái xử lý) còn blob store giữ nội dung.

Công việc nền cho tác vụ lâu

Nếu request không thể hoàn thành trong timeout HTTP bình thường, chuyển công việc vào queue.

Mẫu điển hình:

  • Frontend gọi API như POST /jobs với đầu vào (file id, conversation id, params).
  • Backend enqueue job (extraction, indexing, batch tool runs) và trả ngay job_id.
  • Worker xử lý bất đồng bộ và ghi kết quả về lưu trữ bền.

Cách này giữ UI phản hồi và làm retry an toàn hơn.

Trạng thái công việc mà UI có thể tin

Làm trạng thái job rõ ràng và có thể truy vấn: queued → running → succeeded/failed (tuỳ chọn canceled). Lưu chuyển trạng thái server-side với timestamp và chi tiết lỗi.

Trên frontend, thể hiện trạng thái:

  • Queued/running: hiển spinner và vô hiệu hoá hành động trùng lặp.
  • Failed: hiển lỗi ngắn gọn, kèm nút Retry.
  • Succeeded: tải artifact hoặc cập nhật cuộc hội thoại.

Cung cấp GET /jobs/{id} (poll) hoặc stream cập nhật (SSE/WebSocket) để UI không phải đoán.

Idempotency keys: retry không tạo ghi trùng

Timeout mạng là chuyện thường. Nếu frontend retry POST /jobs, bạn không muốn hai job giống nhau (và hai hóa đơn).

Yêu cầu một Idempotency-Key cho mỗi hành động logic. Backend lưu key với job_id/response và trả kết quả giống nhau cho các request lặp lại.

Chính sách dọn dẹp và hết hạn

App AI chạy lâu tích tụ dữ liệu nhanh. Xác định quy tắc lưu giữ sớm:

  • Xoá cuộc hội thoại cũ sau N ngày (hoặc cho người dùng cấu hình).
  • Xoá artifact dẫn xuất khi nguồn bị xoá.
  • Định kỳ dọn các job thất bại và file trung gian.

Xem dọn dẹp là một phần của quản lý trạng thái: giảm rủi ro, chi phí và nhầm lẫn.

Streaming response và cập nhật thời gian thực: quản lý trạng thái từng phần

Streaming làm trạng thái phức tạp hơn vì “câu trả lời” không còn là một khối duy nhất. Bạn xử lý token từng phần (văn bản đến từng từ) và đôi khi công việc công cụ cũng về từng phần (tìm kiếm bắt đầu rồi xong sau). Điều này nghĩa là UI và backend phải đồng ý thế nào là tạm thời và thế nào là cuối cùng.

Backend: stream sự kiện có kiểu, không chỉ text

Mẫu sạch là stream một chuỗi sự kiện nhỏ, mỗi sự kiện có kiểu và payload. Ví dụ:

  • token: văn bản tăng dần
  • tool_start: một cuộc gọi công cụ bắt đầu (ví dụ “Searching…”, có id)
  • tool_result: output công cụ sẵn sàng (cùng id)
  • done: tin nhắn trợ lý hoàn tất
  • error: xảy ra lỗi (kèm thông điệp an toàn cho người dùng và debug id)

Dòng sự kiện này dễ version và debug hơn streaming text thô, vì frontend có thể render tiến độ chính xác (và hiển thị trạng thái công cụ) mà không phải đoán.

Frontend: cập nhật dạng append-only, rồi commit cuối

Trên client, coi streaming như append-only: tạo một tin nhắn trợ lý “nháp” và cứ nối thêm khi nhận token. Khi nhận done, thực hiện commit: đánh dấu tin nhắn là cuối cùng, persist nếu lưu cục bộ, và mở các hành động như copy, rate hoặc regenerate.

Cách này tránh ghi lại lịch sử giữa chừng và giữ UI dự đoán được.

Xử lý gián đoạn (cancel, rớt, timeout)

Streaming tăng xác suất công việc dở dang:

  • Người dùng huỷ: gửi tín hiệu cancel; dừng render token; giữ nháp hiển thị là đã huỷ.
  • Mất mạng: dừng stream; hiển “reconnecting...” và không giả định hoàn tất.
  • Server timeout/lỗi: finalise nháp là failed, cung cấp retry tạo request mới (không nối các stream một cách im lặng).

Rehydration: load lại và tái dựng trạng thái ổn định

Nếu trang reload giữa chừng stream, tái dựng từ trạng thái ổn định mới nhất: các tin nhắn đã commit cộng metadata nháp (message id, văn bản tích luỹ, trạng thái công cụ). Nếu không thể resume stream, hiển nháp là gián đoạn và cho phép người dùng retry, thay vì giả vờ nó đã hoàn tất.

Bảo mật và quyền riêng tư: bảo vệ trạng thái đầu-cuối

Export and own the code
Generate fast, then export the source code to extend it your way.

Trạng thái không chỉ là “dữ liệu bạn lưu”—mà còn prompt người dùng, upload, tuỳ chọn, output sinh ra và metadata liên kết. Trong ứng dụng AI, trạng thái có thể nhạy cảm bất thường (thông tin cá nhân, tài liệu nội bộ, quyết định nội bộ), nên bảo mật cần thiết kế vào từng lớp.

Giữ bí mật ở server

Bất cứ thứ gì cho phép client giả mạo app phải ở phía server: API keys, connector bí mật (Slack/Drive/DB creds), và system prompts hoặc logic định tuyến nội bộ. Frontend chỉ gửi yêu cầu hành động (“tóm tắt file này”), nhưng backend quyết định thực thi ra sao và bằng credentials nào.

Ủy quyền mọi viết (và nhiều đọc)

Xem mỗi phép biến đổi trạng thái là thao tác đặc quyền. Khi client cố tạo tin nhắn, đổi tên cuộc hội thoại hoặc đính kèm file, backend nên kiểm tra:

  • Người dùng đã xác thực.
  • Người dùng sở hữu resource (conversation, workspace, project).
  • Người dùng được phép thực hiện hành động đó (vai trò, giới hạn gói, chính sách tổ chức).

Điều này ngăn tấn công “đoán ID” nơi kẻ xấu đổi conversation_id truy cập lịch sử người khác.

Đừng tin trình duyệt: validate và sanitize

Giả định mọi input từ client là không tin cậy. Validate schema và ràng buộc (kiểu, độ dài, enum cho phép) và sanitize cho điểm đến (SQL/NoSQL, logs, render HTML). Nếu chấp nhận “cập nhật trạng thái” (ví dụ settings, tham số công cụ), whitelist các trường được phép thay vì merge JSON tuỳ ý.

Nhật ký kiểm toán cho hành động quan trọng

Với hành động thay đổi trạng thái bền—chia sẻ, xuất, xoá, truy cập connector—ghi lại ai đã làm gì và khi nào. Một audit log nhẹ hữu ích cho điều tra sự cố, hỗ trợ khách hàng và tuân thủ.

Tối giản dữ liệu và mã hoá

Chỉ lưu những gì bạn cần để cung cấp tính năng. Nếu không cần prompt đầy đủ mãi mãi, cân nhắc window lưu trữ hoặc gỡ nhãn. Mã hoá dữ liệu nhạy cảm khi lưu (token, creds connector, tài liệu upload) và dùng TLS khi truyền. Tách metadata vận hành khỏi nội dung để giới hạn truy cập chặt hơn.

Kiến trúc tham khảo thực tế và checklist xây dựng

Một mặc định hữu ích cho app AI là đơn giản: backend là nguồn sự thật, frontend là cache nhanh, lạc quan. UI có thể cảm thấy tức thì, nhưng bất cứ thứ gì bạn buồn khi mất (tin nhắn, trạng thái job, kết quả công cụ, sự kiện tính phí) nên được xác nhận và lưu phía server.

Nếu bạn xây với workflow “vibe-coding”—nhiều bề mặt sản phẩm sinh nhanh—mô hình trạng thái càng quan trọng. Các nền tảng như Koder.ai có thể giúp nhóm giao mã web, backend và mobile từ chat, nhưng quy tắc vẫn vậy: lặp nhanh an toàn khi nguồn sự thật, ID và chuyển trạng thái được thiết kế từ đầu.

Kiến trúc tham khảo (có thể triển khai)

Frontend (browser/mobile)

  • Trạng thái UI: panel mở, tin nhắn nháp, model được chọn, chỉ báo “typing” tạm thời.
  • Cache trạng thái server: preview cuộc hội thoại gần nhất, trạng thái job cuối biết, buffer stream một phần.
  • Một pipeline request duy nhất luôn đính kèm: session_id, conversation_id, và một request_id mới.

Backend (API + workers)

  • API service: validate input, tạo bản ghi, phát streaming response.
  • Store bền (SQL/NoSQL): conversations, messages, tool calls, trạng thái job.
  • Queue + workers: tác vụ chạy lâu (RAG indexing, parse file, image gen).
  • Cache (tuỳ chọn): hot reads (tóm tắt cuộc hội thoại, metadata embedding), luôn đánh key với version/timestamp.

Ghi chú: một cách thực tế để giữ nhất quán là chuẩn hoá stack backend sớm. Ví dụ, backend do Koder.ai-generated thường dùng Go với PostgreSQL (và React ở frontend), giúp tập trung trạng thái “có thẩm quyền” vào SQL trong khi cache client là tạm.

Thiết kế model trạng thái trước

Trước khi xây giao diện, định nghĩa các trường bạn sẽ dựa vào trên mọi lớp:

  • ID và ownership: user_id, org_id, conversation_id, message_id, request_id.
  • Timestamps và ordering: created_at, updated_at, và sequence rõ ràng cho tin nhắn.
  • Trường trạng thái: queued | running | streaming | succeeded | failed | canceled (cho job và tool calls).
  • Versioning: etag hoặc version cho cập nhật an toàn khi có xung đột.

Điều này ngăn lỗi kinh điển khi UI “trông đúng” nhưng không hòa giải được retry, refresh hoặc edit đồng thời.

Dùng API shapes nhất quán

Giữ endpoint dễ đoán:

  • GET /conversations (list)
  • GET /conversations/{id} (get)
  • POST /conversations (create)
  • POST /conversations/{id}/messages (append)
  • PATCH /jobs/{id} (update status)
  • GET /streams/{request_id} hoặc POST .../stream (stream)

Trả về kiểu envelope giống nhau ở mọi nơi (kể cả lỗi) để frontend có thể cập nhật trạng thái đồng nhất.

Thêm observability nơi trạng thái dễ hỏng

Log và trả về một request_id cho mọi call AI. Ghi input/output công cụ (kèm redaction), latency, retry, và trạng thái cuối. Hãy làm cho câu trả lời cho câu hỏi: “Mô hình đã thấy gì, công cụ nào đã chạy và trạng thái nào chúng ta đã persist?” trở nên dễ dàng.

Checklist xây dựng (để tránh lỗi trạng thái phổ biến)

  • Backend là nguồn sự thật; cache frontend được gắn nhãn rõ và có thể vứt.
  • Mọi ghi là idempotent (an toàn để retry) bằng request_id (và/hoặc Idempotency-Key).
  • Chuyển trạng thái được định nghĩa rõ và validate (không nhảy im lặng từ queued sang succeeded).
  • Cập nhật streaming hợp nhất theo ID/sequence, không theo “message cuối cùng thắng”.
  • Xung đột xử lý qua version/etag hoặc quy tắc merge phía server.
  • PII và bí mật không bao giờ lưu trong trạng thái client; redact logs mặc định.
  • Có một dashboard để debug: requests, tool calls, trạng thái job và lỗi.

Khi bạn chấp nhận chu kỳ build nhanh hơn (bao gồm generation hỗ trợ AI), hãy cân nhắc thêm hàng rào bắt buộc các mục checklist này—validate schema, idempotency và streaming evented—để “đi nhanh” không biến thành drift trạng thái. Thực tế, đó là nơi một nền tảng end-to-end như Koder.ai hữu ích: tăng tốc giao hàng, đồng thời cho phép xuất mã nguồn và giữ pattern xử lý trạng thái nhất quán giữa web, backend và mobile build.

Câu hỏi thường gặp

Trạng thái trong ứng dụng AI nghĩa là gì?

Trạng thái là thông tin mà ứng dụng ghi nhớ để có thể phản hồi chính xác. Trong cuộc trò chuyện AI, thông tin đó gồm tin nhắn, cuộc trò chuyện đang hoạt động, tiến trình truyền phát, các công cụ đã bật và lỗi.

Những trạng thái nào nên đặt ở frontend?

Giữ các chi tiết tạm thời do người dùng điều khiển ở giao diện người dùng, chẳng hạn như lời nhắc đang soạn, bảng điều khiển đang mở hoặc chỉ báo đang tải. Lưu mọi thứ nhạy cảm, được chia sẻ hoặc cần thiết sau khi làm mới trang ở phía máy chủ.

Những trạng thái nào thuộc về backend?

Backend nên quản lý quyền, giới hạn thanh toán, lịch sử trò chuyện, trạng thái tác vụ và cài đặt mô hình. Nó có thể xác thực mọi thay đổi và duy trì cùng một bản ghi trên nhiều thiết bị.

Trạng thái ở frontend và backend được đồng bộ như thế nào?

Dùng backend làm nguồn dữ liệu chính xác. Frontend có thể lưu đệm một bản sao để tăng tốc, sau đó thay thế hoặc đối chiếu bản sao đó với phản hồi từ máy chủ sau mỗi lần ghi.

Ứng dụng nên xử lý phản hồi AI được truyền phát như thế nào?

Tạo một tin nhắn trợ lý đang chờ và nối thêm văn bản nhận được vào đó khi các sự kiện đến. Chỉ đánh dấu hoàn tất sau khi máy chủ gửi sự kiện hoàn tất cuối cùng, để văn bản chưa đầy đủ không bao giờ trông như một câu trả lời đã xong.

Vì sao các yêu cầu AI cần ID?

Gán cho mỗi cuộc trò chuyện, tin nhắn và yêu cầu một ID riêng. Máy chủ có thể dùng các ID đó để từ chối yêu cầu trùng lặp, còn giao diện người dùng có thể bỏ qua các cập nhật đến muộn từ một yêu cầu cũ hơn.

Cách thực tế để quản lý bộ nhớ cuộc trò chuyện là gì?

Giữ một cửa sổ ngắn gồm các tin nhắn gần đây cùng bản tóm tắt được duy trì của những lượt trao đổi cũ hơn. Cách này thường giữ được ngữ cảnh hữu ích, đồng thời hạn chế chi phí và giảm khả năng các chi tiết cũ ảnh hưởng đến phản hồi.

Ứng dụng AI nên xử lý các tác vụ chạy lâu như thế nào?

Gửi tác vụ vào hàng đợi chạy nền và trả về ID tác vụ ngay lập tức. Lưu các trạng thái rõ ràng như đã xếp hàng, đang chạy, thành công, thất bại và đã hủy để người dùng có thể làm mới hoặc thử lại một cách an toàn.

Làm thế nào để bảo vệ trạng thái nhạy cảm trong ứng dụng AI?

Coi mọi giá trị từ trình duyệt là không đáng tin cậy. Backend phải xác minh người dùng, quyền sở hữu tài nguyên, quyền hạn, các trường dữ liệu gửi lên và các giới hạn trước khi đọc hoặc thay đổi trạng thái đã lưu.

Điều gì xảy ra khi hai tab cùng chỉnh sửa một cuộc trò chuyện?

Dùng số phiên bản, dấu thời gian hoặc ETag khi các ứng dụng khách cập nhật bản ghi dùng chung. Nếu phiên bản được gửi lên đã cũ, hãy trả về bản ghi hiện tại trên máy chủ và yêu cầu ứng dụng khách làm mới hoặc giải quyết xung đột.

Related posts