paint-brush
Как создать приложение для суммирования веб-страниц с помощью Next.js, OpenAI, LangChain и Supabaseк@nassermaronie
6,088 чтения
6,088 чтения

Как создать приложение для суммирования веб-страниц с помощью Next.js, OpenAI, LangChain и Supabase

к Nasser Maronie13m2024/06/27
Read on Terminal Reader

Слишком долго; Читать

В этой статье мы покажем вам, как создать удобное веб-приложение, которое может суммировать содержимое любой веб-страницы. Используя [Next.js] для бесперебойной и быстрой работы в Интернете, [LangChain] для языка обработки, [OpenAI](https://openai.com/) для создания сводок и [Supabase] для управления и хранения векторных данных, мы вместе создадим мощный инструмент.
featured image - Как создать приложение для суммирования веб-страниц с помощью Next.js, OpenAI, LangChain и Supabase
Nasser Maronie HackerNoon profile picture

Приложение, которое может понять контекст любой веб-страницы.

В этой статье мы покажем вам, как создать удобное веб-приложение, которое может суммировать содержимое любой веб-страницы. Используя Next.js для бесперебойной и быстрой работы в Интернете, LangChain для обработки языка, OpenAI для создания сводок и Supabase для управления и хранения векторных данных, мы вместе создадим мощный инструмент.



Почему мы это строим

Мы все сталкиваемся с информационной перегрузкой из-за большого количества контента в Интернете. Создавая приложение, предоставляющее быстрые сводки, мы помогаем людям экономить время и оставаться в курсе событий. Независимо от того, являетесь ли вы занятым работником, студентом или просто человеком, который хочет быть в курсе новостей и статей, это приложение станет для вас полезным инструментом.

Как это будет

Наше приложение позволит пользователям вводить любой URL-адрес веб-сайта и быстро получать краткую информацию о странице. Это означает, что вы можете понять основные моменты длинных статей, сообщений в блогах или исследовательских работ, не читая их полностью.

Потенциал и влияние

Это приложение для обобщения может быть полезным во многих отношениях. Это может помочь исследователям просматривать научные статьи, держать любителей новостей в курсе и многое другое. Кроме того, разработчики могут использовать это приложение для создания еще более полезных функций.


Технические стеки

Next.js

Next.js — это мощная и гибкая среда React, разработанная Vercel, которая позволяет разработчикам с легкостью создавать серверный рендеринг (SSR) и статические веб-приложения. Он сочетает в себе лучшие функции React с дополнительными возможностями для создания оптимизированных и масштабируемых веб-приложений.

ОпенАИ

Модуль OpenAI в Node.js предоставляет способ взаимодействия с API OpenAI, позволяя разработчикам использовать мощные языковые модели, такие как GPT-3 и GPT-4. Этот модуль позволяет вам интегрировать расширенные функции искусственного интеллекта в ваши приложения Node.js.

LangChain.js

LangChain — мощная платформа, предназначенная для разработки приложений с языковыми моделями. Первоначально разработанный для Python, с тех пор он был адаптирован для других языков, включая Node.js. Вот обзор LangChain в контексте Node.js:

Что такое Лангчейн?

LangChain — библиотека, упрощающая создание приложений с использованием больших языковых моделей (LLM) . Он предоставляет инструменты для управления LLM и их интеграции в ваши приложения, обработки цепочки вызовов этих моделей и с легкостью обеспечивает сложные рабочие процессы.

Как работают модели большого языка (LLM)?

Модели больших языков (LLM), такие как GPT-3.5 от OpenAI, обучаются на огромных объемах текстовых данных, чтобы понимать и генерировать текст, похожий на человеческий. Они могут генерировать ответы, переводить языки и выполнять множество других задач по обработке естественного языка.

Супабаза

Supabase — это платформа BaaS с открытым исходным кодом, предназначенная для того, чтобы помочь разработчикам быстро создавать и развертывать масштабируемые приложения. Он предлагает набор инструментов и сервисов, которые упрощают управление базами данных, аутентификацию, хранение и возможности работы в реальном времени, и все это построено на базе PostgreSQL.


Предварительные условия

Прежде чем мы начнем, убедитесь, что у вас есть следующее:

  • Node.js и npm установлены.
  • Учетная запись Supabase
  • Аккаунт OpenAI

Шаг 1: Настройка Supabase

Сначала нам нужно настроить проект Supabase и создать необходимые таблицы для хранения наших данных.

Создать проект супабазы

  1. Перейдите на Supabase и зарегистрируйте учетную запись.


  2. Создайте новый проект и запишите URL-адрес Supabase и ключ API. Они понадобятся вам позже.

SQL-скрипт для Supabase

Создайте новый SQL-запрос на панели управления Supabase и запустите следующие сценарии для создания необходимых таблиц и функций:

Сначала создайте расширение для нашего векторного хранилища, если оно еще не существует:

 create extension if not exists vector;


Затем создайте таблицу с именем «документы». Эта таблица будет использоваться для хранения и встраивания содержимого веб-страницы в векторном формате:

 create table if not exists documents ( id bigint primary key generated always as identity, content text, metadata jsonb, embedding vector(1536) );


Теперь нам нужна функция для запроса наших встроенных данных:

 create or replace function match_documents ( query_embedding vector(1536), match_count int default null, filter jsonb default '{}' ) returns table ( id bigint, content text, metadata jsonb, similarity float ) language plpgsql as $$ begin return query select id, content, metadata, 1 - (documents.embedding <=> query_embedding) as similarity from documents where metadata @> filter order by documents.embedding <=> query_embedding limit match_count; end; $$;


Далее нам нужно настроить таблицу для хранения сведений о веб-странице:

 create table if not exists files ( id bigint primary key generated always as identity, url text not null, created_at timestamp with time zone default timezone('utc'::text, now()) not null );

Шаг 2. Настройка OpenAI

Создать проект OpenAI


  • Перейдите к API: после входа в систему перейдите в раздел API и создайте новый ключ API. Обычно это доступно из панели управления.

Шаг 3. Настройка Next.js

Создать приложение Next.js

 $ npx create-next-app summarize-page $ cd ./summarize-page


Установите необходимые зависимости:

 npm install @langchain/community @langchain/core @langchain/openai @supabase/supabase-js langchain openai axios


Затем мы установим Material UI для создания нашего интерфейса; не стесняйтесь использовать другую библиотеку:

 npm install @mui/material @emotion/react @emotion/styled

Шаг 4. Клиенты OpenAI и Supabase

Далее нам нужно настроить клиенты OpenAI и Supabase. Создайте каталог libs в своем проекте и добавьте следующие файлы.

src/libs/openAI.ts

Этот файл будет настраивать клиент OpenAI.

 import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai"; const openAIApiKey = process.env.OPENAI_API_KEY; if (!openAIApiKey) throw new Error('OpenAI API Key not found.') export const llm = new ChatOpenAI({ openAIApiKey, modelName: "gpt-3.5-turbo", temperature: 0.9, }); export const embeddings = new OpenAIEmbeddings( { openAIApiKey, }, { maxRetries: 0 } );
  • llm : экземпляр языковой модели, который будет генерировать наши сводки.


  • embeddings : для наших документов будут созданы вложения, которые помогут находить похожий контент.

src/libs/supabaseClient.ts

Этот файл будет настраивать клиент Supabase.

 import { createClient } from "@supabase/supabase-js"; const supabaseUrl = process.env.SUPABASE_URL || ""; const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || ""; if (!supabaseUrl) throw new Error("Supabase URL not found."); if (!supabaseAnonKey) throw new Error("Supabase Anon key not found."); export const supabaseClient = createClient(supabaseUrl, supabaseAnonKey);
  • supabaseClient : экземпляр клиента Supabase для взаимодействия с нашей базой данных Supabase.

Шаг 5. Создание сервисов для контента и файлов

Создайте каталог services и добавьте следующие файлы для обработки получения содержимого и управления файлами.

src/services/content.ts

Эта служба получит содержимое веб-страницы и очистит его, удалив HTML-теги, сценарии и стили.

 import axios from "axios"; export async function getContent(url: string): Promise<string> { let htmlContent: string = ""; const response = await axios.get(url as string); htmlContent = response.data; if (!htmlContent) return ""; // Remove unwanted elements and tags return htmlContent .replace(/style="[^"]*"/gi, "") .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "") .replace(/\s*on\w+="[^"]*"/gi, "") .replace( /<script(?![^>]*application\/ld\+json)[^>]*>[\s\S]*?<\/script>/gi, "" ) .replace(/<[^>]*>/g, "") .replace(/\s+/g, " "); }

Эта функция извлекает HTML-содержимое заданного URL-адреса и очищает его, удаляя стили, сценарии и HTML-теги.

src/services/file.ts

Этот сервис сохранит содержимое веб-страницы в Supabase и получит сводки.

 import { embeddings, llm } from "@/libs/openAI"; import { supabaseClient } from "@/libs/supabaseClient"; import { SupabaseVectorStore } from "@langchain/community/vectorstores/supabase"; import { StringOutputParser } from "@langchain/core/output_parsers"; import { ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate, } from "@langchain/core/prompts"; import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables"; import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; import { formatDocumentsAsString } from "langchain/util/document"; export interface IFile { id?: number | undefined; url: string; created_at?: Date | undefined; } export async function saveFile(url: string, content: string): Promise<IFile> { const doc = await supabaseClient .from("files") .select() .eq("url", url) .single<IFile>(); if (!doc.error && doc.data?.id) return doc.data; const { data, error } = await supabaseClient .from("files") .insert({ url }) .select() .single<IFile>(); if (error) throw error; const splitter = new RecursiveCharacterTextSplitter({ separators: ["\n\n", "\n", " ", ""], }); const output = await splitter.createDocuments([content]); const docs = output.map((d) => ({ ...d, metadata: { ...d.metadata, file_id: data.id }, })); await SupabaseVectorStore.fromDocuments(docs, embeddings, { client: supabaseClient, tableName: "documents", queryName: "match_documents", }); return data; } export async function getSummarization(fileId: number): Promise<string> { const vectorStore = await SupabaseVectorStore.fromExistingIndex(embeddings, { client: supabaseClient, tableName: "documents", queryName: "match_documents", }); const retriever = vectorStore.asRetriever({ filter: (rpc) => rpc.filter("metadata->>file_id", "eq", fileId), k: 2, }); const SYSTEM_TEMPLATE = `Use the following pieces of context, explain what is it about and summarize it. If you can't explain it, just say that you don't know, don't try to make up some explanation. ---------------- {context}`; const messages = [ SystemMessagePromptTemplate.fromTemplate(SYSTEM_TEMPLATE), HumanMessagePromptTemplate.fromTemplate("{format_answer}"), ]; const prompt = ChatPromptTemplate.fromMessages(messages); const chain = RunnableSequence.from([ { context: retriever.pipe(formatDocumentsAsString), format_answer: new RunnablePassthrough(), }, prompt, llm, new StringOutputParser(), ]); const format_summarization = ` Give it title, subject, description, and the conclusion of the context in this format, replace the brackets with the actual content: [Write the title here] By: [Name of the author or owner or user or publisher or writer or reporter if possible, otherwise leave it "Not Specified"] [Write the subject, it could be a long text, at least minimum of 300 characters] ---------------- [Write the description in here, it could be a long text, at least minimum of 1000 characters] Conclusion: [Write the conclusion in here, it could be a long text, at least minimum of 500 characters] `; const summarization = await chain.invoke(format_summarization); return summarization; }
  • saveFile : сохраняет файл и его содержимое в Supabase, разбивает содержимое на управляемые фрагменты и сохраняет их в векторном хранилище.


  • getSummarization : извлекает соответствующие документы из векторного хранилища и генерирует сводку с помощью OpenAI.

Шаг 6. Создание обработчика API

Теперь давайте создадим обработчик API для обработки контента и создания сводки.

pages/api/content.ts

 import { getContent } from "@/services/content"; import { getSummarization, saveFile } from "@/services/file"; import { NextApiRequest, NextApiResponse } from "next"; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { if (req.method !== "POST") return res.status(404).json({ message: "Not found" }); const { body } = req; try { const content = await getContent(body.url); const file = await saveFile(body.url, content); const result = await getSummarization(file.id as number); res.status(200).json({ result }); } catch (err) { res.status( 500).json({ error: err }); } }

Этот обработчик API получает URL-адрес, извлекает контент, сохраняет его в Supabase и генерирует сводку. Он обрабатывает функции saveFile и getSummarization из наших сервисов.


Шаг 7: Создание внешнего интерфейса

Наконец, давайте создадим интерфейс в src/pages/index.tsx чтобы пользователи могли вводить URL-адреса и отображать сводные данные.

src/pages/index.tsx

 import axios from "axios"; import { useState } from "react"; import { Alert, Box, Button, Container, LinearProgress, Stack, TextField, Typography, } from "@mui/material"; export default function Home() { const [loading, setLoading] = useState(false); const [url, setUrl] = useState(""); const [result, setResult] = useState(""); const [error, setError] = useState<any>(null); const onSubmit = async () => { try { setError(null); setLoading(true); const res = await axios.post("/api/content", { url }); setResult(res.data.result); } catch (err) { console.error("Failed to fetch content", err); setError(err as any); } finally { setLoading(false); } }; return ( <Box sx={{ height: "100vh", overflowY: "auto" }}> <Container sx={{ backgroundColor: (theme) => theme.palette.background.default, position: "sticky", top: 0, zIndex: 2, py: 2, }} > <Typography sx={{ mb: 2, fontSize: "24px" }}> Summarize the content of any page </Typography> <TextField fullWidth label="Input page's URL" value={url} onChange={(e) => { if (result) setResult(""); setUrl(e.target.value); }} sx={{ mb: 2 }} /> <Button disabled={loading} variant="contained" onClick={onSubmit} > Summarize </Button> </Container> <Container maxWidth="lg" sx={{ py: 2 }}> {loading ? ( <LinearProgress /> ) : ( <Stack sx={{ gap: 2 }}> {result && ( <Alert> <Typography sx={{ whiteSpace: "pre-line", wordBreak: "break-word", }} > {result} </Typography> </Alert> )} {error && <Alert severity="error">{error.message || error}</Alert>} </Stack> )} </Container> </Box> ); }

Этот компонент React позволяет пользователям вводить URL-адрес, отправлять его и отображать сгенерированную сводку. Он обрабатывает состояния загрузки и сообщения об ошибках, чтобы обеспечить лучшее взаимодействие с пользователем.


Шаг 8: Запуск приложения

Создайте файл .env в корне вашего проекта для хранения переменных среды:

 SUPABASE_URL=your-supabase-url SUPABASE_ANON_KEY=your-supabase-anon-key OPENAI_API_KEY=your-openai-api-key


Наконец, запустите приложение Next.js:

 npm run dev


Теперь у вас должно быть работающее приложение, в котором вы можете ввести URL-адрес веб-страницы и получить сводные ответы страницы.


Заключение

Поздравляем! Вы создали полнофункциональное приложение для обобщения веб-страниц, используя Next.js, OpenAI, LangChain и Supabase. Пользователи могут ввести URL-адрес, получить контент, сохранить его в Supabase и создать сводку, используя возможности OpenAI. Эта настройка обеспечивает надежную основу для дальнейших улучшений и настройки в соответствии с вашими потребностями.


Не стесняйтесь расширять этот проект, добавляя дополнительные функции, улучшая пользовательский интерфейс или интегрируя дополнительные API.

Проверьте исходный код в этом репо:

https://github.com/firstpersoncode/summarize-page


Приятного кодирования!