前言
这一篇我们来把端侧AI和React 19完美结合!
1. 创建AI Context
jsx
import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
const AIContext = createContext(null);
// AI Provider
export function AIProvider({ children }) {
const [session, setSession] = useState(null);
const [isReady, setIsReady] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
// 初始化AI
const initAI = useCallback(async (options = {}) => {
try {
setIsLoading(true);
setError(null);
if (!('ai' in window)) {
throw new Error('当前浏览器不支持Chrome AI');
}
const newSession = await window.ai.assistant.create({
systemPrompt: options.systemPrompt || '你是一个AI助手。',
temperature: options.temperature || 0.7
});
setSession(newSession);
setIsReady(true);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
}, []);
// 发送消息
const sendMessage = useCallback(async (prompt) => {
if (!session) {
throw new Error('AI未初始化,请先调用initAI()');
}
return await session.prompt(prompt);
}, [session]);
// 流式发送
const sendMessageStream = useCallback(async function* (prompt) {
if (!session) {
throw new Error('AI未初始化');
}
const stream = session.promptStreaming(prompt);
for await (const chunk of stream) {
yield chunk;
}
}, [session]);
// 销毁
const destroy = useCallback(() => {
if (session) {
session.destroy();
setSession(null);
setIsReady(false);
}
}, [session]);
return (
<AIContext.Provider value={{
isReady,
isLoading,
error,
initAI,
sendMessage,
sendMessageStream,
destroy
}}>
{children}
</AIContext.Provider>
);
}
// Hook
export function useAI() {
const context = useContext(AIContext);
if (!context) {
throw new Error('useAI must be used within AIProvider');
}
return context;
}
2. 完整的AI聊天应用
jsx
import React, { useState, useRef, useEffect } from 'react';
import { AIProvider, useAI } from './ai-context';
function ChatMessage({ role, content }) {
return (
<div className={`p-4 rounded-lg max-w-[80%] ${role === 'user' ? 'ml-auto bg-blue-600 text-white' : 'bg-gray-100'}`}>
<p>{content}</p>
</div>
);
}
function ChatApp() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [isGenerating, setIsGenerating] = useState(false);
const messagesEndRef = useRef(null);
const { isReady, initAI, sendMessageStream } = useAI();
// 初始化
useEffect(() => {
if (!isReady) {
initAI({
systemPrompt: '你是一个友好的AI助手。用中文回答。'
});
}
}, [isReady, initAI]);
// 自动滚动到底部
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
const handleSend = async () => {
if (!input.trim() || isGenerating || !isReady) return;
// 添加用户消息
const userMessage = { role: 'user', content: input };
setMessages(prev => [...prev, userMessage]);
setInput('');
setIsGenerating(true);
try {
// 添加AI回复占位符
const aiMessageId = Date.now();
setMessages(prev => [...prev, { role: 'assistant', content: '', id: aiMessageId }]);
// 流式生成
let fullContent = '';
for await (const chunk of sendMessageStream(input)) {
fullContent += chunk;
setMessages(prev => prev.map(msg =>
msg.id === aiMessageId ? { ...msg, content: fullContent } : msg
));
}
} catch (error) {
console.error('生成失败:', error);
} finally {
setIsGenerating(false);
}
};
return (
<div className="max-w-3xl mx-auto p-6 h-screen flex flex-col">
<h1 className="text-2xl font-bold mb-6">🤖 AI聊天</h1>
{/* 消息列表 */}
<div className="flex-1 overflow-y-auto space-y-4 mb-6">
{messages.length === 0 ? (
<div className="text-center py-12 text-gray-500">
开始和AI聊天吧!
</div>
) : (
messages.map((msg, index) => (
<ChatMessage key={msg.id || index} role={msg.role} content={msg.content} />
))
)}
{isGenerating && (
<div className="p-4 bg-gray-100 rounded-lg max-w-[80%]">
<div className="flex gap-2">
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" />
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }} />
<span className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }} />
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
{/* 输入区 */}
<div className="flex gap-2">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSend()}
placeholder="输入消息..."
disabled={!isReady || isGenerating}
className="flex-1 p-3 border rounded-lg disabled:opacity-50"
/>
<button
onClick={handleSend}
disabled={!input.trim() || !isReady || isGenerating}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
>
发送
</button>
</div>
</div>
);
}
// 主App
function App() {
return (
<AIProvider>
<ChatApp />
</AIProvider>
);
}
export default App;
3. 各种AI Hook工具
jsx
import { useAI } from './ai-context';
// AI写Hook
export function useAIWriter() {
const { isReady, sendMessage } = useAI();
const write = useCallback(async (prompt, mode = 'write') => {
let systemPrompt = '';
switch (mode) {
case 'write':
systemPrompt = '你是一个写作助手。';
break;
case 'improve':
systemPrompt = '请润色以下内容,让它更流畅。';
break;
case 'translate':
systemPrompt = '请把以下内容翻译成中文。';
break;
}
return await sendMessage(`${systemPrompt}\n\n${prompt}`);
}, [sendMessage]);
return { isReady, write };
}
// AI代码Hook
export function useAICode() {
const { isReady, sendMessage } = useAI();
const generate = useCallback(async (description) => {
return await sendMessage(`请根据以下描述写代码:\n\n${description}\n\n只返回代码。`);
}, [sendMessage]);
const fix = useCallback(async (code, error) => {
return await sendMessage(`请修复以下代码的错误:\n\n代码:\n${code}\n\n错误:\n${error}`);
}, [sendMessage]);
return { isReady, generate, fix };
}
4. 今日小结
这一篇我们学习了:
- AI Context封装
- 完整的AI聊天应用
- 各种AI Hook工具
5. 下篇预告
下一篇,我们学习 端侧AI与Vue 3结合!