# Agent Output Is Not UX: Rendering Layer Your LLM Pipeline Is Missing

Bala Ramdoss · Amazon · AI Engineer World's Fair 2026: Online Track

> Lớp generative UI giữa model và màn hình: rendering contract có version, streaming vào typed component, và BFF giữ cho mobile client an toàn.

Topics: Agents, Context Engineering, AI UX

Canonical: https://homus.dev/talks/agent-output-is-not-ux-rendering-layer-your-llm-pipeline-is-missing

## 1. Đặt một bàn ăn: model làm đúng, trải nghiệm thì chưa

Bala mở đầu bằng một tình huống rất đời thường. Anh nhờ AI assistant của mình giúp đặt một bàn ở một nhà hàng đông khách. Và đây là thứ nó trả về: một khối chữ dài. Phải nói ngay là câu trả lời đó không sai. Số điện thoại có đủ. Giờ mở cửa đúng. Nó thậm chí còn biết chuyện quầy hàu ở phía trước nhà hàng chỉ nhận khách vãng lai, không cần đặt chỗ. Model đã làm phần việc thật sự, phần tìm kiếm và tổng hợp thông tin.

Nhưng hãy nhìn vào kết quả cuối cùng. Người dùng vẫn phải tự đi tìm hiểu, tự làm từng bước để thật sự đặt được cái bàn đó. Bala nhận xét rằng chúng ta đã ở trong tình trạng này khá lâu rồi.

![Câu trả lời dạng văn bản dài của Claude khi được nhờ đặt bàn ở Zuni Cafe](https://homus.dev/photos/maTp79FD9gI-0000.jpg)

Câu trả lời thật từ Claude cho yêu cầu "help me make a reservation at zuni cafe": nó hỏi ngày giờ và số người, giải thích rằng nó không tự đặt được vì cần đăng nhập vào hệ thống của nhà hàng, rồi liệt kê số điện thoại, giờ nhận đặt bàn, OpenTable và Tock, quy định đặt trước tối đa 60 ngày, nhóm 5 đến 12 người phải gọi điện, và quầy bar phía trước chỉ nhận khách vãng lai. Đúng hết, nhưng người dùng vẫn phải tự làm mọi việc.

Giờ hãy tưởng tượng bạn build một thứ như vậy cho khách hàng của mình. Bạn sẽ muốn nó làm gì? Bala đưa ra cùng một câu trả lời đó, nhưng được render theo một cách khác: một ô chọn ngày, một ô chọn giờ, vài cú chạm, và xong. Không còn đoạn văn nào phải đọc.

![Mockup một thẻ đặt bàn Zuni Cafe với ngày, số người, các khung giờ và nút đặt bàn](https://homus.dev/photos/maTp79FD9gI-0040.jpg)

Cùng câu trả lời, render thành UI: một thẻ "Zuni Cafe" có ô ngày (Fri, Jun 26), ô số người (Party of 2), các khung giờ còn trống (6:00, 6:30, 7:15, 8:00) và nút "Book table for 7:15". Góc slide ghi rõ đây là mockup minh hoạ, không phải Claude thật.

Thông điệp của Bala: sản phẩm agentic bạn đang build đã có đủ tool để đáp ứng nhu cầu của khách hàng. Thứ duy nhất bạn cần tập trung là lớp nằm giữa model và một thứ mà con người có thể tương tác được. Lớp đó chính là chủ đề của talk. Model và agent sẽ còn ở lại lâu dài, và chúng ta nên học cách làm cho chúng thân thiện với con người.

## 2. Bala Ramdoss và Amazon Lens

![Slide tiêu đề Agent Output Is Not UX của Bala Ramdoss](https://homus.dev/photos/maTp79FD9gI-0056.jpg)

Slide tiêu đề: "Agent Output Is Not UX", dòng phụ "Building the generative-UI rendering layer your LLM pipeline is missing", Bala Ramdoss, AI Engineer World's Fair.

Bala tự giới thiệu. Anh đã build các ứng dụng hướng tới khách hàng (customer-facing app) hơn mười năm, và sáu năm gần đây anh dành cho việc build **Amazon Lens**. Slide giới thiệu ghi chức danh của anh là Sr SDE tại Amazon Search.

Amazon Lens là bộ tính năng camera chạy bằng AI của Amazon. Nó cho phép bạn mua sắm bằng hình ảnh, screenshot và mã vạch để tìm ra những sản phẩm trông giống nhau. Nếu bạn có cài app Amazon, anh khuyến khích bạn thử. Talk này đến từ kinh nghiệm của anh khi build những sản phẩm customer-facing chạy trên hàng triệu, hàng triệu thiết bị mobile.

![Slide Amazon Lens: See it. Shop it. với các ảnh minh hoạ tìm sản phẩm bằng camera](https://homus.dev/photos/maTp79FD9gI-0120.jpg)

"See it. Shop it.": chụp ảnh bất cứ thứ gì bạn thấy và lập tức tìm ra món giống hoặc tương tự trong app mua sắm Amazon. Các ảnh minh hoạ cho thấy Lens nhận diện kính râm, kẹp tóc, bình pha cà phê, chậu cây, đôi giày.

Trước khi đi tiếp, anh đưa ra một lời miễn trừ nhanh: anh trình bày talk này với tư cách cá nhân. Những quan điểm trong talk là của anh, không phải của công ty nơi anh làm việc.

## 3. Bức tường khi build UX cho agentic AI

Khi nghĩ tới việc build UX cho một agentic AI, đương nhiên ChatGPT là cái tên hiện ra đầu tiên. Nhìn vào những màn hình như vậy, câu hỏi lập tức xuất hiện: bạn vẽ những tấm thẻ (card) này như thế nào? Khi nào chọn carousel, khi nào chọn list? Bạn thiết kế ra sao để con người hiểu và tương tác tốt hơn?

![Bốn màn hình ChatGPT trên điện thoại hiển thị carousel nhà hàng pizza, thẻ chi tiết, danh sách món và bản đồ](https://homus.dev/photos/maTp79FD9gI-0152.jpg)

Bốn màn hình từ blog của OpenAI: hỏi nhà hàng pizza ở San Francisco thì ra một carousel thẻ nhà hàng có ảnh và nút đặt giờ; hỏi thêm về một quán thì ra thẻ chi tiết; hỏi món ngon thì ra danh sách có ảnh và giá; hỏi xung quanh có gì thì ra bản đồ. Đây chính là loại trải nghiệm mà talk muốn mổ xẻ.

Khi thử build một thứ như vậy, bạn sẽ đâm vào một bức tường. Có những vấn đề phải được giải ngay từ những giai đoạn rất sớm của hệ thống. Trải nghiệm đó sẽ nhanh nhạy hay chậm chạp? Chúng ta nhận toàn bộ thông tin ngay lập tức, hay từng thứ một? Và làm sao scale điều này cho mobile app, nơi phiên bản app và khả năng của thiết bị phân mảnh đến vậy?

![Slide The wall với gạch đầu dòng Latency feels broken](https://homus.dev/photos/maTp79FD9gI-0208.jpg)

"The wall": gạch đầu dòng đầu tiên là "Latency feels broken", cảm giác độ trễ như thể sản phẩm đang hỏng.

Nếu để ý, bạn sẽ thấy không vấn đề nào trong số đó do chính model gây ra. Model làm tốt phần việc của nó. Đây là những vấn đề về **delivery** (đưa kết quả tới người dùng), và chúng nằm ở khoảng giữa output của model và thứ hiện trên màn hình. Theo Bala, chính lớp đó quyết định sản phẩm của bạn thành công hay không.

## 4. Lớp này giờ đã có tên: Generative UI và A2UI

Bala kể rằng nếu hai năm trước có ai hỏi anh về lớp này, anh sẽ không biết gọi nó là gì. Lúc đó anh đã build vài tính năng AI, và lần nào anh cũng giải phần này lại từ đầu: một pattern may đo, được uốn theo đúng hệ thống mà anh đang làm. Không có một bộ từ vựng chung nào cho nó cả.

Điều khiến anh thích thú là giờ thứ này đã có tên: **Generative UI**. Và đã có một spec mở cho nó: [A2UI](https://a2ui.org) của Google. Thay vì agent đưa cho bạn một đoạn text thô hay HTML, nó mô tả UI dưới dạng dữ liệu, tức là một danh sách component, và client render những component đó bằng chính các widget native của mình. Một vấn đề trước đây anh phải tự giải một mình giờ đang trở thành điểm xuất phát chung cho các nhóm, và đó chính là thứ anh hào hứng muốn đào sâu.

![Slide The layer has a name: generative UI, A2UI (Google)](https://homus.dev/photos/maTp79FD9gI-0256.jpg)

"The layer has a name: generative UI". A2UI (Google) định nghĩa các component, tức là payload. Dòng cuối slide: A2UI là một chuẩn mở, declarative và được build cho streaming.

Ý tưởng của A2UI: thay vì trả text hay HTML để client tự đoán (hàng trên), agent mô tả UI như một danh sách component, và client render bằng widget native của chính nó (hàng dưới).

## 5. Từ data API sang UI-shaping API: ba nấc

Một API bình thường trả về dữ liệu, và client tự quyết định vẽ dữ liệu đó ra sao. Model của bạn giỏi tool use, giỏi viết code, giỏi cả những task phức tạp khác. Nó cũng làm được UI, và Generative UI nói về đúng điều đó.

Đây là một dải phổ (spectrum). [CopilotKit](https://www.copilotkit.ai), một công ty làm chủ yếu về mảng này, chia nó thành [ba nấc](https://www.copilotkit.ai/generative-ui-spectrum):

- **Controlled** (ở dưới cùng): model chọn một component dựng sẵn, ví dụ một product card. Nó không bao giờ tự nghĩ ra thứ gì mới.

- **Declarative** (ở giữa): model ghép UI từ một catalog, ví dụ một ô ngày, một ô giờ, một nút submit. A2UI nằm ở nấc này.

- **Open-ended** (trên cùng): model sinh ra một UI hoàn toàn mới ngay lúc chạy, ví dụ [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps).

![Slide From data APIs to UI-shaping APIs với ba nấc Controlled, Declarative, Open-ended](https://homus.dev/photos/maTp79FD9gI-0352.jpg)

"From data APIs to UI-shaping APIs": Controlled, model chọn component dựng sẵn (ví dụ product_card); Declarative, model ghép từ catalog (A2UI); Open-ended, model sinh UI mới (MCP Apps). Nguồn phổ: CopilotKit.

Càng leo lên cao, client của bạn càng phải tin tưởng nhiều hơn vào bất cứ thứ gì model đưa cho nó. Phần lớn mobile app trong production sống ở hai nấc dưới, vì đó là chỗ an toàn, và talk này cũng tập trung vào hai nấc đó.

## 6. Mobile thêm một tầng phức tạp

Làm cho việc build UX phức tạp hơn nữa là vai trò quan trọng của mobile app. Trên web, nếu renderer hỏng, bạn ship một bản sửa và nó lên live trong vài phút. Mobile app không làm được như vậy. Bạn đang nhìn vào hàng trăm triệu lượt cài đặt, và bạn không kiểm soát được khi nào bất kỳ máy nào trong số đó cập nhật.

![Slide Mobile adds complexity so sánh vòng đời bản sửa trên web và mobile](https://homus.dev/photos/maTp79FD9gI-0504.jpg)

"Mobile adds complexity". Web: deploy fix, live sau vài phút, 100% người dùng ở bản đã sửa, bạn kiểm soát timeline. Mobile: build và review, staged rollout, tăng từ 5% lên 100% trong nhiều ngày tới nhiều tuần, phần đuôi dài (long tail) kéo dài hàng tuần trở lên với những người vẫn ở bản cũ; người dùng kiểm soát timeline.

Vì thế, khi một client gặp một loại nội dung (content type) mà nó chưa từng thấy, nó không suy giảm một cách êm ái (gracefully degrade). Nó crash, và nó cứ tiếp tục crash trong nhiều ngày hoặc nhiều tuần, trên tay những người chưa cập nhật app.

Nên với mobile client, có một quy tắc chi phối mọi thứ phía sau: **bạn không thể patch client một cách có ý nghĩa**. Mọi thiết kế ở các phần sau đều xuất phát từ ràng buộc này.

## 7. Toàn cảnh rendering layer và ba pattern

Hệ thống trông như thế nào khi nhìn từ xa? Bala đưa ra toàn bộ pipeline ở dạng đơn giản hoá:

- Một **context phase** có nhận biết phiên bản (version-aware) để nạp thông tin cho model. Đúng vậy, anh nói, đó chính là context engineering.

- Model xuất ra **typed UI intent**, tức là ý định UI có kiểu rõ ràng.

- Intent đó chảy qua một **BFF** (Backend for frontend).

- Rồi tới **client renderer**, nơi vẽ nó ra và lùi về phương án an toàn (fallback) khi không vẽ được.

![Sơ đồ The rendering layer, mapped: Context Phase, Model output, BFF, Client](https://homus.dev/photos/maTp79FD9gI-0552.jpg)

"The rendering layer, mapped": Context Phase (version aware surfacing) nạp vào Model output (typed UI intent); output được stream sang BFF (format, context, hydration, actions); BFF stream tiếp tới Client (rendering, fallback).

Anh chia hệ thống thành ba pattern:

- **Rendering contract**.

- **Streaming**, dòng chảy nằm giữa các thành phần.

- **BFF**, lớp nằm ở giữa.

Talk đi qua từng pattern một, bắt đầu với contract.

## 8. Pattern 1: rendering contract

Contract là chuyện làm cho model biết được client có những khả năng gì. Bạn bảo đảm model luôn bám đúng client mà nó đang vẽ UI cho. Điều contract bảo đảm là trách nhiệm không đổ lên client hay rendering layer, bắt chúng phải nhìn vào token output rồi tự suy ra nên hiển thị gì.

![Slide The rendering contract: Typed, prevent client from trying to use patterns from raw token output](https://homus.dev/photos/maTp79FD9gI-0648.jpg)

"The rendering contract": Typed, để client không phải cố dò pattern từ raw token output.

Bạn cũng duy trì một repository chứa **version map**, ánh xạ phiên bản app với các khả năng (capability). Ví dụ, bạn giới thiệu một UI flight card mới ở phiên bản 2.0. Hãy bảo đảm chỉ đưa nó ra cho model từ phiên bản 2.0 trở đi khi bạn build context.

Version map trong context phase: một component mới như flight card ra đời ở bản 2.0 thì chỉ được đưa vào context của model khi client đang chạy từ 2.0 trở lên. Client bản cũ không bao giờ nhận một component mà nó không biết vẽ.

Bài học ở đây: model sẽ là bên chọn CX (customer experience), còn bạn là bên cung cấp đúng thông tin trong context của nó.

## 9. Phần contract nhìn từ phía model

Bala đưa ra một ví dụ, và nói rằng đây thường là phần khó khi build hệ thống này. Đây là nửa của contract hướng về phía model. Bạn không muốn model trả về text. Bạn muốn nó stream về từng khối (block) UI component: một **conversation block** cho những gì nó nói bằng chữ, và một **UI block** cho thứ nó muốn client render.

Contract thậm chí còn mã hoá cả quy tắc layout. Trong ví dụ này: một đến ba chuyến bay thì dùng carousel vuốt ngang; bốn chuyến trở lên thì dùng list dọc. Model chọn intent.

![Slide The rendering contract với định nghĩa JSON cho flight_carousel và flight_list](https://homus.dev/photos/maTp79FD9gI-0728.jpg)

Model stream conversation_block cộng ui_block. Quy tắc: 1 đến 3 kết quả thì dùng flight_carousel, từ 4 trở lên thì dùng flight_list. Mỗi component có layout, giới hạn số mục và data_spec với kiểu dữ liệu của từng trường.

```
Model streams: conversation_block + ui_block
Rule: 1-3 results -> flight_carousel · 4+ -> flight_list

"flight_carousel": {
"layout": "horizontal_swipeable",
"max_items": 3,
"data_spec": { "airline": "string", "price": "number", "depart": "time" }
},
"flight_list": {
"layout": "vertical_scrollable",
"data_spec": { "airline": "string", "price": "number", "depart": "time" }
}
```

Và hãy để ý điều model không bao giờ làm: nó không bao giờ tự sáng chế ra một component. Nó chọn từ một menu cố định mà bạn đưa cho nó. Hiển nhiên menu đó sẽ không có tới hàng triệu món, chỉ vài món thôi. Hoá ra mọi thứ khó lên đáng kể khi bạn scale các tính năng ra nhiều bề mặt (surface) hơn. Bala để khán giả tự nghĩ xem nên engineer context như thế nào để bảo đảm model chọn đúng một component.

Đó là phần contract.

![Sơ đồ rendering layer với Context Phase và Model output sáng, BFF và Client mờ đi](https://homus.dev/photos/maTp79FD9gI-0840.jpg)

Context Phase và Model output sáng lên, BFF và Client mờ đi. Contract sống ở hai ô này.

## 10. Pattern 2: streaming vào typed UI

Bước sang phần streaming, Bala nói đây là điều làm anh "mở mắt" khi lần đầu gặp nó. Theo cách truyền thống, app gọi một API rồi chờ cho tới khi có gì đó xảy ra. Khi có LLM tham gia, pattern này trở nên kém hiệu quả, vì latency thường cao hơn do chính bản thân model. Thêm vào đó, còn vô số bước kiểm tra bổ sung để bảo đảm an toàn và những thứ tương tự. Và chúng ta còn đưa vào một lớp mới, khá phức tạp, để làm dynamic UI.

Streaming giúp ở đây bằng cách không bắt client chờ toàn bộ response. Nó render từng phần (chunk) một.

![Slide Streaming into typed UI: chunks over SSE với sơ đồ request và response giữa điện thoại và server](https://homus.dev/photos/maTp79FD9gI-0848.jpg)

"Streaming into typed UI": các chunk đi qua SSE (Server-Sent Events). Chú thích dưới slide: A2UI chuẩn hoá phần này.

Bala cho xem một ví dụ minh hoạ, khi bạn phải đưa thông tin ra từng chunk một: đầu tiên hiện một khung xương (skeleton), rồi điền một phần, rồi hoàn chỉnh. Có thể mất ba đến bốn giây để tới được bước cuối, nhưng sự chờ đợi đó chịu được.

Một thẻ UI lớn dần theo từng chunk: skeleton xuất hiện gần như ngay lập tức, rồi được điền dần, rồi hoàn chỉnh. Tổng thời gian có thể ba đến bốn giây, nhưng người dùng thấy thứ hữu ích từ rất sớm.

Điều đó thay đổi thứ bạn đo cho một app. Bạn ngừng đuổi theo tổng latency, việc mà chúng ta đã làm suốt hơn một thập kỷ. Bạn bắt đầu đuổi theo **time to first chunk**, tức là thứ hữu ích đầu tiên mà người dùng nhìn thấy. Vì lý do này, loading spinner truyền thống không còn dùng được cho các tính năng AI.

![Slide Streaming into typed UI với ba gạch đầu dòng về SSE, time to first chunk và spinner](https://homus.dev/photos/maTp79FD9gI-0952.jpg)

Ba ý trên slide: chunk đi qua SSE; tối ưu time to first chunk chứ không phải tổng latency (TTFT / TTFC); và trên slide ghi một spinner bị đọc như là hỏng sau khoảng 2 giây.

## 11. Latency là một quyết định sản phẩm

Đôi khi bạn phải sáng tạo và thiết kế tính năng của mình xoay quanh latency. Cách này có thể không hợp với mọi trường hợp, nhưng Bala đưa ra một ví dụ sản phẩm. Khi bạn biết sẽ mất thời gian để render một thứ gì đó, hãy giữ người dùng bận rộn với việc khác. **Lens Live** cho phép người dùng lấy nét vào những thứ khác nhau, chạm vào một vật họ quan tâm trong lúc chờ kết quả.

![Slide Latency as a product decision với màn hình Lens Live đang nhận diện một chậu cây](https://homus.dev/photos/maTp79FD9gI-1016.jpg)

"When you can't beat latency, design around it": khi không thắng được latency, hãy thiết kế quanh nó. Màn hình Lens Live đang khoanh vùng một chậu cây, phía dưới hiện dần các sản phẩm khớp và gợi ý câu hỏi.

Nếu bạn không kiểm soát toàn bộ màn hình, bạn có thể hiện một **thinking CX**, kiểu trải nghiệm "đang suy nghĩ", nhưng hãy dùng nó một cách tiết kiệm. Người dùng AI nói chung đã ra khỏi giai đoạn dễ dãi, và giờ họ muốn biết chuyện gì đang diễn ra.

![Meme Willy Wonka: Oh! You are thinking? I should wait for a response then](https://homus.dev/photos/maTp79FD9gI-1032.jpg)

"Do not use thinking CX for everything", kèm meme Willy Wonka mỉa mai: "Ồ, bạn đang suy nghĩ à? Vậy chắc tôi nên ngồi chờ câu trả lời nhỉ."

Khi đã có cách stream về các trạng thái khác nhau, bạn biết cách cho người dùng thấy agent của mình đang làm gì. Bala đưa ra một ví dụ anh lấy từ Gemini: khi bắt đầu làm một task, nó cho người dùng thấy thoáng qua agent đang làm gì. Dù mất tới 10 giây, anh vẫn thấy ổn nếu anh biết chuyện gì đang xảy ra, và nhờ vậy có thể tin vào output cuối cùng của agent.

![Hai dòng trạng thái của agent: Listing Custom Sunshades và Comparing Sunshade Features](https://homus.dev/photos/maTp79FD9gI-1056.jpg)

Ví dụ trạng thái agent Bala lấy từ Gemini: "Listing Custom Sunshades", rồi "Comparing Sunshade Features". Mỗi bước hiện một dòng ngắn cho biết agent đang làm gì, thay vì một vòng quay vô nghĩa.

Vậy là xong phần streaming.

## 12. Pattern 3: server-driven UI và BFF

Bala chuyển sang phần cuối cùng, và theo anh là phần quan trọng nhất: BFF mới của bạn. Anh tóm lại mạch của ba pattern: pattern một nói về cách UI được chọn. Pattern hai nói về cách các chunk được giao tới. Pattern ba nói về cách các chunk trở thành những phần tử UI có ý nghĩa.

![Sơ đồ rendering layer với BFF và Client được làm nổi](https://homus.dev/photos/maTp79FD9gI-1120.jpg)

"Pattern 3: SDUI and BFF": trên sơ đồ, BFF (format, context, hydration, actions) và Client được làm nổi.

Đây là khái niệm **server-driven UI** (SDUI), và nó cũng là một dải phổ về mức kiểm soát của server: server kiểm soát càng nhiều, bạn càng phải build nhiều. BFF là một tập con của nó và quyết định cách render. Nó nắm các quy tắc riêng của từng nền tảng, như Android khác iOS, những thứ kiểu như vậy. Nó cũng giúp client phải ra ít quyết định hơn và chỉ việc vẽ thứ được trao cho nó. Toàn bộ ý tưởng là như vậy.

![Slide Pattern 3: SDUI and BFF với các gạch đầu dòng về client dumb và BFF quyết định](https://homus.dev/photos/maTp79FD9gI-1136.jpg)

Backend For Frontend làm cho client "dumb" (nhỏ gọn); server-driven UI đưa nhiều quyền kiểm soát hơn về server, đổi lại tốn nhiều công hơn; BFF quyết định cách render và CX riêng cho từng nền tảng.

Đọc thêm về pattern BFF gốc: [Backends For Frontends](https://samnewman.io/patterns/architectural/bff/) của Sam Newman.

## 13. BFF làm nhiều hơn layout

BFF không chỉ ship layout. Nó làm thêm một chút. Ngoài format và context, nó làm **hydration** và gắn thêm **action**. Mỗi phần tử được render đều mang theo một action payload, quy định một cú chạm sẽ làm gì, mở deep link nào. Nó thậm chí có thể đặt tên cho impression metric cần log.

![Slide Pattern 3: BFF does more với thẻ chuyến bay và action payload kèm theo](https://homus.dev/photos/maTp79FD9gI-1208.jpg)

"Pattern 3: BFF does more": bên trái là thẻ người dùng nhìn thấy (Acme Air, $214, khởi hành 08:15, nút "Select flight"); bên phải là action payload mà BFF gắn vào thẻ đó.

```
action_payload
on_tap: open flight_details
deeplink: app://flights/AC214
on_long_press: save_to_trip
impression: flight_row_view
```

BFF cũng mang conversational context qua các lượt hội thoại, để response tiếp theo biết điều gì đã xảy ra trước đó.

Và điểm hay là bạn có thể áp dụng điều này cho chính những app đang có. Bạn có thể tái sử dụng các đơn vị CX (CX unit) sẵn có: dòng chuyến bay, product card, những component mà app của bạn đã ship trong production. Bạn không phải dựng một kiểu giao diện agentic mới tinh trong app, và đó là một cách tốt để build cho con người. Cùng thương hiệu, cùng mật độ thông tin, cùng cảm giác quen thuộc. Nó trông và cảm giác như native.

## 14. Takeaways: agent output không phải là CX

Điều đó dẫn tới phần takeaways.

![Slide Takeaways với ba gạch đầu dòng](https://homus.dev/photos/maTp79FD9gI-1344.jpg)

"Takeaways": model đủ thông minh để viết code thì cũng chọn được CX; stream vào typed component, không phải text; BFF hấp thụ model, client giữ ở trạng thái dumb và an toàn.

- **Model đã rất có năng lực.** Bạn đưa cho nó một contract có kiểu rõ ràng, có version đàng hoàng, để nó tự chọn đúng CX.

- **Stream vào typed component, không phải text.** Cho người dùng biết agent của bạn đang làm gì.

- **Để BFF hấp thụ output của model**, nhờ vậy client có thể giữ "dumb" và an toàn.

Không điều nào trong số này nói về model. Model vốn đã ổn. Chính lớp này mới là thứ ship sản phẩm.

Quay lại điểm xuất phát của talk: output của agent không phải là CX. Bạn build trên nó. Bala cảm ơn khán giả, và nếu muốn tìm hiểu thêm hay kết nối với anh, có một mã QR trên slide cuối.

![Slide kết với tiêu đề talk, ảnh Bala Ramdoss và mã QR](https://homus.dev/photos/maTp79FD9gI-1352.jpg)

Slide kết quay lại tiêu đề "Agent Output Is Not UX", kèm ảnh Bala và mã QR để kết nối.

## Nguồn và liên kết

- [Trang talk trên ai.engineer](https://ai.engineer/talks/maTp79FD9gI) · [Video gốc](https://www.youtube.com/watch?v=maTp79FD9gI)

- [A2UI](https://a2ui.org): spec mở của Google để agent mô tả UI dưới dạng dữ liệu, client render bằng widget native; mã nguồn: [a2ui-project/a2ui](https://github.com/a2ui-project/a2ui)

- [Generative UI spectrum](https://www.copilotkit.ai/generative-ui-spectrum) của [CopilotKit](https://www.copilotkit.ai): ba nấc controlled, declarative, open-ended

- [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps): ví dụ cho nấc open-ended; spec và SDK: [modelcontextprotocol/ext-apps](https://github.com/modelcontextprotocol/ext-apps)

- [Server-Sent Events (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events): cơ chế stream chunk từ server về client nhắc trên slide

- [Backends For Frontends](https://samnewman.io/patterns/architectural/bff/), Sam Newman: nguồn gốc pattern BFF

- Amazon Lens: tính năng tìm sản phẩm bằng camera trong app Amazon (amazon.com/lens)

- Bài giới thiệu apps trong ChatGPT trên blog OpenAI: openai.com/index/introducing-apps-in-chatgpt

- LinkedIn của Bala Ramdoss: linkedin.com/in/bala-ramdoss
