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.

“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.
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ư:
Ứ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:
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.
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ệ.
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.
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.
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:
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.
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ớ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.
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:
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.
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ệ.
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 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:
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 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:
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.
Một số trạng thái tự nhiên là chia sẻ:
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.
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.
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.
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:
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.
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_id và message_id.
Bước lưu này ngăn “lịch sử ảo” khi người dùng refresh giữa chừng.
Để gọi mô hình, server xây dựng lại ngữ cảnh từ nguồn tin cậy:
conversation_idÝ chính: đừng tin client cung cấp toàn bộ lịch sử. Client có thể lỗi thời.
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.
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.
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.
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.
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ở”.
Bạn thường chọn một trong các cách sau để định danh phiên:
HttpOnly, Secure, SameSite) và xử lý CSRF.“Bộ nhớ” chỉ là trạng thái bạn chọn gửi lại vào mô hình.
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ờ.
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.
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 ý.
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.
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 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.
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)If-Match (thuần HTTP)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.
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 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.
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.
Cache server nên tập trung công việc đắt hoặc lặp lại:
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.
Ba quy tắc thực tế:
user_id, model, tham số công cụ, phiên bản tài liệu).Nếu bạn không giải thích được khi một cache trở nên sai, đừng cache nó.
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.
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.
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.
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.
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:
POST /jobs với đầu vào (file id, conversation id, params).job_id.Cách này giữ UI phản hồi và làm retry an toàn hơn.
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:
Cung cấp GET /jobs/{id} (poll) hoặc stream cập nhật (SSE/WebSocket) để UI không phải đoán.
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.
App AI chạy lâu tích tụ dữ liệu nhanh. Xác định quy tắc lưu giữ sớm:
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 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.
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ầntool_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ấterror: 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.
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.
Streaming tăng xác suất công việc dở dang:
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.
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.
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.
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:
Đ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.
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ỳ ý.
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ủ.
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.
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.
Frontend (browser/mobile)
session_id, conversation_id, và một request_id mới.Backend (API + workers)
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.
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:
user_id, org_id, conversation_id, message_id, request_id.created_at, updated_at, và sequence rõ ràng cho tin nhắn.queued | running | streaming | succeeded | failed | canceled (cho job và tool calls).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.
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.
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.
request_id (và/hoặc Idempotency-Key).queued sang succeeded).version/etag hoặc quy tắc merge phía server.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.