js開發(fā)一個(gè)類似ChatGPT的AI應(yīng)用助手
"你說我們能不能開發(fā)一個(gè)類似 ChatGPT 的應(yīng)用?"上個(gè)月,一位創(chuàng)業(yè)朋友找到我,想做一個(gè)垂直領(lǐng)域的 AI 助手。作為一個(gè)經(jīng)常和 AI API 打交道的全棧開發(fā)者,這個(gè)想法立刻勾起了我的興趣。不過說實(shí)話,從零開始構(gòu)建一個(gè) AI 應(yīng)用,還是讓我有點(diǎn)小緊張。
經(jīng)過一個(gè)月的開發(fā)迭代,我們成功上線了第一個(gè)版本,用戶反饋出奇的好。今天就來分享這個(gè)過程中的技術(shù)選型、架構(gòu)設(shè)計(jì)和實(shí)戰(zhàn)經(jīng)驗(yàn)。
技術(shù)選型
首先面臨的是技術(shù)棧的選擇。考慮到實(shí)時(shí)性、性能和開發(fā)效率,我們最終選定了這套技術(shù)棧:
// 項(xiàng)目技術(shù)棧 const techStack = { frontend: { framework: 'Next.js 14', // App Router + React Server Components ui: 'Tailwind CSS + Shadcn UI', state: 'Zustand', realtime: 'Server-Sent Events' }, backend: { runtime: 'Node.js', framework: 'Next.js API Routes', database: 'PostgreSQL + Prisma', cache: 'Redis' }, ai: { provider: 'OpenAI API', framework: 'Langchain', vectorStore: 'PineconeDB' } }
核心功能實(shí)現(xiàn)
1. 流式響應(yīng)的實(shí)現(xiàn)
最關(guān)鍵的是實(shí)現(xiàn)打字機(jī)效果的流式響應(yīng):
// app/api/chat/route.ts import { OpenAIStream } from '@/lib/openai' import { StreamingTextResponse } from 'ai' export async function POST(req: Request) { const { messages } = await req.json() // 調(diào)用 OpenAI API 獲取流式響應(yīng) const stream = await OpenAIStream({ model: 'gpt-4', messages, temperature: 0.7, stream: true }) // 返回流式響應(yīng) return new StreamingTextResponse(stream) } // components/Chat.tsx function Chat() { const [messages, setMessages] = useState<Message[]>([]) const [isLoading, setIsLoading] = useState(false) const handleSubmit = async (content: string) => { setIsLoading(true) try { const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: [...messages, { role: 'user', content }] }) }) if (!response.ok) throw new Error('請(qǐng)求失敗') // 處理流式響應(yīng) const reader = response.body!.getReader() const decoder = new TextDecoder() let aiResponse = '' while (true) { const { done, value } = await reader.read() if (done) break // 解碼并追加新內(nèi)容 aiResponse += decoder.decode(value) // 更新UI setMessages(prev => [...prev.slice(0, -1), { role: 'assistant', content: aiResponse }]) } } catch (error) { console.error('聊天出錯(cuò):', error) } finally { setIsLoading(false) } } return ( <div className='flex flex-col h-screen'> <div className='flex-1 overflow-auto p-4'> {messages.map((message, index) => ( <Message key={index} {...message} /> ))} {isLoading && <TypingIndicator />} </div> <ChatInput onSubmit={handleSubmit} disabled={isLoading} /> </div> ) }
2. 上下文記憶系統(tǒng)
為了讓對(duì)話更連貫,我們實(shí)現(xiàn)了基于向量數(shù)據(jù)庫的上下文記憶系統(tǒng):
// lib/vectorStore.ts import { PineconeClient } from '@pinecone-database/pinecone' import { OpenAIEmbeddings } from 'langchain/embeddings/openai' export class VectorStore { private pinecone: PineconeClient private embeddings: OpenAIEmbeddings constructor() { this.pinecone = new PineconeClient() this.embeddings = new OpenAIEmbeddings() } async initialize() { await this.pinecone.init({ environment: process.env.PINECONE_ENV!, apiKey: process.env.PINECONE_API_KEY! }) } async storeConversation(messages: Message[]) { const index = this.pinecone.Index('conversations') // 將對(duì)話轉(zhuǎn)換為向量 const vectors = await Promise.all( messages.map(async message => { const vector = await this.embeddings.embedQuery(message.content) return { id: message.id, values: vector, metadata: { role: message.role, timestamp: Date.now() } } }) ) // 存儲(chǔ)向量 await index.upsert({ upsertRequest: { vectors } }) } async retrieveContext(query: string, limit = 5) { const index = this.pinecone.Index('conversations') const queryVector = await this.embeddings.embedQuery(query) // 查詢相似向量 const results = await index.query({ queryRequest: { vector: queryVector, topK: limit, includeMetadata: true } }) return results.matches.map(match => ({ content: match.metadata.content, score: match.score })) } }
3. 提示詞優(yōu)化
好的提示詞對(duì) AI 輸出質(zhì)量至關(guān)重要:
// lib/prompts.ts export const createChatPrompt = (context: string, query: string) => ({ messages: [ { role: 'system', content: `你是一個(gè)專業(yè)的AI助手。請(qǐng)基于以下上下文信息, 用簡(jiǎn)潔專業(yè)的語言回答用戶問題。如果問題超出上下文范圍, 請(qǐng)誠實(shí)告知。 上下文信息: ${context} ` }, { role: 'user', content: query } ], temperature: 0.7, // 控制創(chuàng)造性 max_tokens: 1000, // 控制回答長(zhǎng)度 presence_penalty: 0.6, // 鼓勵(lì)話題擴(kuò)展 frequency_penalty: 0.5 // 避免重復(fù) })
性能優(yōu)化
AI 應(yīng)用的性能優(yōu)化主要從這幾個(gè)方面入手:
- 請(qǐng)求優(yōu)化
// hooks/useChat.ts export function useChat() { const [messages, setMessages] = useState<Message[]>([]) // 使用防抖避免頻繁請(qǐng)求 const debouncedChat = useMemo( () => debounce(async (content: string) => { // ... 發(fā)送請(qǐng)求 }, 500), [] ) // 使用緩存避免重復(fù)請(qǐng)求 const cache = useMemo(() => new Map<string, string>(), []) const sendMessage = async (content: string) => { // 檢查緩存 if (cache.has(content)) { setMessages(prev => [...prev, { role: 'assistant', content: cache.get(content)! }]) return } // 發(fā)送請(qǐng)求 await debouncedChat(content) } return { messages, sendMessage } }
- 流式傳輸優(yōu)化:
// lib/streaming.ts export class StreamProcessor { private buffer: string = '' private decoder = new TextDecoder() process(chunk: Uint8Array, callback: (text: string) => void) { this.buffer += this.decoder.decode(chunk, { stream: true }) // 按完整的句子進(jìn)行處理 const sentences = this.buffer.split(/([.!?。?。縘\s)/) if (sentences.length > 1) { // 輸出完整的句子 const completeText = sentences.slice(0, -1).join('') callback(completeText) // 保留未完成的部分 this.buffer = sentences[sentences.length - 1] } } }
部署與監(jiān)控
我們使用了 Vercel 進(jìn)行部署,并建立了完整的監(jiān)控體系:
// lib/monitoring.ts export class AIMonitoring { // 記錄請(qǐng)求延遲 async trackLatency(startTime: number) { const duration = Date.now() - startTime await this.metrics.gauge('ai_request_latency', duration) } // 監(jiān)控令牌使用 async trackTokenUsage(prompt: string, response: string) { const tokenCount = await this.countTokens(prompt + response) await this.metrics.increment('token_usage', tokenCount) } // 監(jiān)控錯(cuò)誤率 async trackError(error: Error) { await this.metrics.increment('ai_errors', 1, { type: error.name, message: error.message }) } }
實(shí)踐心得
開發(fā) AI 應(yīng)用的過程中,我學(xué)到了很多:
- 流式響應(yīng)是提升用戶體驗(yàn)的關(guān)鍵
- 上下文管理要平衡準(zhǔn)確性和性能
- 錯(cuò)誤處理和降級(jí)策略很重要
- 持續(xù)優(yōu)化提示詞能帶來明顯提升
最讓我驚喜的是用戶的反饋。有用戶說:"這是我用過的響應(yīng)最快的 AI 應(yīng)用!"這讓我們備受鼓舞。
寫在最后
AI 應(yīng)用開發(fā)是一個(gè)充滿挑戰(zhàn)但也充滿機(jī)遇的領(lǐng)域。關(guān)鍵是要專注用戶體驗(yàn),不斷優(yōu)化和迭代。正如那句話說的:"AI不是魔法,而是工程。"
到此這篇關(guān)于js開發(fā)一個(gè)類似ChatGPT的AI應(yīng)用助手的文章就介紹到這了,更多相關(guān)js開發(fā)一個(gè)AI應(yīng)用助手內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Web使用webpack構(gòu)建前端項(xiàng)目
本篇文章主要介紹了詳解Web使用webpack構(gòu)建前端項(xiàng)目,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09js根據(jù)當(dāng)前日期獲取前一周或者后一周等日期
有的時(shí)候要獲取當(dāng)前日期,或者前一天、后一天的日期,下面這篇文章主要給大家介紹了關(guān)于js根據(jù)當(dāng)前日期獲取前一周或者后一周等日期的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04JavaScript 網(wǎng)頁中實(shí)現(xiàn)一個(gè)計(jì)算當(dāng)年還剩多少時(shí)間的倒數(shù)計(jì)時(shí)程序
這篇文章主要介紹了JavaScript 網(wǎng)頁中實(shí)現(xiàn)一個(gè)計(jì)算當(dāng)年還剩多少時(shí)間的倒數(shù)計(jì)時(shí)程序,需要的朋友可以參考下2017-01-01js實(shí)現(xiàn)一個(gè)簡(jiǎn)單的數(shù)字時(shí)鐘效果
本文主要介紹了js實(shí)現(xiàn)一個(gè)簡(jiǎn)單的數(shù)字時(shí)鐘效果的示例代碼。具有很好的參考價(jià)值,下面跟著小編一起來看下吧2017-03-03如何讓div span等元素能響應(yīng)鍵盤事件操作指南
在我這幾天的工作中遇到了一個(gè)問題,我有一個(gè)可編輯的div,并且在DIV里面還有一個(gè)可編輯的span,我想要讓span能響應(yīng)鍵盤事,想實(shí)現(xiàn)這種效果,應(yīng)該如何實(shí)踐呢2012-11-11