•10 min read

Xây dựng giao diện người dùng dựa trên ý định: Tương lai của phát triển frontend

Xây dựng giao diện người dùng dựa trên ý định: Tương lai của phát triển frontend

Web đã tĩnh quá lâu rồi. Ngay cả khi các Ứng dụng Trang Đơn (SPA) và các framework frontend phức tạp ra đời, về cơ bản chúng ta vẫn xây dựng ứng dụng theo cách tương tự như hai thập kỷ trước: chúng ta thiết kế các đường dẫn điều hướng tĩnh, các biểu mẫu cứng nhắc và các chế độ xem bảng điều khiển được mã hóa cứng, buộc người dùng phải học mô hình tư duy của chúng ta để đạt được mục tiêu của họ.

Điều gì sẽ xảy ra nếu giao diện người dùng tự động thích ứng với những gì người dùng thực sự muốn hoàn thành? Thay vì nhấp qua năm màn hình lồng nhau để lọc một tập dữ liệu và xuất biểu đồ, người dùng thể hiện ý định của họ, và ứng dụng khách sẽ tự động lắp ráp chính xác không gian làm việc tương tác mà họ cần.

Chào mừng bạn đến với mô hình kiến trúc của Phát triển Giao diện Người dùng Dựa trên Ý định (còn gọi là Generative UI).

Audio Briefing
0:00 / 0:00
Giao diện người dùng dựa trên ý định là gì?

Giao diện người dùng dựa trên ý định là một giao diện tự động giải quyết và hiển thị các thành phần frontend dựa trên ngôn ngữ tự nhiên hoặc sự hiểu biết theo ngữ cảnh về mục tiêu tức thời của người dùng, được hỗ trợ bởi các LLM gọi hàm và các registry thành phần được định kiểu mạnh.


Vấn đề với Kiến trúc dựa trên Tuyến đường tĩnh

Trong kỹ thuật client truyền thống, các ứng dụng ánh xạ các đường dẫn URL trực tiếp đến các mẫu chế độ xem tĩnh:

// Traditional Route Mapping
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/analytics/reports/new" element={<CreateReport />} />
<Route path="/settings/billing" element={<BillingSettings />} />

Mặc dù sạch sẽ cho các ứng dụng CRUD đơn giản, mô hình này gây ra ma sát nghiêm trọng khi hệ thống mở rộng:

  1. Tải nhận thức cao: Người dùng phải nhớ các tính năng nằm ở đâu trong các cây điều hướng lồng sâu.
  2. Chuyển đổi ngữ cảnh: So sánh dữ liệu giữa hai module yêu cầu mở nhiều tab trình duyệt hoặc điều hướng qua lại.
  3. Phình to tính năng: Mỗi khả năng doanh nghiệp mới yêu cầu thêm nhiều mục thanh bên, hộp thoại modal và menu con cho đến khi giao diện trở nên không thể quản lý được.

Advertisement

Kiến trúc Công cụ Ý định

Một giao diện người dùng dựa trên ý định thay thế điều hướng trang cố định bằng một Đường ống Giải quyết Ý định. Client nhận ngôn ngữ tự nhiên hoặc cử chỉ theo ngữ cảnh, dịch chúng thành các payload ý định có cấu trúc thông qua một LLM, và giải quyết các payload đó dựa trên một Registry Thành phần đã được xác minh.

1. Thu thập Ý định

Người dùng thể hiện mục tiêu của họ thông qua bảng lệnh (Cmd+K), nhập liệu hội thoại, hoặc kích hoạt theo ngữ cảnh: "Hiển thị cho tôi các hợp đồng gia hạn doanh nghiệp EU hết hạn trong tháng này và cho phép tôi soạn thảo báo giá gia hạn."

2. Giải quyết Ý định có cấu trúc

Một mô hình ngôn ngữ nhẹ (như Claude 3.5 Haiku hoặc GPT-4o-mini) phân tích lời nhắc thành một schema JSON đã được xác thực chứa các hành động, tham số và các widget UI được đề xuất.

3. Tra cứu Registry Thành phần

Runtime frontend ánh xạ các token hành động của ý định tới các thành phần React được tải lười biếng được khai báo trong một manifest thành phần nghiêm ngặt.

4. Kết hợp & Thực thi Động

Ứng dụng gắn các widget đã lắp ráp vào một không gian làm việc lưới linh hoạt, đồng bộ hóa trạng thái bộ lọc giữa các thành phần khác nhau.


Triển khai: Hợp đồng được định kiểu & Đầu ra có cấu trúc

Để đảm bảo độ tin cậy của hệ thống, giao tiếp giữa LLM và frontend phải tuân theo một hợp đồng xác định, an toàn kiểu. Chúng tôi định nghĩa điều này bằng TypeScript và Zod:

// intent-contract.ts
import { z } from 'zod';

export const IntentActionSchema = z.enum([
  'VIEW_ANALYTICS',
  'GENERATE_REPORT',
  'TRIGGER_WORKFLOW',
  'MODIFY_SETTINGS'
]);

export const IntentPayloadSchema = z.object({
  action: IntentActionSchema,
  confidence: z.number().min(0).max(1),
  parameters: z.object({
    timeRange: z.enum(['7d', '30d', '90d', '1y']).optional(),
    region: z.string().optional(),
    segment: z.string().optional(),
    metrics: z.array(z.string()).default([])
  }),
  layout: z.enum(['single-card', 'split-view', 'dashboard-grid']),
  componentManifest: z.array(z.string())
});

export type IntentPayload = z.infer<typeof IntentPayloadSchema>;

Trình phân tích Ý định phía máy chủ với Gọi công cụ LLM

Bằng cách sử dụng gọi công cụ có cấu trúc, chúng tôi đảm bảo rằng LLM trả về các đối tượng schema hợp lệ mà không có các bất thường phân tích cú pháp markdown:

// api/intent/route.ts
import { NextResponse } from 'next/server';
import { IntentPayloadSchema } from '@/lib/intent-contract';

export async function POST(req: Request) {
  const { prompt, userContext } = await req.json();

  const response = await fetch('https://api.anthropic.com/v1/messages', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.ANTHROPIC_API_KEY!,
      'anthropic-version': '2023-06-01',
      'content-type': 'application/json'
    },
    body: JSON.stringify({
      model: 'claude-3-5-haiku-20241022',
      max_tokens: 1024,
      system: `You are an intent parser for an enterprise analytics dashboard. 
Translate user requests into the UI Intent Schema. Available components:
[RevenueTrendChart, RegionalBreakdownTable, RenewalActionBar, ChurnRiskHeatmap].`,
      tools: [
        {
          name: 'render_intent_ui',
          description: 'Renders dynamic UI widgets matching user intent',
          input_schema: {
            type: 'object',
            properties: {
              action: { type: 'string', enum: ['VIEW_ANALYTICS', 'GENERATE_REPORT', 'TRIGGER_WORKFLOW'] },
              confidence: { type: 'number' },
              parameters: { type: 'object' },
              layout: { type: 'string', enum: ['single-card', 'split-view', 'dashboard-grid'] },
              componentManifest: { type: 'array', items: { type: 'string' } }
            },
            required: ['action', 'confidence', 'layout', 'componentManifest']
          }
        }
      ],
      tool_choice: { type: 'tool', name: 'render_intent_ui' },
      messages: [{ role: 'user', content: prompt }]
    })
  });

  const data = await response.json();
  const toolCall = data.content.find((c: any) => c.type === 'tool_use');
  const parsed = IntentPayloadSchema.parse(toolCall.input);

  return NextResponse.json(parsed);
}

Registry Thành phần Động & Lắp ráp Runtime

Trên client, các thành phần được đăng ký trong một registry trung tâm. Chúng tôi hiển thị chúng một cách động bằng cách sử dụng import động của React để ngăn chặn sự phình to:

// components/intent/IntentWorkspace.tsx
'use client';

import React, { Suspense } from 'react';
import dynamic from 'next/dynamic';
import type { IntentPayload } from '@/lib/intent-contract';

const ComponentRegistry: Record<string, React.ComponentType<any>> = {
  RevenueTrendChart: dynamic(() => import('@/components/charts/RevenueTrendChart')),
  RegionalBreakdownTable: dynamic(() => import('@/components/tables/RegionalBreakdownTable')),
  RenewalActionBar: dynamic(() => import('@/components/actions/RenewalActionBar')),
  ChurnRiskHeatmap: dynamic(() => import('@/components/charts/ChurnRiskHeatmap'))
};

export function IntentWorkspace({ intent }: { intent: IntentPayload }) {
  if (intent.confidence < 0.75) {
    return (
      <div className="p-6 border border-amber-300 bg-amber-50 rounded-lg">
        <p className="font-semibold text-amber-900">Intent Unclear</p>
        <p className="text-amber-700 text-sm">Did you mean to view regional revenue or renewal actions?</p>
      </div>
    );
  }

  const gridStyles = {
    'single-card': 'grid grid-cols-1',
    'split-view': 'grid grid-cols-1 lg:grid-cols-2 gap-6',
    'dashboard-grid': 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6'
  };

  return (
    <div className={`intent-canvas ${gridStyles[intent.layout]}`}>
      {intent.componentManifest.map((componentName) => {
        const Widget = ComponentRegistry[componentName];
        if (!Widget) return null;

        return (
          <Suspense fallback={<div className="h-64 animate-pulse bg-slate-100 rounded-lg" />} key={componentName}>
            <Widget params={intent.parameters} />
          </Suspense>
        );
      })}
    </div>
  );
}

Advertisement

Định tuyến truyền thống so với Giao diện người dùng dựa trên ý định

Chiều kiến trúcĐịnh tuyến tĩnh truyền thốngGiao diện người dùng dựa trên ý định (Generative)
Mô hình điều hướngURL phân cấp (/a/b/c)Mô tả ý định khai báo
Lắp ráp thành phầnCây trang được mã hóa cứngLắp ráp động tại thời gian chạy
Đường cong học tậpCao (người dùng học bố cục ứng dụng)Thấp (ứng dụng thích ứng với ý định người dùng)
Độ trễĐịnh tuyến client tức thì (< 10ms)Độ trễ suy luận mô hình (150–400ms)
Chế độ lỗiCác tuyến đường bị hỏng (404s)Ý định mơ hồ / dự phòng độ tin cậy thấp
Chia sẻ trạng tháiTham số truy vấn URL & kho lưu trữ toàn cụcTruyền tham số phiên theo ngữ cảnh
Phù hợp nhất choCác trang tiếp thị tĩnh, CRUD đơn giảnBI doanh nghiệp, phân tích, SaaS phức tạp

Các vấn đề sản xuất & Hàng rào bảo mật

  1. Cô lập trạng thái: Khi các thành phần được gắn động, tránh liên kết chúng với các kho lưu trữ toàn cục nguyên khối. Truyền các tham số đã phân tích cú pháp thông qua props hoặc React Context có phạm vi.
  2. Dự phòng xác định: Nếu người dùng nhập một truy vấn không được nhận dạng, hãy cung cấp các hành động được đề xuất bằng một cú nhấp chuột thay vì một màn hình trống.
  3. Các hành động phá hủy: Không bao giờ cho phép một mô hình ý định tự động thực hiện các hành động thay đổi trạng thái (như DELETE /customers hoặc POST /refund). Yêu cầu hộp thoại xác nhận rõ ràng từ người dùng trước khi kích hoạt các thay đổi.
  4. Phòng thủ chống tiêm nhắc: Xác thực tất cả các tham số được trích xuất bởi LLM bằng Zod trước khi đưa chúng vào các truy vấn cơ sở dữ liệu hoặc các endpoint API.

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

Không. Kiến trúc tối ưu sử dụng định tuyến lai: URL tiêu chuẩn cho các không gian làm việc cấp cao (/workspace, /settings), với bảng lệnh dựa trên ý định tự động sắp xếp các chế độ xem trong các không gian làm việc đó.

Bằng cách thực thi xác thực schema nghiêm ngặt (thông qua Zod) và hạn chế đầu ra của LLM vào một danh sách trắng rõ ràng các định danh thành phần đã đăng ký. Nếu mô hình trả về một thành phần chưa đăng ký, runtime sẽ bỏ qua nó.

Sử dụng các mô hình nhẹ cực nhanh (Claude 3.5 Haiku hoặc GPT-4o-mini), truyền tải shell bố cục ban đầu và lưu trữ các truy vấn người dùng thường xuyên trong Redis bằng cách sử dụng độ tương đồng nhúng ngữ nghĩa.


Kết luận

Sự chuyển đổi từ các cây tuyến đường cứng nhắc, được xác định trước sang các giao diện thích ứng, dựa trên ý định đánh dấu biên giới lớn tiếp theo trong kỹ thuật web. Bằng cách kết hợp các schema được định kiểu mạnh, các mô hình suy luận nhanh và các registry thành phần mô-đun, chúng ta có thể xây dựng phần mềm tự định hình theo người dùng—loại bỏ gánh nặng nhận thức và định nghĩa lại năng suất của nhà phát triển.


Bạn cũng có thể thích

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement