<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Next.js 項目接入 AI 的利器 —— Vercel AI SDK

          共 20124字,需瀏覽 41分鐘

           ·

          2024-06-19 11:50

             

          大廠技術  高級前端  Node進階

          點擊上方 程序員成長指北,關注公眾號

          回復1,加入高級Node交流群


          ?

          前言

          首先我們花 10 分鐘使用 Next.js 快速部署一個 ChatGPT 聊天網(wǎng)站,效果如下:


          不過巧婦難為無米之炊,首先你要有:

          1. 一個 ChatGPT 3.5 的 API KEY(必須,4.0 也行,修改對應的 model 名就行)
          2. 一個好的網(wǎng)速(非必須,但可能 10 分鐘就搞不定了)

          廢話不多說,讓我們直接開始吧!

          ?
          1. 本篇已收錄到掘金專欄《Next.js 開發(fā)指北》(https://juejin.cn/column/7343569488744611849)

          2. 系統(tǒng)學習 Next.js,歡迎入手小冊《Next.js 開發(fā)指南》?;A篇、實戰(zhàn)篇、源碼篇、面試篇四大篇章帶你系統(tǒng)掌握 Next.js!

          ?

          十分鐘部署版

          使用 Next.js 官方腳手架創(chuàng)建一個新項目:

          npx create-next-app@latest

          運行效果如下:


          為了樣式美觀,我們會用到 Tailwind CSS,所以「注意勾選 Tailwind CSS」,其他隨意。

          為了快速實現(xiàn),我們需要用到 ai 、@ai-sdk/openai 這兩個 npm 包,其中ai 是 Vercel 提供的用于接入 AI 產(chǎn)品、處理流式數(shù)據(jù)的庫, @ai-sdk/openai是 Vercel 基于 openAI 官方提供的 SDK openai 的封裝。

          安裝一下依賴項:

          npm install ai @ai-sdk/openai
          ?

          注:寫這篇文章的時候,ai 版本為 3.1.23, @ai-sdk/openai 版本為 0.0.18,未來 SDK 的用法可能會修改

          ?

          修改 app/page.js,代碼如下:

          'use client';

          import { useChat } from 'ai/react';

          export default function Chat({
            const { messages, input, handleInputChange, handleSubmit } = useChat();
            return (
              <div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
                {messages.map(m => (
                <div key={m.id} className="whitespace-pre-wrap">
                  {m.role === 'user' ? 'User: ' : 'AI: '}
                  {m.content}
                </div>
              ))}

                <form onSubmit={handleSubmit}>
                  <input
                    className="fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl"
                    value={input}
                    placeholder="Say something..."
                    onChange={handleInputChange}
                    />

                </form>
              </div>

            );
          }

          新建 app/api/chat/route.js代碼如下:

          import { createOpenAI } from '@ai-sdk/openai';
          import { streamText } from 'ai';

          const openai = createOpenAI({
            apiKey: process.env.OPENAI_API_KEY || '',
            baseURL"https://api.openai-proxy.com/v1"
          });

          export const maxDuration = 30;

          export async function POST(req{
            const { messages } = await req.json();

            const result = await streamText({
              model: openai('gpt-3.5-turbo'),
              messages,
            });

            return result.toAIStreamResponse();
          }

          新建 .env.local文件,代碼如下:

          OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

          修改 app/globals.css,注釋掉這些部分(為了樣式美觀而已):

          /* :root {
            --foreground-rgb: 0, 0, 0;
            --background-start-rgb: 214, 219, 220;
            --background-end-rgb: 255, 255, 255;
          }

          @media (prefers-color-scheme: dark) {
            :root {
              --foreground-rgb: 255, 255, 255;
              --background-start-rgb: 0, 0, 0;
              --background-end-rgb: 0, 0, 0;
            }
          } */

          命令行運行 npm run dev,瀏覽器無痕模式(為了避免插件等干擾)打開 http://localhost:3000/,運行效果如下:


          接下來我們部署到 Vercel 上。巧婦再次難為無米之炊,你需要一個 Vercel  賬號并全局安裝了 Vercel Cli,具體參考 《實戰(zhàn)篇 | React Notes | Vercel 部署》(https://juejin.cn/book/7307859898316881957/section/7309114840307400714#heading-3)。

          項目根目錄運行:

          vercel

          接下來等待 Vercel 自動部署(大概 1 分鐘左右),交互效果如下:


          此時 Vercel 部署完成。打開 Vercel 平臺查看項目的線上地址:


          部署地址是 https://next-chatgpt-amber.vercel.app/,頁面雖然能訪問,但此時并沒有效果,因為我們還沒有設置我們的環(huán)境變量。

          打開項目的 Settings,添加 OPENAI_API_KEY環(huán)境變量的值,然后點擊 Save 按鈕添加:


          添加后,為了讓環(huán)境變量生效,此時需要 Redeploy 一次:


          此時再訪問 https://next-chatgpt-amber.vercel.app/,已經(jīng)能夠正常運行:


          不過 Vercel 部署的地址默認國內無法訪問,但也有解決方法,參考 《實戰(zhàn)篇 | React Notes | Vercel 部署》(https://juejin.cn/book/7307859898316881957/section/7309114840307400714#heading-3)。

          五分鐘部署版

          是不是感覺還是有點麻煩,沒有關系,還有 5 分鐘部署版。前提是你有 Vercel 賬號以及一個 API KEY。

          打開 next-openai(https://github.com/vercel/ai/tree/main/examples/next-openai),點擊 Deploy 按鈕:


          然后在 Deploy 界面創(chuàng)建一個 GitHub 倉庫,配置一下環(huán)境變量,最后等待部署即可:


          最后獲取一下生產(chǎn)地址:


          這個是 Vercel 提供的 Next.js + OpenAI 的官方模板,除了剛才的例子,還提供了各種示例,也支持 GPT 4,從源碼中也可以看出:


          除了 Next.js + OpenAI,其實 Vercel AI 還提供了其他模板和例子:


          Vercel AI SDK

          如果你要在 Next.js 項目中接入 AI 比如 OpenAI、Anthropic、Google、Mistral 等,尤其要使用 Stream 的時候,雖然可以手動處理,參考:

          1. 《如何用 Next.js v14 實現(xiàn)一個 Streaming 接口?》(https://juejin.cn/post/7344089411983802394#heading-5)
          2. 《Next.js v14 如何實現(xiàn) SSE、接入 ChatGPT Stream?》
            (https://juejin.cn/post/7372020457124659234#heading-11)

          但流的處理是非常讓人頭疼的,Node 有自己的 Stream 同時也支持 Web Stream,各種類型的流牽涉到各種概念和 API,繁瑣的讓人頭疼。就連 Dan 也感到害怕(??):


          所以處理 AI + Stream 的時候,最好是使用 Vercel 的 AI SDK,它針對多個 AI 模型都提供了 Providers,也支持Stream。加上是 Vercel 出品,質量有保證,屬于官方推薦產(chǎn)品,已經(jīng)成為 Next.js 項目接入 AI 的第一選擇。

          本篇就以 OpenAI 為例,為大家講解如何使用 Vercel AI SDK。

          1. 基礎配置

          首先是安裝依賴項:

          npm install ai @ai-sdk/openai

          配置 OpenAI  API Key:

          OPENAI_API_KEY=xxxxxxxxx

          創(chuàng)建一個路由處理程序:

          // app/api/chat/route.ts

          import { openai } from '@ai-sdk/openai';
          import { generateText } from "ai"

          export async function POST(req{
            const { messages } = await req.json();
            const { text } = await generateText({
              model: openai('gpt-3.5-turbo'),
              messages
            })

            return Response.json({ text })
          }

          但如果你在國內調用,因為一些原因,需要配置代理,所以需要寫成這樣:

          import { createOpenAI } from '@ai-sdk/openai';
          import { generateText } from "ai"

          const openai = createOpenAI({
            apiKey: process.env.OPENAI_API_KEY || '',
            baseURL"https://api.openai-proxy.com/v1"
          });

          export async function POST(req{
            const { messages } = await req.json();
            const { text } = await generateText({
              model: openai('gpt-3.5-turbo'),
              messages
            })

            return Response.json({ text })
          }

          2. AI SDK Core

          2.1. 核心函數(shù)

          之前的例子中,我們用的是 ai導出的 generateText 函數(shù),這就是 ai 的核心函數(shù),一共有 4 個:

          1. generateText:生成文本,適合非交互式用例,例如需要編寫文本(例如起草電子郵件或總結網(wǎng)頁)的自動化任務
          2. streamText:生成流文本。適合用于交互式用例,例如聊天機器人和內容流
          3. generateObject:生成結構化對象,很多大模型支持返回結構化對象,比如 OpenAI(在官方文檔搜 “JSON mode”查看具體介紹)
          4. streamObject:生成流式結構化對象

          常用的是 streamText,因為大型語言模型 (LLM) 可能需要長達一分鐘才能完成生成響應,對于聊天機器人這種交互場景來說,這種延遲是不可接受的,用戶希望立刻得到響應,所以使用 Stream 格式很重要。

          streamText 的基本用法如下:

          export async function POST(req{
            const { messages } = await req.json();

            const result = await streamText({
              model: openai('gpt-3.5-turbo'),
              messages,
            });

            return result.toAIStreamResponse();
          }

          2.2. ReadableStream

          其中 result.textStream 是一個 ReadableStream,你可以在瀏覽器或者 Node 中使用:

            const result = await streamText({
              model: openai('gpt-3.5-turbo'),
              messages,
            });

            for await (const textPart of result.textStream) {
              console.log(textPart);
            }

          打印結果如下:


          稍微進階一點的用法,我們可以在實戰(zhàn)中體會。

          新建 app/learn/page.js,代碼如下:

          'use client';

          import { createOpenAI } from '@ai-sdk/openai';
          import { streamText } from 'ai';
          import { useEffect, useState } from 'react';

          const openai = createOpenAI({
            apiKey'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',
            baseURL"https://api.openai-proxy.com/v1"
          });

          const fetch = async (cb) => {
            const result = await streamText({
              model: openai('gpt-3.5-turbo'),
              prompt'如何學習 JavaScript,請詳細描述',
            });

            const reader = result.textStream.getReader();

            reader.read().then(function processText({ done, value }{
              if (done) {
                console.log("Stream complete");
                return;
              }
              cb(value)
              return reader.read().then(processText);
            });
          }

          export default function Chat({

            const [text, setText] = useState('');
            const [charsReceived, setCharsReceived] = useState('');
            const [chunk, setChunk] = useState('');
            
            useEffect(() => {
              fetch((text) => {
                setChunk(text)
                setText((prev) => {
                  const res = prev + text
                  setCharsReceived(res.length)
                  return res
                })
              })
            }, [])

            return (
              <>
                <p>{text}</p>
                <div className="bg-cyan-300 text-xl text-white text-center fixed inset-x-0 bottom-0 p-4">已收到 {charsReceived} 字符。當前片段:{chunk}</div>
              </>

              
            )
          }

          瀏覽器效果如下:


          2.3. 完成回調

          AI SDK 同時提供了完成回調函數(shù):

            const result = await streamText({
              model: openai('gpt-3.5-turbo'),
              messages,
              onFinish({ text, toolCalls, toolResults, finishReason, usage }) {
                console.log(text, finishReason, usage)
              },
            });

          2.4. 輔助函數(shù)

          streamText 的返回對象包含多個輔助函數(shù),以便更輕松地集成到 AI SDK UI 中:

          1. result.toAIStream(): 返回一個 AI stream 對象,可以和 StreamingTextResponse()、 StreamData 一起使用
          2. result.toAIStreamResponse(): 返回一個 AI stream response
          3. result.toTextStreamResponse(): 返回一個普通的文字 stream response
          4. result.pipeTextStreamToResponse(): 將數(shù)據(jù)寫入類似于 Node.js response 的對象
          5. result.pipeAIStreamToResponse(): 將 AI 流數(shù)據(jù)寫入類似于 Node.js response 的對象

          在上面的例子中,我們就是直接使用 result.toAIStreamResponse作為路由處理程序的返回。

          3. AI SDK RSC

          除了 ai,Vercel 針對服務端組件還提供了 ai/rsc,用于在服務端流式返回內容。借助 ai/rsc,你不再需要手動創(chuàng)建 API 接口,可直接使用 Server Actions 完成前后端交互。

          AI SDK RSC 也提供了多個核心函數(shù),就比如用于處理 Stream Value 的 createStreamableValue,它可以將可序列化的 JS 值從服務器流式傳輸?shù)娇蛻舳?,例如字符串、?shù)字、對象和數(shù)組:

          'use server';

          import { createStreamableValue } from 'ai/rsc';

          export const runThread = async () => {
            const streamableStatus = createStreamableValue('thread.init');

            setTimeout(() => {
              streamableStatus.update('thread.run.create');
              streamableStatus.update('thread.run.update');
              streamableStatus.update('thread.run.end');
              streamableStatus.done('thread.end');
            }, 1000);

            return {
              status: streamableStatus.value,
            };
          };

          readStreamableValue 搭配 createStreamableValue 使用,用于在客戶端讀取流式值,它返回一個異步迭代器,該迭代器在更新時生成新值:

          import { readStreamableValue } from 'ai/rsc';
          import { runThread } from '@/actions';

          export default function Page({
            return (
              <button
                onClick={async () =>
           {
                  const { status } = await runThread();

                  for await (const value of readStreamableValue(status)) {
                    console.log(value);
                  }
                }}
              >
                Ask
              </button>

            );
          }

          具體在項目中怎么使用呢?我給大家舉個完整可用的例子。

          修改 app/page.js,代碼如下:

          'use client';

          import { useState } from 'react';
          import { generate } from './actions';
          import { readStreamableValue } from 'ai/rsc';

          export const dynamic = 'force-dynamic';
          export const maxDuration = 30;

          export default function Home({
            const [generation, setGeneration] = useState('');

            return (
              <div>
                <button
                  onClick={async () =>
           {
                    const { output } = await generate('如何學習 JavaScript?');

                    for await (const delta of readStreamableValue(output)) {
                      setGeneration(currentGeneration => `${currentGeneration}${delta}`);
                    }
                  }}
                >
                  如何學習 JavaScript?
                </button>

                <div>{generation}</div>
              </div>

            );
          }

          新建 app/actions.js,代碼如下:

          'use server';

          import { streamText } from 'ai';
          import { createOpenAI } from '@ai-sdk/openai';
          import { createStreamableValue } from 'ai/rsc';

          const openai = createOpenAI({
            apiKey: process.env.OPENAI_API_KEY || '',
            baseURL"https://api.openai-proxy.com/v1"
          });


          export async function generate(input{
            'use server';

            const stream = createStreamableValue('');

            (async () => {
              const { textStream } = await streamText({
                model: openai('gpt-3.5-turbo'),
                prompt: input,
              });

              for await (const delta of textStream) {
                stream.update(delta);
              }

              stream.done();
            })();

            return { output: stream.value };
          }

          瀏覽器效果如下:


          當然 AI SDK RSC 的核心函數(shù)還有流式傳輸 UI 的 createStreamableUI、常用于查詢聊天記錄的 AI and UI State 等,具體查看 https://sdk.vercel.ai/docs/ai-sdk-rsc

          4. AI SDK UI

          Vercel AI SDK UI,雖然名字上帶了 UI,但其實跟框架、UI 都無關,主要是用于簡化前端管理 Stream 和 UI 的過程,更高效的開發(fā)界面。

          主要提供了 3 個 hook:

          1. useChat:對應 OpenAI 的 ChatCompletion,專為生成對話場景設計
          2. useCompletion:對應 OpenAI 的 Completion,是一個通用的自然語言生成接口,支持生成各種類型的文本,包括段落、摘要、建議、答案等等
          3. useAssistant:對應 OpenAI 的 Assistants API

          簡單的來說就是用法基本類似,但背后調用的 OpenAI 的接口有所不同,實現(xiàn)的效果也不同。我們以 useChat 為例:

          'use client';

          import { useChat } from 'ai/react';

          export default function Page({
            const { messages, input, handleInputChange, handleSubmit } = useChat({
              api'api/chat',
            });

            return (
              <>
                {messages.map(message => (
                <div key={message.id}>
                  {message.role === 'user' ? 'User: ' : 'AI: '}
                  {message.content}
                </div>
              ))}
                <form onSubmit={handleSubmit}>
                  <input
                    name="prompt"
                    value={input}
                    onChange={handleInputChange}
                    id="input"
                    />

                  <button type="submit">Submit</button>
                </form>
              </>

            );
          }

          不需要再定義其他狀態(tài),就實現(xiàn)了一個最基本的數(shù)據(jù)提交和數(shù)據(jù)展示。

          除此之外還支持  loading 和 error 狀態(tài):

          const { isLoading, ... } = useChat()

          return <>
            {isLoading ? <Spinner /> : null}
            ...
          const { error, ... } = useChat()

          useEffect(() => {
            if (error) {
              toast.error(error.message)
            }
          }, [error])

          // Or display the error message in the UI:
          return <>
            {error ? <div>{error.message}</div> : null}
            ...

          可大幅簡化前端界面的開發(fā)成本。

          完整的文檔可以參考 https://sdk.vercel.ai/docs/ai-sdk-rsc/overview

          總結

          借助 Vercel AI SDK 可快捷接入 AI 產(chǎn)品,處理流式返回,構建前端界面,堪稱 Next.js 接入 AI 的第一選擇。

          Node 社群

             


          我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。

             “分享、點贊、在看” 支持一下

          瀏覽 108
          1點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          1點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人看人人摸人人操天天看天天摸天天操 | 午夜爆乳 | 中文无码99 | 色姑娘大香蕉 | 玖玖视频免费在线观看 |