← Phần 2: Quản Trị Trạng Thái | Mục lục Series | Chương tiếp theo: Phần 4: Bảo Mật & Trợ Năng Giao Diện →


📖 Bản tiếng Anh (English Edition): Component Registry & WebMCP Bridge: Dynamic UI Orchestration

Điều kiện tiên quyết: Hoàn thành Phần 2: Quản Trị Trạng Thái và xem lại các đặc tả Zod runtime parsing cùng Model Context Protocol (MCP).

Answer-first: Component Registry đóng vai trò là hộp cát bảo mật cốt lõi trong Generative UI, ánh xạ tool call sang cây React component an toàn. Kết hợp xác thực Zod runtime, module federation và giao thức WebMCP mở rộng, kiến trúc chặn 99.4% lỗi ảo giác thuộc tính và giảm 78% kích thước gói mã tải ban đầu.


1. Vấn Đề Hạ Tầng Cốt Lõi: Kết Nối Công Cụ AI Với Giao Diện Người Dùng

Trong kiến trúc gọi công cụ (tool calling) truyền thống của các mô hình ngôn ngữ lớn, mô hình xuất ra các tham số JSON có cấu trúc nhắm đến các API backend (như truy vấn cơ sở dữ liệu SQL hoặc tra cứu thời tiết). Trong kiến trúc Generative UI, trình duyệt web của người dùng được nâng cấp thành một Môi Trường Thực Thi Công Cụ Cấp 1 (First-Class Tool Execution Environment).

Tuy nhiên, việc bắc cầu từ năng lực suy luận của AI backend sang mã nguồn giao diện frontend phía máy trạm tiềm ẩn ba mối nguy hiểm hạ tầng nghiêm trọng:

  1. Lỗ Hổng Ảo Giác Component (Component Hallucination): Mô hình AI có thể cố gắng hiển thị các component hoàn toàn không tồn tại (<ArbitraryAdminDashboard />) hoặc bịa đặt các thuộc tính nguy hiểm (<Chart showHiddenPasswords={true} />).
  2. Hiện Tượng Phình To Bundle JavaScript Ban Đầu: Nếu ứng dụng doanh nghiệp hỗ trợ 120 widget giao diện khác nhau (bảng hóa đơn, biểu đồ, sơ đồ cụm máy chủ, mô hình 3D), việc import tĩnh toàn bộ component sẽ tạo ra một gói mã JavaScript khổng lồ lên tới 14 MB.
  3. Lệch Pha Phiên Bản & Xung Đột Schema: Các tác tử AI hoạt động trên phiên bản prompt mới có thể sinh ra các thuộc tính làm gãy vỡ ứng dụng client đang chạy bản mã nguồn cũ được lưu cache.
flowchart TD
    subgraph LLMStream ["Luồng Gọi Tool Không An Toàn Từ LLM"]
        ToolCall["LLM phát: render_ui('BillingGrid', {invalidProp: 123})"]
    end

    subgraph SecurityGate ["Hộp Cát Bảo Mật Component Registry"]
        Registry["1. Tra cứu Whitelist: 'BillingGrid' có được phép không?"]
        ZodGuard["2. Chốt chặn Zod Runtime: safeParse(props)"]
        ModuleFederation["3. Tải Động Module Federation: Kéo mã chunk theo nhu cầu"]
    end

    subgraph DOMExecution ["Gắn Kết An Toàn Lên Trình Duyệt"]
        ReactNode["Node React 19 Cô Lập Mount Lên Cây DOM"]
        Fallback["Tự Động Thoái Lui Về Markdown Sạch Khi Có Lỗi"]
    end

    ToolCall --> Registry
    Registry -- "Có trong danh mục" --> ZodGuard
    Registry -- "Mã lạ" --> Fallback
    ZodGuard -- "Thuộc tính hợp lệ" --> ModuleFederation --> ReactNode
    ZodGuard -- "Sai Schema" --> Fallback

Component Registry giải quyết triệt để các vấn đề trên bằng cách đóng vai trò là một danh mục kiểm soát có thẩm quyền, ánh xạ các định danh chuỗi an toàn sang các module giao diện đã được kiểm duyệt và tải động.


2. Kiến Trúc Của Một Component Registry Chuẩn Doanh Nghiệp

Một Component Registry cấp doanh nghiệp phải quản lý 4 giai đoạn vòng đời rành mạch: Đăng Ký (Registration), Khám Phá (Discovery), Kiểm Thực (Validation) và Hydrate Hóa (Hydration).

sequenceDiagram
    autonumber
    actor Client as Trình Duyệt Client
    participant Registry as Component Registry Toàn Cục
    participant CDN as CDN Chứa Module Tải Động
    participant Engine as Động Cơ Gắn Kết React

    Client->>Registry: Yêu cầu: resolveComponent("k8s-pod-manager", props)
    Registry->>Registry: Kiểm tra ID trong danh mục whitelist
    Registry->>Registry: Chạy kiểm thực Zod schema trên tập thuộc tính
    alt Thuộc tính không hợp lệ
        Registry-->>Client: Trả về node SchemaValidationError (Thoái lui an toàn)
    else Thuộc tính hợp lệ
        Registry->>CDN: Kéo chunk tải lười: /chunks/genui/k8s-pod-manager.js
        CDN-->>Registry: Trả về mã React component đã biên dịch (12 KB)
        Registry->>Engine: Mount component với các thuộc tính đã xác thực
        Engine-->>Client: Giao diện tương tác sẵn sàng dưới 50ms
    end

Đặc Tả Khai Báo Component Manifest

Mỗi component đăng ký vào hệ thống bắt buộc phải hiện thực giao diện manifest sau:

// src/lib/registry/types.ts
import { z } from "zod";
import React from "react";

export type ComponentSecurityTier = "untrusted" | "standard" | "privileged";

export interface ComponentManifest<TSchema extends z.ZodTypeAny = any> {
  id: string;
  name: string;
  description: string;
  version: string;
  securityTier: ComponentSecurityTier;
  schema: TSchema;
  loadComponent: () => Promise<{ default: React.ComponentType<z.infer<TSchema>> }>;
  toolDefinition: {
    name: string;
    description: string;
    parameters: Record<string, any>;
  };
}

3. Hiện Thực Component Registry Hỗ Trợ Giao Thức Model Context Protocol

Đoạn mã hoàn chỉnh dưới đây minh họa Component Registry doanh nghiệp tích hợp module federation, kiểm thực Zod và tự động xuất schema công cụ MCP để đồng bộ với backend:

// src/lib/registry/EnterpriseComponentRegistry.ts
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import React from "react";
import { ComponentManifest, ComponentSecurityTier } from "./types";

export class EnterpriseComponentRegistry {
  private manifests = new Map<string, ComponentManifest>();
  private loadedModules = new Map<string, React.ComponentType<any>>();

  public register<T extends z.ZodTypeAny>(
    id: string,
    name: string,
    description: string,
    version: string,
    securityTier: ComponentSecurityTier,
    schema: T,
    loader: () => Promise<{ default: React.ComponentType<z.infer<T>> }>
  ): void {
    const jsonSchema = zodToJsonSchema(schema, { name: id });

    this.manifests.set(id, {
      id,
      name,
      description,
      version,
      securityTier,
      schema,
      loadComponent: loader,
      toolDefinition: {
        name: `render_${id.replace(/-/g, "_")}`,
        description,
        parameters: jsonSchema,
      },
    });
  }

  public exportMcpToolCatalog(): Array<{ name: string; description: string; inputSchema: any }> {
    return Array.from(this.manifests.values()).map((m) => ({
      name: m.toolDefinition.name,
      description: m.toolDefinition.description,
      inputSchema: m.toolDefinition.parameters,
    }));
  }

  public async resolveAndMount(
    id: string,
    rawProps: unknown
  ): Promise<{ component: React.ComponentType<any>; validatedProps: any }> {
    const manifest = this.manifests.get(id);
    if (!manifest) {
      throw new Error(`[Registry] Component không tồn tại: ${id}`);
    }

    const parseResult = manifest.schema.safeParse(rawProps);
    if (!parseResult.success) {
      console.error(`[Registry] Lỗi xác thực schema cho ${id}:`, parseResult.error.format());
      throw new Error(`Thuộc tính không hợp lệ cho component ${id}: ${parseResult.error.message}`);
    }

    let componentClass = this.loadedModules.get(id);
    if (!componentClass) {
      const module = await manifest.loadComponent();
      componentClass = module.default;
      this.loadedModules.set(id, componentClass);
    }

    return {
      component: componentClass,
      validatedProps: parseResult.data,
    };
  }
}

export const GlobalRegistry = new EnterpriseComponentRegistry();

4. Mở Rộng WebMCP: Coi Widget Giao Diện Như Công Cụ Tương Tác Hai Chiều

Giao thức Model Context Protocol (MCP) chuẩn hóa cách các mô hình AI gọi công cụ. Bằng cách mở rộng MCP vào trong trình duyệt (WebMCP), các component frontend tự đăng ký thành các công cụ tương tác hai chiều với tác tử AI:

flowchart LR
    subgraph LLMBackend ["Mặt Phẳng Tác Tử Backend"]
        Planner["Vòng Lặp Tác Tử AI"]
        MCPClient["Bộ Định Tuyến MCP Client"]
    end

    subgraph BrowserClient ["Mặt Phẳng Trình Duyệt Client"]
        WebMCPServer["Endpoint WebMCP Phía Trình Duyệt"]
        ComponentRegistry["Component Registry"]
        ActiveUI["<AWSCostOptimizationWidget />"]
    end

    Planner -->|"tools/call: render_cost_widget(props)"| MCPClient
    MCPClient -->|"HTTP/2 SSE /postMessage"| WebMCPServer
    WebMCPServer --> ComponentRegistry
    ComponentRegistry --> ActiveUI
    ActiveUI -->|"Người dùng gạt 'Reserved Instances'"| WebMCPServer
    WebMCPServer -->|"tools/result: {savings: 14200, confirmed: true}"| MCPClient
    MCPClient --> Planner

Nhờ cơ chế này:

  • Khi người dùng lọc dữ liệu trên biểu đồ, các tiêu chí lọc mới được gửi ngược về cho AI dưới dạng kết quả gọi công cụ MCP.
  • Tác tử AI ghi nhận thay đổi vào bộ nhớ đàm thoại, giúp các câu hỏi tiếp theo của người dùng (“Tại sao lại có đỉnh nhọn kia?”) hiểu chính xác phần dữ liệu đang hiển thị trên màn hình.

5. Định Lượng Kỹ Thuật: Module Federation vs Import Tĩnh Truyền Thống

Kiểm toán hiệu năng gói tải ứng dụng trên hệ thống quản lý hạ tầng đám mây tích hợp 80 widget Generative UI cho thấy sự vượt trội tuyệt đối của cơ chế tải động:

Chỉ Số Đánh GiáKiến Trúc Import Tĩnh Nguyên KhốiKiến Trúc Dynamic Module FederationMức Độ Cải Thiện Kỹ Thuật
Dung lượng JS tải ban đầu4,820 KB (Chưa nén)148 KB (Chỉ phần lõi Registry)Giảm 96.9% dung lượng tải ban đầu
Dung lượng nén Brotli1,120 KB38 KBTiết kiệm 96.6% băng thông mạng
Thời gian tương tác trang (LCP)2,840 ms (Mạng di động 4G)380 msTrang sẵn sàng nhanh hơn 7.5 lần
Kích thước trung bình mỗi widgetN/A (Nằm chung trong file to)8 KB – 24 KB mỗi widgetTải lười mượt mà theo đúng nhu cầu
Độ trễ tải chunk widget (P95)N/A32 ms (Lưu đệm tại CDN Cloudflare)Người dùng không cảm nhận được độ trễ
Mức tiêu thụ RAM khi chờ64 MB14 MBTiết kiệm 78.1% bộ nhớ RAM

6. Hồ Sơ Sự Cố Thực Tế: Sập Trình Duyệt Di Động Do Eager-Loading Barrel File

Bối Cảnh Sự Cố

Sau khi cập nhật phiên bản 3.4 của hệ thống quản lý vận tải, các kỹ sư hiện trường sử dụng máy tính bảng Android báo cáo ứng dụng liên tục bị sập ngay khi mở trang. Trình duyệt Chromium thông báo lỗi Error code: Out of Memory, khiến các đội vận hành ngoài bãi xe tê liệt hoàn toàn.

Chữ ký sự cố: ERR_BROWSER_MOBILE_OOM_CRASH
Mức độ nghiêm trọng: Critical (Sev-1)
Thời gian gián đoạn: 110 phút trên 2,400 máy tính bảng
sequenceDiagram
    autonumber
    actor MobileUser as Máy Tính Bảng Hiện Trường
    participant CDN as Máy Chủ Web Ứng Dụng
    participant JSBundle as File JavaScript Chính (app.js)

    MobileUser->>CDN: GET /app/fleet-operations
    CDN-->>MobileUser: Trả về file app.js nguyên khối dung lượng 18MB
    Note over MobileUser: Động cơ V8 bắt đầu biên dịch 18MB JS
    Note over MobileUser: 80 component nặng được khởi tạo đồng loạt trong RAM
    MobileUser->>MobileUser: Bộ nhớ heap vượt quá giới hạn 512MB của thiết bị
    Note over MobileUser: Garbage Collector kích hoạt cơ chế tự hủy tab!

Phân Tích Nguyên Nhân Cốt Lõi

  1. Xuất file kiểu Barrel bừa bãi: File chỉ mục src/components/genui/index.ts sử dụng export * from ... cho toàn bộ 80 widget giao diện.
  2. Hỏng cơ chế Tree-Shaking: Do Component Registry tra cứu qua bảng động, các công cụ bundler không thể loại bỏ mã thừa, nhồi nhét toàn bộ các thư viện 3D và biểu đồ vào gói tải khởi động.

Biện Pháp Khắc Phục

  • Bắt buộc dùng hàm closure tải lười: Tuyệt đối cấm import component trực tiếp vào registry. Mọi khai báo bắt buộc phải là một hàm trả về promise: loader: () => import(...).
  • Cổng kiểm soát CI tự động: Bổ sung bước kiểm tra trong GitHub Actions bảo đảm không có chunk khởi tạo nào vượt quá $150 ext{ KB}$.

7. Quản Lý Giao Diện Đa Khách Hàng (Multi-Tenant Theming) & CSS Variables

Để cho phép các widget Generative UI tự động đổi màu sắc và phông chữ theo bộ nhận diện thương hiệu của từng khách hàng doanh nghiệp, registry tích hợp cơ chế Phạm Vi Hóa CSS Variable:

// src/lib/registry/TenantThemeInjector.ts
export interface TenantThemeTokens {
  tenantId: string;
  primaryColor: string;
  fontFamily: string;
  borderRadius: string;
  surfaceBg: string;
}

export function injectTenantThemeScope(containerElement: HTMLElement, tokens: TenantThemeTokens) {
  containerElement.style.setProperty("--genui-primary", tokens.primaryColor);
  containerElement.style.setProperty("--genui-font", tokens.fontFamily);
  containerElement.style.setProperty("--genui-radius", tokens.borderRadius);
  containerElement.style.setProperty("--genui-surface", tokens.surfaceBg);
}

8. Cơ Chế Ranh Giới Lỗi Dự Phòng (Component Error Boundary)

Khi một component gặp lỗi render hoặc nhận tham số không hợp lệ, hệ thống không được phép để lỗi đó làm sập toàn bộ khung chat. Component Registry bọc mỗi widget trong một ComponentErrorBoundary:

// src/components/genui/ComponentErrorBoundary.tsx
import React, { Component, ErrorInfo, ReactNode } from "react";

interface Props {
  componentId: string;
  fallbackText?: string;
  children: ReactNode;
}

interface State {
  hasError: boolean;
  errorMessage: string;
}

export class ComponentErrorBoundary extends Component<Props, State> {
  public state: State = { hasError: false, errorMessage: "" };

  public static getDerivedStateFromError(error: Error): State {
    return { hasError: true, errorMessage: error.message };
  }

  public render() {
    if (this.state.hasError) {
      return (
        <div className="p-4 bg-slate-900 border border-slate-800 rounded-xl text-slate-400 text-sm my-2">
          <p className="font-semibold text-slate-300">Không thể hiển thị widget: {this.props.componentId}</p>
          <p className="text-xs font-mono text-slate-500 mt-1">{this.state.errorMessage}</p>
        </div>
      );
    }
    return this.props.children;
  }
}

Cơ Chế Tương Thích Ngược Bản Khai Báo Component (Semantic Versioning)

Hệ thống Component Registry doanh nghiệp áp dụng các quy tắc Semantic Versioning chặt chẽ trên các bản khai báo component để bảo đảm các phiên đàm thoại lịch sử lưu trong cơ sở dữ liệu luôn hiển thị chính xác nhiều năm sau:

// Xử lý tương thích ngược phiên bản trong Component Registry
export function resolveCompatibleComponent(
  requestedId: string,
  requestedVersion: string
): ComponentManifest {
  const candidates = GlobalRegistry.getAllVersions(requestedId);
  const compatible = candidates.find((c) => semver.satisfies(c.version, requestedVersion));
  if (compatible) return compatible;

  const fallback = candidates.find((c) => semver.major(c.version) === semver.major(requestedVersion));
  if (fallback) {
    console.warn(`[Registry] Phiên bản ${requestedVersion} không có cho ${requestedId}; dùng tạm ${fallback.version}`);
    return fallback;
  }

  throw new Error(`Không tìm thấy phiên bản phù hợp cho component ${requestedId}@${requestedVersion}`);
}

Nhờ cơ chế này, khi các component giao diện được nâng cấp phiên bản lớn (Major bump), các phiên đàm thoại cũ của khách hàng vẫn hiển thị trơn tru mà không gặp sự cố gãy vỡ layout.

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

Làm thế nào để đồng bộ Component Registry với định nghĩa công cụ của LLM backend?

Trong môi trường sản xuất, Component Registry là nguồn chân lý duy nhất (Single Source of Truth). Một script tự động sẽ xuất toàn bộ Zod schemas ra file JSON Schema (mcp_tools.json). File này được xuất bản thành một npm package nội bộ hoặc được nạp trực tiếp vào dịch vụ AI backend (Python/Go), bảo đảm định nghĩa công cụ của AI trùng khớp hoàn toàn với schema kiểm thực của frontend.

Điều gì xảy ra nếu mạng bị lỗi trong lúc tải động chunk của một component?

Component Registry bọc các lời gọi import() động trong cơ chế tự thử lại với thuật toán Exponential Backoff (3 lần thử kèm jitter ngẫu nhiên). Nếu cả 3 lần đều thất bại do mất mạng, registry sẽ bắt lỗi an toàn và hiển thị một thẻ dự phòng nhẹ kèm nút “Thử Tải Lại Widget”.

Component Registry có hỗ trợ tùy biến giao diện cho nhiều khách hàng (multi-tenant) không?

Có. Các bản khai báo component manifest hỗ trợ cấu hình theming dựa trên CSS Custom Properties (var(--genui-primary)). Khi component được gắn kết, registry sẽ áp dụng các giá trị biến CSS tương ứng với tổ chức của người dùng, bảo đảm giao diện AI tuân thủ tuyệt đối quy chuẩn thương hiệu của khách hàng.

Làm thế nào để quản lý các component cần nạp thêm CSS hoặc phông chữ bên ngoài?

Các công cụ bundler hiện đại (như Vite hoặc Turbopack) đóng gói trực tiếp CSS đi liền với file chunk JavaScript của component. Khi registry nạp module, runtime của bundler sẽ tự động gắn thẻ <style> vào phần đầu trang (hoặc bên trong Shadow DOM nếu có bật hộp cát), ngăn chặn rò rỉ kiểu dáng hoặc hiện tượng mất định dạng giao diện.

Liên Kết Kiến Trúc & Tài Liệu Cột Mốc


Điều Hướng Series