LLM 正在成為現代開發工具和 AI 代理的支柱。它已經可以產生程式碼。但問題是:輸出是靜態的。
你仍然需要複製程式碼,貼上到專案中,啟動開發伺服器,修復建置問題,並手動連接邏輯或狀態。這些工作如果擴展到幾十個 UI 元件,很快就會變得混亂不堪。
Thesys React SDK 讓您跳過這一步。您可以即時將 LLM 答案轉換為即時渲染的互動式 UI。
今天,我們將了解「生成式 UI」的真正意義,Thesys 如何將 LLM 與前端層連接起來,以及如何使用 C1 API 和 React SDK 將其與您的 AI 堆疊整合。
讓我們開始吧。
總之,我們將詳細介紹這些主題。
1)什麼是生成式UI?
2)Thesys 的工作原理:C1 API 和 GenUI React SDK
3) 使用 Thesys 建立生成式 UI 的逐步指南。
4) 所有主要的 AI 堆疊都可以與 Thesys 一起使用。
如果您有興趣自己探索,請查看thesys.dev 。
{% 嵌入 https://www.youtube.com/watch?v=oyAKkW9q1Mg
%}如果您最近使用過 Slack、Gmail 或任何儀表板工具等應用程式,您會注意到一件事: they all look insanely similar
。
傳統的 UI 是靜態的。無論使用者是誰、出於什麼原因,每個使用者看到的都是相同的佈局。而生成式 UI 變化則允許介面根據每個使用者進行動態組合。
但並非所有基於提示的 UI 系統都是一樣的。它們的差異如下:
Prompt to design
:將 LLM 與電腦視覺模型(如擴散或 GAN)結合,用於佈局生成。它通常嵌入在 Figma 等設計工具中。
Prompt to UI
:使用 LLM(如 GPT-4、Claude)來理解提示並產生生產級前端/後端程式碼。
Generative UI
:需要可自訂的 UI 庫、用於儲存使用者上下文的內存,以及一個能夠持續重塑 UI 的即時 GenUI 引擎。 Thesys 就屬於此類。
如果您有興趣了解更多有關幕後發生的事情,請查看生成 UI、提示 UI 和提示設計。
讓我們簡單了解一下生成式 UI 的工作原理(概念上):
在生成式 UI 系統中,佈局並非硬編碼。它會根據使用者身分、操作方式以及與應用程式的互動方式而改變。系統會收集以下上下文資訊:
你過去的行為
您的偏好(例如黑暗模式)
設備類型、螢幕尺寸、位置
甚至一天中的時間或可存取性設置
例子?
假設您正在使用一款共乘應用程式。該應用程式會偵測運動和位置訊號,辨識您正在駕駛,並立即切換到語音優先的使用者介面。
大按鈕、最少的干擾和語音提示可引導您完成拾取和導航等操作。
這不僅僅是響應式設計。
這是一個understands your context and adapts itself
以適應情況的使用者介面。
現在,人工智慧不再幻想出新的元件。它不再從頭開始設計按鈕。
相反,設計師和開發人員提供了一個結構化的 UI 元素庫,例如卡片、圖表、模式、文字區塊、表單等。
這些是經過批准的可重複使用元件。 AI只需根據佈局和品牌約束,從這些模組中進行選擇即可。
利用可用的上下文和預先定義的 UI 庫,AI 層產生適合當前情況的結構化 UI 規格。
它定義了要渲染哪些元件、如何排列它們以及要傳遞哪些屬性或資料。例如Render a balance chart at the top, followed by a currency selector and a transfer form pre-filled with the last used account.
人工智慧可能會使用經過訓練的機器學習模型、簡單的啟發式方法,甚至兩者兼而有之來做出這些決策。例如: If the system detects visually impaired users, it automatically enables high-contrast mode
。
隨著使用者環境的變化,介面也會隨之更新。
現在我們已經從概念上了解了生成式 UI 的工作原理,讓我們繼續討論 Thesys 的技術方面以及它如何使這一切成為可能。
如您現在所知,Thesys 是一個用於建立生成式 UI 應用程式的開發者平台。它支援多個 LLM,例如 OpenAI 和 Anthropic。
涉及兩個主要元件:
當您向 C1 API 發送提示時,它會傳回基於 JSON 的 UI 規格(JSON/XML)而不是純文字。
它與 OpenAI 完全相容(相同的端點/參數),因此您可以使用任何 OpenAI 客戶端(JS 或 Python SDK)呼叫它,只需將您的baseURL
指向:
https://api.thesys.dev/v1/embed
然後,您可以使用訊息呼叫client.chat.completions.create({...})
。使用特殊的模型名稱(例如"c1-nightly"
),Thesys API 將呼叫 LLM 並傳回 UI 規格。
client.chat.completions.create({
model: "c1-nightly",
messages: [...],
});
根據您的使用情況,您可以透過兩種方式使用 C1:
a)將現有的 LLM 替換為 C1,以便優先處理 UI 問題
如果您正在建立新應用程式並希望將延遲降至最低,您可以直接向c1-nightly
發送提示,它會立即返回要呈現的 UI 規格。
如果您已經擁有 LLM 管線(聊天機器人/代理程式),您可以將其輸出傳遞給 C1,作為第二步,產生視覺化佈局。這將在現有流程的基礎上加入更豐富的 UI,而無需更改核心邏輯。
一旦您從 C1 收到 UI 規範,GenUI SDK 就會處理前端的渲染。
該 SDK 是一個 React 框架(基於Crayon 的元件庫建置),它讀取基於 JSON 的 UI 規格並將其映射到視覺化元件。
其核心是<C1Component>
(以及用於聊天介面的<C1Chat>
),它接受C1 API回應並將其轉換為即時UI。
以下是範例程式碼:
import { C1Component, ThemeProvider } from "@thesys/genui-sdk";
const App = () => {
const response = await fetch("<your-backend-url>");
return (
<ThemeProvider>
<C1Component c1Response={response.text()} />
</ThemeProvider>
);
};
這就是 C1 和 React SDK 如何協同工作以將 LLM 回應即時轉換為即時 UI。
在本節中,我們將討論如何使用 Thesys 的完整流程。我將使用 Next.js。
為了保持實用性,我們將使用 C1 和 Thesys GenUI SDK 建立一個基於表單的工作助理。
這可以讓你建構:
提示驅動的使用者介面,使用者可以要求如下操作:
“我想訂購一條圍巾”
“給我看看庫存裡的帽子”
然後 LLM 產生 UI 結構(輸入、選擇、按鈕)。
表單資料透過定義的工具提交回助手。
我們開始做吧。
我們將從頭開始整合。如果您還沒有 Next.js 應用程式,可以使用此命令進行安裝。
npx create-next-app@latest thesys-demo
這是我們將要遵循的專案結構。
您需要從C1 控制台建立新的 API 金鑰,以將其設定為環境變數。請依照以下約定將此 API 金鑰新增至.env
檔。
THESYS_API_KEY=<your-api-key>
我們需要一個基本的聊天介面,其UI由提示符號產生。以下是在src/app/page.tsx
檔案中使用<C1Chat>
實作此功能的方法。
<C1Component />
只需要來自 C1 API 的原始 JSON/文本,但對於聊天應用程式,建議使用<C1Chat>
元件,因為它支援訊息流。
'use client'
import '@crayonai/react-ui/styles/index.css'
import { C1Chat } from '@thesysai/genui-sdk'
export default function Home() {
return <C1Chat theme={{ mode: 'dark' }} apiUrl="/api/chat" />
}
程式碼的作用如下:
使用<C1Chat />
元件來呈現完整的聊天UI。
apiUrl
指向將處理提示提交的後端路由。
如您所見,這是整個前端,無需手動 HTML 或表單程式碼。
表單資料將透過自訂tools
提交回助手。我們將建立自訂工具,例如createOrder
、 getInventory
和in-memory message store
。
為了處理表單資料並維護對話上下文,我們首先要建立一個訊息儲存。此工具可協助按執行緒儲存助手的訊息。
安裝openai軟體套件。它是用於與 OpenAI API(例如 GPT-4 或與 OpenAI 相容的 Thesys C1)互動的官方 Node.js SDK。
npm install openai
使用以下程式碼在src/app/api/chat/messageStore.ts
建立一個新檔案。
import OpenAI from 'openai'
export type DBMessage = OpenAI.Chat.ChatCompletionMessageParam & {
id?: string
}
const messagesStore: {
[threadId: string]: DBMessage[]
} = {}
export const getMessageStore = (id: string) => {
if (!messagesStore[id]) {
messagesStore[id] = []
}
const messageList = messagesStore[id]
return {
addMessage: (message: DBMessage) => {
messageList.push(message)
},
messageList,
getOpenAICompatibleMessageList: () => {
return messageList.map((m) => {
const message = {
...m,
}
delete message.id
return message
})
},
}
}
以下是程式碼每個部分的作用:
維護記憶體中的聊天訊息存儲,按threadId
組織。
messagesStore
是一個普通的 JS 物件,它將每個threadId
映射到一個訊息陣列。
公開一個實用程式getMessageStore(threadId)
該實用程式傳回幫助程式以:
addMessage(message)
→ 新增訊息至執行緒。
messageList
→傳回線程的所有儲存的訊息。
getOpenAICompatibleMessageList()
→ 傳回不含id
欄位的訊息列表,使其與 OpenAI 的ChatCompletion
API 相容。
該儲存並非持久化,因為伺服器重新啟動後所有內容都會遺失。您可以將其替換為資料庫(Redis、PostgreSQL)以供生產使用。
該工具將幫助助理建立並列出手套、帽子、圍巾等不同產品的客戶訂單。它使用Zod進行執行時驗證和模式安全,並將訂單儲存在記憶體中。
您無需安裝 zod,因為它是傳遞依賴項。如果您檢查它的package.json
檔案(位於node_modules/@thesysai/genui-sdk/package.json
),您可能會發現類似以下內容:
"dependencies": {
"zod": "^3.24.1"
}
使用以下程式碼在src/app/api/chat/orderManagement.ts
建立一個新檔案。
import { z } from 'zod'
export const gloveOrderSchema = z.object({
kind: z.literal('gloves'),
quantity: z.number(),
unit: z.enum(['boxes', 'pairs']),
deliveryDate: z.string(),
shipping: z.enum(['normal', 'express']),
})
export const hats = z.object({
kind: z.literal('hats'),
quantity: z.number(),
variants: z.enum(['top', 'beanie', 'cap']),
deliveryDate: z.string(),
shipping: z.enum(['normal', 'express']),
})
export const scarfOrderSchema = z.object({
kind: z.literal('scarves'),
quantity: z.number(),
colors: z.enum(['red', 'blue', 'green', 'yellow', 'purple', 'orange']),
deliveryDate: z.string(),
shipping: z.enum(['normal', 'express']),
})
export const orderSchema = z.object({
order: z.discriminatedUnion('kind', [
gloveOrderSchema,
hats,
scarfOrderSchema,
]),
})
type Order = z.infer<typeof orderSchema>
const orders: Order['order'][] = []
export const createOrder = async (orderJson: unknown) => {
const order = orderSchema.safeParse(orderJson)
if (!order.success) {
console.error('Invalid order', order.error)
return {
success: false,
error: order.error.message,
}
}
const deliveryDate = new Date(order.data.order.deliveryDate)
console.log('Creating order', { ...order.data.order, deliveryDate })
orders.push(order.data.order)
return {
success: true,
}
}
export const getOrderSchema = z.object({
number: z.number().optional().default(10),
})
export const getOrders = (params: unknown) => {
const parsedParams = getOrderSchema.safeParse(params)
if (!parsedParams.success) {
console.error('Invalid params', parsedParams.error)
return {
success: false,
error: parsedParams.error.message,
}
}
return {
success: true,
orders: orders.slice(0, parsedParams.data.number),
}
}
程式碼的作用如下:
支援使用獨特模式欄位動態建立gloves
、 hats
、 scarves
的訂單。
使用z.discriminatedUnion("kind", [...])
依照kind
排序。以便在一個統一的模式中推理多種產品形式。
定義一個頂級orderSchema
,它包裝各個模式並確保輸入與有效訂單類型之一相符。
建立一個記憶體陣列orders[]
來暫時儲存傳入的訂單。
公開createOrder()
函數:
接受原始輸入( orderJson
)
使用 Zod 進行解析和驗證
如果有效,則記錄並儲存訂單
返回成功/失敗回應
公開getOrders()
函數:
接受可選的number
參數(預設值:10)
返回最新的n
訂單
有助於列出聊天或使用者介面中最近的使用者操作
我們在建立和檢索時都使用了safeParse()
來防止因錯誤輸入而導致的執行時間崩潰。
讓我們建立一個工具,幫助助手根據手套、帽子、圍巾或所有類型獲取可用的產品庫存。
使用以下程式碼在src/app/api/chat/inventory.ts
建立一個新檔案。我使用模擬資料,因為我們只是在本地使用。
import { z } from 'zod'
export const inventoryQuerySchema = z.object({
productType: z
.enum(['gloves', 'hats', 'scarves', 'all'])
.optional()
.default('all'),
})
const allInventory = [
{
productType: 'gloves',
quantity: 100,
priceInUSD: 10.0,
urgentDeliveryDate: '2025-04-15',
normalDeliveryDate: '2025-04-20',
imageSrc: 'https://images.unsplash.com/photo-1617118602199-d3c05ae37ed8',
},
{
productType: 'hats',
quantity: 200,
priceInUSD: 15.0,
urgentDeliveryDate: '2025-04-15',
normalDeliveryDate: '2025-04-20',
imageSrc: 'https://images.unsplash.com/photo-1556306535-0f09a537f0a3',
},
{
productType: 'scarves',
quantity: 300,
priceInUSD: 5.0,
urgentDeliveryDate: '2025-04-15',
normalDeliveryDate: '2025-04-20',
imageSrc: 'https://images.unsplash.com/photo-1457545195570-67f207084966',
},
]
export const getInventory = (params: unknown) => {
const parsedParams = inventoryQuerySchema.safeParse(params)
if (!parsedParams.success) {
console.error('Invalid params', parsedParams.error)
return { success: false, error: parsedParams.error.message }
}
return {
success: true,
inventory: allInventory.filter(
(item) =>
parsedParams.data.productType === 'all' ||
item.productType === parsedParams.data.productType
),
}
}
上述程式碼的作用如下:
使用 Zod 定義輸入模式inventoryQuerySchema
:
接受可選的productType
欄位( "gloves"
, "hats"
, "scarves"
或"all"
)
如果未提供,則預設為"all"
聲明一個包含模擬資料的靜態allInventory
陣列。每件商品包含數量、價格、出貨日期和圖片(這些資料可能來自資料庫或 API)。
導出getInventory(params)
函數:
使用safeParse()
驗證輸入
如果指定,請按類型過濾庫存
回success: true
響應
如果輸入無效,則傳回錯誤訊息
在回答「哪些帽子有貨?」或「顯示所有產品」等查詢時,它將顯示可用的商品以及價格和運送資訊。
現在我們需要一個簡單的後端來處理使用與OpenAI-compatible
API 呼叫。
現在我們需要建立一個後端來處理 AI 互動( /api/chat
),使用與OpenAI-compatible
API 呼叫和串流響應並支援自訂工具。
在建構後端之前,我們先來了解系統提示,它是C1行為控制的關鍵部分。
雖然 C1 本身就很強大,但用合適的提示引導 LLM 學習對於獲得一致的 UI 至關重要。 Thesys 支援系統提示,以便:
定義助理的角色(例如“你是樂於助人的庫存經理”)或特定的語氣/語言
指定渲染行為(例如始終在清單中包含圖像)
使用<ui_rules>
標籤強制執行格式規則
閱讀官方文件,了解如何向 AI 應用程式加入系統提示的逐步指南
以下是官方文件中的範例。實際操作中,你應該在每個 API 呼叫前加入此提示:
const systemPrompt = `
You are a data assistant. Use tables for related info,
charts for comparisons, and carousels for lists of items.
`;
const resp = await client.chat.completions.create({
model: 'c1-nightly',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userQuery }
],
stream: true
});
以下是src/app/api/chat/route.ts
的程式碼。
import { NextRequest, NextResponse } from 'next/server'
import OpenAI from 'openai'
import { transformStream } from '@crayonai/stream'
import { DBMessage, getMessageStore } from './messageStore'
import {
createOrder,
getOrders,
getOrderSchema,
orderSchema,
} from './orderManagement'
import { zodToJsonSchema } from 'zod-to-json-schema'
import { JSONSchema } from 'openai/lib/jsonschema.mjs'
import { inventoryQuerySchema } from './inventory'
import { getInventory } from './inventory'
const SYSTEM_MESSAGE = `
You are a helpful assistant who can help with placing orders and checking inventory.
<ui_rules>
- When showing inventory, use the list component to show the inventory along with its image.
Always add the imageSrc to the list component.
</ui_rules>
`
export async function POST(req: NextRequest) {
const { prompt, threadId, responseId } = (await req.json()) as {
prompt: DBMessage
threadId: string
responseId: string
}
const client = new OpenAI({
baseURL: 'https://api.thesys.dev/v1/embed/',
apiKey: process.env.THESYS_API_KEY,
})
const messageStore = getMessageStore(threadId)
if (messageStore.getOpenAICompatibleMessageList().length === 0) {
messageStore.addMessage({
role: 'system',
content: SYSTEM_MESSAGE,
})
}
messageStore.addMessage(prompt)
const llmStream = client.chat.completions.runTools({ // OpenAI SDK v5+
model: 'c1-nightly',
messages: messageStore.getOpenAICompatibleMessageList(),
stream: true,
tools: [
{
type: 'function',
function: {
name: 'createOrder',
description: 'Create an order',
parameters: zodToJsonSchema(orderSchema) as JSONSchema,
function: createOrder,
parse: JSON.parse,
},
},
{
type: 'function',
function: {
name: 'getOrders',
description: 'Get all orders',
parameters: zodToJsonSchema(getOrderSchema) as JSONSchema,
function: getOrders,
parse: JSON.parse,
},
},
{
type: 'function',
function: {
name: 'getInventory',
description: 'Get the current inventory',
parameters: zodToJsonSchema(inventoryQuerySchema) as JSONSchema,
function: getInventory,
parse: JSON.parse,
},
},
],
})
const responseStream = transformStream(
llmStream,
(chunk) => {
return chunk.choices[0].delta.content
},
{
onEnd: ({ accumulated }) => {
const message = accumulated.filter((message) => message).join('')
messageStore.addMessage({
role: 'assistant',
content: message,
id: responseId,
})
},
}
) as ReadableStream<string>
return new NextResponse(responseStream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
Connection: 'keep-alive',
},
})
}
上述程式碼的作用如下:
使用使用者提示、線程 ID、回應 ID 處理傳入到/api/chat
的 POST 請求。
初始化系統提示,告訴助手如何行為以及如何呈現 UI(顯示庫存時始終包含imageSrc
)。
使用getMessageStore()
維護記憶體中的聊天歷史記錄,以使訊息保持線程特定並與 OpenAI 相容。
使用runTools()
方法呼叫 Thesys 的聊天完成 API ( /v1/embed/
),該方法:
即時傳輸 LLM 回應。
支援多種自訂工具:
- `createOrder`: for placing an order
- `getOrders`: for listing previous orders
- `getInventory`: for querying stock info
每個工具都使用透過zodToJsonSchema()
轉換為 JSON Schema 的zod
模式進行註冊,以便 LLM 知道輸入/輸出結構。
使用transformStream()
將助手的輸出串流回客戶端:
處理流程以提取 LLM 內容。
累積最終回應並將其儲存在訊息歷史記錄中(使用responseId
對其進行標記)。
最後返回text/event-stream
回應,讓前端在產生內容時逐步呈現內容。
耶! 🎉您已成功建立生成式 UI。
若要啟動本機開發伺服器,請在終端機中執行npm run dev
。
現在我們已經連接了前端、後端和所有支援工具,讓我們看看最終的結果。
這是使用者介面的樣子,使用crayonai/react-ui
進行樣式設計。
你可以自然地與助手互動,例如hi
或I want to place an order
。 LLM 能夠理解你的提示,並根據你的查詢動態呈現合適的 UI。
每個工具都使用 Zod 進行模式綁定,並使用有效的 UI 區塊即時回應。
如您所見,它列出了選項,甚至提供了按鈕: Place New Order
、 Check Inventory
和View Orders
。
讓我們檢查一下有哪些商品有貨。助手會使用getInventory()
工具取得庫存,並渲染包含商品資訊、價格、配送選項(硬編碼值)的卡片。
點擊「帽子」之類的產品會產生完整的訂單,該訂單由 LLM 根據我們提供的模式即時建立。
助理知道需要哪些輸入、輸入類型,甚至加入下拉式選單、日期選擇器和單選按鈕組等使用者體驗元素,然後由 SDK 渲染。
一旦提交,表單資料就會傳回給助手,並由適當的後端函數( createOrder()
)處理,該函數會儲存結果並確認操作。
它還將列出下達另一個訂單和檢查現有訂單的其他選項。
您無需編寫任何 HTML 表單,使用者就能獲得功能齊全的輸入欄位和動態流程。生成式 UI 非常酷,能夠解鎖眾多強大的用例。
Thesys 旨在存取現代 AI 堆疊和工作流程。例如:
LLM
→ Anthropic、OpenAI(預覽)、自訂模型(透過結構化 JSON)
Client SDKs
→ 標準 OpenAI 相容客戶端(Python、JS)
Chaining Tools
→ LangChain、LlamaIndex 管道 → C1 渲染 UI
Agent Framework
→ AutoGPT/BabyAGI 代理可以呼叫工具並渲染 UI
Backend
→ 任何 REST 框架,例如 Python、Node.js、FastAPI、Django、Next.js
Frontend
→ 任何 React 框架,例如 Next.js
Deployment
→ Vercel、Netlify、AWS、GCP、您的基礎設施
生成式 UI 是前端的未來。
使用 Thesys,您的前端可以根據上下文做出回應、調整並自行建立。
希望你找到了一些有價值的東西。嘗試一下,建立一些有用的東西。
祝你今天過得愉快!下次見 :)
| 您可以檢查
我在anmolbaranwal.com上工作。
感謝您的閱讀! 🥰 |
|
| --------- | -------- |
原文出處:https://dev.to/anmolbaranwal/thesys-react-sdk-turn-llm-responses-into-real-time-user-interfaces-30d5