一、前端开发者的优势与劣势
你已经有的优势
- 用户体验意识强:知道怎么做交互才让用户舒服
- 异步编程熟练:Promise、async/await用得飞起
- API调用经验:fetch、axios调了几百次了
- 状态管理能力:Redux、Zustand、Pinia玩得溜
- 实时通信经验:WebSocket、SSE做过聊天室
- 前后端协作经验:知道怎么对接后端接口
需要补充的短板
- 后端开发经验:可能没写过后端服务
- 数据库操作:SQL写得少
- 向量检索概念:完全陌生
- Python生态:习惯了JavaScript
- Prompt工程:不知道怎么让AI听话
好消息: 前端开发者转做大模型应用开发,是最有优势的群体之一!因为80%的工作都在前端交互和用户体验上。
二、核心技能地图(按重要性排序)
Level 1:必须掌握(立即能用)
1. 大模型API调用(前端直接调用)
最简单的方式 - 前端直接调用
// 1. OpenAI官方SDK(最常用)
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.NEXT_PUBLIC_OPENAI_KEY,
dangerouslyAllowBrowser: true // 仅开发用,生产环境要走后端
});
async function chat(message: string) {
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: message }],
stream: true, // 流式返回,用户体验更好
});
for await (const chunk of response) {
const text = chunk.choices[0]?.delta?.content || '';
console.log(text); // 逐字显示
}
}
实际项目中的正确做法
// 前端代码 - 调用你自己的后端API
async function chatWithAI(message: string) {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const text = decoder.decode(value);
// 更新UI显示
appendToChat(text);
}
}
你需要掌握的概念
- 流式返回(Stream)vs 一次性返回
- Token计数(影响成本)
- Temperature参数(控制创造性)
- System prompt vs User prompt
- max_tokens限制
2. 流式响应处理(前端核心技能)
为什么要用流式?
- GPT、Deepseek生成一个回答要5-10秒,用户不能干等
- 逐字显示像打字机,用户体验好100倍
- 类似ChatGPT的效果
三种实现方式
方式1:Server-Sent Events (SSE) - 最常用
// React组件示例
function ChatBox() {
const [messages, setMessages] = useState<string[]>([]);
const [currentResponse, setCurrentResponse] = useState('');
async function sendMessage(text: string) {
setCurrentResponse(''); // 清空上次的响应
const eventSource = new EventSource(
`/api/chat-stream?message=${encodeURIComponent(text)}`
);
eventSource.onmessage = (event) => {
const chunk = event.data;
setCurrentResponse(prev => prev + chunk); // 逐字累加
};
eventSource.onerror = () => {
eventSource.close();
setMessages(prev => [...prev, currentResponse]);
setCurrentResponse('');
};
}
return (
<div>
{messages.map((msg, i) => <p key={i}>{msg}</p>)}
{currentResponse && <p className="typing">{currentResponse}</p>}
</div>
);
}
方式2:Fetch Stream - 更灵活
async function streamChat(message: string) {
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ message }),
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let fullText = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
fullText += chunk;
// 实时更新UI
updateChatDisplay(fullText);
}
}
方式3:WebSocket - 需要双向通信时
const ws = new WebSocket('ws://localhost:3000/chat');
ws.onopen = () => {
ws.send(JSON.stringify({ message: 'Hello AI' }));
};
ws.onmessage = (event) => {
const chunk = JSON.parse(event.data);
appendText(chunk.text);
};
3. 打字机效果实现
基础版 - 手动控制速度
function useTypewriter(text: string, speed = 30) {
const [displayText, setDisplayText] = useState('');
useEffect(() => {
let i = 0;
const timer = setInterval(() => {
if (i < text.length) {
setDisplayText(text.slice(0, i + 1));
i++;
} else {
clearInterval(timer);
}
}, speed);
return () => clearInterval(timer);
}, [text, speed]);
return displayText;
}
// 使用
function AIResponse({ text }: { text: string }) {
const displayed = useTypewriter(text);
return <div>{displayed}<span className="cursor">|</span></div>;
}
进阶版 - 配合真实流式数据
function StreamingText({ stream }: { stream: AsyncIterable<string> }) {
const [text, setText] = useState('');
useEffect(() => {
(async () => {
for await (const chunk of stream) {
setText(prev => prev + chunk);
// 自动滚动到底部
scrollToBottom();
}
})();
}, [stream]);
return <div className="markdown-body">{text}</div>;
}
4. Markdown渲染(AI回答都是Markdown格式)
必备库
npm install react-markdown remark-gfm rehype-highlight
完整示例
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeHighlight from 'rehype-highlight';
import 'highlight.js/styles/github-dark.css';
function AIMessage({ content }: { content: string }) {
return (
<ReactMarkdown
remarkPlugins={[remarkGfm]} // 支持表格、删除线等
rehypePlugins={[rehypeHighlight]} // 代码高亮
components={{
// 自定义代码块样式
code({ node, inline, className, children, ...props }) {
if (inline) {
return <code className="inline-code">{children}</code>;
}
return (
<div className="code-block">
<button onClick={() => copyCode(children)}>复制</button>
<code className={className} {...props}>
{children}
</code>
</div>
);
},
// 自定义链接在新窗口打开
a({ href, children }) {
return <a href={href} target="_blank" rel="noopener">{children}</a>;
},
}}
>
{content}
</ReactMarkdown>
);
}
5. 对话历史管理
数据结构设计
interface Message {
id: string;
role: 'user' | 'assistant' | 'system';
content: string;
timestamp: number;
tokens?: number; // 可选:记录消耗的token
}
interface Conversation {
id: string;
title: string; // 对话标题(通常用第一句话生成)
messages: Message[];
createdAt: number;
updatedAt: number;
}
状态管理(Zustand示例)
import create from 'zustand';
import { persist } from 'zustand/middleware';
interface ChatStore {
conversations: Conversation[];
currentConvId: string | null;
// 创建新对话
createConversation: () => string;
// 添加消息
addMessage: (convId: string, message: Omit<Message, 'id' | 'timestamp'>) => void;
// 删除对话
deleteConversation: (convId: string) => void;
// 切换对话
setCurrentConversation: (convId: string) => void;
}
export const useChatStore = create<ChatStore>()(
persist(
(set, get) => ({
conversations: [],
currentConvId: null,
createConversation: () => {
const id = crypto.randomUUID();
const newConv: Conversation = {
id,
title: '新对话',
messages: [],
createdAt: Date.now(),
updatedAt: Date.now(),
};
set(state => ({
conversations: [newConv, ...state.conversations],
currentConvId: id,
}));
return id;
},
addMessage: (convId, message) => {
const fullMessage: Message = {
...message,
id: crypto.randomUUID(),
timestamp: Date.now(),
};
set(state => ({
conversations: state.conversations.map(conv =>
conv.id === convId
? {
...conv,
messages: [...conv.messages, fullMessage],
updatedAt: Date.now(),
// 如果是第一条消息,用它作为标题
title: conv.messages.length === 0
? message.content.slice(0, 30)
: conv.title,
}
: conv
),
}));
},
deleteConversation: (convId) => {
set(state => ({
conversations: state.conversations.filter(c => c.id !== convId),
currentConvId: state.currentConvId === convId
? state.conversations[0]?.id || null
: state.currentConvId,
}));
},
setCurrentConversation: (convId) => {
set({ currentConvId: convId });
},
}),
{
name: 'chat-storage', // localStorage key
}
)
);
使用示例
function ChatInterface() {
const {
conversations,
currentConvId,
addMessage,
createConversation
} = useChatStore();
const currentConv = conversations.find(c => c.id === currentConvId);
async function handleSend(text: string) {
// 1. 添加用户消息
addMessage(currentConvId!, { role: 'user', content: text });
// 2. 调用AI
const response = await chatWithAI(text);
// 3. 添加AI回复
addMessage(currentConvId!, { role: 'assistant', content: response });
}
return (
<div className="chat-container">
<MessageList messages={currentConv?.messages || []} />
<InputBox onSend={handleSend} />
</div>
);
}
Level 2:重要但可以边做边学
6. 基础后端开发(Node.js/Next.js)
为什么前端也要写后端?
- API Key不能暴露在前端
- 需要做访问控制和用户认证
- 要记录日志、统计用量
- 可能需要连接数据库
Next.js API Routes(最简单的后端)
// app/api/chat/route.ts
import { OpenAI } from 'openai';
import { StreamingTextResponse, OpenAIStream } from 'ai';
export const runtime = 'edge'; // 边缘运行时,更快
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export async function POST(req: Request) {
const { messages } = await req.json();
// 调用OpenAI
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages,
stream: true,
});
// 转换为前端可读的流
const stream = OpenAIStream(response);
return new StreamingTextResponse(stream);
}
Vercel AI SDK(强烈推荐!)
import { useChat } from 'ai/react';
function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat();
return (
<div>
{messages.map(m => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit">发送</button>
</form>
</div>
);
}
这个SDK帮你搞定了流式、状态管理、错误处理,超级好用
7. Prompt工程(前端视角)
动态Prompt构建
interface PromptTemplate {
system: string;
examples?: Array<{ user: string; assistant: string }>;
userMessage: string;
}
function buildPrompt(template: PromptTemplate): Message[] {
const messages: Message[] = [
{ role: 'system', content: template.system },
];
// 添加Few-shot示例
if (template.examples) {
template.examples.forEach(ex => {
messages.push(
{ role: 'user', content: ex.user },
{ role: 'assistant', content: ex.assistant }
);
});
}
// 添加实际用户消息
messages.push({ role: 'user', content: template.userMessage });
return messages;
}
// 使用
const codeReviewPrompt: PromptTemplate = {
system: `你是一个专业的代码审查专家。
分析代码时要关注:
1. 潜在的bug
2. 性能问题
3. 代码风格
4. 最佳实践
用中文回答,格式清晰。`,
examples: [
{
user: 'const arr = [1,2,3]; for(let i=0; i<=arr.length; i++) console.log(arr[i]);',
assistant: '发现问题:\n1. 边界错误:i<=arr.length 会导致访问undefined\n应改为:i<arr.length',
},
],
userMessage: userCode,
};
const messages = buildPrompt(codeReviewPrompt);
Prompt最佳实践(前端常用场景)
// 场景1:代码生成
const codeGenPrompt = `请生成一个React组件,要求:
- 使用TypeScript
- 使用Tailwind CSS
- 支持暗黑模式
- 添加完整的TypeScript类型定义
组件需求:${userRequirement}
返回格式:只返回代码,不要解释。`;
// 场景2:文档总结
const summaryPrompt = `请总结以下文档的核心内容,要求:
- 3-5个要点
- 每个要点一句话
- 用中文
文档内容:
${document}`;
// 场景3:数据提取(返回JSON)
const extractPrompt = `从以下文本中提取信息,以JSON格式返回。
期望格式:
{
"name": "姓名",
"email": "邮箱",
"phone": "电话"
}
文本:${text}
只返回JSON,不要其他内容。`;
8. 错误处理与重试机制
async function callAIWithRetry(
message: string,
maxRetries = 3
): Promise<string> {
let lastError: Error | null = null;
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ message }),
signal: AbortSignal.timeout(30000), // 30秒超时
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.text();
} catch (error) {
lastError = error as Error;
// 区分不同错误类型
if (error.name === 'AbortError') {
console.log(`请求超时,重试 ${i + 1}/${maxRetries}`);
} else if (error.message.includes('429')) {
console.log('Rate limit,等待5秒后重试');
await sleep(5000);
} else if (error.message.includes('500')) {
console.log('服务器错误,重试');
await sleep(1000 * (i + 1)); // 指数退避
} else {
throw error; // 其他错误直接抛出
}
}
}
throw new Error(`请求失败,已重试${maxRetries}次: ${lastError?.message}`);
}
function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
9. Token计数与成本控制
前端实时估算Token
npm install gpt-tokenizer
import { encode } from 'gpt-tokenizer';
function estimateTokens(text: string): number {
return encode(text).length;
}
function estimateCost(tokens: number, model: string): number {
const pricing = {
'gpt-4': { input: 0.03 / 1000, output: 0.06 / 1000 },
'gpt-3.5-turbo': { input: 0.0015 / 1000, output: 0.002 / 1000 },
};
const price = pricing[model];
return tokens * price.input; // 简化计算
}
// 在发送前提示用户
function InputBox() {
const [text, setText] = useState('');
const tokens = estimateTokens(text);
const cost = estimateCost(tokens, 'gpt-4');
return (
<div>
<textarea value={text} onChange={e => setText(e.target.value)} />
<div className="stats">
大约 {tokens} tokens,预计花费 ${cost.toFixed(4)}
</div>
</div>
);
}
10. 用户认证与权限控制
简单的API Key管理
// 前端 - 让用户输入自己的API Key
function Settings() {
const [apiKey, setApiKey] = useState(
localStorage.getItem('openai_key') || ''
);
function saveKey() {
localStorage.setItem('openai_key', apiKey);
}
return (
<div>
<input
type="password"
value={apiKey}
onChange={e => setApiKey(e.target.value)}
placeholder="输入你的OpenAI API Key"
/>
<button onClick={saveKey}>保存</button>
</div>
);
}
// 调用时带上Key
async function chat(message: string) {
const apiKey = localStorage.getItem('openai_key');
const response = await fetch('/api/chat', {
headers: {
'X-API-Key': apiKey,
},
body: JSON.stringify({ message }),
});
}
Level 3:进阶技能(提升竞争力)
11. RAG(检索增强生成)- 前端集成
什么是RAG? 用户问"公司Q3财报怎么样?" → 不是让AI瞎猜,而是:
- 在公司文档库里搜索相关内容
- 把搜到的内容塞进Prompt
- 让AI基于这些内容回答
前端需要做的
// 1. 文件上传
function DocumentUpload() {
async function handleUpload(file: File) {
const formData = new FormData();
formData.append('file', file);
await fetch('/api/documents/upload', {
method: 'POST',
body: formData,
});
alert('文档已上传,正在处理...');
}
return <input type="file" onChange={e => handleUpload(e.target.files[0])} />;
}
// 2. 显示引用来源
interface Source {
content: string;
filename: string;
page?: number;
}
function AnswerWithSources({ answer, sources }: {
answer: string;
sources: Source[]
}) {
return (
<div>
<div className="answer">{answer}</div>
<div className="sources">
<h4>来源:</h4>
{sources.map((src, i) => (
<div key={i} className="source-card">
<strong>{src.filename}</strong>
{src.page && <span>第{src.page}页</span>}
<p>{src.content.slice(0, 200)}...</p>
</div>
))}
</div>
</div>
);
}
调用RAG API
async function askWithRAG(question: string) {
const response = await fetch('/api/rag/query', {
method: 'POST',
body: JSON.stringify({
question,
topK: 3, // 取最相关的3个文档片段
}),
});
const data = await response.json();
return {
answer: data.answer,
sources: data.sources,
};
}
12. 函数调用(Function Calling)
让AI调用你定义的函数
// 定义可用的工具
const tools = [
{
type: 'function',
function: {
name: 'get_weather',
description: '获取指定城市的天气',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: '城市名称' },
unit: { type: 'string', enum: ['celsius', 'fahrenheit'] },
},
required: ['city'],
},
},
},
{
type: 'function',
function: {
name: 'search_products',
description: '在商品数据库中搜索',
parameters: {
type: 'object',
properties: {
query: { type: 'string' },
category: { type: 'string' },
},
required: ['query'],
},
},
},
];
// 实际的函数实现
async function getWeather(city: string, unit = 'celsius') {
const response = await fetch(`https://api.weather.com/${city}`);
return response.json();
}
async function searchProducts(query: string, category?: string) {
// 搜索你的商品数据库
return await db.products.search({ query, category });
}
// 处理流程
async function chatWithTools(userMessage: string) {
// 1. 发送消息和可用工具列表
let response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: userMessage }],
tools,
});
let message = response.choices[0].message;
// 2. 如果AI要调用函数
if (message.tool_calls) {
for (const toolCall of message.tool_calls) {
const functionName = toolCall.function.name;
const args = JSON.parse(toolCall.function.arguments);
// 3. 执行对应的函数
let result;
if (functionName === 'get_weather') {
result = await getWeather(args.city, args.unit);
} else if (functionName === 'search_products') {
result = await searchProducts(args.query, args.category);
}
// 4. 把结果返回给AI
response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{ role: 'user', content: userMessage },
message,
{
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result),
},
],
});
message = response.choices[0].message;
}
}
return message.content;
}
前端展示函数调用过程
function FunctionCallDisplay({ call }: { call: FunctionCall }) {
return (
<div className="function-call">
<div className="icon">🔧</div>
<div>
调用函数: <code>{call.name}</code>
<pre>{JSON.stringify(call.arguments, null, 2)}</pre>
</div>
</div>
);
}
13. 多模态交互(图片、语音)
图片上传并让AI分析
async function analyzeImage(file: File) {
// 转成base64
const base64 = await fileToBase64(file);
const response = await openai.chat.completions.create({
model: 'gpt-4-vision-preview',
messages: [
{
role: 'user',
content: [
{ type: 'text', text: '这张图片里有什么?' },
{
type: 'image_url',
image_url: { url: `data:image/jpeg;base64,${base64}` }
},
],
},
],
});
return response.choices[0].message.content;
}
function fileToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const result = reader.result as string;
resolve(result.split(',')[1]); // 去掉data:image/jpeg;base64,前缀
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
语音输入(Web Speech API)
function VoiceInput({ onTranscript }: { onTranscript: (text: string) => void }) {
const [isListening, setIsListening] = useState(false);
function startListening() {
const recognition = new (window as any).webkitSpeechRecognition();
recognition.lang = 'zh-CN';
recognition.continuous = false;
recognition.onstart = () => setIsListening(true);
recognition.onend = () => setIsListening(false);
recognition.onresult = (event: any) => {
const transcript = event.results[0][0].transcript;
onTranscript(transcript);
};
recognition.start();
}
return (
<button onClick={startListening}>
{isListening ? '正在听...' : '🎤 语音输入'}
</button>
);
}
14. 向量搜索UI组件
文档相似度搜索
function DocumentSearch() {
const [query, setQuery] = useState('');
const [results, setResults] = useState<SearchResult[]>([]);
async function search() {
const response = await fetch('/api/search', {
method: 'POST',
body: JSON.stringify({ query }),
});
const data = await response.json();
setResults(data.results);
}
return (
<div>
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="搜索文档..."
/>
<button onClick={search}>搜索</button>
<div className="results">
{results.map(r => (
<div key={r.id} className="result-card">
<h3>{r.title}</h3>
<p>{r.content}</p>
<div className="score">相似度: {(r.score * 100).toFixed(1)}%</div>
</div>
))}
</div>
</div>
);
}
15. 实时协作(多用户同时使用)
WebSocket + 共享对话
function CollaborativeChat({ roomId }: { roomId: string }) {
const [messages, setMessages] = useState<Message[]>([]);
const wsRef = useRef<WebSocket | null>(null);
useEffect(() => {
wsRef.current = new WebSocket(`ws://localhost:3000/room/${roomId}`);
wsRef.current.onmessage = (event) => {
const message = JSON.parse(event.data);
setMessages(prev => [...prev, message]);
};
return () => wsRef.current?.close();
}, [roomId]);
function sendMessage(text: string) {
wsRef.current?.send(JSON.stringify({
type: 'user_message',
content: text,
userId: getCurrentUserId(),
}));
}
return (
<div>
{messages.map(msg => (
<div key={msg.id}>
<Avatar userId={msg.userId} />
{msg.content}
</div>
))}
</div>
);
}
三、实战项目推荐(从易到难)
项目1:智能聊天机器人(入门)
技能练习
- ✅ API调用
- ✅ 流式响应
- ✅ 对话历史
- ✅ Markdown渲染
参考开源项目
- ChatGPT-Next-Web
- BetterChatGPT
项目2:AI代码助手(中级)
功能
- 代码审查
- 代码补全
- 错误解释
- 单元测试生成
技能练习
- Prompt工程
- Function Calling
- Monaco Editor集成
项目3:企业知识库问答(进阶)
功能
- 文档上传
- 向量检索
- 引用来源
- 权限控制
技能练习
- RAG
- 后端开发
- 数据库
项目4:AI作图工具(高级)
功能
- 文字生成图片
- 图片编辑
- 风格转换
- 历史记录
技能练习
- DALL-E / Midjourney API
- Canvas / Fabric.js
- 图片处理
四、技术栈推荐
前端框架选择
推荐:Next.js 14 (App Router)
- 自带API Routes,前后端一体
- 部署简单(Vercel一键部署)
- SSR/SSG支持好
- 大部分AI应用都在用
备选:
- Nuxt 3(Vue生态)
- Remix(如果你喜欢)
UI组件库
推荐:shadcn/ui + Tailwind CSS
- 现代设计
- 复制即用,不需要npm install
- 暗黑模式支持好
- AI应用标配
备选:
- Chakra UI
- Ant Design
- Material UI
状态管理
推荐:Zustand
- 轻量,学习成本低
- TypeScript支持好
- 配合persist中间件做持久化
备选:
- Redux Toolkit(如果团队在用)
- Jotai / Recoil
AI SDK
必备:Vercel AI SDK
- 统一封装了各家API
- 自动处理流式响应
- React Hooks超好用
const { messages, input, handleSubmit } = useChat();
数据库
开发阶段:
- SQLite(本地开发)
- Firebase(快速原型)
生产环境:
- PostgreSQL + Prisma ORM(推荐)
- Supabase(PostgreSQL + 即时API)
向量数据库(做RAG时用)
推荐:Pinecone
- 托管服务,不用自己部署
- 免费额度够用
- API简单
备选:
- Supabase Vector(如果你已经在用Supabase)
- Weaviate(开源,功能强)
五、学习路线(30天速成)
Week 1:基础搭建
Day 1-2:环境配置
- 注册OpenAI账号,获取API Key
- 搭建Next.js项目
- 配置Tailwind CSS
Day 3-5:第一个聊天应用
- 实现基本的对话界面
- 接入OpenAI API
- 添加流式响应
Day 6-7:优化体验
- 添加Markdown渲染
- 实现打字机效果
- 添加对话历史
Week 2:进阶功能
Day 8-10:状态管理
- 用Zustand管理对话
- 实现多对话切换
- LocalStorage持久化
Day 11-12:Prompt工程
- 学习Prompt最佳实践
- 实现System Prompt设置
- 添加Few-shot示例
Day 13-14:错误处理
- 添加加载状态
- 实现重试机制
- 优雅的错误提示
Week 3:后端开发
Day 15-17:API Routes
- 把API Key移到后端
- 添加用户认证
- 记录日志和统计
Day 18-19:数据库
- 连接PostgreSQL
- 存储对话历史
- 实现用户系统
Day 20-21:部署上线
- 部署到Vercel
- 配置环境变量
- 绑定域名
Week 4:高级特性
Day 22-24:RAG实现
- 文档上传功能
- 向量化处理
- 检索并回答
Day 25-26:Function Calling
- 定义可用函数
- 处理函数调用
- 展示调用过程
Day 27-28:多模态
- 图片上传
- 图片分析
- 语音输入
Day 29-30:打磨细节
- 响应式设计
- 暗黑模式
- 性能优化
六、避坑指南
❌ 常见错误1:API Key暴露
// 错误示范
const openai = new OpenAI({
apiKey: 'sk-xxxxxx', // 千万不要硬编码在前端!
dangerouslyAllowBrowser: true,
});
// 正确做法:通过后端代理
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ message }),
});
❌ 常见错误2:没有处理流式中断
// 错误示范
for await (const chunk of stream) {
appendText(chunk);
// 如果用户点了停止,流还在继续
}
// 正确做法
const abortController = new AbortController();
async function streamChat() {
try {
for await (const chunk of stream) {
if (abortController.signal.aborted) break;
appendText(chunk);
}
} catch (e) {
if (e.name === 'AbortError') {
console.log('用户停止了生成');
}
}
}
// 停止按钮
<button onClick={() => abortController.abort()}>停止</button>
❌ 常见错误3:没有控制Token消耗
// 错误示范:把所有历史都发给AI
const response = await openai.chat.completions.create({
messages: allMessages, // 可能有几千条消息!
});
// 正确做法:只取最近的N条
const recentMessages = messages.slice(-10); // 最近10条
const response = await openai.chat.completions.create({
messages: [
systemMessage,
...recentMessages,
],
});
❌ 常见错误4:UI卡顿
// 错误示范:每收到一个字符就更新整个组件
setMessages([...messages, newMessage]); // 每次都复制整个数组
// 正确做法:用reducer或者只更新最后一条
const [messages, dispatch] = useReducer(messageReducer, []);
dispatch({ type: 'APPEND_TO_LAST', text: chunk });
七、推荐学习资源
文档与教程
- Vercel AI SDK 官方文档(必看)
- OpenAI Cookbook(实战案例)
- LangChain.js 文档(如果要做RAG)
开源项目学习
- ChatGPT-Next-Web
- GitHub: Yidadaa/ChatGPT-Next-Web
- 学习:对话管理、UI设计
- Lobe Chat
- GitHub: lobehub/lobe-chat
- 学习:插件系统、多模态
- Dify
- GitHub: langgenius/dify
- 学习:工作流设计
视频课程
- B站搜索:
- "Next.js AI应用开发"
- "Vercel AI SDK教程"
- "RAG实战"
八、薪资与职业发展
薪资范围(2026,前端转AI应用)
- 初级(0-1年): 20-30k/月
- 能做基本的聊天应用
- 会接入API
- 中级(1-3年): 30-50k/月
- 能做RAG应用
- 能优化性能和成本
- 有完整项目经验
- 高级(3+年): 50-80k/月
- 能设计复杂系统架构
- 熟悉多种AI能力集成
- 能带团队
职业路径
纯前端
↓
AI应用前端(会调API)
↓
全栈AI应用开发(前后端都会)
↓
AI产品架构师(懂产品+技术)
竞争优势
作为前端开发者,你的优势是:
- ✅ 用户体验意识强(AI应用体验很重要)
- ✅ 异步编程能力强(流式响应是核心)
- ✅ 快速迭代能力(AI应用变化快)
- ✅ 前端人才基数大(机会多)
九、总结:30天行动计划
Week 1:先跑起来
- 搭建项目,调通第一个API
- 实现基本聊天界面
- 加入流式响应和Markdown
Week 2:做得好用
- 完善对话管理
- 学习Prompt工程
- 优化用户体验
Week 3:做得专业
- 搭建后端API
- 加入数据库
- 部署上线
Week 4:做得高级
- 实现RAG或Function Calling
- 加入多模态能力
- 开源到GitHub