🇬🇧 Read the English version of this article on tanhdev.com

Answer-first: Generative UI kết hợp Model Context Protocol (MCP) và React Server Components (RSC) chuyển đổi giao diện AI từ chat văn bản sang UI tương tác động. LLM tạo ra component specification qua Zod schema, frontend phân giải từ Component Registry và fetch dữ liệu thực tế an toàn qua Iframe Sandbox và CSP.

Điều kiện tiên quyết: Kinh nghiệm lập trình React/Next.js hiện đại, hiểu biết về giao thức JSON-RPC 2.0 và cơ chế streaming / function calling của các mô hình LLM.

Thế hệ đầu tiên của giao diện chat tích hợp AI đi theo một mô típ đơn giản: người dùng gõ tin nhắn, LLM tạo văn bản (text), UI hiển thị văn bản đó. Thế hệ thứ hai bổ sung tool calls (lệnh gọi công cụ) — LLM có thể gọi các hàm (functions) và hiển thị kết quả dưới dạng văn bản. Thế hệ thứ ba — Generative UI (UI Sinh Tự Động) — tiến xa hơn thế: LLM không chỉ tạo ra các câu trả lời bằng văn bản mà còn tạo ra các thành phần UI tương tác (interactive UI components) được render (kết xuất) trực tiếp trong trình duyệt, mang đến những trải nghiệm không giống như đang trò chuyện với một khung văn bản mà giống như đang sử dụng một ứng dụng thông minh, phản hồi siêu nhạy.

Generative UI đại diện cho một sự chuyển dịch kiến trúc thực sự. Nó yêu cầu một giao kèo (contract) mới giữa mô hình AI và frontend: Model Context Protocol (MCP), được phát triển bởi Anthropic. MCP định nghĩa cách các mô hình AI khám phá các công cụ và UI components (thành phần giao diện) có sẵn, cách chúng gọi các component đó với các tham số định kiểu, cũng như cách frontend kết xuất và cô lập (sandbox) các giao diện được sinh tự động.

Bài viết này bao quát thiết kế kiến trúc của một hệ thống Generative UI sử dụng MCP: sự tiến hóa từ văn bản đến UI tương tác, lớp hợp đồng MCP, đồng bộ trạng thái client-agent, registry của các component, kiểm soát bảo mật, và một cấu trúc tham chiếu (reference implementation) sử dụng Next.js.

Để xem toàn bộ chuỗi các bài viết triển khai, hãy khám phá Series Kiến Trúc Generative UI.


Sự Tiến Hóa Của Giao Diện Chat: Hướng Tới Generative UI

Để hiểu Generative UI, cần phải hiểu những gì các thế hệ giao diện AI trước đây có thể và không thể làm được.

Thế Hệ 1 — Trả Về Văn Bản (Text Output): LLM trả về văn bản thuần túy (plain text). Frontend kết xuất nó dưới dạng markdown. Những tương tác phong phú (lọc bảng dữ liệu, đặt lịch hẹn, điều chỉnh thanh trượt) yêu cầu người dùng rời khỏi ngữ cảnh của AI và điều hướng đến một tính năng riêng biệt khác của ứng dụng.

Thế Hệ 2 — Gọi Công Cụ (Tool Calls - Dựa Trên Văn Bản): LLM có thể gọi các hàm (tìm kiếm database, kiểm tra trạng thái đơn hàng). Kết quả được định dạng thành văn bản và trả về trong cuộc hội thoại. Người dùng vẫn chỉ tương tác với văn bản.

Thế Hệ 3 — Generative UI (UI Sinh Tự Động): LLM có thể chỉ định không chỉ những thông tin gì cần hiển thị mà còn hiển thị nó như thế nào. Thay vì trả lời “Chuyến bay của bạn khởi hành lúc 14:30 ở Cổng B12 — bạn có muốn check-in không?”, nó trả về một thẻ chuyến bay (flight card) sinh động với nút check-in chỉ bằng một cú nhấp chuột, tính năng chọn ghế ngồi và đồng hồ đếm ngược giờ lên máy bay trực tiếp — tất cả được render linh hoạt ngay trong trình duyệt mà không cần tải lại hoặc chuyển trang.

flowchart LR
    G1["Gen 1: Text"] -->|model output| TEXT["Trạng thái đơn hàng của bạn: Đã giao"]
    G2["Gen 2: Tool Call"] -->|tool result| TEXT2["Đơn hàng #123 đã được vận chuyển vào 2026-06-01, ETA 2026-06-03"]
    G3["Gen 3: Generative UI"] -->|component spec| UI["Thẻ OrderStatusCard hiển thị trực quan"]

Generative UI là kiến trúc phù hợp khi:

  • Người dùng cần tương tác với dữ liệu phức tạp (không chỉ là đọc nó)
  • AI cần hiển thị các giao diện theo ngữ cảnh thay đổi dựa trên trạng thái, vai trò người dùng, hoặc nghiệp vụ
  • Ứng dụng muốn loại bỏ sự phiền phức của kiểu “AI bảo bạn phải làm gì, bạn phải tự đi tìm chỗ để làm việc đó”

Model Context Protocol (MCP): Tầng Giao Kèo Hệ Thống Cho Việc Tạo UI

MCP là một đặc tả giao thức mở định nghĩa một giao diện tiêu chuẩn cho các mô hình AI để khám phá và gọi các công cụ, bao gồm cả các công cụ kết xuất UI. Nếu không có MCP, mỗi ứng dụng AI phải xây dựng một bản giao kèo độc quyền giữa mô hình và frontend — khiến các component không thể tái sử dụng và các tích hợp trở nên dễ gãy vỡ.

Hợp Đồng Của Component MCP (MCP Component Contract)

Một UI component trong MCP được xác định bởi:

  1. Schema (Lược đồ): Một JSON Schema hoặc một định nghĩa kiểu TypeScript mô tả các props của component
  2. Renderer (Trình kết xuất): Một React component (hoặc không phụ thuộc framework) render các props được cung cấp
  3. Permissions (Quyền hạn): Những dữ liệu nào component được quyền truy cập và những hành động nào nó có thể kích hoạt
  4. Sandbox level (Cấp độ Sandbox): Liệu component đó chạy trong môi trường an toàn (trusted) hay một ngữ cảnh iframe cô lập (sandboxed)

Một định nghĩa component theo chuẩn MCP:

// mcp-registry/components/order-status-card.ts
import { z } from "zod";

export const OrderStatusCardSchema = z.object({
    orderId: z.string().describe("Mã định danh duy nhất của đơn hàng"),
    status: z.enum(["pending", "processing", "shipped", "delivered", "cancelled"])
        .describe("Trạng thái hiện tại của đơn hàng"),
    items: z.array(z.object({
        name: z.string(),
        quantity: z.number().int().positive(),
        priceCents: z.number().int(),
    })).describe("Các mặt hàng trong đơn"),
    trackingNumber: z.string().optional().describe("Mã vận đơn"),
    estimatedDelivery: z.string().optional().describe("Ngày dự kiến giao hàng chuẩn ISO 8601"),
    allowedActions: z.array(z.enum(["track", "cancel", "return", "reorder"]))
        .describe("Các thao tác mà người dùng được phép thực hiện trên đơn hàng này"),
});

export type OrderStatusCardProps = z.infer<typeof OrderStatusCardSchema>;

export const OrderStatusCardMCPDefinition = {
    name: "order-status-card",
    description: "Hiển thị trạng thái hiện tại của một đơn hàng kèm theo các tùy chọn thao tác",
    schema: OrderStatusCardSchema,
    permissions: ["read:orders", "write:orders:cancel"],
    sandboxLevel: "trusted",   // Quyền truy cập vào ngữ cảnh xác thực (auth context) của ứng dụng
};

Cách Mô Hình Khám Phá Component

Server MCP mở ra (expose) một endpoint tools/list để trả về toàn bộ các components đã được đăng ký. Khi LLM tạo câu trả lời, nó sẽ chọn component phù hợp nhất từ registry và sinh ra (generate) các props cho nó:

// Phản hồi của LLM (khối MCP tool_use)
{
  "type": "tool_use",
  "id": "toolu_01XyZ",
  "name": "render_component",
  "input": {
    "component": "order-status-card",
    "props": {
      "orderId": "ORD-456789",
      "status": "shipped",
      "trackingNumber": "1Z999AA10123456784",
      "estimatedDelivery": "2026-06-03",
      "allowedActions": ["track", "return"]
    }
  }
}

Đồng Bộ Trạng Thái Client-Agent: Quản Lý Khởi Tạo UI Bất Đồng Bộ

Generative UI đưa ra một bài toán hóc búa về việc đồng bộ trạng thái: LLM tạo ra các thuộc tính kĩ thuật cho component (component specification) một cách bất đồng bộ, nhưng component đó lại cần phải tương tác với trạng thái chạy trực tiếp của ứng dụng (phiên hoạt động hiện tại của người dùng, trạng thái đơn hàng thời gian thực, hàng tồn kho hiện tại).

Kiến Trúc Đồng Bộ Trạng Thái

flowchart TD
    USER["Tin nhắn Người Dùng"] --> LLM_SVC["Dịch vụ LLM qua Server Action"]
    LLM_SVC -->|các khối tool_use được stream| STREAM["Luồng SSE/RSC"]
    STREAM --> REGISTRY["Component Registry"]
    REGISTRY -->|phân giải component| RENDERER["React Component"]
    RENDERER -->|fetch dữ liệu thực tế| API["Application API"]
    RENDERER --> DOM["Browser DOM"]
    
    DOM -->|hành động người dùng| ACTION["Server Action Handler"]
    ACTION -->|cập nhật trạng thái| LLM_SVC

Sự thấu hiểu cốt lõi ở đây là LLM tạo ra các đặc tả thành phần (component specifications) (component nào sẽ hiển thị, với các prop ban đầu là gì), nhưng chính các component đó lại tự nạp (fetch) dữ liệu trực tiếp của riêng chúng từ API của ứng dụng. Việc chia tách này có ý nghĩa:

  • LLM không cần quyền truy cập vào trạng thái thời gian thực của ứng dụng
  • Component luôn được render cùng với lượng dữ liệu tươi mới, bất chấp khi nào thì LLM tạo ra đặc tả đó
  • Các thao tác trên component (nhấp nút, gửi form) quay lại chạy qua trình quản lý state ứng dụng thông thường, chứ không đi qua LLM

React Server Components Cho Việc Generative Bằng Stream

Next.js React Server Components (RSC) là cơ sở render tốt nhất cho Generative UI vì chúng có khả năng streaming:

// app/chat/route.ts
import { streamText, convertToCoreMessages } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { getMCPTools } from "@/lib/mcp/registry";

export async function POST(req: Request) {
    const { messages, sessionId } = await req.json();
    
    // Nạp toàn bộ các UI components đăng kí dưới dạng MCP tools
    const mcpTools = await getMCPTools(sessionId);
    
    const result = streamText({
        model: anthropic("claude-opus-4-5"),
        messages: convertToCoreMessages(messages),
        tools: mcpTools,
        system: `Bạn là trợ lý hữu ích cho một nền tảng thương mại điện tử. 
        Khi hiển thị trạng thái đơn hàng, thông tin sản phẩm, hoặc thực hiện 
        thao tác cho người dùng, hãy ưu tiên việc sử dụng UI components được cung cấp 
        hơn là đưa ra những dòng chat văn bản đơn điệu.`,
        maxSteps: 5,  // Cho phép gọi công cụ (tool) nhiều bước
    });
    
    return result.toDataStreamResponse();
}
// app/chat/components/message-renderer.tsx
"use client";

import { useChat } from "@ai-sdk/react";
import { ComponentRegistry } from "@/lib/mcp/client-registry";

export function ChatInterface() {
    const { messages, append } = useChat({ api: "/api/chat" });
    
    return (
        <div className="chat-container">
            {messages.map(message => (
                <div key={message.id}>
                    {message.role === "assistant" && message.toolInvocations?.map(tool => {
                        if (tool.toolName === "render_component" && tool.state === "result") {
                            // Xử lí thông qua và render component từ registry
                            return (
                                <ComponentRegistry
                                    key={tool.toolCallId}
                                    componentName={tool.args.component}
                                    props={tool.args.props}
                                    onAction={(action, payload) => {
                                        // Định tuyến (Route) hành động về đoạn chat
                                        append({
                                            role: "user",
                                            content: `[Action: ${action}] ${JSON.stringify(payload)}`,
                                        });
                                    }}
                                />
                            );
                        }
                        return null;
                    })}
                    {message.content && <TextMessage content={message.content} />}
                </div>
            ))}
        </div>
    );
}

Component Registry: Tải Động Trình Đăng Ký Và Định Phiên Bản (Versioning)

Component registry là trung tâm chỉ huy xác nhận xem AI có quyền tạo component nào. Nó cần trang bị các công cụ:

  • Đăng ký tại Runtime (Runtime registration): Có thể tự tải mã cài mới những component vào mã nguồn và bỏ ngỏ khâu phải chạy triển khai ứng dụng lại từ đầu
  • Định danh phiên bản (Versioning): AI bắt buộc phải hỏi thông số cấu hình cụ thể từng bản của component không để nó có các hoạt động thất thường
  • Truy vấn phân quyền (Permission checking): Chỉ cho người dùng đúng cấp độ chức vụ mới sử dụng được

Từng model component luôn có bản thiết kế kèm bằng Zod schema để nhận thông tin định nghĩa props. Schema này lo 2 việc: Kiểm chứng ở mức thực thi (bác bỏ ngay định dạng sai khi LLM truyền về) và hỗ trợ sinh chỉ số (có thể đưa JSON Schema để tool phân giải thẳng). Hãy nhìn schema order-status-card của chúng ta:

// lib/mcp/schemas/order-status-card.ts
import { z } from "zod";

export const OrderStatusCardSchema = z.object({
    orderId:           z.string().uuid(),
    status:            z.enum(["pending", "processing", "shipped", "delivered", "cancelled"]),
    estimatedDelivery: z.string().datetime().optional(),
    trackingNumber:    z.string().max(50).optional(),
    allowedActions:    z.array(z.enum(["cancel", "return", "reorder"])).default([]),
});

// Chuyển kiểu TypeScript tương thích qua schema — thông số cốt lõi trung thực duy nhất
export type OrderStatusCardProps = z.infer<typeof OrderStatusCardSchema>;

Thông số allowedActions được gắn đuôi .default([]) nếu LLM xót (bỏ quên) sẽ tránh phát sinh lỗi xác thực, còn các trường ví dụ estimatedDelivery đánh .optional() phòng hờ đơn hàng chưa xuất bến. File đăng ký (Registry) theo đường truyền đó sẽ dẫn chứng ngược tới schema khi load mã lên:

// lib/mcp/registry.ts
import { z } from "zod";

interface ComponentDefinition {
    name: string;
    version: string;
    schema: z.ZodType;
    permissions: string[];
    sandboxLevel: "trusted" | "sandboxed";
    loader: () => Promise<React.ComponentType<any>>;
}

class ComponentRegistry {
    private components = new Map<string, ComponentDefinition>();
    
    register(definition: ComponentDefinition) {
        const key = `${definition.name}@${definition.version}`;
        this.components.set(key, definition);
        // Song song đó cũng gọi dưới định dạng tên mới nhất để dễ gọi
        this.components.set(definition.name, definition);
    }
    
    async resolve(
        name: string,
        version?: string,
        userPermissions: string[] = []
    ): Promise<React.ComponentType<any> | null> {
        const key = version ? `${name}@${version}` : name;
        const definition = this.components.get(key);
        
        if (!definition) {
            console.warn(`Không tìm thấy Component: ${key}`);
            return null;
        }
        
        // Càn soát coi người dùng sở hữu mọi cấp độ quyền đòi hỏi chưa
        const hasPermissions = definition.permissions.every(
            perm => userPermissions.includes(perm)
        );
        if (!hasPermissions) {
            console.warn(`Từ chối quyền truy cập component: ${name}`);
            return null;
        }
        
        // Lazy-load component
        return definition.loader();
    }
    
    // Chạy đưa file bộ xử lý registry MCP ra các luồng kết nối tools để gửi thông số cho LLM
    async toMCPTools(userPermissions: string[]): Promise<Record<string, any>> {
        const tools: Record<string, any> = {};
        
        for (const [name, def] of this.components) {
            if (name.includes('@')) continue; // Lướt đi luôn các bản định dạng đuôi phụ
            if (!def.permissions.every(p => userPermissions.includes(p))) continue;
            
            tools[`render_${name.replace(/-/g, '_')}`] = {
                description: `Render component ${name}`,
                parameters: def.schema,
            };
        }
        
        return tools;
    }
}

export const registry = new ComponentRegistry();

// Đăng ký components ở thời điểm bật (startup)
registry.register({
    name: "order-status-card",
    version: "2.1.0",
    schema: OrderStatusCardSchema,
    permissions: ["read:orders"],
    sandboxLevel: "trusted",
    loader: () => import("@/components/ai/OrderStatusCard").then(m => m.OrderStatusCard),
});

Xem đầy đủ thông số cho trình theo dõi xử lý registry với hỗ trợ tùy biến thiết kế UI, cấp phép tính định phiên bản, kèm thêm hiệu năng hot-reload tại Phần 3: Component Registry, Styling and Versioning.


Bảo Mật Và Môi Trường Sandboxing: Chặn Bắt Ghi Chèn (Prompt Injection) Lên Động Component

Sáng chế ra UI (Generative UI) tạo đường mở cho những con virus lợi dụng sơ hở: tiêm nhồi (prompt injection) trực tiếp từ nguồn text đầu vào chứa mưu mô độc hại của người dùng. Nếu AI gọi nhầm component chuyên biệt để xem text chưa biên khảo (ví dụ: dòng cmt, nhận xét sp, đề tài báo cáo), kẻ gian sẽ tinh ranh giấu mưu đồ vào đó khiến máy móc trích xuất sinh ra component độc hại hoặc ngó lơ quyền chặn.

Lớp Phòng Vệ Thứ 1: Xác Thực Chuỗi Schema Trước Lúc Sinh Code

Thuộc tính đầu não cho từng Props phải đi dò bắt lại đối xứng cùng cấu tạo của Zod schema ngay trước vạch bắt đầu gọi kết xuất ra HTML. Những chuỗi hỏng ngay tức khắc loại ra không nương tay:

function ComponentRegistry({ componentName, props, onAction }: RegistryProps) {
    const [Component, setComponent] = useState<React.ComponentType | null>(null);
    const [validatedProps, setValidatedProps] = useState<any>(null);
    
    useEffect(() => {
        async function load() {
            const definition = await registry.get(componentName);
            if (!definition) return;
            
            // Soi dò kĩ tính tương tác cho props thông qua file schema — bài trừ triệt để mã độc đính kèm (injected)
            const parsed = definition.schema.safeParse(props);
            if (!parsed.success) {
                console.error("Component prop validation failed:", parsed.error);
                return; // Chặn đứng - thả về màn trắng thay vì ép khởi tạo phần tử mang lỗi
            }
            
            const component = await registry.resolve(componentName, undefined, userPermissions);
            setComponent(() => component);
            setValidatedProps(parsed.data);
        }
        load();
    }, [componentName, props]);
    
    if (!Component || !validatedProps) return null;
    return <Component {...validatedProps} onAction={onAction} />;
}

Lớp Phòng Vệ Thứ 2: Quây Cô Lập Component Cho Phần Mềm Chưa Xét Duyệt

Nhưng nhóm component lấy nội dung hở (nhận xét, nội dung post dài, thư điện tử) nên khoanh rào chúng trong một cấu trúc iframe nội tại ở cấp sandbox (sandboxed iframe):

function SandboxedComponent({ componentName, props }: { componentName: string; props: unknown }) {
    const iframeRef = useRef<HTMLIFrameElement>(null);
    
    useEffect(() => {
        // Xuất đẩy props (dã lọc và làm sạch) đi vào con iframe khép rào (sandboxed iframe)
        iframeRef.current?.contentWindow?.postMessage({
            type: "RENDER_COMPONENT",
            componentName,
            props,
        }, window.location.origin);
    }, [componentName, props]);
    
    return (
        <iframe
            ref={iframeRef}
            src="/sandbox/component-host"
            sandbox="allow-scripts allow-same-origin"  // Rút phép allow-forms, allow-top-navigation
            style={{ border: "none", width: "100%", height: "auto" }}
        />
    );
}

Lớp Phòng Vệ Thứ 3: Cấu Trúc Khung Ngăn Cách (Content Security Policy)

Mở khai báo thông số chỉ đường (CSP headers) để dập tắt ngay ý nghĩ của những component lấy data động có quyền móc ra các file dữ liệu (resources) từ miền xa xôi:

// next.config.ts
const ContentSecurityPolicy = `
    default-src 'self';
    script-src 'self' 'unsafe-eval';  // Cấp cờ 'unsafe-eval' để hỗ trợ RSC, ở mode production phải hạn hẹp lại
    style-src 'self' 'unsafe-inline';
    img-src 'self' data: blob:;
    connect-src 'self' https://api.stripe.com;
    frame-src 'none';
`;

Sự Kết Hợp Mang Tính Con Người (Human-in-the-Loop): Ứng Dụng Nút Đợi Xét Duyệt Vô Cấu Trúc Trực Diện UI

Đôi lúc các phản hồi sinh tạo từ AI bắt buộc sự đối chiếu quyết tâm (confirmation) của con người trực tiếp mới vận hành được tính năng. Ở đây UI trở thành nhân vật thích đáng nhất cho vụ này: bỏ qua dòng báo động chằng chịt ngòi nổ “Bạn chuẩn bị khóa nhé? Chắc Chưa/Rồi” mà đập ngay cho khách một UI mang đầy dòng chiết xuất về các điều sắp xảy ra một cách tinh gọn, đẹp đẽ.

// components/ai/ConfirmAction.tsx
interface ConfirmActionProps {
    actionType: "cancel_order" | "process_refund" | "send_contract";
    entityId: string;
    summary: string;           // Khúc tổng thể tự gõ từ AI về miêu tả 
    impact: string[];          // Nhóm các phản ứng chuỗi ngầm ở sau cần biết
    requiresTypedConfirmation?: boolean;
}

export function ConfirmAction({
    actionType,
    entityId,
    summary,
    impact,
    requiresTypedConfirmation,
    onAction,
}: ConfirmActionProps & { onAction: ActionHandler }) {
    const [confirmed, setConfirmed] = useState(false);
    const [typedValue, setTypedValue] = useState("");
    
    const canProceed = requiresTypedConfirmation 
        ? typedValue === "CONFIRM" 
        : confirmed;
    
    return (
        <div className="confirm-action-card">
            <h3>Xác nhận lệnh: {summary}</h3>
            <ul className="impact-list">
                {impact.map((item, i) => (
                    <li key={i} className="impact-item">{item}</li>
                ))}
            </ul>
            
            {requiresTypedConfirmation ? (
                <div>
                    <p>Điền chữ <strong>CONFIRM</strong> vào để sang bước tiếp:</p>
                    <input
                        value={typedValue}
                        onChange={e => setTypedValue(e.target.value)}
                        placeholder="CONFIRM"
                    />
                </div>
            ) : (
                <label>
                    <input
                        type="checkbox"
                        checked={confirmed}
                        onChange={e => setConfirmed(e.target.checked)}
                    />
                    Tôi hiểu kỹ và quyết đi tới cùng bước trên
                </label>
            )}
            
            <div className="action-buttons">
                <button
                    disabled={!canProceed}
                    onClick={() => onAction("confirm", { actionType, entityId })}
                    className="btn-confirm"
                >
                    Xác nhận
                </button>
                <button
                    onClick={() => onAction("cancel", { actionType, entityId })}
                    className="btn-cancel"
                >
                    Thôi
                </button>
            </div>
        </div>
    );
}

Bản thân AI được quyền phái các bước cấp quyền sang con UI này. Đáp án từ Component (ở dạng cờ confirm / cancel) chạy ngược về bên ứng dụng (conversation) theo format hành động từ User, giúp con AI tính toán được là nên đi tiếp hay lùi lại với lời báo ngắt nhịp (cancellation).


Kiến Trúc Cấu Hình Chuẩn Cho Hệ Sinh Thái Next.js + MCP Và Mẫu Khảo Sát (để đi sâu hơn vào cách xây dựng bản thân backend server, xem Phát triển Go MCP Server)

Đắp nặn mọi phần cùng với nhau, đây là nguyên dạng cây thư mục tinh giảm vừa khít đối với dạng Next.js tích hợp nền năng lượng UI (Generative UI):

/app
  /api
    /chat/route.ts         ← Trạm cấp luồng phản hồi streaming từ LLM (MCP tools)
    /actions               ← Bộ Server Actions đảm trách nhịp kết nối
  /chat/page.tsx           ← Khung UI chat nối ComponentRegistry
  /sandbox/component-host  ← Môi trường chạy cách li các luồng Component không kiểm (untrusted)

/lib
  /mcp
    registry.ts            ← Hộp phân phát thành tố (registry - góc độ server-side)
    client-registry.tsx    ← Góc giải nén mã lệnh dành cho client 
    tools.ts               ← Các luật thông số mCP tách rời được gọi

/components
  /ai
    OrderStatusCard.tsx    ← Chuẩn Trusted Component: Màn Trạng Thái + Trực tiếp chốt
    ProductCard.tsx        ← Chuẩn Trusted Component: Phụ lục ảnh vật phẩm + Trực tiếp chốt vào rỏ
    ConfirmAction.tsx      ← Nền tảng chèn con người vào đánh nhịp vòng thao tác 
    DataTable.tsx          ← Chuẩn Sandboxed Component: Thông kê số liệu từ user chưa chốt 

/mcp-registry
  order-status-card.ts     ← Thông số thiết kế khung MCP + Đi kèm Zod schema 
  product-card.ts
  confirm-action.ts

Chuỗi phân nhánh cấu trúc phía trên làm thân tự nhiên được nối trơn mịn ngay vào lõi hệ mạng triển khai tại Astro on Cloudflare: Full-Stack Edge Architecture dành ra cung cấp luồng trả AI UI thấp trễ cho môi trường thế giới, còn việc giữ luồng Real-Time nối vô được cấu tạo trong ngách ở Cloudflare D1 + Durable Objects: Build a Real-Time Cart với sức đẩy sinh động.


Bảng So Sánh Các Mô Hình Kết Xuất Giao Diện Generative UI

Dưới đây là ma trận đánh giá hiệu năng và độ an toàn giữa các kiến trúc hiển thị UI sinh tự động bởi trí tuệ nhân tạo:

Mô Hình Kiến TrúcClient-Side MarkdownReact Server Components (RSC)Streaming JSON Component SpecSandboxed Iframe (Untrusted)
Mức Độ Tương Tác Của Người DùngRất thấp (Chỉ đọc text tĩnh)Rất cao (Đầy đủ state & event handlers)Cao (Render theo component registry)Trung bình (Bị cô lập qua postMessage)
Độ Trễ Hiển Thị Khung Hình Đầu (TTFB)< 100 ms150 ms – 300 ms120 ms – 250 ms350 ms – 600 ms (Khởi tạo iframe)
Khả Năng Bảo Vệ Chống Tấn Công XSSTrung bình (Cần DOMPurify)Cực cao (Mã nguồn đã biên dịch sẵn)Rất cao (Ép kiểu nghiêm ngặt qua Zod)Tuyệt đối (Cách ly hoàn toàn qua CSP)
Tính Linh Hoạt Của Giao DiệnKémTrung bình (Giới hạn trong bộ components)Rất cao (Lắp ghép component linh hoạt)Vô hạn (Render mã tùy ý từ LLM)
Bảo Mật Quyền Truy Cập (Token/Cookie)Kế thừa từ trang chínhBảo mật phía serverKế thừa từ trang chínhHoàn toàn cô lập (Không truy cập cookie)

❓ Câu Hỏi Thường Gặp (FAQ)

Generative UI là gì và nguyên lý hoạt động của nó khác với Chatbot truyền thống ra sao?

Generative UI là kiến trúc frontend cho phép mô hình AI quyết định không chỉ nội dung câu trả lời mà còn chỉ định thành phần giao diện tương tác (Interactive Components) cần hiển thị. Thay vì trả về văn bản mô tả, LLM gọi một Tool qua giao thức MCP để trả về mã đặc tả thành phần kèm tham số đã thẩm định. Frontend phân giải từ Component Registry và kết xuất thẻ tương tác (như thẻ chuyến bay, bảng số liệu, nút xác nhận) ngay trong luồng đàm thoại.

Giao thức MCP (Model Context Protocol) đóng vai trò gì trong việc tạo giao diện Generative UI?

MCP đóng vai trò là tầng hợp đồng tiêu chuẩn hóa giữa LLM và Frontend. Nó chuẩn hóa cách mô hình khám phá danh mục UI component có sẵn, cách khai báo cấu trúc thuộc tính (Props Schema qua JSON Schema/Zod) và cách truyền tải dữ liệu có cấu trúc hai chiều, giúp loại bỏ sự phụ thuộc vào các định dạng độc quyền của từng nhà cung cấp AI.

Làm thế nào để bảo vệ ứng dụng Generative UI trước các nguy cơ tấn công mã độc XSS và Prompt Injection?

Áp dụng cơ chế phòng thủ 3 tầng: (1) Thẩm định nghiêm ngặt toàn bộ tham số Props đầu vào bằng Zod Schema trước khi đưa vào component; (2) Đối với các component do AI tự sinh mã nguồn động, bắt buộc phải kết xuất bên trong một Iframe có thuộc tính sandbox="allow-scripts" và chính sách Content Security Policy (CSP) nghiêm ngặt; (3) Cấm tuyệt đối việc sử dụng dangerouslySetInnerHTML với các chuỗi chưa qua lọc độc tính.

Mô hình Human-in-the-Loop được triển khai như thế nào trong Generative UI?

Đối với các hành động quan trọng (Write Actions như đặt hàng, hủy lịch, chuyển khoản), LLM không được phép tự ý thực thi. Thay vào đó, mô hình sinh ra một thành phần xác nhận <ConfirmActionCard /> hiển thị tóm tắt giao dịch và các nút bấm phê duyệt. Thao tác backend chỉ được kích hoạt khi người dùng nhấp xác nhận trực tiếp trên giao diện hoặc hoàn tất bước xác thực sinh trắc học / OTP.

Làm sao để truyền luồng (Streaming) các thành phần Generative UI qua Server-Sent Events mà không bị giật lag?

Frontend sử dụng cơ chế xử lý luồng theo khối (Chunked Stream Parsing). Trong khi các đoạn văn bản giải thích của LLM được truyền từng token qua SSE, các khối dữ liệu UI component được đóng gói thành các sự kiện đặc biệt (ví dụ event: ui-component). Trình phân giải ở client bắt sự kiện này để hiển thị khung xương (Skeleton Loader) ngay lập tức và nạp dần dữ liệu vào component khi luồng dữ liệu hoàn tất.

🔗 Đọc Thêm Các Chuyên Đề & Series Liên Quan


🤝 Kết nối với tôi

Bạn đang gặp phải những thách thức tương tự về kiến trúc hệ thống, mở rộng quy mô (scaling) hay dịch chuyển (migration)? Hãy kết nối với tôi trên LinkedIn, theo dõi GitHub của tôi, hoặc gửi một email để trao đổi nhé.